playbook

package module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: BSD-3-Clause Imports: 15 Imported by: 0

README

playbook

Playbook/role/task/handler engine: loops, conditionals, blocks, strategies.

Part of go-ansible — a pure-Go (CGO=0), functional-parity port of Ansible.

CI Go Reference License

Usage

data, err := os.ReadFile("site.yml")
pb, err := playbook.Parse(data)

eng := playbook.New(inv) // inv: a *github.com/go-ansible/inventory.Inventory
result, err := eng.RunPlaybook(ctx, pb)
if result.Failed() {
    // per-host summaries are in result.Hosts
}

Ties inventory+vars+template+modules+facts together with real per-host linear-strategy execution: when/loop/register, block/rescue/always with per-host recovery, notify/handlers, become, and pre_tasks/post_tasks ordering. Some playbook keys are parsed but not yet wired into the engine (roles, tags, serial, delegate_to, vars_files, include_tasks/import_tasks) — see the org's feature matrix for the current, re-checked status of each.

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

Constants

This section is empty.

Variables

This section is empty.

Functions

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 (default the current OS user), ansible_password / ansible_ssh_pass, ansible_ssh_private_key_file, ansible_host_key_checking (default true, matching Ansible), 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.

Types

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)
}

Engine runs playbooks against an inventory. The zero value is not usable — use New.

func New

func New(inv *inventory.Inventory) *Engine

New returns an Engine with the built-in module registry, a fresh template engine, and DefaultConnect.

func (*Engine) RunPlaybook

func (e *Engine) RunPlaybook(ctx context.Context, pb Playbook) (*RunResult, error)

RunPlaybook runs every play in pb in order.

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
}

Play runs a set of tasks against a pattern of inventory hosts.

type PlayResult

type PlayResult struct {
	Play    string
	Results []Result
	// contains filtered or unexported fields
}

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

func Parse(data []byte) (Playbook, error)

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.

func ParseFile added in v0.2.0

func ParseFile(path string) (Playbook, error)

ParseFile reads and parses the playbook at path, resolving every file-referencing directive relative to path's directory (matching ansible-playbook, which resolves roles/ and included files relative to the playbook file, not the current working directory).

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 RoleRef added in v0.2.0

type RoleRef struct {
	Name string
	Vars map[string]any
}

RoleRef is one entry of a play's roles: list.

type RunResult

type RunResult struct {
	Plays []PlayResult
}

RunResult aggregates every play's results, in playbook order.

func (*RunResult) Failed

func (rr *RunResult) Failed() bool

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

	// RoleDefaults/RoleVars are set only on the synthetic block task
	// produced for a roles: entry or include_role/import_role — the
	// engine pushes them onto the RoleDefaults/RoleVars layers for the
	// duration of the block, then restores the prior layer content.
	// Nested roles (a role that itself includes another role) are not
	// scoped correctly by this single-level save/restore — documented
	// limitation, not silently wrong: it's the outer role's vars that
	// win, matching what would happen if the inner include_role simply
	// didn't reset the layer.
	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.

func (Task) IsBlock

func (t Task) IsBlock() bool

IsBlock reports whether t is a block task (block/rescue/always) rather than a module invocation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL