Puppet and Git, 204: r10k Workflow for Existing Module

We’ve installed and configured r10k, are using it for deployments, and have a workflow for new modules. More commonly, we will be working on existing modules, a slightly different workflow. We can examine this new workflow by modifying the base module only.

Workflow to Modify Existing Module

Unlike adding a new module, the Puppetfile only needs to be modified to reflect the feature branch. This is where the workflows diverge: instead of requiring a merge, commit, and push, we can create a temporary branch and just delete it when we’re done. Only the module branch needs merged. We’ll show this by making a simple change, modifying Dave’s name. We have another Dave Smith who works here, so we’ll add a middle initial and the name of Dave’s organization, to prevent confusion.

The feature branch is just called dave. We’ll work on the module repo first. Make sure you’re in master and checkout the new branch. Dave’s description should be updated to “Dave G. Smith – IT Administrator” – everything will be alright unless they hire another Dave G. Smith over there. Commit the change and push it.

[root@puppet rnelson0-base]# git branch
* master
  motd
[root@puppet rnelson0-base]# git checkout -b dave
Switched to a new branch 'dave'
[root@puppet rnelson0-base]# vi manifests/
init.pp  user.pp
[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 6f78ed2..0f097b1 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -53,7 +53,7 @@ class base {
     id       => 'dave',
     uid      => '507',
     pass     => $defaultpass,
-    realname => 'Dave Smith',
+    realname => 'Dave G. Smith - IT Administrator',
     sgroups  => [],
   }

[root@puppet rnelson0-base]# git commit -a -m "Update Dave's name in the user definition"
[dave 096e9c1] Update Dave's name in the user definition
 1 files changed, 1 insertions(+), 1 deletions(-)
[root@puppet rnelson0-base]# git push origin dave
Counting objects: 7, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 417 bytes, done.
Total 4 (delta 2), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/rnelson0-base.git
 * [new branch]      dave -> dave

Next, move to the puppet repo. Make sure you’re on production this time and checkout the new branch. In the Puppetfile, add a :ref to the dave branch of our module. Even though this is a temporary branch, we still need to commit and push it upstream.

[root@puppet puppet-tutorial]# git push origin dave
* master
  motd
[root@puppet rnelson0-base]# git checkout -b dave
Switched to a new branch 'dave'
[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 6f78ed2..0f097b1 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -53,7 +53,7 @@ class base {
     id       => 'dave',
     uid      => '507',
     pass     => $defaultpass,
-    realname => 'Dave Smith',
+    realname => 'Dave G. Smith - IT Administrator',
     sgroups  => [],
   }

[root@puppet rnelson0-base]# git commit -a -m "Update Dave's name in the user definition"
[dave 096e9c1] Update Dave's name in the user definition
 1 files changed, 1 insertions(+), 1 deletions(-)
[root@puppet puppet-tutorial]# git push origin dave
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 399 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://rnelson0@github.com/rnelson0/puppet-tutorial
 * [new branch]      dave -> dave

Deploy and test both production and dave environments. You can use noop, or just make sure production is the last one you run against:

[root@puppet puppet-tutorial]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
[root@puppet puppet-tutorial]# puppet agent --test --environment dave
Notice: /Stage[main]/Base/Base::User[dave]/User[dave]/comment: comment changed 'Dave Smith' to 'Dave G. Smith - IT Administrator'
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.07 seconds
[root@puppet puppet-tutorial]# puppet agent --test
Notice: /Stage[main]/Base/Base::User[dave]/User[dave]/comment: comment changed 'Dave G. Smith - IT Administrator' to 'Dave Smith'
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 0.93 seconds

Once everything’s good to go, switch back to the module repo. Checkout master, merge dave, and push it upstream.

[root@puppet rnelson0-base]# git branch
* dave
  master
  motd
[root@puppet rnelson0-base]# git checkout master
Switched to branch 'master'
[root@puppet rnelson0-base]# git merge dave
Updating ad2a585..096e9c1
Fast-forward
 manifests/init.pp |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
[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
   ad2a585..096e9c1  master -> master

Redeploy again and test both environments. Now production and dave do the same thing – unlike when adding a new module, the existing module works fine because production‘s Puppetfile already pointed to the module with no changes.

[root@puppet rnelson0-base]# r10k deploy environment -p
Faraday: you may want to install system_timer for reliable timeouts
[root@puppet rnelson0-base]# puppet agent --test --environment dave --noop
Notice: /Stage[main]/Base/Base::User[dave]/User[dave]/comment: current_value Dave Smith, should be Dave G. Smith - IT Administrator (noop)
Notice: Base::User[dave]: Would have triggered 'refresh' from 1 events
Notice: Class[Base]: Would have triggered 'refresh' from 1 events
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 2 events
Notice: Finished catalog run in 0.99 seconds
[root@puppet rnelson0-base]# puppet agent --test
Notice: /Stage[main]/Base/Base::User[dave]/User[dave]/comment: comment changed 'Dave Smith' to 'Dave G. Smith - IT Administrator'
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.25 seconds

Because we don’t have to merge changes in the puppet repo, we can go into both repos, delete the feature branch (using the -D switch for puppet, as we never merged the branch), and push changes upstream. After re-deploying, there won’t be a dave feature branch so there won’t be a dave environment, as shown below (for those playing along via github, I DID delete the branch on the github repo, so you’ll have to perform the above changes yourself). However, running the agent against production shows everything is still working.

[root@puppet rnelson0-base]# git branch
  dave
* master
  motd
[root@puppet rnelson0-base]# git branch -D dave
Deleted branch dave (was 096e9c1).
[root@puppet rnelson0-base]# git push origin :dave
To https://rnelson0@github.com/rnelson0/rnelson0-base.git
 - [deleted]         dave
[root@puppet rnelson0-base]# cd ../puppet-tutorial/
[root@puppet puppet-tutorial]# git branch
* dave
  master
  motd
  production
[root@puppet puppet-tutorial]# git checkout production
Switched to branch 'production'
[root@puppet puppet-tutorial]# git branch -D dave
Deleted branch dave (was 580b993).
[root@puppet puppet-tutorial]# git push origin :dave
To https://rnelson0@github.com/rnelson0/puppet-tutorial
 - [deleted]         dave
[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/
master  motd  production
[root@puppet puppet-tutorial]# puppet agent --test
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.48 seconds

Summary of Existing Module Workflow

Here’s another quick recap of the workflow.

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 :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 to your module and discard the changes to puppet:

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
  • 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

UPDATE: In the comments, David B. asked about preserving development and production environments in the workflow. The above workflow assumes only production (and the corresponding master branches) are long-lived. If you need a long-lived development environment, the workflow will be slightly different and a bit more tedious. This should be easy to setup.

One Time Setup Work

Puppet repo:

  • Create a branch for each long-lived environment you need – development, testing, qa, etc. – with git checkout -b <environment>
  • In each branch (git checkout <environment>), edit Puppetfile and add a ref for each module you will modify to the branch name matching the environment. I recommend that the branch and environment names match, but any name will work as long as the ref matches.
  • Commit changes: git commit -a -m ‘Add long-lived environment <environment> and references to the environment branch on all developed modules’
  • Push upstream: git push origin <environment>
  • Repeat for the next environment.

Module repos:

  • Create a branch for each long-lived environment you need – development, testing, qa, etc. – with git checkout -b <environment>
  • Commit changes: git commit -a -m ‘Add long-lived environment <environment>. No changes.’
  • Push upstream: git push origin <environment>
  • Repeat for the next environment.

If you add a new module, you’ll need to add it to each Puppet repo branch with the correct refs, and create the environment branches in the module’s repo.

Recurring Workflow

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 :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 to your module into the next environment:

Module repo:

  • Repeat the following three steps, merging feature into development, then again into testing, qa, etc., until it is time to merge it into production:
  • Checkout the environment branch: git checkout <environment>
  • Merge changes from the feature branch: git merge <feature>
  • Push upstream: git push origin <environment>
  • OPTIONAL – Delete feature branch (recommended for short lived features, not recommended for version tags): git branch -D feature; git push origin :feature

Puppet repo:

  • No work needs done on any branch, as the refs all point to the environment branch for each module.
  • When all work is done, checkout the production branch so that future branches are based off it: git checkout 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 environment branch: puppet agent –test –noop –environment <environment>

I do not use such a workflow in my lab or at work so the above is best effort. If you do implement this, please let me know how it works out and any tweaks you needed to make.

What’s Next?

You now have two workflows you can use:

  • Static environments where root does push pulls
  • Dynamic environments where users push and r10k pulls

We still haven’t eliminated the need for someone to log into the master as root and do some work, though. Next week, we’ll look at git hooks and how they can help automate our dynamic environments even further, almost eliminating the need to log in to the master.

4 thoughts on “Puppet and Git, 204: r10k Workflow for Existing Module

  1. Hi

    Would it possible to have a production AND a development branch on which new module features get merged?
    First on development, once validated, merged to production as well.
    This way, you keep working branches similar to real environment.

    David.

    • Yes, this is doable. This would modify the workflow slightly, both with respect to the Puppetfile and the module repos. I’ll update the article shortly to describe this process.

  2. Pingback: Improved r10k deployment patterns | rnelson0
  3. 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 )

Google+ photo

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

Connecting to %s