From: Alex Willmer <alex@moreati.org.uk>
Date: Fri, 7 Jul 2023 21:32:02 +0100
Subject: ansible_mitogen: Fix --ask-become-pass, add test coverage

Previously f1503874de82353cbed8b51408d20fdfa899f8f7 fixed the priority of
ansible_become_pass over ansible_become_password, but broke --ask-become-pass.
Fixes #952.

Origin: upstream, https://github.com/mitogen-hq/mitogen/pull/1007
Bug-Debian: https://bugs.debian.org/1042556
---
 .ci/ansible_tests.py                                | 11 ++++++++++-
 ansible_mitogen/transport_config.py                 | 16 ++++++++++++----
 tests/ansible/hosts/default.hosts                   |  2 ++
 .../integration/transport_config/become_pass.yml    |  5 +++--
 tests/ansible/regression/all.yml                    |  1 +
 tests/ansible/regression/become_test.yml            |  9 +++++++++
 .../regression/issue_952__ask_become_pass.yml       | 21 +++++++++++++++++++++
 tests/requirements.txt                              |  1 +
 8 files changed, 59 insertions(+), 7 deletions(-)
 create mode 100644 tests/ansible/regression/become_test.yml
 create mode 100644 tests/ansible/regression/issue_952__ask_become_pass.yml

diff --git a/.ci/ansible_tests.py b/.ci/ansible_tests.py
index 43d4f4c..0dd978c 100755
--- a/.ci/ansible_tests.py
+++ b/.ci/ansible_tests.py
@@ -6,6 +6,7 @@ import glob
 import os
 import signal
 import sys
+import textwrap
 
 import ci_lib
 
@@ -74,7 +75,15 @@ with ci_lib.Fold('job_setup'):
             fp.write('\n[%s]\n' % family)
             fp.writelines('%s\n' % name for name in hostnames)
 
-        fp.write('\n[linux:children]\ntest-targets\n')
+        fp.write(textwrap.dedent(
+            '''
+            [linux:children]
+            test-targets
+
+            [linux_containers:children]
+            test-targets
+            '''
+        ))
 
     ci_lib.dump_file(inventory_path)
 
diff --git a/ansible_mitogen/transport_config.py b/ansible_mitogen/transport_config.py
index 5fc7818..1fc1e80 100644
--- a/ansible_mitogen/transport_config.py
+++ b/ansible_mitogen/transport_config.py
@@ -79,7 +79,6 @@ try:
 except ImportError:
     from ansible.vars.unsafe_proxy import AnsibleUnsafeText
 
-import ansible_mitogen.loaders
 import mitogen.core
 
 
@@ -436,9 +435,18 @@ class PlayContextSpec(Spec):
         return self._play_context.become_user
 
     def become_pass(self):
-        become_method = self.become_method()
-        become_plugin = ansible_mitogen.loaders.become_loader.get(become_method)
-        become_pass = become_plugin.get_option('become_pass', hostvars=self._task_vars)
+        # become_pass is owned/provided by the active become plugin. However
+        # PlayContext is intertwined with it. Known complications
+        # - ansible_become_password is higher priority than ansible_become_pass,
+        #   `play_context.become_pass` doesn't obey this (atleast with Mitgeon).
+        # - `meta: reset_connection` runs `connection.reset()` but
+        #   `ansible_mitogen.connection.Connection.reset()` recreates the
+        #   connection object, setting `connection.become = None`.
+        become_plugin = self._connection.become
+        try:
+            become_pass = become_plugin.get_option('become_pass', playcontext=self._play_context)
+        except AttributeError:
+            become_pass = self._play_context.become_pass
         return optional_secret(become_pass)
 
     def password(self):
diff --git a/tests/ansible/hosts/default.hosts b/tests/ansible/hosts/default.hosts
index 1bec001..4f5ea4c 100644
--- a/tests/ansible/hosts/default.hosts
+++ b/tests/ansible/hosts/default.hosts
@@ -10,3 +10,5 @@ target ansible_host=localhost ansible_user="{{ lookup('pipe', 'whoami') }}"
 
 [test-targets]
 target
+
+[linux_containers]
diff --git a/tests/ansible/integration/transport_config/become_pass.yml b/tests/ansible/integration/transport_config/become_pass.yml
index 8fbe725..b9bf1a8 100644
--- a/tests/ansible/integration/transport_config/become_pass.yml
+++ b/tests/ansible/integration/transport_config/become_pass.yml
@@ -143,8 +143,9 @@
       - out.result|length == 2
       - out.result[0].method == "ssh"
       - out.result[1].method == "sudo"
-      # Ansible >= 2.10 builtin become plugins (e.g. sudo, su) give priority
-      # to ansible_become_pass over ansible_become_password.
+      # Ansible <= 2.9.1 prioritises ansible_become_password.
+      # Ansible >= 2.9.2 prioritises ansible_become_pass.
+      # https://github.com/ansible/ansible/commit/480b106d6535978ae6ecab68b40942ca4fa914a0
       - out.result[1].kwargs.password == "bpass"
       fail_msg: out={{out}}
   tags:
diff --git a/tests/ansible/regression/all.yml b/tests/ansible/regression/all.yml
index 0e59947..477cacc 100644
--- a/tests/ansible/regression/all.yml
+++ b/tests/ansible/regression/all.yml
@@ -14,3 +14,4 @@
 - import_playbook: issue_615__streaming_transfer.yml
 - import_playbook: issue_655__wait_for_connection_error.yml
 - import_playbook: issue_776__load_plugins_called_twice.yml
+- import_playbook: issue_952__ask_become_pass.yml
diff --git a/tests/ansible/regression/become_test.yml b/tests/ansible/regression/become_test.yml
new file mode 100644
index 0000000..5af2e12
--- /dev/null
+++ b/tests/ansible/regression/become_test.yml
@@ -0,0 +1,9 @@
+- name: regression/become_test.yml
+  hosts: test-targets:&linux_containers
+  become: true
+  become_user: mitogen__pw_required
+  strategy: mitogen_linear
+  tasks:
+    - command: whoami
+      changed_when: false
+      check_mode: false
diff --git a/tests/ansible/regression/issue_952__ask_become_pass.yml b/tests/ansible/regression/issue_952__ask_become_pass.yml
new file mode 100644
index 0000000..da269e2
--- /dev/null
+++ b/tests/ansible/regression/issue_952__ask_become_pass.yml
@@ -0,0 +1,21 @@
+- name: regression/issue_952__ask_become_pass.yml
+  hosts: test-targets[0]:&linux_containers
+  gather_facts: false
+  tags:
+    - issue_952
+  tasks:
+    - name: Test --ask-become-pass
+      delegate_to: localhost
+      expect:
+        command: >
+          ansible-playbook
+          {% for inv in ansible_inventory_sources %}
+          -i "{{ inv }}"
+          {% endfor %}
+          --ask-become-pass
+          regression/become_test.yml
+        chdir: ../
+        responses:
+          'BECOME password:': pw_required_password
+      changed_when: false
+      check_mode: false
diff --git a/tests/requirements.txt b/tests/requirements.txt
index 6d87d17..ae3abb8 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -12,6 +12,7 @@ Django==3.2.20;     python_version >= '3.6'
 mock==3.0.5;                python_version == '2.7'
 mock==5.1.0;                python_version >= '3.6'
 
+pexpect==4.8
 psutil==5.9.8
 
 pytest==4.6.11;             python_version == '2.7'
