ansible_user_dir как он получается?

Начнем с чтения сорцов и разберемся что такое ansible_user_dir

ansible использует https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/setup.py для сбора фактов, это и так все знают. Внутри модуля импортятся коллекторы https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/setup.py#L195

fact_collector = ansible_collector.get_ansible_collector(all_collector_classes=all_collector_classes,
                                                         namespace=namespace,
                                                         filter_spec=filter_spec,
                                                         gather_subset=gather_subset,
                                                         gather_timeout=gather_timeout,
                                                         minimal_gather_subset=minimal_gather_subset)

Нас интересует UserFactCollector https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/default_collectors.py#L117

_general = [
    PythonFactCollector,
    SystemCapabilitiesFactCollector,
    PkgMgrFactCollector,
    OpenBSDPkgMgrFactCollector,
    ServiceMgrFactCollector,
    CmdLineFactCollector,
    DateTimeFactCollector,
    EnvFactCollector,
    SshPubKeyFactCollector,
    UserFactCollector
]

...

collectors = _base + _restrictive + _general + _virtual + _hardware + _network + _extra_facts

Теперь смотрим код коллектора https://github.com/ansible/ansible/blob/9c2f44b8848a317a2304254eba0b9b348d5034ad/lib/ansible/module_utils/facts/system/user.py#L46

class UserFactCollector(BaseFactCollector):
    name = 'user'
    _fact_ids = set(['user_id', 'user_uid', 'user_gid',
                     'user_gecos', 'user_dir', 'user_shell',
                     'real_user_id', 'effective_user_id',
                     'effective_group_ids'])

    def collect(self, module=None, collected_facts=None):
        user_facts = {}

        user_facts['user_id'] = getpass.getuser()

        try:
            pwent = pwd.getpwnam(getpass.getuser())
        except KeyError:
            pwent = pwd.getpwuid(os.getuid())

        user_facts['user_uid'] = pwent.pw_uid
        user_facts['user_gid'] = pwent.pw_gid
        user_facts['user_gecos'] = pwent.pw_gecos
        user_facts['user_dir'] = pwent.pw_dir
        user_facts['user_shell'] = pwent.pw_shell
        user_facts['real_user_id'] = os.getuid()
        user_facts['effective_user_id'] = os.geteuid()
        user_facts['real_group_id'] = os.getgid()
        user_facts['effective_group_id'] = os.getgid()

        return user_facts

видим 2 интересные вещи

Вроде понятно. получили пользователя и вернули его домашнюю директорию

Есть же ~

Зачем так сложно? можно же просто взять ~. Можно, но не всегда, Т.к. ~ это сущность shell и разворачивается им. Может выстрелить когда shell явно не используется и/или требуется абсолютный путь.

Но, давайте глянем пример, что ~ и ansible_user_dir иногда могут выдать разный результат.

- tasks:
    - shell: eval echo "~"
      register: stdout
    - name: ansible_user_dir - ~ without become in play
      debug:
        msg: "{{ ansible_user_dir }} - {{ stdout.stdout_lines | join(' ') }}"

    - shell: eval echo "~"
      become: true
      become_user: lgonchar
      register: stdout
    - name: ~ without become in play with become in task
      debug:
        msg: "{{ ansible_user_dir }} - {{ stdout.stdout_lines | join(' ') }}"
      become: true
      become_user: lgonchar

- become: true
  become_user: lgonchar
  tasks:
    - shell: eval echo "~"
      register: stdout
    - name: ansible_ansible_user_dir - ~ user_dir with become in task
      debug:
        msg: "{{ ansible_user_dir }} - {{ stdout.stdout_lines | join(' ') }}"

А вот и результат работы:

TASK [ansible_user_dir - ~ without become in play] 
ok: [lindev-lgonchar2.imagemaster.local] => {
    "msg": "/home/deploy - /home/deploy"
}

TASK [~ without become in play with become in task]
changed: [lindev-lgonchar2.imagemaster.local]

TASK [ansible_ansible_user_dir - ~ user_dir with become in task]
ok: [lindev-lgonchar2.imagemaster.local] => {
    "msg": "/home/deploy - /home/ms/lgonchar"
}

поведение логичное. на момент сбора фактов у нас пользователь deploy поэтому он и сохраняется в ansible_user_dir

Вместо выводов

Выводов не будет. Это просто рассказ, что даже простая задача может скрывать подводные камни.

Комментарии (5)


  1. amarao
    02.09.2021 09:57
    +1

    Плейбука с become, а факты без become собирались. Поставьте become на плей, будет вам home рута. (Одна из причин, почему нельзя так делать).


    1. ultral Автор
      02.09.2021 11:14

      не рута, а на кого become делается. если уж точнее


  1. avg
    02.09.2021 10:33

    Use lookup('env', 'HOME'), Luke!


    1. ultral Автор
      02.09.2021 11:17

      а lookup выполнится на contorl машине где ansible-playbook Запущен? или на целевой?


    1. amarao
      02.09.2021 14:35
      +1

      Очень оригинальный совет. Во-первых HOME может быть не задан (привет, CI'и), во-вторых локальный пользователь вовсе не обязан совпадать с удалённым.