Coffee & Beer

Rantings and Ravings of the technical sort

Moving Puppet From Subversion to GIT in 15 Minutes While Adding Dynamic Environments

For (almost) as long as we’ve had a pupet installation at work, we’ve had it in Subversion to track changes. This has changed/evolved a few times, but has always remained in subversion in one way or another. Recently we starting tracking other bits (documentation, scripts, etc) in git, and the idea of being on 2 different revision systems didn’t really sit well with me. Most of the team has taken up git very well, so the choice was made to move our puppet manifests etc to git. Once i started loking into it, also found the very cool “Dynamic Environments w/ git branches” trick, talked about here and here, and thought the move to git would be the perfect time to move to this.

I’m going to skip the testing/waiting I had to do (making sure our git server and puppet amster could talk ssh to each other, setting up the ssh keys for git to use to push and puppet to pull, etc), and jump right into the implementation and transition, which took about 15 minutes total yesterday.

So, first, To get the lions share of the data shifting done, I used svn2git to get the svnrepo sync’d to a git repo. our svn repo, while it used to have a few branches, prior to this had been compacted to just a trunk (which wasn’t called “trunk”) and no tags etc. So, I ran:

1
$> svn2git --username matt --nobranches --notags --rootistrunk -v https://puppet.server/svn/puppet/

And let that chug along for a while.

Once that was done I could add the remote git server as an origin:

1
2
$> git remote add origin git@git.server:puppet.git
$> git push

Now, the puppet svn repo is in git and on the git server. I had to sync a few times due to some quick changes going into the svn repo, so that was a simple

1
2
$> svn2git --rebase
$> git push

Okay, finally it was go time. Step 1, make the svn repo readonly. How? Thisis served with apache, so, a simple pre-commit hook did the job:

1
2
3
#!/bin/sh
echo "This svn repo is now read-only! No Commits accepted!"
exit 1

See what I did there? The pre-commit never DOES anything with the commit, so the commit is never acepted by the svnserver!

Okay, on to the transition. On our puppet master, everything lived in /etc/puppet which was an svn repo. Lets stop the puppet master for a moment (clients will go about their merry way) move that aside, and setup the new location:

1
2
3
4
5
6
$>service  httpd stop (we run puppet via passenger)
$>cd /etc
$>mv puppet puppet.svn.backup
$>mkdir -p puppet/environments
$>chown -R puppet:apache puppet
$>chmod -R g+w puppet

Okay. Now, lets get into the weeds for a second here. So, using the dynamic environments, our git server will, with a post-receive hook I’ll show in a second, ssh to our puppet master, and checkout the branch (master or otherwise) to /etc/puppet/environments/$BRANCH. Now, we tend to use “master” for most of our small edits (adding a node, etc), so “master” => “production”. I didn’t want a puppet environment called “master”, so a tiny bit extra logic was added to the post-receive to change the location of the branch checkout to “production” if the master branch was change, but otherwise use the branchname, such as “matts_new_feature”. Okay here is the post-receive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/sh
read oldrev newrev refname

REPO="git@git.server/puppet.git"
BRANCH=`echo $refname | sed -n 's/^refs\/heads\///p'`
BRANCH_DIR="/etc/puppet/environments"
SSH_ARGS="-i /var/lib/puppet/.ssh/id_puppet_rsa"
SSH_DEST="puppet@puppet.server"

#If working on the master branch, its really production and should go in environments/production
if [ "$BRANCH" == "master" ] ; then
        BRANCH_REAL="production"
else
        #Otherwise its a non-master/production branch and the env can be created w/ the branch name
        BRANCH_REAL="$BRANCH"
fi

if [ "$newrev" -eq 0 ] 2> /dev/null ; then
  # branch is being deleted
  echo "Deleting remote branch $BRANCH_DIR/$BRANCH_REAL"
  ssh $SSH_ARGS $SSH_DEST /bin/sh <<-EOF
  cd $BRANCH_DIR && rm -rf $BRANCH_REAL
EOF
else
  # branch is being updated
  echo "Updating remote branch $BRANCH_DIR/$BRANCH_REAL"
  ssh  $SSH_ARGS $SSH_DEST /bin/sh <<-EOF
  { cd $BRANCH_DIR/$BRANCH_REAL && git pull origin $BRANCH ; } \
  || { mkdir -p $BRANCH_DIR && cd $BRANCH_DIR \
  && git clone $REPO $BRANCH_REAL && cd $BRANCH_REAL \
  && git checkout -b $BRANCH origin/$BRANCH ; 
  EOF
fi 

This, paired with a nice pre-receive server side syntax check, and we’re looking pretty good and automatic (I’ll share that in another post) Okay, so thats in place. Now, svn2git works great, btu i want to use a clean, clean git-only checkout of the new puppet repo to finish this off with, so, on my local system:

1
2
3
4
$> mv puppet puppet.svn-git
$> git clone git@git.server:puppet.git puppet.git
$> cd puppet.git
$> vi puppet.conf

Now, in puppet.conf, I setup the dynamic environments:

1
2
3
environment = production
manifest = $confdir/environments/$environment/manifests/site.pp
modulepath = $confdir/environments/$environment/modules

Now lets commit/push:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$> gits
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   puppet.conf
#
no changes added to commit (use "git add" and/or "git commit -a")

$> git commit -a
[master d846620] change to puppet.conf for the environments change
  1 files changed, 7 insertions(+), 7 deletions(-)

$> git push
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 383 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Updating remote branch /etc/puppet/environments/production
remote: Cloning into production...
To git@git.server:puppet.git
      73f5405..d846620  master -> master

Ta Da! Now, lets look on the puppet master:

1
2
3
4
$>pwd
/etc/puppet/
$>ls environments
production

So, its there, lets start puppet back up:

1
$>service httpd start

Get on a client of two, run puppet agent -t, all is well, and its great success! This litterally (of course, with testing, setting up keys, etc out o the way) took about 15 minutes yesterday. Nothing like pulling the rug out from under 1800+ systems/14000 cores and putting it back without them noticing!

Now, the really cool part is:

1
2
3
4
$>git checkout -b matts_test
$>vim somefiles.pp
$>git commit -a
$>git push

Will make a new branch/environment in /etc/puppet/environments/matts_test, which clients can use like puppet agent -t --environment=matts_test! Want it to go away after changes have merged? Simple!

1
2
3
4
5
  $> git checkout master
  $> git merge matts_test
  $> git push origin :matts_test
  $> git branch -d matts_test
  $> git push

Ta da! The branch/environment is no longer valid, and as been removed form /etc/puppet/environments/!

Comments