Используйте модуль пакета ansible для работы с apt и homebrew - PullRequest
0 голосов
/ 04 августа 2020

У меня две проблемы с созданием playbook, который работает на linux и на macOS.

У меня много шагов, подобных этому, в моих playbooks:

- name: install something
  package:
    name: [something_1, something_2, ...]
    state: present
  become: yes

Он отлично работает для apt и yum, но когда я попытался запустить это на macOS, homebrew пожаловался:

Running Homebrew as root is extremely dangerous and no longer supported.

Я не мог найти элегантный способ исправить это во многих местах. Скопируйте все задачи и используйте , когда предложение кажется мне непосильным. Вероятно, я мог бы использовать переменную begin_user для root / local_user в зависимости от дистрибутива, но это тоже много изменений.

Вторая проблема связана с формулой только для головы (пакеты homebrew, которые можно установить только с флагом --head) . Что делать, если с этим флагом нужно установить something_2? Опять же, я мог скопировать задачу и изменить модуль package на homebrew , но это много шаблонов.

Любая помощь?

1 Ответ

1 голос
/ 04 сентября 2020

Если вам нужен единый набор задач, достаточно гибкий для нескольких Linux менеджеров пакетов и macOS brew, вы можете выбрать больше logi c или больше дублирования.

Эти три шаблона должны помочь . У них все еще есть повторение и шаблонный код, но это территория, на которой мы находимся с Ansible воспроизведением для кроссплатформенных.

  1. Объявить become: yes (root) глобально только для Linux
  2. Адреса пакетов, требующих специфической для платформы c обработки по мере необходимости с помощью when
    • Это может быть --head для brew или настройка PPA для apt , et c
  3. Сопоставьте несоответствия имен пакетов с переменными
    • Например: brew install ncurses, apt install libncurses5-dev и dnf install ncurses-devel - это одна и та же библиотека.

1) Объявить become: yes (root) глобально только для Linux

Для Linux хостов, переключение на root для установка - это предполагаемое поведение. Для macOS a la Homebrew установка root не подходит. Итак, нам нужно become: yes (true) при использовании brew и become: no (false) в противном случае (для Linux).

В вашем примере директива become вложены в каждую задачу («шаг»). Чтобы предотвратить дублирование, перед запуском задач вызовите become в более высокой лексической области. Последующие задачи затем унаследуют состояние become, которое устанавливается на основе условного выражения.

К сожалению, переменная для become в области воспроизведения root будет неопределенной и вызовет ошибку перед запускается первая задача:

# playbook.yml
- name: Demo
  hosts: localhost
  connection: local
  # This works
  become: True
  # This doesn't - the variable is undefined
  become: "{{ False if ansible_pkg_mgr == 'brew' else True }}"
  # Nor does this - also undefined
  become: "{{ False if ansible_os_family == 'Darwin' else True }}"

  tasks:
    # ...

Чтобы исправить это, мы можем сохранить задачи в другом файле и импортировать их или заключить задачи в блок . Любой из этих шаблонов предоставит возможность объявить become с нашим значением настраиваемой переменной вовремя, чтобы задачи его подобрали:

# playbook.yml
---
- name: Demo
  hosts: localhost
  connection: local
  vars:
    # This variable gives us a boolean for deciding whether or not to become
    # root. It cascades down to any subsequent tasks unless overwritten.
    should_be_root:  "{{ true if ansible_pkg_mgr != 'brew' else false }}"

    # It could also be based on the OS type, but since brew is the main cause
    # it's probably better this way.
    # should_be_root: "{{ False if ansible_os_family == 'Darwin' else True }}"

  tasks:
    # Import the tasks from another file, which gives us a chance to pass along
    # a `become` context with our variable:
    - import_tasks: test_tasks.yml
      become: "{{ should_be_root }}"

    # Wrapping the tasks in a block will also work:
    - block:
      - name: ncurses is present
        package:
          name: [libncurses5-dev, libncursesw5-dev]
          state: present
      - name: cmatrix is present
        package:
          name: cmatrix
          state: present
      become: "{{ should_be_root }}"

Теперь есть одна logi c проверка на brew и одна директива before (в зависимости от того, какой шаблон задачи используется выше). Все задачи будут выполняться от имени пользователя root, за исключением случаев, когда используется менеджер пакетов brew.

2) Адресация пакетов, требующих обработки c в зависимости от платформы, с помощью when

Пакетный модуль очень удобен, но весьма ограничен. Сам по себе он работает только для идеальных сценариев; Это означает, что пакет не требует специальной обработки или флагов от базового менеджера пакетов. Все, что он может сделать, это передать буквальную строку пакета для установки, state и необязательный параметр для принудительного использования указанного c исполняемого файла диспетчера пакетов.

Вот пример, который устанавливает wget с красивой короткой задачей и становится подробным только для обработки особого случая ffmpeg при установке с brew:

# playbook.yml
# ...
  tasks:
    # wget is the same among package managers, nothing to see here
    - name: wget is present
      when: ansible_pkg_mgr != 'brew'
      package:
        name: wget
        state: present

    # This will only run on hosts that do not use `brew`, like linux
    - name: ffmpeg is present
      when: ansible_pkg_mgr != 'brew'
      package:
        name: ffmpeg
        state: present

    # This will only run on hosts that use `brew`, i.e. macOS
    - name: ffmpeg is present (brew)
      when: ansible_pkg_mgr == 'brew'
      homebrew:
        name: ffmpeg
        # head flag
        state: head
        # --with-chromaprint --with-fdk-aac --with-etc-etc
        install_options: with-chromaprint, with-fdk-aac, with-etc-etc

Приведенная выше игра даст этот результат для ffmpeg против Linux box:

TASK [youtube-dl : ffmpeg is present] ******************************************
ok: [localhost]

TASK [youtube-dl : ffmpeg is present (brew)] ***********************************
skipping: [localhost]

3) Сопоставьте несоответствия в названии пакетов с переменными

Это не является частью вашего вопроса, но, скорее всего, он появится позже.

Модуль пакета в документации также упоминается:

Имена пакетов также зависят от диспетчера пакетов; этот модуль не будет «переводить» их для каждого дистрибутива. Например, libyaml-dev, libyaml-devel.

Итак, мы сами можем обрабатывать случаи, когда одно и то же программное обеспечение использует разные имена между платформами диспетчера пакетов. Это довольно часто.

Для этого существует несколько шаблонов, например:

Ни один из них не очень приятный. Вот подход с использованием роли . Роли действительно включают больше шаблонов и манипуляций с каталогами, но взамен они обеспечивают модульность и среду локальных переменных. Когда набор задач в роли требует большего количества изысков, чтобы получить правильное решение, это не приводит к загрязнению других наборов задач.

# playbook.yml
---
- name: Demo
  hosts: localhost
  connection: local
  roles:
    - cmatrix

# roles/cmatrix/defaults/main.yml
---
ncurses:
  default:
    - ncurses
  # Important: these keys need to exactly match the name of package managers for
  # our logic to hold up
  apt:
    - libncurses5-dev
    - libncursesw5-dev
  brew:
    - pkg-config
    - ncurses

# roles/cmatrix/tasks/main.yml
---
- name: cmatix and its dependencies are present
  become: "{{ should_be_root }}"
  block:
    - name: ncurses is present
      package:
        name: '{{ item }}'
        state: latest
      loop: "{{ ncurses[ansible_pkg_mgr] | default(ncurses['default']) }}"

    - name: cmatrix is present
      when: ansible_pkg_mgr != 'brew'
      package:
        name: cmatrix
        state: present

Задача для ncurses ищет массив элементов для l oop с помощью ключей соответствующего пакета управляющий делами. Если используемый диспетчер пакетов не определен в объекте переменной, фильтр Jinja по умолчанию используется для ссылки на установленное нами значение default.

В этом шаблоне просто добавляется поддержка другого диспетчера пакетов или дополнительных зависимостей. включает обновление объекта переменных:

# roles/cmatrix/defaults/main.yml
---
ncurses:
  default:
    - ncurses
  apt:
    - libncurses5-dev
    - libncursesw5-dev
    # add a new dependency for Debian
    - imaginarycurses-dep
  brew:
    - pkg-config
    - ncurses
  # add support for Fedora
  dnf:
    - ncurses-devel

Объединение всего в реальную игру

Вот полный пример, охватывающий все три аспекта. В сборнике есть две роли, каждая из которых использует правильное значение become на основе одной переменной. Он также включает особые случаи для cmatrix и ffmpeg при установке с brew и обрабатывает альтернативные имена для ncurses между менеджерами пакетов.

# playbook.yml
---
- name: Demo
  hosts: localhost
  connection: local
  vars:
    should_be_root:  "{{ true if ansible_pkg_mgr != 'brew' else false }}"
  roles:
    - cmatrix
    - youtube-dl
# roles/cmatrix/defaults/main.yml
ncurses:
  default:
    - ncurses
  apt:
    - libncurses5-dev
    - libncursesw5-dev
  brew:
    - pkg-config
    - ncurses
  dnf:
    - ncurses-devel

# roles/cmatrix/tasks/main.yml
---
- name: cmatrix and dependencies are present
  # A var from above, in the playbook
  become: "{{ should_be_root }}"

  block:
    - name: ncurses is present
      package:
        name: '{{ item }}'
        state: latest
      # Get an array of the correct package names to install from the map in our
      # default variables file
      loop: "{{ ncurses[ansible_pkg_mgr] | default(ncurses['default']) }}"

    # Install as usual if this is not a brew system
    - name: cmatrix is present
      when: ansible_pkg_mgr != 'brew'
      package:
        name: cmatrix
        state: present
    # If it is a brew system, use this instead
    - name: cmatrix is present (brew)
      when: ansible_pkg_mgr == 'brew'
      homebrew:
        name: cmatrix
        state: head
        install_options: with-some-option
# roles/youtube-dl/tasks/main.yml
---
- name: youtube-dl and dependencies are present
  become: "{{ should_be_root }}"

  block:
    - name: ffmpeg is present
      when: ansible_pkg_mgr != 'brew'
      package:
        name: ffmpeg
        state: latest
    - name: ffmpeg is present (brew)
      when: ansible_pkg_mgr == 'brew'
      homebrew:
        name: ffmpeg
        state: head
        install_options: with-chromaprint, with-fdk-aac, with-etc-etc

    - name: atomicparsley is present
      package:
        name: atomicparsley
        state: latest

    - name: youtube-dl is present
      package:
        name: youtube-dl
        state: latest

Результат для Ubuntu:

$ ansible-playbook demo.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

PLAY [Demo] ********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [cmatrix : ncurses is present] ********************************************
ok: [localhost] => (item=libncurses5-dev)
ok: [localhost] => (item=libncursesw5-dev)

TASK [cmatrix : cmatrix is present] ********************************************
ok: [localhost]

TASK [cmatrix : cmatrix is present (brew)] *************************************
skipping: [localhost]

TASK [youtube-dl : ffmpeg is present] ******************************************
ok: [localhost]

TASK [youtube-dl : ffmpeg is present (brew)] ***********************************
skipping: [localhost]

TASK [youtube-dl : atomicparsley is present] ***********************************
ok: [localhost]

TASK [youtube-dl : youtube-dl is present] **************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=6    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
...