Documentation
¶
Overview ¶
Package playbook implements Ansible's playbook execution model: plays over an inventory pattern, tasks with when/loop/register/notify/ block-rescue-always, and handlers — wired to github.com/go-ansible/{inventory,vars,template,modules,facts} and github.com/go-remoteexec/transport.
Index ¶
- func ConfigFilePath() string
- func ConfigFileValueForEnv(envVar string) (string, bool)
- func DefaultConnect(ctx context.Context, hostName string, hostVars map[string]any) (remoteexec.Connection, error)
- type ConfigSetting
- type Connector
- type Engine
- type HostSummary
- type Play
- type PlayResult
- type Playbook
- type Result
- type RoleRef
- type RunResult
- type Task
- type VarPrompt
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ConfigFilePath ¶ added in v0.4.1
func ConfigFilePath() string
ConfigFilePath returns the ansible.cfg go-ansible would read from — see findConfigFile — or "" if none of the usual locations has one. go-ansible/cli's ansible-config uses this for `view` and to report each setting's real origin (env var / config file / compiled default) in `dump`, rather than guessing from whether the resolved value merely differs from the default.
func ConfigFileValueForEnv ¶ added in v0.4.1
ConfigFileValueForEnv reports whether the ansible.cfg go-ansible would read from actually sets the [defaults] key matching envVar (e.g. "ANSIBLE_REMOTE_USER" -> "remote_user") — go-ansible/cli's ansible-config dump uses this to report a setting's real origin rather than guessing "cfg" just because some config file happens to exist, whether or not it sets that particular key.
func DefaultConnect ¶
func DefaultConnect(ctx context.Context, hostName string, hostVars map[string]any) (remoteexec.Connection, error)
DefaultConnect implements Ansible's connection-variable conventions: ansible_connection ("local" or "ssh", default "ssh" — "smart" is treated as ssh, this package has no separate paramiko/openssh split to be smart about), ansible_host, ansible_port (default 22), ansible_user, ansible_password / ansible_ssh_pass, ansible_ssh_private_key_file, ansible_host_key_checking, ansible_ssh_timeout. ansible_ssh_common_args is not implemented (OpenSSH-specific flags have no equivalent in a Go SSH client). "localhost"/"127.0.0.1" (by hostName, unless ansible_connection overrides it) use Local.
Three of these settings — remote_user, host_key_checking, timeout — match real Ansible's own config precedence: an inventory/host var wins if set, otherwise an ANSIBLE_* environment variable if set, otherwise ansible.cfg's [defaults] section (see configFileValue) if it sets the matching key, otherwise the compiled-in default below. (forks is a fourth setting on the same precedence, minus the host-var layer — see Engine.Forks/ConfigDefaults — since it's a global concurrency cap, not a per-host connection detail, so it doesn't belong in this function's own resolution chain.) These four settings are the full extent of this port's ansible.cfg support — no other section or key is read at all (a real, stated gap — see go-ansible/cli's ansible-config, which reports exactly this precedence and these four settings, nothing more). Unlike real Ansible's lenient boolean parsing (yes/no/on/off/1/0/true/false, case-insensitive), ANSIBLE_HOST_KEY_CHECKING here is parsed with Go's strconv.ParseBool (true/false/1/0/t/f, case-sensitive on the word forms) — an invalid value falls back to the compiled-in default rather than erroring, matching how an unset var behaves.
Types ¶
type ConfigSetting ¶ added in v0.2.7
ConfigSetting is one entry of ConfigDefaults' report: a named setting, its ANSIBLE_* environment variable, its compiled-in default, and its current effective value (env var if set and valid, else the default) — the same precedence DefaultConnect itself uses, minus the inventory/host-var layer (which needs a specific host to resolve against, and so isn't part of this global view).
func ConfigDefaults ¶ added in v0.2.7
func ConfigDefaults() []ConfigSetting
ConfigDefaults reports every setting this package honors from the environment and ansible.cfg's [defaults] section — go-ansible/cli's ansible-config is a thin printer over this. This is the full list: go-ansible reads no other ansible.cfg section or key, and no other ANSIBLE_* variable, anywhere in the org.
type Connector ¶
type Connector func(ctx context.Context, hostName string, hostVars map[string]any) (remoteexec.Connection, error)
Connector dials a connection to a host, given its name and fully merged variables (before task-level rendering — this only needs the ansible_* connection variables, which are ordinary inventory/group vars).
type Engine ¶
type Engine struct {
Inventory *inventory.Inventory
Modules *modules.Registry
Template *template.Engine
ExtraVars map[string]any
Connect Connector
// BaseDir resolves include_vars' file argument at run time (every
// other file-referencing directive — vars_files, roles,
// include_tasks/import_tasks, include_role/import_role — resolves
// at parse time via ParseFile's directory instead). Defaults to "."
// when unset, matching Parse.
BaseDir string
// RunTags/SkipTags filter which tasks execute, matching
// ansible-playbook's --tags/--skip-tags: a task runs if its
// effective tag set (its own tags unioned with every enclosing
// block's and the play's, computed once at parse time — see
// propagateTags) intersects RunTags, or RunTags is empty, or the
// task carries the "always" tag; and does not intersect SkipTags.
// Filtered-out tasks are reported Skipped (real Ansible omits them
// from output entirely — this port reports them instead, for
// visibility). Tag filtering does not apply to handlers.
RunTags []string
SkipTags []string
// OnResult, if set, is called synchronously as each task result is
// produced (from whichever goroutine ran that host's task) — for
// live progress reporting. It must not block or panic; callers
// wanting ordering should serialize themselves.
OnResult func(Result)
// Forks caps how many hosts run concurrently at once — connecting/
// gathering facts, and each task/handler fan-out (runSingleTask,
// runFree) all respect it — matching real Ansible's own forks
// setting (default 5; New sets this field to 5 for the same
// reason). 0 or negative means unlimited concurrency, which was
// this port's only behavior before Forks existed; New's default
// changes that for anyone constructing an Engine the normal way,
// but a caller that explicitly wants the old unbounded behavior can
// still set Forks to 0 after New returns.
Forks int
// Prompt implements vars_prompt's actual interactive prompting:
// given the fully-formatted message (already combining the prompt
// text and "[default]" the way real Ansible's own do_var_prompt
// does) and whether the input should be hidden, it returns what the
// user entered. New's default (defaultPrompt) is a plain
// bufio-over-os.Stdin read with no terminal awareness at all — it
// works the same whether stdin is a real terminal or a pipe (never
// hides input, and never detects a non-interactive session the way
// real Ansible does to skip prompting and warn instead), which
// matches a raw library caller or a piped/test invocation but not
// real Ansible's actual interactive behavior. go-ansible/cli's
// ansible-playbook overrides this with a real, terminal-aware
// implementation (golang.org/x/term) for actual interactive use.
Prompt func(msg string, private bool) (string, error)
}
Engine runs playbooks against an inventory. The zero value is not usable — use New.
type HostSummary ¶
type HostSummary struct {
Ok, Changed, Failed, Skipped int
}
Summary counts changed/failed/skipped/ok results across the whole run, keyed by host — Ansible's PLAY RECAP.
type Play ¶
type Play struct {
Name string
Hosts string
GatherFacts bool // default true
Become bool
BecomeUser string
BecomeMethod string
Vars map[string]any
VarsFiles []string // paths, resolved relative to the playbook's directory
Tasks []Task
Handlers []Task
Tags []string
Serial int // 0 means "all hosts at once" (linear strategy default)
Roles []RoleRef
// Strategy is "linear" (the default: every host finishes task N
// before any host starts task N+1) or "free" (each host runs its
// entire task list, and its own notified handlers, independently —
// a slow host never holds back a fast one). Any other named
// strategy real Ansible supports (debug, host_pinned, or a
// strategy plugin) is rejected at parse time rather than silently
// treated as linear.
Strategy string
// VarsPrompt, resolved once per play (not per host) before its
// tasks run — see Engine.applyVarsPrompt — into an ordinary play
// var, same scope as Vars. Real Ansible only accepts a list of
// maps here, never a bare-string shorthand (confirmed from
// ansible-core's own Play._load_vars_prompt/preprocess_vars: an
// item that isn't a mapping raises a parse error there too).
VarsPrompt []VarPrompt
}
Play runs a set of tasks against a pattern of inventory hosts.
type PlayResult ¶
PlayResult aggregates every Result from one play, in the order record was called — not necessarily task-list order, since the engine runs one goroutine per active host and every host's goroutine calls record on the same *PlayResult concurrently. mu is a pointer (not an embedded sync.Mutex) specifically so a PlayResult can still be copied by value — as RunPlaybook does, appending *pr into RunResult.Plays — without go vet's copylocks check firing; every copy keeps pointing at the one real mutex.
type Playbook ¶
type Playbook []Play
Playbook is an ordered list of plays, as ansible-playbook reads it.
func Parse ¶
Parse parses a playbook YAML document (a top-level list of plays). File-referencing directives (vars_files, roles, include_tasks, import_tasks, include_role, import_role) resolve their paths relative to the current working directory — use ParseFile when the playbook lives elsewhere and its includes should resolve relative to it.
type Result ¶
type Result struct {
Host string
Task string
Module string
Changed bool
Failed bool
Skipped bool
Msg string
Extra map[string]any
}
Result is one task's outcome on one host (one per loop iteration when a task loops).
type RunResult ¶
type RunResult struct {
Plays []PlayResult
}
RunResult aggregates every play's results, in playbook order.
func (*RunResult) Failed ¶
Failed reports whether any result in the run failed (and was not subsequently rescued — a rescued failure still appears here, since Result records history, not final host status; check Ok for that question instead).
func (*RunResult) Summary ¶
func (rr *RunResult) Summary() map[string]*HostSummary
type Task ¶
type Task struct {
Name string
Module string
Args map[string]any
When string // Jinja2 expression, already normalized from a string or []string
Loop any // a literal list, or a "{{ expr }}" string rendered at run time
LoopVar string // default "item"
Register string
IgnoreErrors bool
ChangedWhen string
FailedWhen string
Tags []string
Become *bool // nil means "inherit the play's setting"
BecomeUser string
Notify []string
Vars map[string]any
DelegateTo string
// Until/Retries/Delay implement the task retry loop: real Ansible
// runs the task 1+Retries times (Retries nil, the "unset" state,
// means no retry loop at all UNLESS Until is non-empty, in which
// case real Ansible defaults Retries to 3 — mirrored in
// runTaskOnHost, not here, since it needs to distinguish "Retries
// explicitly 0" from "Retries unset"), re-checking Until (or, if
// Until is empty but Retries was explicitly set, "not failed")
// after each attempt and sleeping Delay seconds before the next one
// if it didn't pass. See runTaskOnHost's retry loop for the exact
// attempt-counting algorithm, including a real, deliberately
// reproduced quirk in ansible-core's own retry loop.
Until string
Retries *int
Delay float64
// RunOnce restricts execution to the first currently-active host in
// a runSingleTask call, broadcasting any Register result to every
// other active host afterward (so a later task on ANY host can
// still read it by bare name) — matching real Ansible's own
// run_once, including its result-sharing, verified against a real
// ansible-playbook run. A real, narrower limitation under strategy:
// free: each host there calls runSingleTask with itself as the only
// active host (see runFree), so there is no cross-host "first one"
// to restrict to — every host still runs its own copy. Real Ansible
// coordinates run_once across free's independent per-host lanes;
// this port does not, and says so here rather than silently
// re-running the task on every host without comment.
RunOnce bool
// Async/Poll implement async:/poll: — see runTaskOnHost's async
// branch and modules.AsyncLaunch/AsyncCheck for the real mechanism
// and its one disclosed limitation (no active kill on timeout).
// Async <= 0 means synchronous, ordinary execution — the
// overwhelming majority of tasks. Only command/shell support
// Async > 0 at all: every other module's work happens as a
// sequence of calls from the control node, not one remote
// invocation that could be backgrounded on the target the way
// async requires — a task on any other module with Async > 0 fails
// loud rather than silently running synchronously and ignoring
// what was explicitly asked for. Poll nil means "unset": defaults
// to 15 seconds (real Ansible's own DEFAULT_POLL_INTERVAL) when
// Async > 0; Poll 0 is fire-and-forget (the task returns immediately
// once the job is launched, for a later task to check via
// async_status); Poll > 0 waits, checking every Poll seconds, until
// the job finishes or Async seconds pass (a timeout failure).
Async int
Poll *int
// RoleDefaults/RoleVars are set only on the synthetic block task
// produced for a roles: entry or include_role/import_role — the
// engine (Engine.pushRoleVars) merges them on top of the
// RoleDefaults/RoleVars layers' current content for the duration of
// the block, then restores the prior (pre-merge) content. Nesting
// (a role that itself includes another role) composes correctly to
// any depth this way: a variable the inner role doesn't define in
// its own defaults/main.yml or vars/main.yml still resolves to
// whatever the enclosing role(s) already had, matching real
// Ansible's behavior of keeping every currently active role's
// defaults/vars in scope at once.
RoleDefaults map[string]any
RoleVars map[string]any
Block []Task
Rescue []Task
Always []Task
}
Task is one step of a play (or of a block's body/rescue/always). Module/Args are populated from whichever single non-reserved key the task's YAML mapping carried (e.g. `copy:` or `command:`) — empty for a block/meta task.
type VarPrompt ¶ added in v0.5.0
VarPrompt is one vars_prompt entry. Prompt defaults to Name when empty, Private defaults to true (confirmed from ansible-core's own playbook_executor.py: private = boolean(var.get("private", True)) — prompts hide input UNLESS private: false is explicit, the opposite of what the name alone might suggest). encrypt/salt/salt_size/unsafe (hashing the prompted value, and disabling template-escaping of it) are real ansible-core vars_prompt keys this port does not implement — accepted and parsed for shape compatibility, silently no-op'd rather than erroring on an unrecognized key, since a real playbook using only the common name/prompt/default/private/confirm subset (the overwhelming majority) should not need every knob wired to run.