Yum repository bootstrapping in Ansible

I don't do cloud-scale work, but I do provision systems a lot, since I develop and maintain a custom Linux distribution that we sell on a box at Code Blue. With that in mind, and after hearing more about some of the options available at the inaugural meeting of DevOps West Michigan, I decided some weeks ago to investigate the current state-of-the-art in that field.

My short time with Puppet was marked with initial intrigue but eventually deep frustration over a specific thing I really wanted to do while provisioning new systems: add third-party yum respositories. A few of them, in fact. Trying to do this with the built-in yumrepo type ran me headlong into a really old bug (which I helped reproduce), which was partly responsible for me opting to check out Ansible.

I discovered that I really liked Ansible, but I was still stuck with a weird chicken-and-egg problem getting the repositories up and running on my systems. If I wanted to add, say, EPEL from the shell, I'd head to the epel-release download page (linked from the Fedora wiki), grab the latest RPM, and rpm -Uvh it. Tedious, manual, and not very automatable. Most users get around this (and the problem of the release package filename's changing) by creating their own .repo files in /etc/yum.repos.d from scratch, but this creates an interesting situation if a wanted package ever depends on its repository's release package—which is exactly the case for packages in the AsteriskNOW repo, since you've now got your little custom repo and the official repos from the release package both hanging around.

But then I stumbled across a really neat little trick. Stack Overflow user ravello posted about using a .repo file to bootstrap installing a release package, without knowing its version number—only requiring a stable repository URL and release package name. From that inspiration, I created a few tasks in my Ansible playbook to bootstrap the repositories using exclusively declarative tasks:

- name: bootstrap epel-release install
  copy: src=ansible-bootstrap-epel.repo
        owner=root group=root mode=0644

- name: epel-release install
  yum: name=epel-release

- name: epel repository enable
  ini_file: dest=/etc/yum.repos.d/epel.repo

The above does three things. First, it copies a bootstrap .repo file to /etc/yum.repos.d. The repository uses the official URL, has a unique name so as not to clash with the release package's .repo files, and—critically—is disabled. For EPEL, it looks like this:

name = Ansible bootstrap for epel
mirrorlist = http://mirrors.fedoraproject.org/mirrorlist?repo=epel-$releasever&arch=$basearch
failovermethod = priority
enabled = 0
gpgcheck = 0

From there, the enablerepo option on the yum task is used to temporarily enable the bootstrap repository long enough to install epel-release. Finally, the last task edits the new epel.repo from epel-release to enable it, since it ships disabled.

For AsteriskNOW, a similar set of tasks:

- name: bootstrap asterisknow-version install
  copy: src=ansible-bootstrap-asterisk-current.repo
        owner=root group=root mode=0644

- name: asterisknow-version install
  yum: name=asterisknow-version

- name: asterisk-11 repository enable
  ini_file: dest=/etc/yum.repos.d/centos-asterisk-11.repo

and a bootstrap .repo file:

name = Ansible bootstrap for asterisk-current
baseurl = http://packages.asterisk.org/centos/$releasever/current/$basearch/
enabled = 0
gpgcheck = 0

get the same job done.

I'm pretty sure this is the most robust way to get third-party yum repositories up and running on Ansible-managed systems. I'm pretty thrilled with it, in any event. I've used it, with slight modification, in the Vagrant hacking setup for octothorpe.