Puppet and Git, 203: r10k Workflow for New Module

Welcome back to our Puppet and Git 200-series classes. With r10k installed and configured, today we can focus on workflows. The first workflow is for a new module, either a brand new module you are creating or simply a “new to you” module, such as importing more modules from the Forge. In our classrom, we will add a single module from the forge and update the base module to make use of it. This will give us a good understanding of the workflow for r10k.

Workflow To Add A New Module

The first step in our workflow is to decided on a module to add to our setup. If you have a particular module you want to use, feel free to substitute it below. I’ve chosen saz-motd, a very simple module that is visible when installed, but will not have a material impact on your nodes. We can see right now that there is no message of the day, so we’ll know when we’re done:

[root@puppet ~]# cat /etc/motd
[root@puppet ~]#

Note: We’ll add our module to a feature branch below. It’s a simple module, so this is fine. More complex modules, such as those that include additional facts and functions, should always be installed on the master first to ensure the plugins are synchronized, which means adding them to production. This was discussed on IRC so I don’t have a link to documentation to show how this works; this is the closest I could find. I’ll mention it again when we install such a module, but I wanted to mention it in case the module you chose provides custom facts/functions.

Working On A Feature Branch

Now that we know what we want to do, our first step is to create a feature branch. We’ll need to do this in both our puppet-tutorial (puppet) and our rnelson0-base (module) repos. Use git checkout -b <featurename> in both repos. In the puppet repo, make sure you’re starting from the production branch. As before, I am doing this on my puppet master but this can be done from anywhere.

[root@puppet ~]# cd git/rnelson0-base/
[root@puppet rnelson0-base]# git checkout -b motd
Switched to a new branch 'motd'
[root@puppet rnelson0-base]# cd ../puppet-tutorial/
[root@puppet puppet-tutorial]# git checkout production
Switched to branch 'production'
[root@puppet puppet-tutorial]# git checkout -b motd
Switched to a new branch 'motd'
[root@puppet puppet-tutorial]# ls
manifests  Puppetfile  README.md

Within the puppet repo, we need to update the Puppetfile to add saz-motd. Here’s the line we add, our commit, and our push:

[root@puppet puppet-tutorial]# git diff
diff --git a/Puppetfile b/Puppetfile
index 9b19e3c..aba0eda 100644
--- a/Puppetfile
+++ b/Puppetfile
@@ -9,6 +9,7 @@ mod "puppetlabs/stdlib", "4.1.0"
 mod "saz/ssh", "1.4.0"
 mod "yguenane/augeas", "0.1.1"
 mod "yguenane/ygrpms", "0.1.0"
+mod "saz/motd"

 # For our r10k installer
 mod "zack/r10k", "1.0.2"
[root@puppet puppet-tutorial]# git commit -a -m 'Add saz-motd module to Puppetfile'
[motd d4c2b22] Add saz-motd module to Puppetfile
 1 files changed, 1 insertions(+), 0 deletions(-)
[root@puppet puppet-tutorial]# git push origin motd
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 380 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/puppet-tutorial
 * [new branch] motd -> motd

Deploy using r10k. When it’s done, you should see that the production environment is unchanged, but there is a new environment motd that contains saz-motd:

[root@puppet puppet-tutorial]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
[root@puppet puppet-tutorial]# ls /etc/puppet/environments/{production,motd}/modules
/etc/puppet/environments/motd/modules:
apache  base    firewall  git      make  ntp     portage  ruby  stdlib   ygrpms
augeas  concat  gcc       inifile  motd  pe_gem  r10k     ssh   vcsrepo

/etc/puppet/environments/production/modules:
apache  base    firewall  git      make  pe_gem   r10k  ssh     vcsrepo
augeas  concat  gcc       inifile  ntp   portage  ruby  stdlib  ygrpms

Halfway there! Now it’s time to update the base module. Move over to the module repo and include motd in the class. Commit the change and push it upstream

[root@puppet puppet-tutorial]# cd ../rnelson0-base/
[root@puppet rnelson0-base]# vi manifests/init.pp
[root@puppet rnelson0-base]# git diff
diff --git a/manifests/init.pp b/manifests/init.pp
index f605392..6f78ed2 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -37,6 +37,8 @@
 #
 class base {

+  include ::motd
+
   include ::ssh
   ::ssh::server::configline { 'PermitRootLogin': value => 'yes' }

[root@puppet rnelson0-base]# git commit -a -m 'Include the motd module in the class'
[motd ad2a585] Include the motd module in the class
 1 files changed, 2 insertions(+), 0 deletions(-)
[root@puppet rnelson0-base]# git push origin motd
Counting objects: 7, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 392 bytes, done.
Total 4 (delta 2), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/rnelson0-base.git
 * [new branch]      motd -> motd

Have r10k deploy again. Check out the content of the base module in each of our two environments:

[root@puppet rnelson0-base]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
grep: /etc/puppet/environments/motd/base/manifests/*: No such file or directory
[root@puppet rnelson0-base]# grep motd /etc/puppet/environments/{production,motd}/modules/base/manifests/*

What the? Our change isn’t present. We made changes, committed them, and pushed them upstream, but they’re nowhere to be found. The key is in how the Puppetfile works. Let’s look at how the module repo is defined:

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

We need to make a slight change here. We need to tell r10k to use the feature branch. We do this by adding a “:ref” key with a value of the branch name. After doing this, we push our change upstream and deploy. This time, the change is visible:

[root@puppet puppet-tutorial]# git diff
diff --git a/Puppetfile b/Puppetfile
index aba0eda..8c93a0f 100644
--- a/Puppetfile
+++ b/Puppetfile
@@ -25,3 +25,4 @@ mod "puppetlabs/vcsrepo", "0.2.0"
 # Modules from Github
 mod "base",
-  :git => "git://github.com/rnelson0/rnelson0-base"
+  :git => "git://github.com/rnelson0/rnelson0-base",
+  :ref => "motd"
[root@puppet puppet-tutorial]# git commit -a -m 'Add motd ref to rnelson0-base'
[motd 0c54ff7] Add motd ref to rnelson0-base
 1 files changed, 1 insertions(+), 1 deletions(-)
[root@puppet puppet-tutorial]# git push origin motd
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 371 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/puppet-tutorial
   84f5c41..0c54ff7  motd -> motd
[root@puppet puppet-tutorial]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
[root@puppet puppet-tutorial]# grep motd /etc/puppet/environments/{production,motd}/modules/base/manifests/*
/etc/puppet/environments/motd/modules/base/manifests/init.pp:  include ::motd

With the change visible, it’s time to test. Run puppet agent against the production environment (no specified environment) and against the new motd environment. We can see that our change is only available in the motd environment:

[root@puppet puppet-tutorial]# puppet agent --test
Info: Applying configuration version '1395849183'
Notice: Generated from our notify branch
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: defined 'message' as 'Generated from our notify branch'
Notice: Finished catalog run in 1.12 seconds

[root@puppet puppet-tutorial]# puppet agent --test --noop --environment motd
Info: Applying configuration version '1395976706'
Notice: /Stage[main]/Motd/File[/etc/motd]/content:
--- /etc/motd   2010-01-12 13:28:22.000000000 +0000
+++ /tmp/puppet-file20140328-9213-jecidq-0      2014-03-28 14:15:25.749425823 +0000
@@ -0,0 +1,8 @@
+
+CentOS 6.5 x86_64
+
+FQDN:      puppet.nelson.va (10.0.0.35)
+Processor: 1x Intel(R) Xeon(R) CPU E31220 @ 3.10GHz
+Kernel:    2.6.32-431.3.1.el6.x86_64
+Memory:    1.83 GB
+

Info: /Stage[main]/Motd/File[/etc/motd]: Filebucketed /etc/motd to puppet with sum d41d8cd98f00b204e9800998ecf8427e
Notice: /Stage[main]/Motd/File[/etc/motd]/content: content changed '{md5}d41d8cd98f00b204e9800998ecf8427e' to '{md5}68330ab5f136e99ad54fc05e133da4e7'
Notice: Generated from our notify branch
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: defined 'message' as 'Generated from our notify branch'
Notice: Finished catalog run in 1.54 seconds

We have one final test. Open a new ssh session to the puppet master and you should see the message of the day on login:

login as: root
root@puppet.nelson.va's password:
Last login: Fri Mar 28 14:15:02 2014 from 10.0.0.200

CentOS 6.5 x86_64

FQDN:      puppet.nelson.va (10.0.0.35)
Processor: 1x Intel(R) Xeon(R) CPU E31220 @ 3.10GHz
Kernel:    2.6.32-431.3.1.el6.x86_64
Memory:    1.83 GB

[root@puppet ~]#

Our feature branch has everything we need. In a production environment, this is when you run all your tests to make sure everything works. This module and the changes aren’t very complex, however, so now we need to merge this successful feature branch into the production branch.

Merge the New Module

The last part of this workflow is to merge our work. In the module repo, this means merging motd into master. In the puppet repo, we have to make one more change, then merge motd into production. Let’s start with the module repo. You’ve seen this before – checkout the branch you want to merge into and merge the feature branch into it. It’s up to your discretion if you want to keep the old branch or delete it. In my case, I’ll keep the branch for people who are following this tutorial, but in a production environment you would probably only keep release tags, like v1.0.0, v1.1.0, etc., and not keep every feature branch.

[root@puppet rnelson0-base]# git branch
  master
* motd
[root@puppet rnelson0-base]# git checkout master
Switched to branch 'master'
[root@puppet rnelson0-base]# git merge motd
Updating af364be..ad2a585
Fast-forward
 manifests/init.pp |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
[root@puppet rnelson0-base]# grep motd manifests/init.pp
  include ::motd
[root@puppet rnelson0-base]# git push origin master
Total 0 (delta 0), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/rnelson0-base.git
   af364be..ad2a585  master -> master

You can see that the change has been merged and we pushed it upstream. Re-deploy and you’ll see that motd is now part of both the production and motd environments:

[root@puppet rnelson0-base]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
[root@puppet rnelson0-base]# grep motd /etc/puppet/environments/{production,motd}/modules/base/manifests/*
/etc/puppet/environments/production/modules/base/manifests/init.pp:  include ::motd
/etc/puppet/environments/motd/modules/base/manifests/init.pp:  include ::motd

Let’s take a look at the puppet repo. Everything’s working, so we don’t have anything to change, right? Not quite. Let’s take a look at the Puppetfile contents in the motd branch of the puppet repo:

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

We now have two problems. First, in production, our master branch would end up not including the base module because it could not find a branch motd to retrieve. We can fix this by removing that ref, committing our changes upstream, and redeploying in a few. The second, and more serious problem, is that our production branch is now broken – it was able to pull down the merged changes from base, but the production Puppetfile doesn’t include the new statement for saz-motd. Even though we see the include ::motd statement above, we’re up a creek without a paddle because the motd module is not present and catalog compilation in the production environment fails:

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

We need to fix both problems. We’ll fix the second one first. In the puppet repo, check out the production branch and merge the changes from motd.

[root@puppet puppet-tutorial]# git branch
  master
* motd
  production
[root@puppet puppet-tutorial]# git checkout production
Switched to branch 'production'
[root@puppet puppet-tutorial]# git merge motd
Updating bef7066..0c54ff7
Fast-forward
 Puppetfile |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

Fix the second issue by removing the references to motd in the Puppetfile (don’t forget to remove the trailing space on the mod line!), committing, and pushing the changes to github. After re-deploying, we can see the base class includes motd, the files for motd are present, and the Puppetfile does not reference motd in any way.

[root@puppet puppet-tutorial]# vi Puppetfile
[root@puppet puppet-tutorial]# tail -3 Puppetfile
# Modules from Github
mod "base",
  :git => "git://github.com/rnelson0/rnelson0-base"
[root@puppet puppet-tutorial]# git status
# On branch production
# Changed but not updated:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       modified:   Puppetfile
#
no changes added to commit (use "git add" and/or "git commit -a")
(reverse-i-search)`push': git ^Csh origin master
[root@puppet puppet-tutorial]# git commit -a -m 'Remove references to motd feature branch after merging'
[production b8a08d0] Remove references to motd feature branch after merging
 1 files changed, 1 insertions(+), 1 deletions(-)
[root@puppet puppet-tutorial]# git push origin production
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 373 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/puppet-tutorial
   2af5e8b..b8a08d0  production -> production
[root@puppet puppet-tutorial]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
[root@puppet puppet-tutorial]# ls /etc/puppet/environments/production/modules/motd
manifests  metadata.json  Modulefile  README.md  spec  templates  tests
[root@puppet puppet-tutorial]# grep motd /etc/puppet/environments/{production,motd}/modules/base/manifests/*
/etc/puppet/environments/production/modules/base/manifests/init.pp:  include ::motd
/etc/puppet/environments/motd/modules/base/manifests/init.pp:  include ::motd

Last but not least, ensure catalog compilation. We’ll delete /etc/motd to ensure it gets added back:

[root@puppet puppet-tutorial]# rm /etc/motd
rm: remove regular file `/etc/motd'? y
[root@puppet puppet-tutorial]# puppet agent --test
Notice: /Stage[main]/Motd/File[/etc/motd]/ensure: defined content as '{md5}68330ab5f136e99ad54fc05e133da4e7'
Notice: Generated from our notify branch
Notice: /Stage[main]/Main/Node[puppet.nelson.va]/Notify[Generated from our notify branch]/message: defined 'message' as 'Generated from our notify branch'
Notice: Finished catalog run in 1.28 seconds
[root@puppet puppet-tutorial]# cat /etc/motd

CentOS 6.5 x86_64

FQDN:      puppet.nelson.va (10.0.0.35)
Processor: 1x Intel(R) Xeon(R) CPU E31220 @ 3.10GHz
Kernel:    2.6.32-431.3.1.el6.x86_64
Memory:    1.83 GB

Summary of New Module Workflow

Our work here is complete! The puppet repo (puppet-tutorial) and the module repo (rnelson0-base) have both been updated to use the new motd module. Here’s a recap of the process:

Module Repo:

  • Create a new branch in the existing module repo(s): git checkout -b <feature>
  • Modify the existing module(s) as needed to reference the new module(s)
  • Commit changes: git commit -a -m ‘Add <feature> reference to module’
  • Push upstream: git push origin <feature>

Puppet Repo:

  • Create a new branch in the puppet repo: git checkout -b <feature>
  • Modify Puppetfile:
    • Add a reference to the new module. Optionally, add a version (forge) or :ref (git).
    • Add a :ref to existing module repos with a value of the new feature branch’s name.
  • Commit changes: git commit -a -m ‘Add module <X> to branch <feature>’
  • Push upstream: git push origin <feature>

Puppet Master:

  • Deploy environments with r10k: r10k deploy environment -p
  • Test catalog compilation in the new feature branch: puppet agent –test –noop –environment <feature>

Repeat the above steps until your changes are complete and all tests succeed. When ready, merge the changes:

Module repo:

  • Checkout the master branch: git checkout master
  • Merge changes from the feature branch: git merge <feature>
  • Push upstream: git push origin master
  • OPTIONAL – Delete feature branch (recommended for short lived features, not recommended for version tags): git branch -D feature; git push origin :feature

Puppet repo:

  • Checkout the production branch: git checkout production
  • Merge changes from the feature branch: git merge <feature>
  • Update Puppetfile to remove any references to the feature branch.
  • Commit changes: git commit -a -m ‘Remove refs to <feature> branches for module <X>’
  • Push upstream: git push origin production
  • OPTIONAL – Delete feature branch (recommended for short lived features, not recommended for version tags): git branch -D feature; git push origin :feature

Puppet Master:

  • Deploy environments with r10k: r10k deploy environment -p
  • Test catalog compilation in the production branch: puppet agent –test –noop

Next Workflow

In the next class, we’ll look at the second workflow, working with an existing module. If you followed everything above, you can probably intuit the differences yourself if you’re trying to stay ahead. Check in next week to see how you did!

2 thoughts on “Puppet and Git, 203: r10k Workflow for New Module

  1. Pingback: Improved r10k deployment patterns | rnelson0
  2. Pingback: Puppet and Git, 206: Git Hooks – Post-Receive | 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 )

Twitter picture

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

Facebook photo

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

Connecting to %s