Subscribe takes precedence over all other idempotency checks - if a subscribed resource changed, the command executes regardless of creates file existence or guard command results.
Exit Code Validation
By default, exit code 0 indicates success. The returns property customizes acceptable codes:
Queries current state normally (checks creates file)
Evaluates guard commands (onlyif/unless) - these run even in noop mode
Evaluates subscribe triggers
Logs what actions would be taken
Sets appropriate NoopMessage:
“Would have executed”
“Would have executed via subscribe”
Reports Changed: true if execution would occur
Does not call provider Execute method
Desired State Validation
After execution (in non-noop mode), the type verifies success:
func (t*Type) isDesiredState(properties, status) bool {
// Creates file check takes precedenceifproperties.Creates!=""&&status.CreatesSatisfied {
returntrue }
// Guard checks only apply before execution (ExitCode is nil)ifstatus.ExitCode==nil {
ifproperties.OnlyIf!=""&& !status.OnlyIfSatisfied {
returntrue// onlyif guard failed, don't run }
ifproperties.Unless!=""&&status.UnlessSatisfied {
returntrue// unless guard succeeded, don't run }
}
// Refresh-only without execution is stableifstatus.ExitCode==nil&&properties.RefreshOnly {
returntrue }
// Check exit code against acceptable returnsreturns:= []int{0}
if len(properties.Returns) > 0 {
returns = properties.Returns }
ifstatus.ExitCode!=nil {
returnslices.Contains(returns, *status.ExitCode)
}
returnfalse}
Guard checks are gated on ExitCode == nil because after execution, the exit code determines success. The post-execution isDesiredState() call must not re-evaluate guards, which would produce incorrect results since guard state is only set on initialStatus.
If the exit code is not in the acceptable returns list, an ErrDesiredStateFailed error is returned.
Command vs Name
The command property is optional. If not specified, the name is used as the command:
# These are equivalent:- exec:
- /usr/bin/myapp --config /etc/myapp.conf:
- exec:
- run-myapp:
command: /usr/bin/myapp --config /etc/myapp.conf
Using a descriptive name with explicit command is recommended for clarity.
Environment and Path
Commands can be configured with custom environment:
This document describes the implementation details of the Posix exec provider for executing commands without a shell.
Provider Selection
The Posix provider is the default exec provider. It is always available and returns priority 1 for all exec resources unless a different provider is explicitly requested via the provider property.
To use the shell provider instead, specify provider: shell in the resource properties.
Comparison with Shell Provider
Feature
Posix
Shell
Shell invocation
No
Yes (/bin/sh -c)
Pipes (|)
Not supported
Supported
Redirections (>, <)
Not supported
Supported
Shell builtins (cd, export)
Not supported
Supported
Glob expansion
Not supported
Supported
Command substitution ($(...))
Not supported
Supported
Argument parsing
shellquote.Split()
Passed as single string
Security
Lower attack surface
Shell injection possible
When to use Posix (default):
Simple commands with arguments
When shell features are not needed
For better security (no shell injection risk)
When to use Shell:
Commands with pipes, redirections, or shell builtins
Complex command strings
When shell expansion is required
Operations
Execute
Process:
Determine command source (Command property or Name if Command is empty)
Parse command string into words using shellquote.Split()
Extract command (first word) and arguments (remaining words)
Execute via CommandRunner.ExecuteWithOptions()
Optionally log output line-by-line if LogOutput is enabled
Command Parsing:
The command string is parsed using github.com/kballard/go-shellquote, which handles:
Syntax
Example
Result
Simple words
echo hello world
["echo", "hello", "world"]
Single quotes
echo 'hello world'
["echo", "hello world"]
Double quotes
echo "hello world"
["echo", "hello world"]
Escaped spaces
echo hello\ world
["echo", "hello world"]
Mixed quoting
echo "it's a test"
["echo", "it's a test"]
Execution Options:
Option
Source
Description
Command
First word after parsing
Executable path or name
Args
Remaining words
Command arguments
Cwd
properties.Cwd
Working directory
Environment
properties.Environment
Additional env vars (KEY=VALUE format)
Path
properties.Path
Search path for executables
Timeout
properties.ParsedTimeout
Maximum execution time
Output Logging:
When LogOutput: true is set and a user logger is provided:
The model validates exec properties before execution:
Property
Validation
name
Must be parseable by shellquote (balanced quotes)
timeout
Must be valid duration format (e.g., 30s, 5m)
subscribe
Each entry must be type#name format
path
Each directory must be absolute (start with /)
environment
Each entry must be KEY=VALUE format with non-empty key and value
Platform Support
The Posix provider works on all platforms supported by Go’s os/exec package. It does not use any platform-specific system calls directly.
The command runner (model.CommandRunner) handles the actual process execution, which may have platform-specific implementations.
Security Considerations
No Shell Injection
Unlike the shell provider, the posix provider does not invoke a shell. Arguments are passed directly to the executable, preventing shell injection attacks:
# Safe with posix provider - $USER is passed literally, not expanded- exec:
- /bin/echo $USER:
provider: posix # Default# Potentially dangerous with shell provider - $USER is expanded- exec:
- /bin/echo $USER:
provider: shell
Path Validation
The path property only accepts absolute directory paths, preventing path traversal via relative paths.
Environment Validation
Environment variables must have non-empty keys and values, preventing injection of empty or malformed entries.
Shell Provider
This document describes the implementation details of the Shell exec provider for executing commands via /bin/sh.
Provider Selection
The Shell provider is selected when provider: shell is explicitly specified in the resource properties. It has a lower priority (99) than the Posix provider (1), so it is never automatically selected.
Availability: The provider checks for the existence of /bin/sh via util.FileExists(). If /bin/sh does not exist, the provider is not available.
Comparison with Posix Provider
Feature
Shell
Posix
Shell invocation
Yes (/bin/sh -c)
No
Pipes (|)
Supported
Not supported
Redirections (>, <, >>)
Supported
Not supported
Shell builtins (cd, export, source)
Supported
Not supported
Glob expansion (*.txt, ?)
Supported
Not supported
Command substitution ($(...), `...`)
Supported
Not supported
Variable expansion ($VAR, ${VAR})
Supported
Not supported
Logical operators (&&, ||)
Supported
Not supported
Argument parsing
Passed as single string
shellquote.Split()
Security
Shell injection possible
Lower attack surface
When to use Shell:
Commands with pipes: cat file.txt | grep pattern | sort
Commands with redirections: echo "data" > /tmp/file
Commands with shell builtins: cd /tmp && pwd
Commands with variable expansion: echo $HOME
Complex one-liners with logical operators
When to use Posix (default):
Simple commands with arguments
When shell features are not needed
For better security (no shell injection risk)
Operations
Execute
Process:
Determine command source (Command property or Name if Command is empty)
Validate command is not empty
Execute via CommandRunner.ExecuteWithOptions() with /bin/sh -c "<command>"
Optionally log output line-by-line if LogOutput is enabled
Execution Method:
The entire command string is passed to the shell as a single argument:
/bin/sh -c "<entire command string>"
This allows the shell to interpret all shell syntax, including:
Pipes and redirections
Variable expansion
Glob patterns
Command substitution
Logical operators
Execution Options:
Option
Value
Description
Command
/bin/sh
Shell executable path
Args
["-c", "<command>"]
Shell flag and command string
Cwd
properties.Cwd
Working directory
Environment
properties.Environment
Additional env vars (KEY=VALUE format)
Path
properties.Path
Search path for executables
Timeout
properties.ParsedTimeout
Maximum execution time
Output Logging:
When LogOutput: true is set and a user logger is provided:
The shell provider uses the same idempotency mechanisms as the posix provider:
Creates File
If creates is specified and the file exists, the command does not run:
- exec:
- extract-archive:
command: cd /opt && tar -xzf /tmp/app.tar.gzprovider: shellcreates: /opt/app/bin/app
Guard Commands
If onlyif is specified, the command only runs when the guard exits 0. If unless is specified, the command only runs when the guard exits non-zero. Guard commands are executed via /bin/sh -c and can use shell features: