modules

package module
v0.22.0 Latest Latest
Warning

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

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

README

modules

Ansible module execution protocol plus the core module library.

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

CI Go Reference License

Usage

reg := modules.Default() // pre-populated with the built-in module set

res, err := reg.Run(ctx, "copy", conn, map[string]any{
    "src": "app.conf", "dest": "/etc/app.conf", "mode": "0644",
})
if res.Failed {
    // res.Msg explains why
}

conn is a github.com/go-remoteexec/transport.Connection (local or SSH). Unlike real Ansible, a module here runs its logic on the control node and reaches the target only through the connection's Exec/Put/Fetch primitives — no Python, no script copied to the target. reg.Names() lists every registered module name; reg.Register adds or overrides one.

Documentation

Overview

Package modules implements Ansible's module execution model: each module is a Go function that takes a target connection (github.com/go-remoteexec/transport) and a set of arguments (already Jinja2-rendered by the caller) and returns a Result — Ansible's changed/failed/msg triple plus any module-specific fields.

Unlike real Ansible, which copies a Python script to the target and runs it there, a module here runs its logic on the control node and reaches the target only through the Connection's Exec/Put/Fetch primitives. The observable behavior is the same (the target ends up in the same state); the difference is architectural, not behavioral, and it means a module needs no Go toolchain on the target.

Index

Constants

This section is empty.

Variables

View Source
var Docs = map[string]string{}/* 564 elements not displayed */

Docs maps every registered module name to its Go doc comment — the same argument/deviation documentation this project has always written on each module<Name> function, extracted once at build time so ansible-doc can print it without the source tree present.

Functions

func AsyncCheck added in v0.19.0

func AsyncCheck(ctx context.Context, conn remoteexec.Connection, jid string) (found, done bool, rc int, stdout, stderr string, err error)

AsyncCheck reports a job's current status. found=false means no job directory exists at all for jid (a typo, or AsyncCleanup already ran) — distinct from done=false, which means the directory exists but the job is still running (or, indistinguishably, has not yet written its first byte of output — matching real Ansible's own async_status in that same ambiguous case). done=true gives rc/ stdout/stderr, fetched only then (not on every poll, to avoid hauling potentially large output over the wire while still waiting).

func AsyncCleanup added in v0.19.0

func AsyncCleanup(ctx context.Context, conn remoteexec.Connection, jid string) error

AsyncCleanup removes a job's directory entirely — async_status's mode=cleanup.

func AsyncLaunch added in v0.19.0

func AsyncLaunch(ctx context.Context, conn remoteexec.Connection, cmdLine string) (jid string, err error)

AsyncLaunch backgrounds cmdLine on conn's target under a fresh job ID, returning immediately without waiting for it to finish — the command keeps running independently of this call and of the connection itself (nohup traps SIGHUP, so it survives the connection closing), which is the entire point of async:.

A real, disclosed limitation: unlike real Ansible's own async wrapper (which forks/setpgids the job so it can SIGKILL the whole process group if async: 's time limit is exceeded), this does NOT actively kill a job that overruns its time limit — there is no portable POSIX shell equivalent to killpg across every target shell this might run against without a dependency (setsid, part of util-linux) that real targets (macOS/BSD in particular) do not ship by default. AsyncCheck (see below) still enforces the time limit on the CONTROLLER side (a poll loop gives up and reports a timeout failure once the limit passes), it just doesn't reach out and stop the job itself the way real Ansible's wrapper does.

func ComposeCommandLine added in v0.19.0

func ComposeCommandLine(ctx context.Context, conn remoteexec.Connection, module string, args map[string]any) (cmdLine string, skip bool, skipMsg string, err error)

ComposeCommandLine composes the exact shell command line the "command" or "shell" module would execute for args (argv-quoting/ chdir handling included), running the real creates/removes short-circuit check against conn but WITHOUT executing the command itself — skip=true means the command should not run at all (skipMsg explains why, matching what a synchronous run would return via Ok(msg) instead of actually running anything).

Exported for go-ansible/playbook's async: task launcher: command and shell are the only two modules whose entire job reduces to one remote command, and so the only two this port can genuinely background on the target the way async requires — the launcher needs the exact command a synchronous run would use, to wrap it instead of running it directly.

func NormalizeName added in v0.18.0

func NormalizeName(name string) string

NormalizeName strips a known collection prefix from an FQCN module or playbook-directive reference, returning name unchanged if it carries none of them.

Types

type Func

type Func func(ctx context.Context, conn remoteexec.Connection, args map[string]any) (Result, error)

Func is a module's entry point. ctx carries cancellation/timeout; conn is already connected to the task's target; args is the task's parameters, already Jinja2-rendered by the caller (this package never templates anything itself). A non-nil error means the module could not determine an outcome at all (a transport failure); an expected failure is a Result with Failed=true and a nil error.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry maps module names to their Func.

func Default

func Default() *Registry

Default returns a Registry pre-populated with this package's built-in module set.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty Registry.

func (*Registry) Get

func (r *Registry) Get(name string) (Func, bool)

Get looks up a module by name. A fully-qualified collection name (FQCN) — "ansible.builtin.copy", "community.general.ufw" — resolves to the same entry as the bare name ("copy", "ufw") once the known collection prefix is stripped; see NormalizeName. This is a deliberate simplification, not full collection-scoped resolution: this registry is a single flat namespace (matching how this port's module set has no real cross-collection name collisions to disambiguate), so an FQCN with the WRONG collection prefix for a given module (e.g. "ansible.builtin.ufw", when ufw is actually community.general's) still resolves — real Ansible would instead fail "couldn't resolve module" in that case. In practice this only diverges from real Ansible on a playbook that already has an incorrect FQCN, which would already be broken there too.

func (*Registry) Names

func (r *Registry) Names() []string

Names returns every registered module name, sorted.

func (*Registry) Register

func (r *Registry) Register(name string, fn Func)

Register adds fn under name, replacing any existing module of the same name (so a caller can override a built-in with a custom module).

func (*Registry) Run

func (r *Registry) Run(ctx context.Context, name string, conn remoteexec.Connection, args map[string]any) (Result, error)

Run looks up name and runs it, returning a Result{Failed:true} (not a Go error) for an unknown module name — matching Ansible's own "couldn't resolve module" being a task failure, not a crash.

type Result

type Result struct {
	Changed bool
	Failed  bool
	Msg     string
	Facts   map[string]any
	Extra   map[string]any
}

Result is a module's outcome: Ansible's changed/failed/msg triple, plus optional facts (merged into ansible_facts, e.g. by set_fact) and module-specific extra fields (e.g. command's stdout/stderr/rc).

func Changed

func Changed(msg string) Result

Changed returns a successful, changed result.

func Fail

func Fail(msg string) Result

Fail returns a failed result. Modules normally return this alongside a non-nil error only when the failure is unexpected (a connection error, an unreadable file); an expected, well-formed failure (e.g. the `fail` module itself, or `assert` on a false condition) returns it with a nil error, since it is not the module's own execution that went wrong.

func Ok

func Ok(msg string) Result

Ok returns a successful, unchanged result.

func (Result) WithExtra

func (r Result) WithExtra(key string, value any) Result

WithExtra returns a copy of r with key set in Extra.

Source Files

Directories

Path Synopsis
internal
gendocs command
Command gendocs extracts each registered module's Go doc comment (already written on its module<Name> function — this project's convention has always been to document arguments and every deviation from real Ansible's behavior there) and emits docs_generated.go: a map from the module's registered name to that comment text, for ansible-doc to print without needing the source tree at runtime.
Command gendocs extracts each registered module's Go doc comment (already written on its module<Name> function — this project's convention has always been to document arguments and every deviation from real Ansible's behavior there) and emits docs_generated.go: a map from the module's registered name to that comment text, for ansible-doc to print without needing the source tree at runtime.

Jump to

Keyboard shortcuts

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