Ansible: font installation vs. idempotence

Hi,

I just wrote the first draft of an Ansible role to download and install Microsoft TrueType fonts on my workstation. Here’s what I have so far:

---  # tasks file for install_msfonts

- name: Download Microsoft TrueType fonts archive
  ansible.builtin.get_url:
    url: https://ponce.cc/slackware/sources/repo/webcore-fonts-3.0.tar.gz
    dest: /tmp
    checksum: sha256:\
              dc72eac479448016286ec66efa6a17ad133d9a9bf685c1f3349a3c4dfce4f419

- name: Extract Microsoft TrueType fonts archive
  ansible.builtin.unarchive:
    src: /tmp/webcore-fonts-3.0.tar.gz
    dest: /tmp
    remote_src: true

- name: Download patched Symbol font
  ansible.builtin.get_url:
    url: https://ponce.cc/slackware/sources/repo/symbol.gz
    dest: /tmp
    checksum: sha256:\
              b1d688bbd0473e8f33600fe9b9c17e4f361ed0890a64a4dc344faac4edc57d00

- name: Extract patched Symbol font
  ansible.builtin.command:
    cmd: gzip -dk /tmp/symbol.gz
    creates: /tmp/symbol

- name: Patch Symbol font
  ansible.builtin.copy:
    src: /tmp/symbol
    dest: /tmp/webcore-fonts/fonts/symbol.ttf
    remote_src: true

- name: Split Cambria TTC file into two TTFs
  ansible.builtin.shell:
    cmd: >
      cd /tmp/webcore-fonts
      && fontforge -lang=ff -c
      'Open("vista/CAMBRIA.TTC(Cambria)");
      Generate("vista/CAMBRIA.TTF");
      Close();
      Open("vista/CAMBRIA.TTC(Cambria Math)");
      Generate("vista/CAMBRIA-MATH.TTF");
      Close();'
      && rm vista/CAMBRIA.TTC
    creates:
      - CAMBRIA.TTF
      - CAMBRIA-MATH.TTF

- name: Create Microsoft TrueType fonts directory
  ansible.builtin.file:
    path: /usr/share/fonts/microsoft
    state: directory
    owner: root
    group: root
    mode: 0755

- name: Install Microsoft TrueType fonts
  ansible.builtin.copy:
    src: "{{ item }}"
    dest: /usr/share/fonts/microsoft
    remote_src: true
  loop:
    - /tmp/webcore-fonts/fonts/
    - /tmp/webcore-fonts/vista/
  notify: Update fonts cache

...

And here’s the handler that goes with it:

---  # handlers file for install_msfonts

- name: Update fonts cache
  ansible.builtin.command:
    cmd: fc-cache -f -v

...

So far the play works in the sense that it does what it is supposed to do. It downloads the fonts, extracts the archives, does a little magic by patching a buggy font and splitting another font that comes in the wrong format. It installs the fonts in a place that makes sense and updates the font cache.

Now I’m relatively new to Ansible, and one thing I wonder about now is how to make this idempotent. So I thought I’d rather ask the Ansible gurus here for the odd suggestion.

Cheers,

Niki

This looks nice! Ansible really is a great tool with many uses!

There are a few ways to do add idempotency. One way would be to register the output of fc-list and use a when statement to search for CAMBRIA.TTF and CAMBRIA-MATH.TTF.

So kinda like this:

 tasks:
    - name: Create variable from command
      command: "echo Hello"
      register: command_output
    - debug: msg="{{command_output.stdout}}"
block:
  - task: do all the installing process here
  

when: '"cambria.tt" not in command.stdout' and '"cambra-math.tt" not in command.stdout'

Another way is to put more functionality into the handler and call it when things are changed…aka they needed changing cause stuff is not installed…

I’m sure there are other ways as well but hopefully this will assist you and get you headed in a good direction! :slight_smile:

1 Like

I experimented some more and found a solution that sticks to the KISS principle. The trick here is to keep downloaded files and archives in a place that makes sense like /var/tmp/download, since /var/tmp is a temporary directory that is supposed to keep its contents after a reboot. Now the script is 100% idempotent and more straightforward:

---  # tasks file for install_msfonts

- name: Create download directory
  ansible.builtin.file:
    path: /var/tmp/download
    state: directory
    owner: root
    group: root
    mode: 0755

- name: Download Microsoft TrueType fonts archive
  ansible.builtin.get_url:
    url: https://ponce.cc/slackware/sources/repo/webcore-fonts-3.0.tar.gz
    dest: /var/tmp/download
    checksum: sha256:\
              dc72eac479448016286ec66efa6a17ad133d9a9bf685c1f3349a3c4dfce4f419

- name: Download patched Symbol font
  ansible.builtin.get_url:
    url: https://ponce.cc/slackware/sources/repo/symbol.gz
    dest: /var/tmp/download
    checksum: sha256:\
              b1d688bbd0473e8f33600fe9b9c17e4f361ed0890a64a4dc344faac4edc57d00

- name: Extract Microsoft TrueType fonts archive
  ansible.builtin.unarchive:
    src: /var/tmp/download/webcore-fonts-3.0.tar.gz
    dest: /var/tmp/download
    remote_src: true
    creates: /var/tmp/download/webcore-fonts/webcore-fonts.spec

- name: Split Cambria TTC file
  ansible.builtin.shell:
    cmd: >
      cd /var/tmp/download/webcore-fonts
      && fontforge -lang=ff -c
      'Open("vista/CAMBRIA.TTC(Cambria)");
      Generate("vista/CAMBRIA.TTF");
      Close();
      Open("vista/CAMBRIA.TTC(Cambria Math)");
      Generate("vista/CAMBRIA-MATH.TTF");
      Close();'
    creates: /var/tmp/download/webcore-fonts/vista/CAMBRIA.TTF

- name: Extract patched Symbol font
  ansible.builtin.command:
    cmd: gzip -dk /var/tmp/download/symbol.gz
    creates: /var/tmp/download/symbol

- name: Patch Symbol font
  ansible.builtin.copy:
    src: /var/tmp/download/symbol
    dest: /var/tmp/download/webcore-fonts/fonts/symbol.ttf
    remote_src: true

- name: Create Microsoft TrueType fonts directory
  ansible.builtin.file:
    path: /usr/share/fonts/microsoft
    state: directory
    owner: root
    group: root
    mode: 0755

- name: Get list of Microsoft TrueType fonts
  ansible.builtin.find:
    paths: /var/tmp/download/webcore-fonts
    recurse: true
    patterns: '*.ttf,*.TTF'
  register: truetype_fonts

- name: Install Microsoft TrueType fonts
  ansible.builtin.copy:
    src: "{{ item.path }}"
    dest: /usr/share/fonts/microsoft
    remote_src: true
  with_items: "{{ truetype_fonts.files }}"
  notify: Update fonts cache

...

And here’s the handler:

---  # handlers file for install_msfonts

- name: Update fonts cache
  ansible.builtin.command:
    cmd: fc-cache -f -v

...

Cheers,

Niki

1 Like

A long time ago …

We gathered MS fonts and packed them into a RPM. We had local repo where that package (and many others) were. We even had some of our configs in RPM packages, installed within kickstart.

That was before Ansible …

1 Like

… in an ansible-galaxy far far away… :upside_down_face:

3 Likes