Ansible: ¿Puedo usar vars_files cuando algunos archivos no existen?

17

Esa es la parte:

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

Si vars/vars.ymlno existe un archivo , aquí hay un error.

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

¿Cómo puedo cargar variables adicionales de este archivo solo si existe? (sin errores)

Sergey
fuente

Respuestas:

27

Es bastante simple en realidad. Puede aplastar sus diferentes elementos vars_files en una sola tupla y Ansible revisará automáticamente cada uno hasta que encuentre un archivo que exista y lo cargue. Ex:

vars_files:
  - [ "vars/foo.yml", "vars/bar.yml", "vars/default.yml" ]
Garrett
fuente
44
Según los desarrolladores de Ansible , esta solución cargará todos los archivos, no solo el primero encontrado.
tjanez
10

Según los desarrolladores de Ansible , la forma correcta de resolver esto es usar algo como:

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

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

Además, dicen :

Lo anterior cargará correctamente solo el primer archivo encontrado, y es más flexible que intentar hacerlo a través de la vars_filespalabra clave del idioma.

tjanez
fuente
"solo se encontró el primer archivo" - la idea era redefinir algunas variables, no todas
Sergey
@Sergey, leyendo de nuevo tu pregunta, veo que lo que querías es un poco diferente. Gracias por señalar esto. Dejaré la respuesta tal como está si alguien más lo encuentra útil.
tjanez
1
excepto que include_varsen la tarea dará una alta precedencia de variables en comparación con el rol defaultsovars
Alex F
2

Encontré este problema en una configuración en la que necesitaba crear múltiples entornos de implementación (en vivo, demo, sandbox) en el mismo servidor físico (no se permiten máquinas virtuales aquí), y luego un script para implementar repositorios svn arbitrarios

Esto requería un árbol de directorios de archivos variables.yml (opcional), que se fusionarían uno encima del otro y no arrojarían una excepción si faltaba alguna.

Comience habilitando la fusión de variables en ansible: tenga en cuenta que esto hace una fusión hash superficial (1 nivel de profundidad) y no una fusión profunda totalmente recursiva

ansible.cfg

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

Diseño de directorio 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

roles / desplegar / tareas / incluye.yml

Esta es la lógica principal para un árbol de directorios de archivos variables opcionales.

;; 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

Configurar variables predeterminadas para el proyecto y varios usuarios y entornos.

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

roles / deploy / vars / main.yml

valores predeterminados del proyecto

ansible_project:
  node_env:   development
  node_port:  4200
  nginx_port: 4400

roles / deploy / vars / project_1.yml

valores predeterminados para proyecto_1

ansible_project:
  node_port:  4201
  nginx_port: 4401

roles / deploy / vars / live / main.yml

valores predeterminados para el entorno en vivo, anula los valores predeterminados del proyecto

ansible_project:
  node_env: production

roles / deploy / vars / live / project_1.yml

anulaciones finales para project_1 en el entorno en vivo

ansible_project:
  nginx_port: 80

playbooks / demo.yml

Configure libros de jugadas separados para cada entorno

- 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 }

ADVERTENCIA: Debido a que todos los entornos viven en un solo host, todos los libros de jugadas deben ejecutarse individualmente; de ​​lo contrario, Ansible intentará ejecutar todos los scripts como el primer usuario de inicio de sesión ssh y solo usará las variables para el primer usuario. Si necesita ejecutar todos los scripts secuencialmente, use xargs para ejecutarlos cada uno como comandos separados.

find ./playbooks/*.yml | xargs -L1 time ansible-playbook
James McGuigan
fuente
1
- 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 }}"

Nota: Las pruebas de ruta (es archivo, existe, ...) funcionan solo con rutas absolutas o rutas relativas al directorio de trabajo actual cuando se ejecuta el comando ansible-playbook. Esta es la razón por la que usamos la búsqueda. la búsqueda acepta rutas relativas al directorio del libro de jugadas y devuelve la ruta absoluta cuando existe el archivo.

Ejez
fuente
0

O de una manera más yaml:

- 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 }}"

Es decir, en lugar de escribir una matriz en una línea con corchetes, como:

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

Use la forma yaml de escribir valores de matriz en varias líneas, como:

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

Como se mencionó, esto busca un archivo vars llamado {{ ansible_hostname }}.yml, y si no existe usadefault.yml

Donn Lee
fuente
Esta respuesta usa el mismo código que este, excepto que usa datos diferentes. Es decir {{ ansible_hostname }}.yml, nombre de archivo en lugar de ../path/to/file1. ¿Cuál es el punto de? Se puede agregar un número ilimitado de nombres de archivos de entrada.
techraf
@techraf: Ok, agregué algunas aclaraciones / amplificaciones sobre por qué se envió una nueva respuesta. Esto se debe a que los comentarios por defecto del servidor no admiten fragmentos de código de varias líneas, y solo estaba señalando que las matrices yaml se escriben con frecuencia (¿preferiblemente?) En varias líneas. También estaría bien si se editara la respuesta anterior y se mostrara el formato de matriz de varias líneas, como lo veo con más frecuencia. Entonces mi respuesta puede ser eliminada.
Donn Lee
Ambas anotaciones se especifican en los documentos de Ansible en un capítulo Conceptos básicos de YAML . El hecho de que vea uno más a menudo que el otro todavía no hace que esta sea una nueva respuesta.
techraf
0

Uniendo varias piezas juntas ... include_vars con una cláusula when que es verdadera cuando existe el archivo. es decir

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

Nueva respuesta basada en las últimas versiones de Ansible: básicamente, debe usarla with_first_foundjunto con skip: truepara omitir la tarea si no se encuentra ningún archivo.

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

Esto hace que no tenga que tener un archivo vars alternativo en esa lista.

Ver relacionado: /programming//a/39544405/100134

geerlingguy
fuente