diff --git a/docs/changelog.rst b/docs/changelog.rst
index 4fc177c1..8cc24424 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -26,6 +26,9 @@ Unreleased
* :gh:issue:`1110` Fix :exc:`mitogen.core.StreamError` when Ansible copy
module is called with a file larger than 124 kibibytes
(:data:`ansible_mitogen.connection.Connection.SMALL_FILE_LIMIT`)
+* :gh:issue:`905` Initial support for templated ``ansible_ssh_args``,
+ ``ansible_ssh_common_args``, and ``ansible_ssh_extra_args`` variables.
+ NB: play or task scoped variables will probably still fail.
v0.3.9 (2024-08-13)
diff --git a/docs/contributors.rst b/docs/contributors.rst
index 1a590869..e40607a0 100644
--- a/docs/contributors.rst
+++ b/docs/contributors.rst
@@ -116,6 +116,7 @@ sponsorship and outstanding future-thinking of its early adopters.
- Alex Willmer
+ - Christian Bourgeois
- Dan Dorman — - When I truly understand my enemy … then in that very moment I also love him.
- Daniel Foerster
- Deps — Private Maven Repository Hosting for Java, Scala, Groovy, Clojure
diff --git a/tests/ansible/hosts/default.hosts b/tests/ansible/hosts/default.hosts
index 4f5ea4c6..adc271e2 100644
--- a/tests/ansible/hosts/default.hosts
+++ b/tests/ansible/hosts/default.hosts
@@ -12,3 +12,10 @@ target ansible_host=localhost ansible_user="{{ lookup('pipe', 'whoami') }}"
target
[linux_containers]
+
+[issue905]
+ssh-common-args ansible_host=localhost ansible_user="{{ lookup('pipe', 'whoami') }}"
+
+[issue905:vars]
+ansible_ssh_common_args=-o PermitLocalCommand=yes -o LocalCommand="touch {{ ssh_args_canary_file }}"
+ssh_args_canary_file=/tmp/ssh_args_{{ inventory_hostname }}
diff --git a/tests/ansible/integration/ssh/all.yml b/tests/ansible/integration/ssh/all.yml
index 1b0f36e4..5c16f187 100644
--- a/tests/ansible/integration/ssh/all.yml
+++ b/tests/ansible/integration/ssh/all.yml
@@ -1,3 +1,4 @@
+- import_playbook: args.yml
- import_playbook: config.yml
- import_playbook: password.yml
- import_playbook: timeouts.yml
diff --git a/tests/ansible/integration/ssh/args.yml b/tests/ansible/integration/ssh/args.yml
new file mode 100644
index 00000000..5892b5fe
--- /dev/null
+++ b/tests/ansible/integration/ssh/args.yml
@@ -0,0 +1,48 @@
+- name: integration/ssh/args.yml
+ hosts: issue905
+ gather_facts: false
+ tasks:
+ # Test that ansible_ssh_common_args are templated; ansible_ssh_args &
+ # ansible_ssh_extra_args aren't directly tested, we assume they're similar.
+ # FIXME This test currently relies on variables set in the host group.
+ # Ideally they'd be set here, and the host group eliminated, but
+ # Mitogen currently fails to template when defined in the play.
+ # TODO Replace LocalCommand canary with SetEnv canary, to simplify test.
+ # Requires modification of sshd_config files to add AcceptEnv ...
+ - name: Test templating of ansible_ssh_common_args et al
+ block:
+ - name: Ensure no lingering canary files
+ file:
+ path: "{{ ssh_args_canary_file }}"
+ state: absent
+ delegate_to: localhost
+
+ - name: Reset connections to force new ssh execution
+ meta: reset_connection
+
+ - name: Perform SSH connection, to trigger side effect
+ ping:
+
+ # LocalCommand="touch {{ ssh_args_canary_file }}" in ssh_*_args
+ - name: Stat for canary file created by side effect
+ stat:
+ path: "{{ ssh_args_canary_file }}"
+ delegate_to: localhost
+ register: ssh_args_canary_stat
+
+ - assert:
+ that:
+ - ssh_args_canary_stat.stat.exists == true
+ quiet: true
+ success_msg: "Canary found: {{ ssh_args_canary_file }}"
+ fail_msg: |
+ ssh_args_canary_file={{ ssh_args_canary_file }}
+ ssh_args_canary_stat={{ ssh_args_canary_stat }}
+ always:
+ - name: Cleanup canary files
+ file:
+ path: "{{ ssh_args_canary_file }}"
+ state: absent
+ delegate_to: localhost
+ tags:
+ - issue_905
diff --git a/tests/ansible/templates/test-targets.j2 b/tests/ansible/templates/test-targets.j2
index f61a3c78..e2708192 100644
--- a/tests/ansible/templates/test-targets.j2
+++ b/tests/ansible/templates/test-targets.j2
@@ -26,3 +26,14 @@ test-targets
[linux_containers:children]
test-targets
+
+[issue905]
+{% for c in containers[:1] %}
+ssh-common-args ansible_host={{ c.hostname }} ansible_port={{ c.port }} ansible_python_interpreter={{ c.python_path }}
+{% endfor %}
+
+[issue905:vars]
+ansible_user=mitogen__has_sudo_nopw
+ansible_password=has_sudo_nopw_password
+ansible_ssh_common_args=-o PermitLocalCommand=yes -o LocalCommand="touch {{ '{{' }} ssh_args_canary_file {{ '}}' }}"
+ssh_args_canary_file=/tmp/ssh_args_{{ '{{' }} inventory_hostname {{ '}}' }}