Using vim to edit puppet code

Created: 05 Oct 2013

These are some bits of vim configuration that have helped speed up both reading and writing puppet code.

I’ve described the way I install things using Ubuntu, but everything should be possible on other distributions and operating systems, if you understand their packaging systems.

Syntax highlighting and indentation

I find colour highlighting to be useful. When reading a codebase it seems to help me read manifests quicker e.g. by highlighting the boundaries between variables and literal strings inside double quoted strings, or between resource names and parameters. When editing, it alerts me to the cases when I’ve missed out a quote to terminate a string.

I install and use the puppet plugin provided by the vim-puppet ubuntu package from the puppetlabs apt repo to match the version of puppet I’m using. I then enable the addon for all users:

sudo vim-addons install -w puppet

If there aren’t any packages for your platform, the plugin resides within the puppet source with installation instructions.

Note: there is an alternative puppet plugin for vim with more features, such as automatic formatting and syntax checking.

These entries in ~/.vimrc ensure that I have all the syntax highlighting on and defines the preferred indentation of two spaces:

 syntax on
 au FileType puppet setlocal tabstop=8 expandtab shiftwidth=2 softtabstop=2

I use the ubuntu exuberant-ctags package to generate tag files for vim to read. Mac users, go here.

To make ctags understand puppet’s syntax, I have the ~/.ctags definition from Paul Nasrat:

--langdef=puppet
--langmap=puppet:.pp
--regex-puppet=/^class[ \t]*([:a-zA-Z0-9_\-]+)[ \t]*/\1/d,definition/
--regex-puppet=/^site[ \t]*([a-zA-Z0-9_\-]+)[ \t]*/\1/d,definition/
--regex-puppet=/^node[ \t]*([a-zA-Z0-9_\-]+)[ \t]*/\1/d,definition/
--regex-puppet=/^define[ \t]*([:a-zA-Z0-9_\-]+)[ \t]*/\1/d,definition/

I then run ctags -R in the base of the manifests directory to generate a tags file.

There are two vim directives that make vim’s ctags support more useful. Firstly, this tells vim to look upwards in the directory hierarchy for a tags file until it finds one:

 set tags=./tags;

This means that if, my current working directory is modules/web_server, vim will still find the tags file I generated in the root of the puppet manifests. The default behaviour is to look for a tags file in the current working directory.

The next directive helps vim properly parse class, define and variable names of the form ‘web_server::config’. By default ‘:’ will end a word, causing that qualified name to be treated as two words.

Adding this to .vimrc causes vim to treat ‘:’ as part of the keyword for tag navigation purposes.

au FileType puppet setlocal isk+=:

Now open a manifest with vim and hit Ctrl+] to follow to the definition of a class or define. Use Ctrl+T to jump back one level in the tag stack.

This is extremely useful in the case of a large set of manifests, especially one unfamiliar to you.

Finally, you’ll probably also want to exclude tags from being noticed by your revision control system.

Creating boilerplate

When writing puppet manifests there can be a lot of semi-repetitive text entry; when defining a file for example, I set a minimum of four parameters in addition to the namevar.

 file { '/etc/default/useradd':
   content => template ('users/default/useradd.erb'),
   owner   => root,
   group   => root,
   mode    => 0644,
 }

In order to speed this up a bit, I have SnipMate configured with snippets for puppet, based on those supplied by R.I.Pienaar, but with edits to suit my preferred style and additions to include local defines/classes that I use frequently.

Closing words

And that’s pretty much it; it doesn’t require a lot of work to set up, but a worthwhile gain in efficiency when maintaining puppet code.

In a future blog post I’ll explain how I use puppet to install this config on all the machines I use regularly.