Home > Puppet > Precommit hook for Puppet code

Precommit hook for Puppet code

Please God, give me the strength to lead
It’s my charge to keep
Make the sorrow the fuel I need
It’s my charge to keep

(Iced Earth – A charge to keep)

As my colleagues slowly pick up the pace with Puppet modules development I’ve charged, we stumbled upon a problem experienced developers see so often. Code that was pushed to our repository started to get messy. Syntax errors were rare, but the coding style standard went berzerk with the first devop that joined the puppet “team”. My initial reaction was to re-factor the code every now and then, and by re-factor I mean running checks on my own working copy. And that started to consume too much of my time… rewriting modules, committing, checking if they they work as intended after my changes… and finally I was pissed… Being obsessive compulsive may seem counter-productive at times but some rules just have to be obeyed.
So, instead of continuing to fix other’s mistakes, I’ve decided to put a stop on them. This is what I was doing on our modules repository:

for i in $(find ~/puppet -regex ".*pp$"); do
  echo $i;
  puppet parser validate $i;
  if [[ ! $? -eq 0 ]]; then
    echo"* puppet syntax errors found!"
    break
  fi
  puppet-lint --no-80chars-check --fail-on-warnings $i
  if [[ ! $? -eq 0 ]]; then
    echo "* puppet code style broken!"
    break
  fi
done

This would spit out any syntax error or code style divergence. So, my idea now was to offload that to people writing code. So, I decided to make a precommit hook. We use mercurial, so my .hg/hgrc looks like this:

[paths]
default = https://code.google.com/p/lutak/

[hooks]
pretxncommit.puppet = .hg/check_puppet.rb

And finally, check_puppet.rb script (which has to be set executable):

#!/usr/bin/ruby

def puppet_parser_validate(file)
    if !system('puppet parser validate ' + file + ' > /dev/null 2>&1')
    print('!!! Syntax error in file: ' + file + "\n")
        system('puppet parser validate ' + file)
    exit(1)
    end
end

def puppet_lint(file)
    if !system('puppet-lint --no-80chars-check --fail-on-warnings ' + file + ' > /dev/null 2>&1')
    print('!!! Coding style error in file: ' + file + "\n")
        system('puppet-lint --no-80chars-check --fail-on-warnings ' + file)
    exit(1)
    end
end

def puppet_erb_check(file)
    if !system('erb -x -T \'-\' ' + file + ' | ruby -c > /dev/null 2>&1')
    print('!!! Syntax error in erb template: ' + file + "\n")
        system('erb -x -T \'-\' ' + file + ' | ruby -c')
    exit(1)
    end
end

def puppet_ruby_check(file)
    if !system('ruby -c ' + file + ' > /dev/null 2>&1')
    print('!!! Syntax error in erb template: ' + file + "\n")
        system('ruby -c ' + file)
    exit(1)
    end
end

# go through list of files, and call adequate checks
IO.popen('hg status | grep -v ^R').readlines.each { |file| 
    file.sub!(/^[\w|\?] (.*)\n/,'\1')
    if file.match('.pp$')
        puppet_parser_validate file
        puppet_lint file
    elsif file.match('.erb$')
    puppet_erb_check file
    elsif file.match('.rb$')
    puppet_ruby_check file
    end
}

Script checks every file that occurs in the output of hg status. Some may not be happy with that solution, so if you want to skip files that are not yet added to your repository, you could change grep -v ^R to egrep -v “^(p|\?)”. If you want to run this script, the machine has to have puppet and rubygem puppet-lint installed. You can fetch the latter from SRCE repositories if you use RHEL/CentOS:

ftp://ftp.srce.hr/redhat/base/el6/x86_64/rubygem-puppet-lint-0.3.2-1.el6.srce.noarch.rpm
ftp://ftp.srce.hr/redhat/base/el5/x86_64/rubygem-puppet-lint-0.3.2-1.el5.srce.noarch.rpm

Now, next problem is how to force every member of a team to adhere to precommit and not turn it off? Well, I haven’t come to that part yet 🙂 But social engineering seems to work so far. So, if the people understand the benefit of such approach, they will in most cases respect it and use it. Otherwise I’ll be hanging at their necks 🙂

Note that these are just basic tests, in the long run I will try to implement some kind of CI solution that runs complex logical tests of the codebase, because with every new commit there comes dozen of new problems 🙂

Advertisements
Categories: Puppet Tags: , , ,
  1. October 29, 2015 at 1:41 pm

    Hello,

    It seemed to me that some of the code towards the bottom of the page appears to have been cut off.

    if file.match(‘.pp puppet_parser_validate file
    puppet_lint file
    elsif file.match(‘.erb puppet_erb_check file
    elsif file.match(‘.rb puppet_ruby_check file
    end

    vs

    if file.match(‘.pp’)
    puppet_parser_validate(file)
    elsif file.match(‘.erb’)
    puppet_erb_check (file)
    elsif file.match(‘.rb’)
    puppet_ruby_check (file)
    end

    cheers,
    Luke

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: