Puppet facter facts for syslog daemon type and version, symantec netbackup

I have a few more custom facts that I’ve added to my puppet-facter-facts github repository:

  • syslog_bin, syslog_type, and syslog_version – tell the absolute path to the running syslog binary, its short name (basename), and its version as a string. Currently only know about /sbin/syslogd and /sbin/rsyslogd.
  • has_netbackup – tests for presence of the /usr/openv/netbackup/bin directory, created by installation of Symantec Netbackup. Useful for making generation of include/exclude files conditional on having NetBackup installed.

Hopefully some of these will be of use to someone else as well.

Puppet facter fact for all applied classes, returned as a CSV list

I’m unfortunatey stuck, at least for the time being, using flat-file manifests to configure my puppet nodes. Without an ENC, it’s pretty difficult to get a good ovewview of what classes are used on each node, and what nodes use a given class. I know I could write up a simple web tool to do this (unfortunately, given my limited Ruby knowledge, it would have to be in PHP or Perl, not a real modification to Dashboard in Ruby). But where to get the data from?

After some research, I found a puppet fact for puppet classes on Matthew Nicholson’s Coffee & Beer blog. It parses /var/lib/puppet/classes.txt and returns the list of classes found as a JSON array. Great base, but I wanted something easier, that would be more easily parsed from its direct storage in MySQL. My modification to his code is onlty a few characters; I dropped out the JSON require, and return the classes as a CSV list. This lets me to easy LIKE '%,classname,%' SELECTs in MySQL, and also gives me the fact value stored in the puppet DB, so I can build a separate tool around that data. Thanks, Matt.

#
# facter fact for puppet classes on node, pulled from /var/lib/puppet/classes.txt
# from <http://sjoeboo.github.com/blog/2012/07/31/updated-puppet-facts-for-puppet-classes/>
#
 
require 'facter'
begin
        Facter.hostname
        Facter.fqdn
rescue
        Facter.loadfacts()
end
hostname = Facter.value('hostname')
fqdn = Facter.value('fqdn')
 
classes_txt = "/var/lib/puppet/classes.txt"
 
if File.exists?(classes_txt) then
        f = File.new(classes_txt)
        classes = Array.new()
        f.readlines.each do |line|
                line = line.chomp.to_s
                line = line.sub(" ","_")
                classes.push(line)
        end
        classes.delete("settings")
        classes.delete("#{hostname}")
        classes.delete("#{fqdn}")
        Facter.add("puppet_classes_csv") do
                setcode do
                        classes.join(",")
                end
        end
end

All of my facts are now available in a GitHub repository: https://github.com/jantman/puppet-facter-facts.

Puppet facter fact for last applied configuration version

For anyone else who sets the Puppet config_version paramater to return the current SVN or Git version of your configuration, here’s a fact that grabs that version (by parsing the cached YAML catalog) and sets it as a fact called “catalog_config_version”. It can then be used for sanity-checking your nodes, looking up via the Inventory Service, or you can display it in the Dashboard using my patch: Patch to Puppet Dashboard 1.2.10 to show arbitrary facts in the main node table.

#
# facter fact for last applied config version, skeleton from /var/lib/puppet/client_yaml/catalog/fqdn.yaml
#
 
require 'puppet'
require 'yaml'
require 'facter'
 
localconfig = ARGV[0] || "#{Puppet[:clientyamldir]}/catalog/#{ Facter.fqdn }.yaml"
 
unless File.exist?(localconfig)
  puts("Can't find #{ Facter.fqdn }.yaml")
  exit 1
end
 
lc = File.read(localconfig)
 
begin
  pup = Marshal.load(lc)
rescue TypeError
  pup = YAML.load(lc)
rescue Exception => e
  raise
end
 
if pup.class == Puppet::Resource::Catalog
        Facter.add("catalog_config_version") do
                setcode do
                        pup.version
                end
        end
else
        Facter.add("catalog_config_version") do
                setcode do
                        "unknown"
                end
        end
end

All of my facts are now available in a GitHub repository: https://github.com/jantman/puppet-facter-facts.

Patch to Puppet Dashboard 1.2.10 to show arbitrary facts in the main node table

We use Puppet Dashboard at work to view the status of our puppet nodes. While it’s very handy, there’s one feature I really wanted: the ability to show the value of arbitrary puppet facts in the main node table on the home page. Specifically, the facts we use for environment (we have eng/dev, qa, prod, and test puppet environments), zone (physical location) and last applied configuration version. I’m not terribly experience with Ruby, but I managed to muddle my way through a working patch to do this, along with options in the settings file to enable it and configure the facts. You’ll need to restart dashboard (or your web server) to change the facts, of course. The commit is currently available on github, but it doesn’t strictly follow the puppet-dashboard contributing checklist so I may have to redo it.

Here’s a screenshot:

Dashboard screenshot after patch

And here’s that the configuration section added to settings.yml looks like:

# Enables display of arbitrary node facts in "home" page node table, between node name and latest report time
enable_home_facts: true
 
# If enable_home_facts is true, the fact names and column headings to display. Simply repeat the following two line pairs
# as needed:
#- name: 'factname'
#  heading: 'heading text'
home_facts: 
- name: 'environment'
  heading: 'Env'
- name: 'zone'
  heading: 'Zone'
- name: 'catalog_config_version'
  heading: 'Cfg Ver'

If I feel really adventurous, I’d like to implement my other big wish, some sort of pop-up list of links, based on arbitrary facts (mainly hostname and fqdn) for each node – something where I can mouse over the node name/table cell, and see links (static URLs with node name/fqdn/other facts plugged in) to things like Nagios/Icinga, our backup system, etc.

Instagram – Scaling a Startup

If you keep up with news on the ‘net, you may have heard about photo sharing startup Instagram, which was purchased by Facebook for $1 Billion just 9 days after they released their Android app. Well, not only are they based mostly on open source software (well, yeah, pretty much a given for web startups these days), but they’ve dealt with scaling issues like a million new users in 12 hours, and they’re talking about it. There’s the slide deck to a talk that co-founder Mike Krieger gave on TechCrunch and Scribd, along with a High Scalability article about it, and an earlier High Scalability article that gives an overview of the company and some details of what and how they’re running. Instagram Engineering also has a tumblr account, with a bunch of cool posts like Keeping Instagram up with over a million new users in twelve hours (which specifically mentions statsd, dogslow, PGFouine, node2dm and some database stuff) and What Powers Instagram: Hundreds of Instances, Dozens of Technologies which talks about their OS and hosting (Ubuntu 11.04 on EC2), load balancing (nginx, DNS and Amazon Elastic Load Balancer), Django, Redis, Solr, Munin, etc.

This is a really cool company, doing some really cool stuff, at a really large scale, and growing fast.

On another note, I’m continuing my attempt to read all of the excellent Puppet articles on Brice Figureau’s (aka masterzen) blog. It’s taking a while, as it’s really good, in-depth information that I want to rememeber, but I’d highly recommend it for anyone working with Puppet.

Python script to find dependency cycles in GraphViz dot files

Using GraphViz to describe configurations is relatively popular in the software and systems architecture world; the simple text-based format makes it quiet simple, and the directed graph (dot file) is a simple method to store a graph of information flow or component relationships. Puppet includes builtin support for generating dot graphs of its configuration resources and relationships (both those specified by the user, and all relationships including ones generated by Puppet itself).

One of the common uses for puppet’s graphs is to identify dependency cycles, as cyclic dependencies cause an error condition. However, Puppet’s own FAQ only mentions using the dot command to generate a PNG graphical representation of the the graph. When debugging a recent problem with puppet, I ended up with a message like:

Could not apply complete catalog: Found dependency cycles in the following relationships: Service[puppet] => File[/var/lib/puppet/yaml/foreman], File[/var/lib/puppet/yaml] => File[/var/lib/puppet/yaml/foreman], Service[puppet] => File[/etc/puppet/node.rb], File[fileserver.conf] => Service[apache], File[namespaceauth.conf] => Service[apache], File[puppet.conf] => Service[apache], File[puppet.conf] => Service[puppet], Service[puppet] => File[foreman-report.rb], File[/var/lib/puppet/yaml/foreman] => Package[puppet-server], Service[foreman-proxy] => Package[puppet-server], File[foreman-proxy-settings.yml] => Package[puppet-server], Package[foreman-proxy] => Package[puppet-server], User[foreman-proxy] => Package[puppet-server], File[/var/lib/puppet/yaml] => Package[puppet-server], File[foreman-report.rb] => Package[puppet-server], File[/etc/puppet/node.rb] => Package[puppet-server], File[/var/lib/puppet/yaml/foreman] => Exec[create_puppetmaster_certs], Service[foreman-proxy] => Exec[create_puppetmaster_certs], File[foreman-proxy-settings.yml] => Exec[create_puppetmaster_certs], Package[foreman-proxy] => Exec[create_puppetmaster_certs], User[foreman-proxy] => Exec[create_puppetmaster_certs], File[/var/lib/puppet/yaml] => Exec[create_puppetmaster_certs], File[foreman-report.rb] => Exec[create_puppetmaster_certs], File[/etc/puppet/node.rb] => Exec[create_puppetmaster_certs], File[/var/lib/puppet/yaml/foreman] => File[/etc/puppet/environments], Service[foreman-proxy] => File[/etc/puppet/environments], File[foreman-proxy-settings.yml] => File[/etc/puppet/environments], Package[foreman-proxy] => File[/etc/puppet/environments], User[foreman-proxy] => File[/etc/puppet/environments], File[/var/lib/puppet/yaml] => File[/etc/puppet/environments], File[foreman-report.rb] => File[/etc/puppet/environments], File[/etc/puppet/node.rb] => File[/etc/puppet/environments], File[foreman-proxy-settings.yml] => Service[foreman-proxy], Package[foreman-proxy] => Service[foreman-proxy], Service[puppet]

Not exactly easy to follow, or to pull much meaning out of, even if I did some slight reformatting by putting in some newlines. I then generated the png as described in the Puppet FAQs, but even scrolling back and forth on this for 20 minutes didn’t help: (note: link is to the original 14405x665px png)
dot file png

With a little research, I managed to find the NetworkX package for Python; according to their site, “NetworkX is a Python language software package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks.” Among its features are the ability to read dot files using the pydot library, and the ability to find simple cycles within a graph. In about 20 minutes, I hacked together the dead-simple script below.

Given a dot file (of the type generated, for example, by Puppet), this will output all of the cycles found within the graph. I ran this script on the expanded_relationships.dot file from Puppet, which had 99 nodes (nodes on the graph, not puppet clients), and got the following output:

['File[foreman-report.rb]', 'File[puppet.conf]', 'Service[puppet]', 'File[foreman-report.rb]']
['File[foreman-report.rb]', 'Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'File[foreman-report.rb]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'Service[foreman-proxy]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'File[/etc/puppet/node.rb]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'File[foreman-proxy-settings.yml]', 'Service[foreman-proxy]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'File[foreman-proxy-settings.yml]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'User[foreman-proxy]', 'File[/etc/puppet/node.rb]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'User[foreman-proxy]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'Service[foreman-proxy]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'File[foreman-proxy-settings.yml]', 'Service[foreman-proxy]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'File[foreman-proxy-settings.yml]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'Package[puppet-server]']
['Package[puppet-server]', 'File[puppet.conf]', 'Service[puppet]', 'File[/var/lib/puppet/yaml/foreman]', 'Package[puppet-server]']
['File[/etc/puppet/node.rb]', 'File[puppet.conf]', 'Service[puppet]', 'File[/etc/puppet/node.rb]']
['File[/etc/puppet/node.rb]', 'File[puppet.conf]', 'Service[puppet]', 'User[foreman-proxy]', 'File[/etc/puppet/node.rb]']
['File[puppet.conf]', 'Service[puppet]', 'Service[foreman-proxy]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'Service[foreman-proxy]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'File[foreman-proxy-settings.yml]', 'Service[foreman-proxy]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'Package[foreman-proxy]', 'File[foreman-proxy-settings.yml]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'File[/var/lib/puppet/yaml/foreman]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'File[foreman-proxy-settings.yml]', 'Service[foreman-proxy]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'File[foreman-proxy-settings.yml]', 'File[puppet.conf]']
['File[puppet.conf]', 'Service[puppet]', 'User[foreman-proxy]', 'File[puppet.conf]']

It probably would have taken me hours to come up with that by hand, let alone realize that Service[puppet] is the common item in all of them, and hence the problem. With that little tidbit of information, I managed to track down an extraneous “require puppet” that this all originated from. I sincerely hope that this script will save someone else at least as much time as it took me to write (I know it will for me…).

You can always obtain the latest version of this script from dot_find_cycles.py (SVN), or by checking out my misc-scripts SVN repository. It’s free for use and distribution, provided that you leave my copyright/attribution and source URL notice intact, update the changelog, and send any features/fixes back to me. The script is written in Python, and depends on the python-networkx, graphviz-python, and pydot packages (all of which are available as packages in the default repos of Fedora and CentOS at least). For Puppet purposes, I’d recommend running this on expanded_relationships.dot to get the full information.

#!/usr/bin/env python
 
"""
dot_find_cycles.py - uses Pydot and NetworkX to find cycles in a dot file directed graph.
 
Very helpful for 
 
By Jason Antman <jason@jasonantman.com> 2012.
 
Free for all use, provided that you send any changes you make back to me, update the changelog, and keep this comment intact.
 
REQUIREMENTS:
Python
python-networkx - <http://networkx.lanl.gov/>
graphviz-python - <http://www.graphviz.org/>
pydot - <http://code.google.com/p/pydot/>
(all of these are available as native packages at least on CentOS)
 
USAGE:
dot_find_cycles.py /path/to/file.dot
 
The canonical source of this script can always be found from:
<http://blog.jasonantman.com/2012/03/python-script-to-find-dependency-cycles-in-graphviz-dot-files/>
 
$HeadURL: http://svn.jasonantman.com/misc-scripts/dot_find_cycles.py $
$LastChangedRevision: 33 $
 
CHANGELOG:
    Wednesday 2012-03-28 Jason Antman <jason@jasonantman.com>:
        - initial script creation
"""
 
import sys
from os import path, access, R_OK
import networkx as nx
 
def usage():
    sys.stderr.write("dot_find_cycles.py by Jason Antman <http://blog.jasonantman.com>\n")
    sys.stderr.write("  finds cycles in dot file graphs, such as those from Puppet\n\n")
    sys.stderr.write("USAGE: dot_find_cycles.py /path/to/file.dot\n")
 
def main():
 
    path = ""
    if (len(sys.argv) > 1):
        path = sys.argv[1]
    else:
        usage()
        sys.exit(1)
 
    try:
        fh = open(path)
    except IOError as e:
        sys.stderr.write("ERROR: could not read file " + path + "\n")
        usage()
        sys.exit(1)
 
    # read in the specified file, create a networkx DiGraph
    G = nx.DiGraph(nx.read_dot(path))
 
    C = nx.simple_cycles(G)
    if(len(C) < 1):
        sys.exit(0)
    for i in C:
        print i
 
# Run
if __name__ == "__main__":
    main()

The state of Puppet External Node Classifiers

At work, we’re in the process of rolling out puppet for configuration management of our servers. It will be an integral part of the provisioning process of all new physical and virtual hosts, and will also be phased in on existing hosts as possible. Right now, we have an initial puppet install that was “development”, but we’re about to move to “production” (new puppetmaster in our production infrastructure, production MySQL, etc.). We’ve been using Dashboard, but just as a report viewer. Up until now, we’ve been using nodes.pp and per-node flat-file manifests. I’ve got a few issues with this, but the biggest is that all of our node definitions (and their classes and parameters) live in the same SVN repository as our modules and other puppet configuration. Not only does this mean a checkout and commit just to change a node parameter or add a module/class to a node, but it also means that for my team members who don’t have previous puppet experience, it greatly blurs the line between administering puppet (developing and maintaining modules) and using puppet (building a node, changing node params or modules/classes), since both tasks are accomplished in the same SVN repository.

So, I’ve been pushing an External Node Classifier (ENC) with a web interface as one of the biggest feature enhancements we need for our puppet install. The complicating factor is that I’ve been given a time frame of approximately 1 week to get the “production” puppetmaster running on our production infrastructure and marked as “done”. That includes the ENC. At my last gig, at Rutgers University, I wrote our ENC in PHP (actually I wrote it for my half-dozen or so boxes at home, and brought it to Rutgers gratis), and it also handled kickstart file distribution and PXE configuration, and was extended to also set DHCP and DNS for the hosts – a one-stop solution. Unfortunately the code is very organization-specific, not terribly solid, and the UI looks awful, so it’s not a fit for the current employer. So I have to find something else that fits the bill. I have a list of initial (“phase 1″) requirements that are a mix of functionality that we require and management requirements:

  • Must support environments, since we make use of them.
  • Must support default values for parameters based on environment, “zone” (a custom fact and variable we define), or a combination of both.
  • For accountability and legal reasons, must have full auditing of all changes by all users (and, obviously, support authentication).
  • Display node last run time and status.

As well as at least the ability to implement some of our phase 2 requirements:

  • Ability to show modules and classes applied to a node, including those required/included through other modules/classes/roles.
  • Should support at least some level of puppet report display.
  • Ability to trigger a node run (kick) from the UI.
  • Some level of permission separation, ACL or RBAC so that we could potentially delegate control of a certain module or parameter, on a certain group of nodes, to the development team.
  • Per-node links to other tools such as Icinga/Nagios or our wiki.
  • Some way of detecting valid classes and modules (and our “role” module) per-environment (i.e. available modules/classes/roles should be pulled from the configs, not manually entered).
  • Ability to display puppet docs from modules/classes

Our current situation makes this even more difficult: we’re an operations team of five (hiring at the moment to fill the position of the sixth), and I believe I’m the only member of the team with any real software development experience. And none of us have experience with Ruby (which Puppet and most of its universe is written in). This means that any in-house solution runs the risk of being unmaintainable should I get hit by a bus (some of our team have various levels of experience with Perl and other scripting languages, but not really from an app development perspective). Because of these reasons, there’s a management aversion to anything that we code ourselves (well, these reasons, and the fact that with a shorthanded team we don’t have much time for projects without an immediate impact).

So, I spent hours looking around online trying to find existing web-based ENC projects, and came up with a pretty small list:

  • Dashboard, the Puppet Labs web UI. It’s the most common web-based puppet ENC as far as I know, and since it’s an official Puppet Labs project (and the basis for their Puppet Enterprise UI), its future is pretty secure. But it’s still very basic (let alone enterprise features), and has a plugin system that is very young.
  • The Foreman is probably the second-most-common puppet ENC, and has also been around about as long as Dashboard. Its features are nice, and it includes support for Kickstart (management of TFTP and DHCP) and DNS, as well as some virtual machine management. Unfortunately, we already have DHCP and DNS infrastructure so I’m sure it would be quite a bit of effort to integrate it with our environment, and for a non-Ruby shop, it has the same problem with maintainability of custom code.
  • initr, a Redmine plugin that functions as an ENC and manages modules. It includes RBAC and leverages Redmine. But since we don’t use Redmine, it’s not much of an advantage.
  • OpenNMS Puppet Node PusherAn ENC script for OpenNMS, which we also don’t use.

I was pretty amazed to see that nobody had written a puppet web UI/ENC in PHP (or Perl or Python), especially since Puppet is now quite popular.

So, I’m essentially left with the following options:

  • Start from scratch and write my own in PHP. By far the worst option, since we don’t have anyone on our team who’s likely to maintain it, and the Puppet community is Ruby-focused.
  • Use Foreman, since it’s the only one that appears to offer audit logging, have a bunch of features that don’t work for us, and hopefully deal with it.
  • Learn Ruby, write plugins for Dashboard, and hope that Puppet Labs or someone else will pick them up and maintain them if I can’t.

At the moment, I’ve decided to investigate Foreman and initr in a bit more depth, and also play around with the Dashboard code and try to pick up some Ruby (as they’re all written in Ruby anyway). I’ll also discuss these options with the team and see how opinions go (keeping in mind that the higher the likelihood of the community picking up/merging my changes, the better).

Puppet Syntax Highlighting with GeSHi

This blog is run on wordpress, and I also do quite a bit in PHP, so I’m familiar with the GeSHi syntax highlighter. It’s PHP-based, and can run both as a WordPress plugin (WP-Syntax) and as a PHP module. It also works quite well with the MediaWiki SyntaxHighlight GeSHi extension.

Today I was documenting some Puppet code in a wiki, and realized that I didn’t have syntax highlighting. Well, fellow Linux sysadmin and puppetmaster Jason Hancock was nice enough to post on his blog (Puppet Syntax Highlighting with GeSHi) that he’s developed a GeSHi language file for Puppet, available from GitHub. Many thanks!

Quick and Simple Timestamping of Debug Logs

I’ve been having some issues that may be Puppet-related. Unfortunately, Puppet (at least the old 0.25.4 client that I’m running) doesn’t timestamp the debug logs sent to stdout. I know it’s hanging somewhere, but I need concrete numbers to look at. Here’s a wonderfully simple bash script that timestamps everything sent to it on stdin, and echoes it back to stdout:

#!/bin/bash
 
DATECMD='date +%H:%M:%S'
 
while read line; do
    echo -e "$($DATECMD) $line"
done

Call it as simply as: command | ~/bin/ts, or maybe like command 2>&1 | ~/bin/ts | tee foo.log. Dead simple, but very helpful when the developers didn’t think to timestamp debug log output.

Client-side subversion commit message hooks

While I know this isn’t best practice, since we use LDAP-based auth for our Linux boxes (including a sudoers file based on LDAP group membership), we usually do work on some boxes as root (sudo su -). This includes our puppetmaster, where configs are kept in subversion and edited as root. The one problem with this is how to get the username of the actual committer, not root, in subversion messages.

The theory that I came up with is a shell script that finds out who the actual user is, and then tacking this onto the beginning of the subversion commit message (since there’s no real way to do client-side hooks in subversion). While I struggled with subversion’s lack of good client hooks, I came up with a theory based on a script that preloads svn-commit.tmp and then calls the text editor. It’s actually quite simple.

First, in your .bashrc or wherever you setup environment variables, export SVN_EDITOR=/usr/local/bin/svnPreCommitClientHook.sh. This way, every time you run svn commit, instead of calling your text editor with svn-commit.tmp as an argument, the bash script will do what it needs to (commit message preloading) with svn-commit.tmp and then call your editor to finish the message.

/usr/local/bin/svnPreCommitClientHook.sh:

#!/bin/bash
LOGNAME=`/usr/local/bin/getLogname.py` # script to get user's actual login name, even if using sudo su
echo -e "\nBY: $LOGNAME" > svn-commit.foo
cat svn-commit.tmp >> svn-commit.foo
mv svn-commit.foo svn-commit.tmp
"$EDITOR" svn-commit.tmp

Using this method, running svn commit will pull up your text editor with “BY: username” already inserted in the commit message.