zester
Guides

Overview

Zester ships with built-in state modules for common configuration management tasks. Each module implements the State interface with Check, Apply, and Revert methods.

Modules are registered in a Registry at startup. The runner instantiates states from YAML configuration by looking up the module name (e.g., file.managed) in the registry and passing the state ID and parameter map to the module's builder function.


Available Modules

State Modules

State modules implement the full Check/Apply/Revert lifecycle and participate in the DAG execution engine.

ModuleDescriptionSource
file.managedEnsure a file exists with desired content, permissions, and ownershippkg/state/modules/file.go
file.directoryEnsure a directory exists with desired permissions and ownershippkg/state/modules/file_directory.go
file.absentEnsure a file or directory does not existpkg/state/modules/file_absent.go
file.appendEnsure lines are present in a filepkg/state/modules/file_append.go
file.symlinkEnsure a symbolic link exists pointing to a targetpkg/state/modules/file_symlink.go
file.recurseRecursively copy a directory with permissionspkg/state/modules/file_recurse.go
file.blockreplaceManage content between marker lines in a filepkg/state/modules/file_blockreplace.go
pkg.installedEnsure a system package is installed (apt, yum, dnf, brew)pkg/state/modules/pkg.go
pkg.removedEnsure a system package is not installedpkg/state/modules/pkg_removed.go
service.runningEnsure a service is running (and optionally enabled)pkg/state/modules/service.go
service.enabledEnsure a service is enabled at bootpkg/state/modules/service.go
service.deadEnsure a service is stopped (and optionally disabled)pkg/state/modules/service.go
cmd.runExecute a command on the target systempkg/state/modules/cmd.go
user.presentEnsure a user account exists with desired propertiespkg/state/modules/user.go
user.absentEnsure a user account does not existpkg/state/modules/user.go
group.presentEnsure a group exists with desired propertiespkg/state/modules/group.go
group.absentEnsure a group does not existpkg/state/modules/group.go
cron.presentEnsure a cron job existspkg/state/modules/cron.go
cron.absentEnsure a cron job does not existpkg/state/modules/cron.go
sysctl.presentEnsure a kernel parameter is set (and persisted)pkg/state/modules/sysctl.go
mount.mountedEnsure a filesystem is mounted (and in fstab)pkg/state/modules/mount.go
git.clonedEnsure a Git repository is cloned and at the desired revisionpkg/state/modules/git.go
git.latestEnsure a Git clone exists and is updated to the latest revisionpkg/state/modules/git_latest.go
pip.installedEnsure a Python package is installed via pippkg/state/modules/pip.go
timezone.systemEnsure the system timezone is setpkg/state/modules/timezone.go
locale.presentEnsure a system locale is generatedpkg/state/modules/locale.go
file.lineManage a single line in a file (ensure/replace/insert/delete)pkg/state/modules/file_line.go
file.replaceRegexp search/replace in a file (Salt MULTILINE default)pkg/state/modules/file_replace.go
file.comment / file.uncommentComment or uncomment lines matching a regexpkg/state/modules/file_comment.go
file.keyvalueEnsure key=value lines (sysctl/os-release style)pkg/state/modules/file_keyvalue.go
file.copyCopy a file, with force/makedirs/preservepkg/state/modules/file_copy.go
file.touchCreate an empty file / update mtime on force-applypkg/state/modules/file_touch.go
pkg.latestEnsure a package is installed at the newest available versionpkg/state/modules/pkg_latest.go
pkg.purgedRemove a package and its configurationpkg/state/modules/pkg_purged.go
pkgrepo.managedManage an apt/yum package repository definitionpkg/state/modules/pkgrepo.go
archive.extractedExtract a tar/zip archive (local path or URL)pkg/state/modules/archive.go
host.present / host.absentManage /etc/hosts entriespkg/state/modules/host.go
ssh_auth.present / ssh_auth.absentManage a key in a user's authorized_keyspkg/state/modules/ssh_auth.go
module.runInvoke another state module by name (Salt escape hatch)pkg/state/modules/module_run.go
test.pingLiveness check that always succeedspkg/state/modules/test_ping.go
test.nop / test.fail_without_changes / test.succeed_with_changes / test.configurable_test_stateSalt test helperspkg/state/modules/test_extra.go

Every state also accepts the generic Salt-parity attributes onlyif, unless, order, retry, failhard, names, and the full requisite set (require, watch, onchanges, onfail, prereq, listen and their _in inverses). See Dependencies & Requisites and the Salt Compatibility guide.

Query Modules

Query modules bypass the state registry and runner. They are handled as special cases in the peel's exec handler because they need direct access to the facts manager or settings resolver. See Query Modules for details.

ModuleDescription
facts.items / facts.get / facts.keysQuery the peel's in-memory facts
facts.setSet a custom fact on the peel
settings.items / settings.get / settings.keysQuery the peel's resolved settings

Execution Modes

Every state module can be used in two ways:

Direct Execution (Salt-Style)

Run a module directly against peels from the CLI, without writing a state file:

zester '<target>' <module> [id] [key=value ...]

This is equivalent to Salt's salt '<target>' <module> [args]. The first positional argument becomes the state ID (and therefore the primary parameter, e.g. the package name or file path); the remaining key=value pairs become the module's config. Add --test (or test=True) for a dry run that reports what would change without applying.

The peel also ships a set of imperative remote-execution functions (pkg.version, pkg.list_pkgs, service.status, service.start, service.stop, service.restart, disk.usage, grains.item, grains.items, test.echo, test.version, test.true, test.false, sys.list_functions) defined in pkg/execmod/builtins.go. These have no Check/Apply lifecycle — they just run and return output. A module name that matches a remote-execution function and is not a registered state module is dispatched there; state module names (like cmd.run) always win.

State Files

Define the desired state declaratively in a .zy YAML file, then apply it:

zester '<target>' state.apply <state-name>

Both modes use the same underlying modules and parameters.


Module Registry

All built-in state modules are registered in the Registry at peel startup. Each module uses a closure-based builder factory that captures the ModuleContext and returns a Builder:

// Builder type: receives state ID and YAML config, returns a State
type Builder func(id string, config map[string]any) (State, error)

// Example: NewPkgInstalledBuilder captures ModuleContext in a closure
func NewPkgInstalledBuilder(mctx *exec.ModuleContext) state.Builder {
    return func(id string, config map[string]any) (state.State, error) {
        // ... uses mctx.Package internally
    }
}

The registry maps module names to these builders:

Registry (47 modules)
  "file.managed"      -> NewFileManagedBuilder(mctx)       -> State
  "file.directory"    -> NewFileDirectoryBuilder(mctx)     -> State
  "file.absent"       -> NewFileAbsentBuilder(mctx)        -> State
  "file.append"       -> NewFileAppendBuilder(mctx)        -> State
  "file.symlink"      -> NewFileSymlinkBuilder(mctx)       -> State
  "file.recurse"      -> NewFileRecurseBuilder(mctx)       -> State
  "file.blockreplace" -> NewFileBlockReplaceBuilder(mctx)  -> State
  "file.line"         -> NewFileLineBuilder(mctx)          -> State
  "file.replace"      -> NewFileReplaceBuilder(mctx)       -> State
  "file.comment"      -> NewFileCommentBuilder(mctx)       -> State
  "file.uncomment"    -> NewFileUncommentBuilder(mctx)     -> State
  "file.keyvalue"     -> NewFileKeyValueBuilder(mctx)      -> State
  "file.copy"         -> NewFileCopyBuilder(mctx)          -> State
  "file.touch"        -> NewFileTouchBuilder(mctx)         -> State
  "pkg.installed"     -> NewPkgInstalledBuilder(mctx)      -> State
  "pkg.removed"       -> NewPkgRemovedBuilder(mctx)        -> State
  "pkg.latest"        -> NewPkgLatestBuilder(mctx)         -> State
  "pkg.purged"        -> NewPkgPurgedBuilder(mctx)         -> State
  "pkgrepo.managed"   -> NewPkgrepoManagedBuilder(mctx)    -> State
  "service.running"   -> NewSvcRunningBuilder(mctx)        -> State
  "service.enabled"   -> NewSvcEnabledBuilder(mctx)        -> State
  "service.dead"      -> NewSvcDeadBuilder(mctx)           -> State
  "cmd.run"           -> NewCmdRunBuilder(mctx)            -> State
  "user.present"      -> NewUserPresentBuilder(mctx)       -> State
  "user.absent"       -> NewUserAbsentBuilder(mctx)        -> State
  "group.present"     -> NewGroupPresentBuilder(mctx)      -> State
  "group.absent"      -> NewGroupAbsentBuilder(mctx)       -> State
  "cron.present"      -> NewCronPresentBuilder(mctx)       -> State
  "cron.absent"       -> NewCronAbsentBuilder(mctx)        -> State
  "sysctl.present"    -> NewSysctlPresentBuilder(mctx)     -> State
  "mount.mounted"     -> NewMountMountedBuilder(mctx)      -> State
  "git.cloned"        -> NewGitClonedBuilder(mctx)         -> State
  "git.latest"        -> NewGitLatestBuilder(mctx)         -> State
  "archive.extracted" -> NewArchiveExtractedBuilder(mctx)  -> State
  "host.present"      -> NewHostPresentBuilder(mctx)       -> State
  "host.absent"       -> NewHostAbsentBuilder(mctx)        -> State
  "ssh_auth.present"  -> NewSSHAuthPresentBuilder(mctx)    -> State
  "ssh_auth.absent"   -> NewSSHAuthAbsentBuilder(mctx)     -> State
  "pip.installed"     -> NewPipInstalledBuilder(mctx)      -> State
  "timezone.system"   -> NewTimezoneSystemBuilder(mctx)    -> State
  "locale.present"    -> NewLocalePresentBuilder(mctx)     -> State
  "module.run"        -> NewModuleRunBuilder(registry)     -> State
  "test.ping"         -> NewTestPing                       -> State
  "test.nop"          -> NewTestNop                        -> State
  "test.fail_without_changes"    -> NewTestFailWithoutChanges    -> State
  "test.succeed_with_changes"    -> NewTestSucceedWithChanges    -> State
  "test.configurable_test_state" -> NewTestConfigurableTestState -> State

Custom modules can be added by calling Registry.Register() with a module name and a Builder function.


Common Parameters

All state modules accept the full requisite set (require, watch, onchanges, onfail, prereq, listen, and their _in inverses) plus the generic Salt-parity attributes (onlyif, unless, order, retry, failhard, names). These are handled outside the module — by the compiler and the runner — so no module documents them individually.

See Dependencies & Requisites for the full list and semantics.

On this page