mitogen icon indicating copy to clipboard operation
mitogen copied to clipboard

The attribute 'remote_user' of the object 'connection._play_context' contains the variable name instead of its value.

Open macksyma opened this issue 1 year ago • 5 comments

I've discovered the incorrect transmission of the remote_user variable's value when using delegate_to with Mitogen 3.4. For example, let's take a simple task:

    - name: "test_task"
      shell: "ls -lah"
      delegate_to: "{{ test_jump_box }}"
      remote_user: "{{ jumper_test_user }}"

Here is the error I've received executing this task:

fatal: [localhost -> XXXXXXXXXX]: UNREACHABLE! => {
    "changed": false,
    "msg": "EOF on stream; last 100 lines received:\nremote username contains invalid characters\r",
    "unreachable": true
}

In the debug log, I see that the ssh command line contains the name of the variable transmitted into the remote_user - {{ jumper_test_user }}, instead of the actual value of this variable:

[mux  2197866] 12:52:15.298866 D mitogen.parent: command line for Connection(None): ssh -o "LogLevel ERROR" -l "{{ jumper_test_user }}" -o "Compression yes" -o "ServerAliveInterval 30" -o "ServerAliveCountMax 10" -o "BatchMode yes" -o "StrictHostKeyChecking no" -o "UserKnownHostsFile /dev/null" -o "GlobalKnownHostsFile /dev/null" -F ssh.cfg ...

After further investigation, I've discovered that Mitogen takes the username for ssh from object _connection._play_context, transmitted to Mitogen's ActionModuleMixin from Ansible's TaskExecutor. Inside the ActionModuleMixin object, I've found that _self._play_context.remote_user contains the correct value of the variable jumper_test_user, but the connection object self._connection._play_context.remote_user contains the variable name, including brackets, just like I saw it in the ssh command line.

I've checked how the method _executeof Ansible's class TaskExecutor updates attributes of its own self._play_context and self._connection._play_context, and found that at some point, self._play_context of TaskExecutor also contains the name of variables instead of their value. But then, it executes the method post_validate() for its self._play_context (PlayContext inherits it from its parent classes):

self._play_context.post_validate(templar=templar)

that replaces variable names with their values. Unfortunately, it doesn't execute this method for _connection._play_context, and as a result, we have variable names instead of values. I'm not sure whether it is a bug or there is some reason to avoid execution of this method for _connection._play_context inside method _execute. As a temporary workaround, I've appended the constructor of ActionModuleMixin with the execution of self._connection._play_context.post_validate(templar=connection.templar):

class ActionModuleMixin(ansible.plugins.action.ActionBase):
    """
    ...
    """
    def __init__(self, task, connection, *args, **kwargs):
        """
        ...
        """
        super(ActionModuleMixin, self).__init__(task, connection, *args, **kwargs)
        if not isinstance(connection, ansible_mitogen.connection.Connection):
            _, self.__class__ = type(self).__bases__

        # required for python interpreter discovery
        connection.templar = self._templar
        self._finding_python_interpreter = False
        self._rediscovered_python = False
        # redeclaring interpreter discovery vars here in case running ansible < 2.8.0
        self._discovered_interpreter_key = None
        self._discovered_interpreter = False
        self._discovery_deprecation_warnings = []
        self._discovery_warnings = []

        self._connection._play_context.post_validate(templar=connection.templar)

and after that got the correct value of remote_user when using delegate_to in my tasks.

Probably you will find better approach solving this issue.

Thank you in advance.

  • Which version of Ansible are you running? 2.13 ansible core 2.14.0 and core 2.13.13 too
  • Is your version of Ansible patched in any way? No
  • Have you tried the latest master version from Git? Yes
  • Mention your host and target OS and versions Host: Ubuntu 22.04.3 LTS (target host doesn't matter for this issue)
  • Mention your host and target Python versions Host: python3.10 (target host doesn't matter for this issue)

macksyma avatar Feb 23 '24 14:02 macksyma

Thank you, this may be closely related to #1022 or a case of it.

moreati avatar Feb 23 '24 14:02 moreati

Hi,

I have the same issue.

mitogen_get_stack print without the changes mentioned by @macksyma

  discovered_interpreter: false
  result:
  - kwargs:
      check_host_keys: ignore
      compression: true
      connect_timeout: 10
      hostname: '{{ my_ip_address }}'
      identities_only: false
      identity_file: null
      keepalive_count: 10
      keepalive_interval: 30
      password: null
      port: null
      python_path:
      - /usr/bin/python3
      remote_name: null
      ssh_args:
      - -F
      - /data/my-work/my_custom_ssh_config
      - -o
      - UserKnownHostsFile=/dev/null
      - -o
      - ConnectTimeout=20
      - -o
      - ControlMaster=auto
      - -o
      - ControlPersist=60s
      ssh_debug_level: null
      ssh_path: ssh
      username: '{{ lookup(''env'', ''USERNAME'') | default(my.username, true) }}'
    method: ssh

mitogen_get_stack print after the changes mentioned by @macksyma

  discovered_interpreter: false
  result:
  - kwargs:
      check_host_keys: ignore
      compression: true
      connect_timeout: 10
      hostname: 10.31.103.20
      identities_only: false
      identity_file: null
      keepalive_count: 10
      keepalive_interval: 30
      password: null
      port: null
      python_path:
      - /usr/bin/python3
      remote_name: null
      ssh_args:
      - -F
      - /data/my-work/my_custom_ssh_config
      - -o
      - UserKnownHostsFile=/dev/null
      - -o
      - ConnectTimeout=20
      - -o
      - ControlMaster=auto
      - -o
      - ControlPersist=60s
      ssh_debug_level: null
      ssh_path: ssh
      username: hrawat
    method: ssh

Ansible: 2.14.0 Target OS: Alpine Linux v3.17 Python: 3.10.13 mitogen: 0.3.6

rawat-he avatar Apr 08 '24 01:04 rawat-he

I've discovered the incorrect transmission of the remote_user variable's value when using delegate_to with Mitogen 3.4. For example, let's take a simple task:

    - name: "test_task"
      shell: "ls -lah"
      delegate_to: "{{ test_jump_box }}"
      remote_user: "{{ jumper_test_user }}"

Notes

  • remote_user here is a keyword, not a variable.
  • The SSH plugin option is also called remote_user. The keyword is listed as a source for the option https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ssh_connection.html#parameter-remote_user
  • The corresponding variables are ansible_user, ansible_ssh_user.
  • The precendence is (in increasing order): config, CLI, keywords, variables, direct assignment https://docs.ansible.com/ansible/latest/reference_appendices/general_precedence.html#precedence-categories

moreati avatar Oct 09 '24 11:10 moreati

@macksyma believe your example will now work, with Mitogen 0.3.13. Sorry for the long wait.

moreati avatar Oct 09 '24 15:10 moreati

@moreati It seems that another bug came up after this fix. Now, variables defined in set_fact and used as remote_user seem not to reach the right context, and then the do_template method of the class Templar (ansible/template/__init__.py) raises AnsibleUndefinedVariable. As a result the task fails because of an undefined variable, which is actually defined. However variables defined before tasks:

---
- hosts: localhost
  gather_facts: no
  vars:
    my_user: "my_user"

  tasks:

or as extra vars:

ansible-playbook -vv  test_playbooks/test_mitogen.yml -e "my_user=my_user"

seem to hit the right context and everything works fine.

Here is the task list used for the test:

---
- hosts: localhost
  gather_facts: no

  tasks:

    - name: "define user and host"
      set_fact:
        my_user: "my_user"
        my_host: "10.192.65.127"
        exec_command: "uname -a"

    - debug:
        var: my_user

    - name: "test"
      shell: "{{ exec_command }}"
      delegate_to: "{{ my_host }}"
      remote_user: "{{ my_user }}"

execution result:

PLAY [localhost] *****************************************************************************************************************************************************************************************
Wednesday 16 October 2024  17:51:01 +0000 (0:00:01.307)       0:00:01.307 *****

TASK [define user and host] ******************************************************************************************************************************************************************************
task path: /home/test/test_playbooks/test_mitogen.yml:7
ok: [localhost] => {"ansible_facts": {"exec_command": "uname -a", "my_host": "10.192.65.127", "my_user": "my_user"}, "changed": false}
Wednesday 16 October 2024  17:51:01 +0000 (0:00:00.064)       0:00:01.371 *****

TASK [debug] *********************************************************************************************************************************************************************************************
task path: /home/test/test_playbooks/test_mitogen.yml:13
ok: [localhost] => {
    "my_user": "my_user"
}
Wednesday 16 October 2024  17:51:01 +0000 (0:00:00.065)       0:00:01.437 *****

TASK [test] **********************************************************************************************************************************************************************************************
task path: /home/test/test_playbooks/test_mitogen.yml:16
fatal: [localhost -> 10.192.65.127]: FAILED! => {"msg": "'my_user' is undefined. 'my_user' is undefined"}

PLAY RECAP ***********************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
  • Which version of Ansible are you running? 2.15.6
  • Is your version of Ansible patched in any way? No
  • Have you tried the latest master version from Git? Yes, I've tried 0.3.13 and 0.3.14
  • Mention your host and target OS and versions Host: Ubuntu 22.04.3 LTS
  • Mention your host and target Python versions Host: 3.10.12

maronovych-godaddy avatar Oct 16 '24 18:10 maronovych-godaddy

Hello, I apologize for hijacking closed issue, but I think it is related and I'm out of ideas. I had small issue when migrating from mitogen 0.3.9 to 0.3.18 and had to add

self._connection._play_context.post_validate(templar=connection.templar)

as mentioned in first message here.

I was hoping to now migrate from mitogen 0.3.18 to mitogen 0.3.22 without any patches (since issue was closed), but I noticed that using delegate_to: in tandem with remote_user: breaks ssh user in certain situations.

e.g. when remote_user can be omitted:

- name: Test
  hosts: all
  gather_facts: false
  tasks:
    - name: Touch file using file module
      ansible.builtin.file:
        path: /tmp/test
        state: touch
      remote_user: "{{ first_run_user | default(omit) }}"
      delegate_to: 1.2.3.4

In mitogen 0.3.18 (with the patch), ssh user is omitted

[mux  93383] 18:18:18.765805 D mitogen.parent: creating connection to context 2 using mitogen.ssh
[mux  93383] 18:18:18.765558 D mitogen.io: PollPoller.poll(None)
[mux  93383] 18:18:18.810675 D mitogen.parent: command line for Connection(None): ssh -o "LogLevel ERROR" -o "Compression yes" ...

In mitogen 0.3.22, it tries to use omit as actual user

[mux  93949] 18:22:39.322810 D mitogen.parent: creating connection to context 2 using mitogen.ssh
[mux  93949] 18:22:39.322571 D mitogen.io: PollPoller.poll(None)
[mux  93949] 18:22:39.350936 D mitogen.parent: command line for Connection(None): ssh -o "LogLevel ERROR" -l __omit_place_holder__12cecd4460ad11a8ab6a9edbde1b2af5a4505b42 -o "Compression yes" ...

Without delegate_to: it works in both versions. I would appreciate any tips!

  • Which version of Ansible are you running? 2.16.13
  • Is your version of Ansible patched in any way? No
  • Are you running with any custom modules, or module_utils loaded? No
  • Have you tried the latest master version from Git? Tested on 0.3.22, it used to work on 0.3.9
  • Mention your host and target OS and versions Host: macOS/Ubuntu 22.04, Target: Ubuntu 22.04 LTS
  • Mention your host and target Python versions Host: Python 3.12.9, Target: Python 3.10.12

golebiewsky avatar Feb 13 '25 09:02 golebiewsky