Authors: Jeffrey McCune James Turnbull
Unlike the testing and production environment we ran the Puppet agent in, this run in the development environment resulted in an error. Such a bad error, in fact, that we didn’t even receive a valid configuration catalog from the Puppet master. So what happened?
Notice that the error message returned by the Puppet master provides the exact line number in the manifest the error occurred on. On this line we’re using the template we modified when we made a change to the development environment, and this change references a variable that we have not defined in the Puppet manifests. If we run the Puppet agent against the production environment, we can see everything is still OK:
$ puppet agent --test --noop
...
notice: Finished catalog run in 0.68 seconds
Let’s go back and fix the problem with the ERB template by removing the reference to the undefined puppet variablethis_will_fail
. As you can see in the following file difference, we’ve fixed the problem in the first line of the template:
diff --git a/postfix/templates/main.cf.erb b/postfix/templates/main.cf.erb
index 3331237..241b4bb 100644
--- a/postfix/templates/main.cf.erb
+++ b/postfix/templates/main.cf.erb
@@ -1,3 +1,4 @@
+# This file managed by puppet.
soft_bounce = no
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
Now, when we run Puppet agent in the development environment, we’re no longer getting the error:
$ puppet agent --test --noop --environment development
This verification step allowed us to make changes and test them in an isolated environment without impacting Puppet nodes with their agent running against the production environment. Now that you’re confident our change will not break production, you can commit the changes:
$ git add /etc/puppet/environments/development/modules/postfix/templates/main.cf.erb
$ git commit -m 'Added comment header, postfix main.cf is managed by puppet.'
Created commit d69bc30: Added comment header, postfix main.cf is managed by puppet.
1 files changed, 2 insertions(+), 1 deletions(-)
In the next section, we examine the workflow of merging changes like this into the testing and production environments. This workflow helps teams of developers and system administrators work together while making changes to the system, without impacting production systems, through the use of Puppet environments.
As you saw in the previous section, configuring multiple environments in Puppet requires three things:
modulepath
One of the key benefits of version control systems is the ability to manage and organize the contributions from a group of people. In this section, we’ll explore how a group of three people may use Puppet Environments, version control, and the concept of a “branch” to effectively coordinate and manage their changes to the configuration system. Branches are lines of independent development in a repository that share a common history. A branch could be a copy of our development environment with changes made to it; it shares a common history with the development environment but has a history of its own too. Branches allow multiple people to maintain copies of an environment, work on them independently and potentially combine changes between branches or back into the main line of development.
Expanding on our hypothetical company, imagine we have a small team of people working together: a system administrator, a developer and an operator. In this exercise, we’ll explore how this team effectively makes changes that do not impact one another, can be merged into the main development and testing branch, and ultimately make their way to the production infrastructure.
Before the small group is able to work together in harmony, you’ll need to make a few slight changes to the version control system. Git is unique compared to other version control systems, such as Subversion, in that each repository stands apart and is complete without the need to perform a checkout from a central repository. When working with a team, however, it is convenient to have a central place to store and track changes over time.
In this section, you’ll clone a copy of the/etc/puppet/modules
repository into/var/lib/puppet/git/modules.git
and use this location as the “central” repository. It is central by convention only; there is technically nothing different about the repository that makes it any different from the other Git repositories we’ve been working with in this chapter. Once you have a repository designated as the central location, everyone will clone this repository and submit their changes back to it for review and testing. Let’s go through this process now.
First, you need to create a “bare” repository containing your Puppet modules. A bare repository in Git is a repository with the history of commits, but no working copy. We want to create a bare repository to help make sure files aren’t accidentally directly modified in the central location. Modifications should only happen through commits pushed to this location. We’re going to perform these steps as the Puppet user, who is usually running aspuppet
, in order to help ensure file permissions and ownership remain consistent when different users are modifying the repository.
$ cd /var/lib/puppet
$ mkdir git
$ chown puppet:puppet git
$ sudo -H -u puppet -s
$ cd /var/lib/puppet/git
$ git clone --bare /etc/puppet/modules modules.git
Initialized empty Git repository in /var/lib/puppet/git/modules.git/
Note
We recommend storing the central version control repository in the home directory of the Puppet user to start. This may vary from system to system, and may not be/var/lib/puppet
on your platform.
Once you have a central repository, it’s time for everyone in the group to check out their own personal copies to work on. We recommend they do this in their home directories. Changes will be made there and submitted to the central repository for review. Let’s first clone a repository for our system administrator, hereaftersysadmin
:
sysadmin:~$ git clone [email protected]:git/modules.git
Initialized empty Git repository in ~/modules/.git/
remote: Counting objects: 36, done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 36 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (36/36), 5.58 KiB, done.
After cloning the repository from the central location, you can begin to make changes. In order to make sure you have the same changes you made to themain.cf.erb
file in the previous section, pull the change made to themain.cf.erb
file from the repository in/etc/puppet/environments/development/modules
. You could directly fetch the change from the repository Puppet is using in/etc/puppet
, but it may become confusing to manage what changes are located in which repositories.
To help coordinate with the rest of the team, instead push the change from the development repository into the central repository. This should be done using thepuppet
user account:
puppet:~$ cd /etc/puppet/environments/development/modules
puppet:development/modules$ git remote rm origin
puppet:development/modules$ git remote add origin [email protected]:git/modules.git
puppet:development/modules$ git push origin master:master
Counting objects: 9, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 499 bytes, done.
Total 5 (delta 2), reused 0 (delta 0)
To [email protected]:git/modules.git
a13c3d8..d69bc30 master -> master
puppet:~$ cd /etc/puppet/environments/testing/modules
puppet:testing/modules$ git remote rm origin
puppet:testing/modules$ git remote add origin [email protected]:git/modules.git
puppet:~$ cd /etc/puppet/modules
puppet:/etc/puppet/modules$ git remote rm origin
puppet:/etc/puppet/modules$ git remote add origin [email protected]:git/modules.git
After executing these commands, you’ve updated each of the three Git repositories containing the production, testing, and development working copies to point at your fourth, central repository. The systems administrator now has a personal working copy which points to the central repository.
In order to make a change, each team member should create a new Git branch for the topic he or she is working on and make their changes in this branch. A topic branch will allow other team members to easily fetch all of their work as a self-contained bundle, rather than requiring them to sort through each commit or set of commits. This will also make it easier to merge each team member’s contributions into the master branch when necessary, as you can see in
Listing 3-2
.
Listing 3-2.
Merging in changes
sysadmin:~$ cd modules
sysadmin:~/modules$ git fetch origin
From [email protected]:git/modules
a13c3d8..d69bc30 master -> origin/master
sysadmin:~/modules$ git checkout master
Already on "master"
Your branch is behind the tracked remote branch 'origin/master' by 1 commit,
and can be fast-forwarded.
sysadmin:~/modules$ git merge origin/master
Updating a13c3d8..d69bc30
Fast forward
postfix/templates/main.cf.erb | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
As you can see, we’ve pushed the change tomain.cf.erb
into the central repository. The sysadmin was able to update her personal copy with this change.
The sysadmin now has her copy and is able to push and pull changes in the central repository, but what about the developer and operator? They should each clone a copy of the central repository URL,[email protected]:git/modules.git,
into their home directory. We’ll run through the situation where the operator needs to make and test a change to the sshd configuration file, while the developer needs to make and test a change to the Postfix configuration files. These two changes will be tested independently in the development environment and then merged together in the testing environment.
Tip
SSH Keys and Agent Forwarding should be employed when using Git in order to increase security, keep file ownership consistent, and manage the central code using the Puppet user. To accomplish this, people with authorization to change Puppet could have their public key added to~puppet/.ssh/authorized_keys
. For more information about SSH public keys, please see:http://www.debian-administration.org/articles/530
We’ll go through the changes to Secure Shell or SSH the operator needs to make first. The operator is working specifically to make sure only members of certain groups are allowed to log in to the system using SSH.
To begin, you should create a topic branch to work on this problem. In Git, unlike other version control systems, a branch does not create a new directory path in the working directory of the repository. Instead, Git checks out the branch into the base directory of the repository.
Let’s create a topic branch based on the current master branch in our central “origin” repository, like so:
operator:~/modules $ git checkout -b operator/ssh origin/master
Branch operator/ssh set up to track remote branch refs/remotes/origin/master.
Switched to a new branch "operator/ssh"
operator:~/modules $ git branch
* operator/ssh
Master
Notice that the operator now has two branches in their personal~/modules/
Git repository. Using a topic branch, we are free to modify things without worrying about impacting the work of the rest of the team. The branch provides a reference point to revert any of the changes we make to the Puppet configuration. Similarly, the development, production, and testing environments in the/etc/puppet
directory on the Puppet master must explicitly check out this new branch in order for our changes to affect any of the Puppet agent systems. This strategy is much less risky and easier to coordinate with team members than directly editing the files contained in the/etc/puppet
directory.