6

Interesting Uses of Ansible's ternary filter

 6 months ago
source link: https://www.zufallsheld.de/2024/02/21/interesting-use-of-ansible-ternary-filter/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Interesting Uses of Ansible’s ternary filter

Posted on Wed 21 February 2024

Some time ago I discovered an interesting use of the ternary-filter in Ansible. A ternary-filter in Ansible is a filter that takes three arguments: a condition, a value if the condition is true and an alternative value if the condition is false.

Here’s a simple example straight from Ansible’s documentation:

- name: service-foo, use systemd module unless upstart is present, then use old service module
  service:
    state: restarted
    enabled: yes
    use: "{{ (ansible_service_mgr == 'upstart') | ternary('service', 'systemd') }}"

But there are many more interesting use cases for this filter and I decided to take a look what Ansible’s collection authors used it for.

Display command output only if verbosity is greater than 0.

This was the usage that initially got me interested in different use-cases for the filter.

Depending on what verbosity level you use when running a playbook (e.g. how many -v you add to the command), the command will run in quiet-mode (with the -quiet flag) or not.

- name: Validate configuration
  become: true
  become_user: "{{ consul_user }}"
  ansible.builtin.command: >
    {{ consul_binary }} validate {{ (ansible_verbosity == 0) | ternary("-quiet", "") }}
    {{ consul_config_path }}/config.json {{ consul_configd_path }}
  changed_when: false

(Source)

HN-User raziel2p gave an even better example to achieve this:

- name: Validate configuration
  become: true
  become_user: "{{ consul_user }}"
  ansible.builtin.command: >
    {{ consul_binary }} validate {{ "-quiet" if ansible_verbosity == 0 }}
    {{ consul_config_path }}/config.json {{ consul_configd_path }}
  changed_when: false

Testing task idempotency in one run without molecule

This use of the ternary-filter is useful for testing task idempotency in one run.

First, the task-file test_create_scheduler.yml is imported without a variable set, so the task will change something. Then, the task-file is imported again, however this time with the variable test_proxysql_scheduler_check_idempotence set to true.

- name: "{{ role_name }} | test_create_scheduler | test create scheduler"
  import_tasks: test_create_scheduler.yml

- name: "{{ role_name }} | test_create_scheduler | test idempotence of create scheduler"
  import_tasks: test_create_scheduler.yml
  vars:
    test_proxysql_scheduler_check_idempotence: true

When importing the test file test_create_scheduler.yml without the variable test_proxysql_scheduler_check_idempotence, the assert will check for status is changed, because the ternary-filter evaluated the variable test_proxysql_scheduler_check_idempotence as false.

- name: "{{ role_name }} | {{ current_test }} | check if create scheduler reported a change"
  assert:
    that:
      - "status is {{ test_proxysql_scheduler_check_idempotence|ternary('not changed', 'changed') }}"

When importing the test file test_create_scheduler.yml with the variable test_proxysql_scheduler_check_idempotence, the assert will check for status is not changed, because the ternary-filter evaluated the variable test_proxysql_scheduler_check_idempotence as true.

(Source)

Do things based on regex searches

In Ansible you can chain filters using a pipe (|). This allows you to filter based on regex searches (which in hindsight is obvious that it works, but I never thought about that).

- name: "{{ role_name }} | {{ current_test }} | are we performing a delete"
  set_fact:
    test_delete: "{{ current_test | regex_search('^test_delete') | ternary(true, false) }}"

(Source)

Handle older Python versions easily

In the following task, the cassandra-driver is installed. If the used (obsolete!) Python version starts with 2.7, pip should install the cassandra-driver in version 3.26.*. If a recent Python version is used, pip will install the latest version of the cassandra-driver.

- name: Install cassandra-driver
  pip:
    name: "cassandra-driver{{ ansible_python_version.startswith('2.7') | ternary('==3.26.*', '') }}"

That’s definitely not the most elegant solution, but it works. (I’d probably have tried to install the correct cassandra-driver version according to the operating system and its Python version, wher eit should be installed)

(Source)

Comment line in template if var is defined

This task will add a line starting with ssl_ciphers, if the variable zabbix_web_ssl_cipher_suite is defined and not none. Otherwise it will add the same line but commented out.

{{ (zabbix_web_ssl_cipher_suite is defined and zabbix_web_ssl_cipher_suite is not none) | ternary('', '# ') }}ssl_ciphers {{ zabbix_web_ssl_cipher_suite | default('') }}

(Source)

I used another way to add commented out lines in a template (see). I used the comment-filter:

{{ "HostKeyAlgorithms " ~ ssh_host_key_algorithms|join(',') if ssh_host_key_algorithms else "HostKeyAlgorithms" | comment }}

Now that I see my own code, I could probably use the ternary-filter here, too!

{{ ssh_host_key_algorithms | ternary("HostKeyAlgorithms " ~ ssh_host_key_algorithms|join(','), "HostKeyAlgorithms" | comment) }}

But I think I actually like the if-else syntax more.

Do you have any other interesting uses of the ternary-filter?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK