Fattening the Puppet workflow, part 1: The control repository
If editing Puppet code and Hiera directly with puppetmaster feels too easy, you can complicate the Puppet workflow as much you like by adding more components to the palette. Adding the so called control repository and r10k to Puppet environment maintenance instantly adds several phases to your initially simple workflow. There are also benefits that I’ll discuss later on.
The control repository (control repo for short) is a Git repository that is used to create dynamic Puppet environments on the Puppet master. Each Git branch can be turned into a matching Puppet environment. The name of the default branch, production, is the same as the environment the Puppet agents use by default. The content of the control repo branches look like this:
Using Puppet Bolt to apply roles to nodes, part 1
NOTE: this article is somewhat outdated. Please refer to Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt instead.
Puppet Bolt is a designed to be an orchestration tool, but it can be used for configuration management as well. For example you may have a small environment of handful of nodes where having a dedicated Puppet server would be an overkill, or you have some nodes in the infrastructure where you can’t or don’t want to install a Puppet Agent.
Improving Vim's Puppet integration
A recent package upgrade seemed to break Puppet syntax highlighting horribly on my Fedora 32 laptop. Not only did syntax highlighting not work, it was actually completely wrong and confusing. It seemed as if the Puppet file type detection was missing, so vim was probably using some a syntax highlighter for a wrong type. To see which file types are enabled I used :set filetype followed by a couple of tabs (more details on StackExchange).
Integrating Puppet Bolt with PuppetDB
In the Puppet Bolt with a Puppet control repository blog post we showed how to integrate Puppet Bolt with an existing control repository. We used a static inventory file, which is ok for testing or when you have a few target nodes. However, if you have a Puppetserver running and are not using Bolt to manage the configurations of your nodes, then the chances are that you have a fairly large node population and maintaining a static inventory would be both tedious and error-prone. One option among many is to use the PuppetDB plugin to generate a dynamic inventory and that is what we describe here.
Puppet environment leakage
Occasionally when working with feature branch and updating types and providers you may run into environment leakage issues. They manifest themselves as issues that make no particular sense and are caused by the wrong version of a type/provider such as puppetlabs-apt (see this issue) leaking into your feature environment. This can happen when you update external modules in your Puppetfile. The solution (or band-aid) is fortunately fairly easy. On the Puppetmaster run this command:
Puppet Bolt with a Puppet control repository
NOTE: this article is somewhat outdated. Please refer to Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt instead.
Puppet Bolt is an orchestration tool similar to Ansible. It is suitable for multiple use-cases:
-
Running ad hoc commands on target nodes. We use this approach for testing Puppet feature branches before approving and merging them.
-
One-off configurations written in the Puppet language
-
Masterless Puppet environments (look here). You may not always have enough privileges, trust or will to manage a complete Puppet infrastructure including Puppetserver, PuppetDB and Puppet Agents.
Simple orchestration with Puppet Bolt
As discussed in the introductory post one of the use-cases for Puppet Bolt is orchestration - running actions on multiple targets in a certain order, possibly using results of actions or data from some targets on other targets. Orchestration with Bolt plans is quite easy: you just create a plan with multiple TargetSpec parameters, each representing a target or a type of target. A classic example is two or more web servers behind a load balancer. In this case we do something even simpler and just take a fact from a node called “first” and create a file on node “second” that uses the value of that fact in a file.
Puppet types and providers development part 2: creating a resource
This blog post is a part of this blog post series:
In the previous blog post we created the Puppet type librenms_service and created a dummy skeleton for the provider implementation. We were able to use the type, but it did not yet do anything. In this blog post we move to implementing the create method, which in this case was the low-hanging fruit. At this point we do not need to worry about any existence checks (the exists? method) or trying to minimizing the number of API calls with self.prefetch or something similar.
Puppet types and providers development part 1: creating the type
This article is a part of this blog post series:
Writing Puppet types and providers looks like black magic even to those who have lots of experience in the Puppet language itself. While the official types development documentation is quite ok, provider documentation is definitely lacking. Not only are there no practical examples from real life, the important features such as prefetching, resource methods and flushing are described only on a very high level. The usual answer to lack of examples is “have a look at the built-in resources”. But when you look at any built-in resource, you realize that the code is very complex because
Giving special treatment to the last entry when iterating over a Puppet hash
When you’re iterating over a hash you may end up having to treat the last hash entry differently. For example, if you’re creating entries to a JSON file you should have “},” in between the entries until you reach the last entry, where you must have just “}” or JSON data will be invalid.
Now, hashes do not have indexes, nor does Puppet support variable reassignment along the lines of $x = $x +1, so you’re out of luck trying to get the length and comparing that to a counter. So, you need some other way to detect when iteration has reached the last entry in the hash.