Managing your manifests with Git
It's a great idea to put your Puppet manifests in a version control system such as Git or Subversion (Git is the de facto standard for Puppet). This gives you several advantages:
- You can undo changes and revert to any previous version of your manifest
- You can experiment with new features using a branch
- If several people need to make changes to the manifests, they can make them independently, in their own working copies, and then merge their changes later
- You can use the
git log
feature to see what was changed, and when (and by whom)
Getting ready
In this section, we'll import your existing manifest files into Git. If you have created a Puppet directory in a previous section use that, otherwise, use your existing manifest directory.
In this example, we'll create a new Git repository on a server accessible from all our nodes. There are several steps we need to take to have our code held in a Git repository:
- Install Git on a central server.
- Create a user to run Git and own the repository.
- Create a repository to hold the code.
- Create SSH keys to allow key-based access to the repository.
- Install Git on a node and download the latest version from our Git repository.
How to do it...
Follow these steps:
- First, install Git on your Git server (
git.example.com
in our example). The easiest way to do this is using Puppet. Create the following manifest, call itgit.pp
:package {'git': ensure => installed }
- Apply this manifest using
puppet apply git.pp
, this will install Git. - Next, create a Git user that the nodes will use to log in and retrieve the latest code. Again, we'll do this with puppet. We'll also create a directory to hold our repository (
/home/git/repos
) as shown in the following code snippet:group { 'git': gid => 1111, } user {'git': uid => 1111, gid => 1111, comment => 'Git User', home => '/home/git', require => Group['git'], } file {'/home/git': ensure => 'directory', owner => 1111, group => 1111, require => User['git'], } file {'/home/git/repos': ensure => 'directory', owner => 1111, group => 1111, require => File['/home/git'] }
- After applying that manifest, log in as the Git user and create an empty Git repository using the following command:
# sudo -iu git git@git $ cd repos git@git $ git init --bare puppet.git Initialized empty Git repository in /home/git/repos/puppet.git/
- Set a password for the Git user, we'll need to log in remotely after the next step:
[root@git ~]# passwd git Changing password for user git. New password: Retype new password: passwd: all authentication tokens updated successfully.
- Now back on your local machine, create an
ssh
key for our nodes to use to update the repository:t@mylaptop ~ $ cd .ssh t@mylaptop ~/.ssh $ ssh-keygen -b 4096 -f git_rsa Generating public/private rsa key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in git_rsa. Your public key has been saved in git_rsa.pub. The key fingerprint is: 87:35:0e:4e:d2:96:5f:e4:ce:64:4a:d5:76:c8:2b:e4 thomas@mylaptop
- Now copy the newly created public key to the
authorized_keys
file. This will allow us to connect to the Git server using this new key:t@mylaptop ~/.ssh $ ssh-copy-id -i git_rsa git@git.example.com git@git.example.com's password: Number of key(s) added: 1
- Now try logging into the machine, with: "ssh 'git@git.example.com'" and check to make sure that only the key(s) you wanted were added.
- Next, configure
ssh
to use your key when accessing the Git server and add the following to your~/.ssh/config
file:Host git git.example.com User git IdentityFile /home/thomas/.ssh/git_rsa
- Clone the repo onto your machine into a directory named Puppet (substitute your server name if you didn't use
git.example.com
):t@mylaptop ~$ git clone git@git.example.com:repos/puppet.git Cloning into 'puppet'... warning: You appear to have cloned an empty repository. Checking connectivity... done.
We've created a Git repository; before we commit any changes to the repository, it's a good idea to set your name and e-mail in Git. Your name and e-mail will be appended to each commit you make.
- When you are working in a large team, knowing who made a change is very important; for this, use the following code snippet:
t@mylaptop puppet$ git config --global user.email"thomas@narrabilis.com" t@mylaptop puppet$ git config --global user.name "ThomasUphill"
- You can verify your Git settings using the following snippet:
t@mylaptop ~$ git config --global --list user.name=Thomas Uphill user.email=thomas@narrabilis.com core.editor=vim merge.tool=vimdiff color.ui=true push.default=simple
- Now that we have Git configured properly, change directory to your repository directory and create a new site manifest as shown in the following snippet:
t@mylaptop ~$ cd puppet t@mylaptop puppet$ mkdir manifests t@mylaptop puppet$ vim manifests/site.pp node default { include base }
- This site manifest will install our base class on every node; we will create the base class using the Puppet module as we did in Chapter 1, Puppet Language and Style:
t@mylaptop puppet$ mkdir modules t@mylaptop puppet$ cd modules t@mylaptop modules$ puppet module generate thomas-base Notice: Generating module at /home/tuphill/puppet/modules/thomas-base thomas-base thomas-base/Modulefile thomas-base/README thomas-base/manifests thomas-base/manifests/init.pp thomas-base/spec thomas-base/spec/spec_helper.rb thomas-base/tests thomas-base/tests/init.pp t@mylaptop modules$ ln -s thomas-base base
- As a last step, we create a symbolic link between the
thomas-base
directory andbase
. Now to make sure our module does something useful, add the following to the body of thebase
class defined inthomas-base/manifests/init.pp
:class base { file {'/etc/motd': content => "${::fqdn}\nManaged by puppet ${::puppetversion}\n" } }
- Now add the new base module and site manifest to Git using
git add
andgit commit
as follows:t@mylaptop modules$ cd .. t@mylaptop puppet$ git add modules manifests t@mylaptop puppet$ git status On branch master Initial commit Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: manifests/site.pp new file: modules/base new file: modules/thomas-base/Modulefile new file: modules/thomas-base/README new file: modules/thomas-base/manifests/init.pp new file: modules/thomas-base/spec/spec_helper.rb new file: modules/thomas-base/tests/init.pp t@mylaptop puppet$ git commit -m "Initial commit with simple base module" [master (root-commit) 3e1f837] Initial commit with simple base module 7 files changed, 102 insertions(+) create mode 100644 manifests/site.pp create mode 120000 modules/base create mode 100644 modules/thomas-base/Modulefile create mode 100644 modules/thomas-base/README create mode 100644 modules/thomas-base/manifests/init.pp create mode 100644 modules/thomas-base/spec/spec_helper.rb create mode 100644 modules/thomas-base/tests/init.pp
- At this point your changes to the Git repository have been committed locally; you now need to push those changes back to
git.example.com
so that other nodes can retrieve the updated files:t@mylaptop puppet$ git push origin master Counting objects: 15, done. Delta compression using up to 4 threads. Compressing objects: 100% (9/9), done. Writing objects: 100% (15/15), 2.15 KiB | 0 bytes/s, done. Total 15 (delta 0), reused 0 (delta 0) To git@git.example.com:repos/puppet.git * [new branch] master -> master
How it works...
Git tracks changes to files, and stores a complete history of all changes. The history of the repo is made up of commits. A commit represents the state of the repo at a particular point in time, which you create with the git commit
command and annotate with a message.
You've now added your Puppet manifest files to the repo and created your first commit. This updates the history of the repo, but only in your local working copy. To synchronize the changes with the git.example.com
copy, the git push
command pushes all changes made since the last sync.
There's more...
Now that you have a central Git repository for your Puppet manifests, you can check out multiple copies of it in different places and work on them before committing your changes. For example, if you're working in a team, each member can have their own local copy of the repo and synchronize changes with the others via the central server. You may also choose to use GitHub as your central Git repository server. GitHub offers free Git repository hosting for public repositories, and you can pay for GitHub's premium service if you don't want your Puppet code to be publicly available.
In the next section, we will use our Git repository for both centralized and decentralized Puppet configurations.