Puppet and Git, 202: r10k Setup – Conversion, Deployment

Welcome back! In our 201 class, we installed r10k, but we still haven’t used it. There’s two tasks we need to complete. First, the existing repo is incompatible with r10k’s dynamic management of modules, so we’ll convert its contents to the proper format. Once that is done, we can deploy dynamic environments using r10k.

Convert existing repo

Check out a clone of the existing puppet repo, rnelson0/puppet-tutorial. As mentioned previously, you can do this on the puppet master, as I will do, or you can perform it on another machine. If you’re on the master, you want to clone the repo into a different directory. After cloning it, check out a new branch called production:

[root@puppet ~]# cd git
[root@puppet git]# git clone https://rnelson0@github.com/rnelson0/puppet-tutorial
Initialized empty Git repository in /root/git/puppet-tutorial/.git/
remote: Reusing existing pack: 775, done.
remote: Total 775 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (775/775), 385.69 KiB, done.
Resolving deltas: 100% (162/162), done.
[root@puppet git]# cd puppet-tutorial/
[root@puppet puppet-tutorial]# git checkout -b production
Switched to a new branch 'production'
[root@puppet puppet-tutorial]# git branch
  master
* production

Last week, we discussed the changes required for the repo. To recap:

The directory structure of an r10k repo is much simpler:

.
|-- manifests
  |-- site.pp
|-- Puppetfile
|-- dist
|-- site
|-- .gitignore

The puppet configuration files are removed. The site manifest stays where it is, the modules directory is replaced by the Puppetfile, and we have two new directories, dist for internally developed generic modules (where roles/profiles may reside) and site for modules deploying custom services.

To accomplish this, we need to add the file Puppetfile and remove some tracked objects. Let’s start with removing the files and directories.

[root@puppet puppet-tutorial]# git rm -r modules auth.conf fileserver.conf puppet.conf
rm 'auth.conf'
rm 'fileserver.conf'
rm 'modules/apache/CHANGELOG.md'
rm 'modules/apache/CONTRIBUTING.md'
<snip>
rm 'modules/ygrpms/tests/init.pp'
rm 'puppet.conf'
[root@puppet puppet-tutorial]#

Next we have to add the Puppetfile. You can find the details of the format on the librarian-puppet github page. You set the forge location, then include a list of mods on the forge, optionally with version, or via git checkout, also with optional version/tag. Unlike librarian-puppet, however, r10k requires you to track dependencies. Let me repeat that: r10k does NOT track dependencies, all dependencies must be stated in your Puppetfile! Up above, we performed two puppet module list commands, or you can run the command yourself. Capture that output and add it to a file. You’ll need to convert it to Puppetfile format. If you toss that all in vi, here are the commands to modify it very quickly:

:%s/\s*[└┬├─]*\s/mod "/
:%s/ (v/", "/
:%s/)/"/
:%s/-/\//g

We need to remove the custom module from the list (rnelson0-base) and add a module from git for it. Be warned, modules from git do NOT have the username before the name of the module, so rnelson0-base becomes simply base. Converting the module lists, replacing rnelson0-base with base from git/rnelson0-base, adding the forge header at the top, and some descriptions, gives us this Puppetfile:

forge "http://forge.puppetlabs.com"

# r10k requirements
mod "zack/r10k", "1.0.2"
mod "gentoo/portage", "2.1.0"
mod "mhuffnagle/make", "0.0.2"
mod "puppetlabs/gcc", "0.1.0"
mod "puppetlabs/git", "0.0.3"
mod "puppetlabs/inifile", "1.0.3"
mod "puppetlabs/pe_gem", "0.0.1"
mod "puppetlabs/ruby", "0.1.0"
mod "puppetlabs/vcsrepo", "0.2.0"

# Additional modules from the Puppet Forge
mod "puppetlabs/apache", "0.11.0"
mod "puppetlabs/concat", "1.0.1"
mod "puppetlabs/firewall", "1.0.0"
mod "puppetlabs/ntp", "3.0.1"
mod "puppetlabs/stdlib", "4.1.0"
mod "saz/ssh", "1.4.0"
mod "yguenane/augeas", "0.1.1"
mod "yguenane/ygrpms", "0.1.0"

# Modules from Github
mod "base",
  :git => "git://github.com/rnelson0/rnelson0-base"

You’ll notice that I included versions for every module, but the librarian-puppet example doesn’t list them. I’m not going to enter a holy war of whether you should always tie to specific versions or not – do whatever is standard for your shop.

Create the Puppetfile, add it to your local repo, commit, and push the production branch upstream

[root@puppet puppet-tutorial]# cat > Puppetfile
forge "http://forge.puppetlabs.com"

# Modules from the Puppet Forge
mod "puppetlabs/apache", "0.11.0"
mod "puppetlabs/concat", "1.0.1"
mod "puppetlabs/firewall", "1.0.0"
mod "puppetlabs/ntp", "3.0.1"
mod "puppetlabs/stdlib", "4.1.0"
mod "saz/ssh", "1.4.0"
mod "yguenane/augeas", "0.1.1"
mod "yguenane/ygrpms", "0.1.0"

# For our r10k installer
mod "zack/r10k", "1.0.2"
mod "gentoo/portage", "2.1.0"
mod "mhuffnagle/make", "0.0.2"
mod "puppetlabs/gcc", "0.1.0"
mod "puppetlabs/git", "0.0.3"
mod "puppetlabs/inifile", "1.0.3"
mod "puppetlabs/pe_gem", "0.0.1"
mod "puppetlabs/ruby", "0.1.0"
mod "puppetlabs/vcsrepo", "0.2.0"

# Modules from Github
mod "base",
  :git => "git://github.com/rnelson0/rnelson0-base"
[root@puppet puppet-tutorial]# git add Puppetfile
[root@puppet puppet-tutorial]# git commit -m 'Conversion to r10k format'
[production bef7066] Conversion to r10k format
 649 files changed, 26 insertions(+), 38106 deletions(-)
 create mode 100644 Puppetfile
 delete mode 100644 auth.conf
 
 delete mode 100644 modules/ygrpms/tests/init.pp
 delete mode 100644 puppet.conf
[root@puppet puppet-tutorial]# git push origin production
Counting objects: 4, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 631 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/puppet-tutorial
 * [new branch]      production -> production

If you refresh your github page now, you should see the new branch production, and it should look markedly different from the master branch.

Deploying using r10k

Now that we have created a repo for our module, installed r10k, and converted our main repo to use r10k, we need to use r10k to deploy everything. This step is important because, after installing r10k with zack/r10k, r10k started managing our modules directory. It’s very empty and catalogs will fail to compile. Switch over to the puppet master and perform a noop agent run.

[root@puppet puppet-tutorial]# puppet agent --test --noop
Info: Retrieving plugin
Error: /File[/var/lib/puppet/lib]: Could not evaluate: Could not retrieve information from environment production source(s) puppet://puppet.nelson.va/plugins
Notice: /File[/var/lib/puppet/lib/puppet]: Dependency File[/var/lib/puppet/lib] has failures: true

Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Could not find class ::base for puppet.nelson.va on node puppet.nelson.va
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
[root@puppet puppet-tutorial]# puppet config print modulepath
/etc/puppet/environments/production/modules:/opt/puppet/share/puppet/modules
[root@puppet puppet-tutorial]# ls /etc/puppet/environments/production/modules
ls: cannot access /etc/puppet/environments/production/modules: No such file or directory
[root@puppet puppet-tutorial]# ls /opt/puppet/share/puppet/modules
ls: cannot access /opt/puppet/share/puppet/modules: No such file or directory

Don’t worry! r10k will take care of this if we let it. We can deploy our dynamic environments with the command r10k deploy environment -p:

[root@puppet puppet-tutorial]# r10k deploy environment -pv
Faraday: you may want to install system_timer for reliable timeouts
[R10K::Task::Deployment::DeployEnvironments - INFO] Loading environments from all sources
[R10K::Task::Environment::Deploy - NOTICE] Deploying environment master
[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
[R10K::Task::Environment::Deploy - NOTICE] Deploying environment production
[R10K::Task::Puppetfile::Sync - INFO] Loading modules from Puppetfile into queue
[R10K::Task::Module::Sync - INFO] Deploying base into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying vcsrepo into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying ruby into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying pe_gem into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying inifile into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying git into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying gcc into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying make into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying portage into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying r10k into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying ygrpms into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying augeas into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying ssh into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying stdlib into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying ntp into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying firewall into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying concat into /etc/puppet/environments/production/modules
[R10K::Task::Module::Sync - INFO] Deploying apache into /etc/puppet/environments/production/modules
[R10K::Task::Deployment::PurgeEnvironments - INFO] Purging stale environments from /etc/puppet/environments
[R10K::Task::Puppetfile::Purge - INFO] Purging stale modules from /etc/puppet/environments/master/modules
[root@puppet puppet-tutorial]# ls /etc/puppet/environments/production/modules
apache  base    firewall  git      make  pe_gem   r10k  ssh     vcsrepo
augeas  concat  gcc       inifile  ntp   portage  ruby  stdlib  ygrpms

If you run the agent now, you’ll see it’s much, much happier:

[root@puppet puppet-tutorial]# puppet agent --test --noop
<snip>
Info: Applying configuration version '1395849183'
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: current_value absent, should be Generated from our notify branch (noop)
Notice: Node[puppet.nelson.va]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Finished catalog run in 2.02 seconds

Congratulations, you’ve successfully configured your repo for dynamic environments! Let’s run through some of the steps above that you’ll be repeating, though:

  • git checkout -b <feature>: Create a new branch. Make edits, commit, push to origin. This step can be done anywhere
  • r10k deploy environment -p[v]: Deploy the environments, one per branch of your repo. Any branches without a Puppetfile, like our master, will be empty. The -v is for verbose and is optional. This must be run on the puppet master (we’ll look at automating it later).
  • puppet agent –test [–environment <name>]: Run a puppet agent against the production environment, or the optional environment specified. This dynamic environment name would be the name of your new feature branch.

Cleanup

We have a little cleanup to do from our pre-r10k setup.

[root@puppet puppet-tutorial]# tree /etc/puppet -L 1
/etc/puppet
├── auth.conf
├── environments
├── fileserver.conf
├── manifests
├── modules
├── puppet.conf
└── README.md

We need the conf files, but modules are now referenced under environments/$environment/modules. Remove the unused modules and you’ll see catalog compilation is unaffected:

[root@puppet puppet-tutorial]# rm -fR /etc/puppet/modules/
[root@puppet puppet-tutorial]# puppet agent --test --noop
Info: Applying configuration version '1395849183'
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: current_value absent, should be Generated from our notify branch (noop)
Notice: Node[puppet.nelson.va]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Finished catalog run in 0.97 seconds

The same goes for manifests which are now under /etc/puppet/environments/$environment/manifests. Get rid of the original copy of this as well:

[root@puppet puppet-tutorial]# rm -fR /etc/puppet/manifests/
[root@puppet puppet-tutorial]# puppet agent --test --noop
Info: Applying configuration version '1396323811'
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: current_value absent, should be Generated from our notify branch (noop)
Notice: Node[puppet.nelson.va]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Finished catalog run in 0.99 seconds

We also have two branches in the puppet-tutorial repo. When the environment is unspecified, production is used, which is why we created the new branch. There’s no need for the master branch anymore. Because of my tutorial articles, I won’t delete the branch, but in a production environment I would. This would leave one main branch, production, plus the feature branches. You could easily do this through the github interface, or you could delete the branch locally and push it upstream. You’ll also need to go to Settings for the repo and set the default branch to production or you’ll get the error “deletion of the current branch prohibited”. You can make the change even if you keep master around, that way anyone doing a git clone will receive production as the starting branch. Here’s an example of me deleting a branch master2 from the CLI rather than github:

[root@puppet puppet-tutorial]# git pull origin master2
From https://github.com/rnelson0/puppet-tutorial
 * branch master2 -> FETCH_HEAD
Already up-to-date.
[root@puppet puppet-tutorial]# git branch
 master
* production
[root@puppet puppet-tutorial]# git branch master2
[root@puppet puppet-tutorial]# git branch
 master
 master2
* production
[root@puppet puppet-tutorial]# git checkout master
Switched to branch 'master'
[root@puppet puppet-tutorial]# git branch -D master2
Deleted branch master2 (was bef7066).
[root@puppet puppet-tutorial]# git branch
* master
 production
[root@puppet puppet-tutorial]# git push origin :master2
To https://rnelson0@github.com/rnelson0/puppet-tutorial
 - [deleted] master2

With our filesystem and github repo cleaned up, we’re all set for dynamic environments without any cruft of the previous configuration.

In the next class, we’ll define a new workflow that utilizes r10k, step one toward a goal of not logging into the master as root every time you make a change.

One thought on “Puppet and Git, 202: r10k Setup – Conversion, Deployment

  1. Pingback: Improved r10k deployment patterns | 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