tl;dr: Use a connection plugin to ensure the filesystem is writable.

Voyage Linux is, according to their website a:

Debian derived distribution that is best run on a x86 embedded platforms such as PC Engines ALIX/WRAP, Soekris 45xx/48xx/65xx and Atom-based boards.

So far so good. One of the features of Voyage is that it is designed to run on a CF card (further implementation details are available in the docs). In short, and in order to avoid wear, only a few directories are mounted read-write.

The idea is that if you wish to make changes to configuration or otherwise write to the filesystem, you run the remountrw command before making the changes, and the remountro command afterwards.

Obviously most of the tasks an IT Automation tool like Ansible will routinely do involve making changes to the state of the system. What we want to do is ensure that the filesystem is writable for the duration of any task that Ansible may be trying to do, and then attempt to put it back at the end.1

There are two obvious ways we can accomplish this:

Manually, with tasks

We can execute tasks at the beginning and end of our playbooks. For example:

- hosts: voyage-hosts
  pre_tasks:
    - shell: /usr/local/sbin/remountrw
  roles:
    # ...
  post_tasks:
    - shell: /usr/local/sbin/remountro
      ignore_errors: yes

This is unwieldy. We have to do this in each playbook, it breaks ad-hoc commands and tagging. In addition, if the play is aborted, the final remountro won’t be executed.

With a Connection Type Plugin

Ansible supports various connection methods (default: ssh) to allow for connecting to managed hosts in different ways. We can write a custom Connection Type Plugin which will do the setup and tear-down for us, which means that we never have to remember, and tagging and ad-hoc commands work as designed. Excellent.

The plugin I have come up with can be found in this gist, and all it does is wrap the built-in ssh connection plugin and attempt to call remountrw and remountro in the appropriate places.

Simply place this file in your connection plugins directory and set the ansible_connection variable to voyage_ssh on all relevant hosts.

A possible refinement would be to only run the commands if the connection plugin has not yet run them, but this works relatively efficiently and has not gotten in my way over the past several months of use.

  1. Notice I said trying - if some other process has a file open for writing, we’ll get a mount: / is busy warning. I don’t want this to cause the Ansible run to fail.