Puppet and Git, 101: Git Basics

Now that we’ve set up a puppet master and puppetized template, created a sample manifest, and started creating our own module, it’s time to take a few moments to talk about using Puppet with a version control systems (VCS). This article is mainly for those new to VCS at all or new to Git; those very familiar will want to skim or skip this article entirely.

What we have done so far is adding and removing a few lines in a couple files, and we’ve treated it as such. But it’s so much more. Writing code that represents an infrastructure state and using software to implement it is the root of two important IT movements: DevOps and the Software Defined Data Center (SDDC). You write code, puppet creates the infrastructure according to your instructions. Need something changed? Update your code, puppet takes care of the rest. What if you mess up? That’s where version control comes into play.

Version control, among other benefits, gives us the option to look at our code at points in time and to track changes over time, usually with some level of audit detail. If I make a change today and everything runs fine for a few days before blowing up, I can use version control to track the changes made to see if someone else made a change in the interval or perhaps go back to the version prior to my change. Without version control, you have no functional ability to audit your changes and revert the state of your code to a particular point in time.

There are a number of different version control systems that you can use. Subversion has been a popular VCS, though it has some long standing limitations and has been losing favor for a while. Git is a newer distributed version control system (DVCS) that has gained massive popularity by addressing some of the limitations of non-distributed VCSes and encouraging public development via Github.com and other cloud DVCS providers. We’re going to focus on Git due to its popularity, the plethora of examples of Puppet + Git available on the internet, and the ability to leverage Github.

There are other Github-like systems out there, such as a corporate implementation of Atlassian’s Stash. In most cases you can replace the github URL with your project URL and see no difference. Regardless of the service you use, the most important detail is the ability to clone the repo to our puppet master. Even though Git is a distributed VCS, we still want a central “server” that’s going to maintain the repo, allowing other people to pull from it without having to hit your puppet master and so proper backups can be done. Though this is a lab, there’s no need to set ourselves up for failure if we have a disk issue; we can always rebuild by retrieving our repo from Github.

I’ve set up a public Github repo at https://github.com/rnelson0/puppet-tutorial for this article. It’s going to be used in a few articles, which I am writing in advance of their publication dates, so be aware that if you clone the repo you may see stuff we haven’t covered yet. You can either ignore the unused components or revert to an older version, which I’ll show you how to do before we finish today.

You may need to edit puppet.conf and change the server value to match your server’s name and do the same in the site manifest, unless you’ve decided to mirror my naming scheme.

If you have not used Git yet, I recommend Github’s and Code School’s Git classes. If you are a frequent Subversion user, be warned, your head will be twisted. You have to add already-tracked files on every commit, push and pull seem a little backward, and the distributed nature can be confusing. Don’t be afraid to take the classes again or revisit particular lessons, the terminology may be extremely unfamiliar and seemingly backwards. You’ll get there.

Configure Git for the first time

We’re going to use git at the command line of our puppet master. One of the first things you need to do is identify yourself to the system. This information will be included in your commits, please set this up on your user account and not as root. (Yes, I’ve skipped ahead in my lab to when I have access to a domain user)

[rnelson0@puppet ~]$ git config --global user.name "Nelson, Robert"
[rnelson0@puppet ~]$ git config --global user.email "rnelson0@domain.com"

Add existing code to our empty repo

If you have been following along with the series, you’ll have a puppet master with some modules and manifests and your Github repo will be empty, except for a readme file if you specified. We want to take what we already have and add it to the repo, rather than start from the repo and lose our existing configuration. If your repo has existing code, that topic won’t be covered here so do some research and be careful not to overwrite your existing modules and manifests.

The first step is to clone your repo. We’ll do this from within /etc/puppet. If you’re using Github, you’ll see a section titled HTTPS clone URL in the bottom right of your repo’s page. Normally you would just copy that string to the clipboard and use git clone <paste> <path> to clone the repo (Note: you may want to insert your username before github.com as I do, or generate SSH keys, as the default clone string has some issues). If you try this with /etc/puppet, you will receive an error as the directory is not empty. There’s a little trick we can do to create a clone in a new directory, move the .git folder into /etc/puppet, and then reset the repo. As long as there are no conflicts with filenames, this should work.

[root@puppet puppet]# git clone https://rnelson0@github.com/rnelson0/puppet-tutorial.git tmp && mv tmp/.git ./ && rm -fR tmp && git reset --hard
Initialized empty Git repository in /etc/puppet/tmp/.git/
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
HEAD is now at 98321b6 Initial commit
[root@puppet puppet]# ls -la
total 40
drwxr-xr-x.  5 root root 4096 Mar  6 15:24 .
drwxr-xr-x. 62 root root 4096 Feb 26 14:22 ..
-rw-r--r--.  1 root root 4133 Jan  6 22:41 auth.conf
-rw-r--r--.  1 root root 1462 Jan  6 22:37 fileserver.conf
drwxr-xr-x.  8 root root 4096 Mar  6 15:24 .git
drwxr-xr-x.  2 root root 4096 Feb  7 01:51 manifests
drwxr-xr-x. 11 root root 4096 Feb 18 21:53 modules
-rw-r--r--.  1 root root  884 Jan 29 16:13 puppet.conf
-rw-r--r--.  1 root root   32 Mar  6 15:24 README.md

Your existing files are okay. The file README.md is new, that was generated by Github for me since I asked it to create a readme. The .git directory is where the magic of git happens. Don’t poke at that unless you really know what you’re doing.

If that seems too tricky, or you think there will be conflicts (as mentioned above, if you clone my repo now you’ll get more than you bargained for and probably have some conflicts!), there’s another way to do it. You would move the puppet files out of /etc/puppet, clone into it, and move the files back. Or you could clone into another directory, copy the puppet files into it, wipe out /etc/puppet, and then clone into it. You could also git init /etc/puppet, add the Github repo as a master, and do a pull. There are numerous ways to do this, but for a clone of an empty repo, the example above should work for most people.

If you run git status now, you should notice that everything is good. It’s the nothing added to commit at the bottom that lets us know this.

[root@puppet puppet]# git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#       auth.conf
#       fileserver.conf
#       manifests/
#       modules/
#       puppet.conf
nothing added to commit but untracked files present (use "git add" to track)

First Commit

Now, you need to get your existing files into your repo. As shown above, we have a few files that are untracked. We haven’t looked at all of these files yet, but let’s add them anyway, they’re important to Puppet if not us. We can then make our first commit. The commands we need are git add and git commit.

[root@puppet puppet]# git add .
[root@puppet puppet]# git commit -m 'Initial puppet config'
[master defbe57] Initial puppet config
 649 files changed, 38109 insertions(+), 0 deletions(-)
 create mode 100644 auth.conf
 create mode 100644 puppet.conf
[root@puppet puppet]#
[root@puppet puppet]# git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
nothing to commit (working directory clean)

The git repo on the puppet master now has the README.md file plus our puppet configuration. However, the Github repo looks suspiciously empty. Notice that the status says Your branch is ahead of ‘origin/master’ by 1 commit. We need to push our change to the Github repo, known as origin, and into the existing master branch.

[root@puppet puppet]# git push origin master
Counting objects: 769, done.
Compressing objects: 100% (697/697), done.
Writing objects: 100% (768/768), 385.17 KiB, done.
Total 768 (delta 161), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/puppet-tutorial.git
   98321b6..bc1e27b  master -> master

Refresh your Github page and you will see your changes. Perfect!

Creating a Release

We have one last thing to do today, create a release. A release gives us a point-in-time snapshot of our repo. Github has instructions for this. I’ve created a release of my repo, v0.1, that reflects the state of the product after the push above. You can view it here. If you just did this yourself, Github knows about your release, but your puppet master doesn’t. We’ll fetch the tags, list them, and then checkout the v0.1 tag. This would also be helpful if you cloned my tutorial and wanted to rewind it to this point in time for viewing.

[root@puppet puppet]# git fetch --tags
From https://github.com/rnelson0/puppet-tutorial
 * [new tag]         v0.1       -> v0.1
[root@puppet puppet]# git branch
* master
[root@puppet puppet]# git tag -l
[root@puppet puppet]# git checkout tags/v0.1
Note: checking out 'tags/v0.1'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at bc1e27b... Initial puppet config
[root@puppet puppet]# git branch
* (no branch)

You might notice that the branch isn’t really a branch, and there was a warning about running with a detached head. Let’s leave that to the chickens for now and treat this as a read-only tag. You can switch back to your master by checking it out, and then everything is up to date and that weird non-branch branch goes away.

[root@puppet puppet]# git checkout master
Switched to branch 'master'
[root@puppet puppet]# git branch
* master


Now you have a v0.1 release of your puppet config. There’s a lot more we will do with git in the future, and a lot more before we put this in production, but for today the important part is that we have everything in version control and as we make changes, we always have a known good state we can roll back to. In future articles, I’ll assume that you have your configuration in git and we’ll start using branches to work on changes. In the meantime go ahead and make some changes to your manifests, break things, and roll back to our known good state. Make some changes that work, as well, and commit them.

One thought on “Puppet and Git, 101: Git Basics

  1. Pingback: Convert a Puppet module from Bundle-based testing to the Puppet Development Kit (PDK) | rnelson0

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 )

Facebook photo

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

Connecting to %s