All versions since 0.1.0-alpha.4
0.1.0-alpha.4
Added
- The full host-pattern grammar: wildcards,
!exclusions and&intersections. (#78) - The
sshconnection, with the agent uploaded once and cached on each host. (#79) - Agent connections are kept across plays, and
forkslimits how many hosts run at once. (#82) becomethroughsudo, from keywords and host variables. (#83)- A task that reads
hostvarswaits for the other hosts to reach it. (#85)
Fixed
- Loop failures, empty loops, and one recap per playbook. (#86)
- Key order,
omitin nested structures, and YAML 1.1 integers. (#87) - Escalation to any user, a bound on open connections, and exit codes that match Ansible’s. (#90)
0.1.0-alpha.5
Added
- The whole ansible-core 2.19 grammar loads, and whatever this release cannot run is refused by name before the first connection. (#98)
- Plays compile into a flat list of steps, with blocks kept as spans. (#101)
- Roles load from the standard paths, with dependencies, argument specs and
import_tasks. (#102) --tags,--skip-tagsand the listing commands, with output identical to Ansible’s. (#103)- Blocks with
rescueandalways, and theansible_failed_taskandansible_failed_resultfacts. (#104) - Handlers, run at every flush point. (#105)
until,retriesanddelay,no_log, andenvironment. (#107)serialbatches, with the run stopping when a batch loses every host. (#108)include_tasks,include_roleandinclude_vars, resolved while the play runs. (#109)run_onceanddelegate_to, with the delegated task running over the delegate’s own connection. (#115)
Changed
- Every host reads one shared
hostvarsmap instead of its own copy. (#116)
Fixed
- The host that runs a
run_oncetask is elected once for the whole batch. (#118)
0.1.0-alpha.6
Added
- Every release archive now carries the two Linux agents, so one download is enough for a first run over SSH. (#147)
- The documentation lists which arguments each native module reads, and the pre-flight refuses the ones it does not. (#145)
Changed
- Hosts now meet in front of every task, as Ansible’s
linearstrategy promises. The previous behavior, where each host ran ahead through tasks that did not read another host’s state, is available behind the[volant] batchingoption. (#140) - Resolving variables on large inventories is faster: inventory-wide values are built once per batch and shared across hosts. (#134)
Fixed
- A module result is data and is never rendered again as a template on the controller. (#139)
- The connection settings come from a host’s effective variables, including
group_vars, taskvars,set_factand--extra-vars, not only from the inventory. (#141) createsandremovesare resolved in the directory the command runs in. (#142)- A task’s timeout covers the child process, its output streams and the write to its standard input. (#144), (#154)
- A
remote_tmpwhose~part is not a user name is refused. (#146) - Every event a task produces is shown, through the same output renderer as the rest of the run. (#143)
- What a dynamic include brings in is checked by the pre-flight before it runs. (#155)
0.1.0-alpha.7
Security
- A managed host can no longer set its own connection settings through the facts a module returns. Until this release, a fact module’s
ansible_factsreached the host’s variables unfiltered, so a host could change the address, user and ssh arguments of its own next task, including-oProxyCommand, which runs a command on the controller. It could also pick the Python interpreter its next module ran under, switch the connection tolocalto move its next task onto the controller, and moveansible_remote_tmpso that a planted agent ran as root on the nextbecometask. Volant now strips the names ansible-core’sclean_facts()strips, plusansible_remote_tmp, which ansible-core does not use this way, and prints the same warning. (#181)
Added
- Run ansible-core’s Python modules on managed hosts. The controller builds one content-addressed zip of the modules a run needs and their
module_utils, and sends it to each host once. A Python server in the agent loads it once and forks a child per task, so running a module costs tens of milliseconds rather than starting a new interpreter for each one. The host needs a Python interpreter but not ansible-core; the controller needs ansible-core. Modules the reference runs through an action plugin, such ascopy,template,serviceandpackage, are still refused before the first connection. (#158, #159, #163, #166, #171) - Gather facts with ansible-core’s own
setupmodule when a play asks for them. Gathered facts are data from the host and are never rendered as templates. (#173) - Run
assert,failandpauseon the controller. (#179)
Changed
- A play that does not write
gather_facts: falsenow gathers facts, so it needs ansible-core on the controller and is refused before the first connection without it. (#173) - Gathered facts now rank above the inventory and below the play’s, role’s and task’s own variables, as in ansible-core. A playbook that sets a variable with the same name as a gathered fact now reads its own value. (#181)
- An escalated connection is kept for the whole play instead of being rebuilt at every task. On a thirty-five task playbook against one host this took the run from 872 ms to 226 ms per task. A run can now hold two connections per host, so a controller limited to 1024 open files reaches that limit at about 169 escalating hosts. (#178)
Fixed
ansible_facts[...]uses the same keys as ansible-core, without anansible_prefix, and a fact a module returns without the prefix keeps its name. (#181)- A command skipped because of
creates:orremoves:is reported asok, as in ansible-core, rather than as skipped. (#181) - A registered result carries
changedandfailedthe way ansible-core’s does, and a result such asrc: "2"counts as a failure as it does there. (#181) - The
timeout:keyword applies to tasks that run on the controller. (#179)
[Unreleased] Latest
Added
- Run
copyas an action plugin:statthe destination, thenfileor a stagedcopy, sending nothing when the file already matches. - Run
packageas an action plugin: pick the module from the host’s package manager, gathered with a filteredsetupwhen it is not already a fact. - Run
serviceas an action plugin: pick the module from the host’s init system (use:, then a fact, then a filteredsetup), falling back to theservicemodule for a name none of the known backends carries rather than failing the task. - Run
templateas an action plugin: render the file on the controller once, then hand it to the same pathcopyuses. - Run
unarchiveas an action plugin: extract a controller archive or one already on the host, skipping the task whencreates:already exists. - Add the Jinja filters, tests, methods and lookups the template engine was missing:
b64decode,b64encode,comment,difference,intersect,union,flatten,from_yaml,to_yaml,to_nice_yaml,to_uuid,type_debug,quoteandregex_escape; thechanged,failed,succeeded,skippedandversiontests; a handful of Python methods on strings and mappings, throughminijinja-contrib; and thefirst_foundandtemplatelookups. - Run
rebootas an action plugin: shut the host down, then reconnect until it answers with a new boot id and its test command succeeds. - Run
with_first_foundandwith_fileglobloops, setansible_run_tagsandansible_skip_tagsfrom--tagsand--skip-tags, and retry a task backed by an action plugin underuntil, running its whole sequence of sub-tasks again on each attempt.retries: Ris now up to R + 1 runs, as in the reference. (#216) - Run
fetchas an action plugin, writing a host’s file onto the controller underdestand nowhere else, refusing a path that would land outside it. (#215) - Run
dnfas an action plugin, picking thednfordnf5module from the host’s package manager. (#213) - Run modules from installed collections, such as
ansible.posix.sysctlorcommunity.general.ufw, on the warm Python path. The controller’s ansible-core says what each name is, and a missing collection, a collection’s action plugin or a module that cannot be built is refused before the first connection. Volant never installs a collection. (#211) - Add the
filegloblookup and theansible.utils.ipwrapfilter, and answer every filter and test under itsansible.builtin.*name too. (#210) - An install script:
curl -fsSL https://volant.sh/install.sh | shinstalls the newest release, controller and agents together, after checking its checksum. - The release controller carries its agents.
cargo binstall volant, the shell installer and the release archives give avolantthat runs local and SSH tasks with nothing else installed. On first use the agents are extracted to~/.cache/volant/agents/<version>, or under$XDG_CACHE_HOME.
Changed
- Agents are looked up in
VOLANT_AGENT_DIRfirst, then among the agents built into the controller, then beside the executable. - Release archives no longer contain the agents. They are still published on their own, in the
volant-agent-*archives. - A
copy,templateorunarchivetask whosesrcwas named by a managed host is now rejected before the file is looked up, rather than sent the way the reference sends it: a host that controls a command’s output or a fact could otherwise have any file the operator can read copied to it. - The documentation site moves to volant.sh and is rebuilt with sections, search and diagrams. It adds pages on installation, compatibility, the command line, configuration, inventories and exit codes.
Fixed
- A controller started through a symbolic link finds its agents next to the file the link points at. On macOS it used to look in the directory of the link.
- A file staged for a module belongs to the connection that sent it. Two links to one host no longer consume each other’s files, and a staged file left by a cancelled batch, a lost link or a killed agent is cleaned up. (#191)
- A vars file holding only
---and a comment loads as an empty mapping, a templatedignore_errorsis rendered when the task runs,ansible_search_pathis set for every task androle_pathfor a role’s, and Python module results getstdout_linesandstderr_lines. (#196) - Tasks brought in by
include_tasksorinclude_roleinherit the tags and other keywords of the play, role and blocks around the statement. Under--tags, a tagged role’s included files used to run nothing while the run exited 0. The Python union also carries the modules of every file of the roles in play, and a failed module’sexceptionno longer shows as a raw object. (#199) - A task’s own
vars:built from a lookup that needs the role’s search path, such aslookup('template'), now resolve. (#200) - A loop whose every item was skipped prints the task’s own
skipping:line after the item lines, as ansible-core does. (#202) - A
notifynaming no handler in a dynamically included file ends the run with exit code 1, as ansible-core does.fatal:lines no longer show"failed": true, module warnings are printed as[WARNING]:lines, and an interruptedpauseends the run without counting a failure. (#204)