Назад | Перейти на главную страницу

Ansible: могу ли я использовать vars_files, когда некоторые файлы не существуют

Это часть:

vars_files:
  - vars/vars.default.yml
  - vars/vars.yml

Если файл vars/vars.yml не существует - вот ошибка.

ERROR: file could not read: /.../vars/vars.yml

Как я могу загрузить дополнительные переменные из этого файла, только если он существует? (без ошибок)

На самом деле это довольно просто. Вы можете объединить различные элементы vars_files в один кортеж, и Ansible будет автоматически просматривать каждый из них, пока не найдет существующий файл и не загрузит его. E.x .:

vars_files:
  - [ "vars/foo.yml", "vars/bar.yml", "vars/default.yml" ]

В соответствии с Ansible разработчики, то правильный способ решить эту проблему - использовать что-то вроде:

vars_files_locs: ['../path/to/file1', '../path/to/file2', ...]

- include_vars: "{{ item }}"
  with_first_found: vars_files_locs

Более того, они говорят:

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

Я столкнулся с этой проблемой в настройке, где мне нужно было создать несколько сред развертывания (live, demo, sandbox) на одном физическом сервере (здесь не разрешены виртуальные машины), а затем сценарий для развертывания произвольных репозиториев svn

Для этого требовалось дерево каталогов (необязательных) файлов variable.yml, которые будут сливаться друг над другом и не генерировать исключение, если оно отсутствует.

Начните с включения слияния переменных в анзибле - обратите внимание, что при этом выполняется неглубокое слияние хэшей (глубина 1 уровня), а не полностью рекурсивное глубокое слияние

ansible.cfg

[defaults]
hash_behaviour=merge ;; merge rather than replace dictionaries http://docs.ansible.com/ansible/intro_configuration.html###hash-behaviour

Разметка каталогов Ansible

/group_vars
└── all.yml

/playbooks
├── boostrap.yml
├── demo.yml
├── live.yml
└── sandbox.yml

/roles/deploy/
├── files
├── tasks
│   ├── includes.yml
│   ├── main.yml
└── vars
    ├── main.yml
    ├── project_1.yml
    ├── project_2.yml
    ├── demo
    │   ├── project_1.yml
    │   ├── project_2.yml   
    │   └── main.yml
    ├── live
    │   ├── project_1.yml
    │   ├── project_2.yml   
    │   └── main.yml
    └── sandbox
        ├── project_1.yml
        ├── project_2.yml   
        └── main.yml

роли / развертывание / задачи / includes.yml

Это основная логика для дерева каталогов необязательных файлов переменных.

;; imports in this order:
;; - /roles/deploy/vars/main.yml
;; - /roles/deploy/vars/{{ project_name }}.yml
;; - /roles/deploy/vars/{{ project_name }}/main.yml
;; - /roles/deploy/vars/{{ project_name }}/{{ project_env }}.yml
- include_vars:
    dir: 'vars'
    files_matching: "{{ item }}"
    depth: 1
  with_items:
    - "main.yml"
    - "{{ project_name }}.yml"

- include_vars:
    dir: 'vars/{{ env_name }}'
    files_matching: "{{ item }}"
    depth: 1
  with_items:
    - "main.yml"
    - "{{ project_name }}.yml"

group_vars / all.yml

Настроить переменные по умолчанию для проекта и различных пользователей и сред

project_users:
    bootstrap:
        env:   bootstrap
        user:  ansible
        group: ansible
        mode:  755
        root:  /cs/ansible/
        home:  /cs/ansible/home/ansible/
        directories:
            - /cs/ansible/
            - /cs/ansible/home/

    live:
        env:   live
        user:  ansible-live
        group: ansible
        mode:  755
        root:  /cs/ansible/live/
        home:  /cs/ansible/home/ansible-live/

    demo:
        env:   demo
        user:  ansible-demo
        group: ansible
        mode:  755
        root:  /cs/ansible/demo/
        home:  /cs/ansible/home/ansible-demo/

    sandbox:
        env:   sandbox
        user:  ansible-sandbox
        group: ansible
        mode:  755
        root:  /cs/ansible/sandbox/
        home:  /cs/ansible/home/ansible-sandbox/    

project_env:  bootstrap
project_user: "{{ ansible_users[project_env] }}" ;; this will be retroactively updated if project_env is redefined later

роли / развертывание / вары / main.yml

параметры проекта по умолчанию

ansible_project:
  node_env:   development
  node_port:  4200
  nginx_port: 4400

роли / развертывание / vars / project_1.yml

значения по умолчанию для project_1

ansible_project:
  node_port:  4201
  nginx_port: 4401

роли / развертывание / варс / жить / main.yml

значения по умолчанию для живой среды, отменяет значения по умолчанию для проекта

ansible_project:
  node_env: production

роли / развертывание / варс / жить / проект_1.yml

окончательные переопределения для project_1 в реальной среде

ansible_project:
  nginx_port: 80

playbooks / demo.yml

Настройте отдельные playbooks для каждой среды

- hosts: shared_server
  remote_user: ansible-demo
  vars:
    project_env: demo
  pre_tasks:
    - debug: "msg='{{ facter_gid }}@{{ facter_fqdn }} ({{ server_pseudonym }})'"
    - debug: var=project_ssh_user
  roles:
    - { role: deploy, project_name: project_1 }

ПРЕДУПРЕЖДЕНИЕ: поскольку все среды находятся на одном хосте, все playbooks должны запускаться индивидуально, иначе Ansible будет некорректно пытаться запустить все сценарии как первый пользователь ssh и использовать переменные только для первого пользователя. Если вам нужно запускать все сценарии последовательно, используйте xargs для запуска каждого из них как отдельных команд.

find ./playbooks/*.yml | xargs -L1 time ansible-playbook
- hosts: all
  vars_files: vars/vars.default.yml
  vars:
    optional_vars_file: "{{ lookup('first_found', 'vars/vars.yml', errors='ignore') }}"
  tasks:
  - when: optional_vars_file is file
    include_vars: "{{ optional_vars_file }}"

Примечание. Тесты пути (это файл, существует, ...) работают только с абсолютными путями или путями относительно текущего рабочего каталога при выполнении команды ansible-playbook. Это причина, по которой мы использовали поиск. поиск принимает пути относительно каталога playbook и возвращает абсолютный путь, если файл существует.

Или более ямльным способом:

- hosts: webservers
  vars:
    paths_to_vars_files:
      - vars/{{ ansible_hostname }}.yml
      - vars/default.yml
  tasks:
    - include_vars: "{{ item }}"
      with_first_found: "{{ paths_to_vars_files }}"

То есть вместо записи массива в одну строку с квадратными скобками, например:

['path/to/file1', 'path/to/file2', ...]

Используйте способ yaml для записи значений массива в несколько строк, например:

- path/to/file1
- path/to/file2

Как уже упоминалось, это ищет файл vars с именем {{ ansible_hostname }}.yml, и если он не существует, использует default.yml

Соединение различных частей вместе ... include_vars с предложением when, которое истинно, когда файл существует. т.е.

vars:
  file_to_include: /path/to/file
tasks:
  - include_vars: "{{ file_to_include }}"
    when: file_to_include is exists

Новый ответ, основанный на последних версиях Ansible - в основном вам следует использовать with_first_found, вместе с skip: true чтобы пропустить задачу, если файл не найден.

- name: Include vars file if one exists meeting our condition.
  include_vars: "{{ item }}"
  with_first_found:
    - files:
        - vars/{{ variable_here }}.yml
      skip: true

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

См. Связанные: https://stackoverflow.com/a/39544405/100134