DevOps Series – Automation with Puppet – Part6
Best Practices to write puppet code – Puppet Style Guide
The Puppet style guide is a list of best practices when writing Puppet code. There are more than a dozen recommended guidelines, and I list some of them here. The rest are available for your reading pleasure at the sites mentioned at the end of this section. Writing Puppet code may occur in manifests or when designing modules.
Commonality
Any large enterprise will have hundreds if not thousands of lines of Puppet code. Going through the code may be a routine job for a Puppet administrator, which brings us to our first principle: Make Puppet code as easy to read as possible. Don’t assume that complex code is a sign of your Puppet skills. I try to write Puppet code in a way that a beginner Puppet practitioner is able to understand.
Although inheritance can be helpful, when possible try to avoid a deep nested inheritance that makes it difficult to trace. Within a module itself, inheritance is fine; however, if you try to inherit from another module, this makes things more complicated. A way around this is to parameterize classes.
#good case of inheritence class database { ... } class database::mysql inherits database { ... } class database::oracle inherits database { ... } #bad case of inheritence class database inherits server { ... } class mysql inherits nagios { ... } class oracle inherits linuxlvm { ... }
Using external node classifiers (ENCs) is OK, but don’t require your module to have an ENC. An ENC is an external script or application that tells Puppet which classes a node is supposed to have. ENCs do not have to be written in Ruby.
Classes should not declare other classes they need; instead, allow class dependency to fail if the needed class is not present. The reason for this is as that you want classes declared as close as possible to the node scope.
Module Metadata
Every module should have metadata associated with it. Metadata are generally stored in themetadata.json file. Some of the items to include in metadata are as follows:
name 'my-ssh-module' version '1.0' author 'Your-Name' summary 'SSH module for CentOS' description 'This module enables you to manage SSH on CentOS v6.x and above' license 'Apace 2.0' project_page 'http://www.example.com' dependency 'name-of-other-modules'
General Formatting
Proper formatting of Puppet code can make it very easy to read if done appropriately. Some recommendations are as follows:
-
Use two-space soft tabs; do not use literal tab characters.
-
Do not use trailing white space and do not exceed the 80-character line width.
-
Align fat comma arrows (=>) within blocks of code.
-
Comments should use #, not // or /*.
-
Strings should use single quotes unless specifying variables. For variable interpolation, use double quotes. On the other hand, variables that stand on their own do not need quoting.
-
The ensure attribute should be listed first for a resource if required.
-
Group logical pieces of code together, rather than type of code
## Good code # This is a comment using a single # # Using double quotes for interpolation # Also using braces for interpolation " /etc/security/${file}.conf" "${::sshmodule} requires ${another_module}" # Proper spacing user admin { ensure => present, uid => 2000, } ## Bad code // Trying a comment " /etc/security/$file.conf" "$::sshmodule requires $another_module" # Improper spacing user admin { uid=>2000, ensure=>present, }
Understanding Puppet Config Files
The main configuration file for Puppet is puppet.conf. This file’s usual location is/etc/puppet. The file consists of at least three sections: main, master, and agent.
The main section applies to both master and clients. The master section is only needed on the Puppet master, and the agent section is only needed on the Puppet client. A very basicpuppet.conf can look like:
$ sudo cat /etc/puppet/puppet.conf [main] server = puppet.example.com report = true pluginsync = true certname = puppet.example.com logdir = /var/log/pe-puppet rundir = /var/run/pe-puppet modulepath = /etc/puppet/modules [master] dns_alt_names = puppet,puppet.example.com [agent]
Many of the options in the file are self-explanatory. The server value should point to the Puppet server name. report = true lets the client know to report its status to the Puppet master.pluginsync asks the master to distribute custom facts to the clients. You can read more about it at https://docs.puppetlabs.com/guides/plugins_in_modules.html . logdir and rundir define where Puppet sends its log and where Puppet runs from. modulepath defines the location of all modules.
In the master section, the dns_alt_names parameter specifies the alternate domain name server names for the server. In our case it’s just puppet and puppet.example.com.
The agent section is blank because we don’t have any specific items to identify here. Some of the items of the agent are already in the [main] section.
Puppet Reporting
By default, Puppet client reports to the Puppet master. This information can be stored in various ways. PuppetDB (https://docs.puppetlabs.com/puppetdb/latest/) is one way you can store reporting information. Additional options include both open source tools and also commercial software.
PuppetDB can store the following information:
-
The most recent facts from every node
-
The most recent catalog for every node
-
Optionally, 14 days (configurable) of event reports for every node
Puppet Enterprise Console is available with the Enterprise edition of Puppet (https://docs.puppetlabs.com/pe/latest/console_accessing.html). Puppet Console is easy to use and has a lot of features that make it a possible option. The caveat with Puppet Console is its cost; the list price for Enterprise is quite high.
Foreman is a very popular Puppet reporting open source tool (http://theforeman.org/). Foreman is more than just a reporting tool, however; it is a full provisioning ecosystem. Some of the features of Foreman as listed on its web site are
-
Discover, provision, and upgrade your entire bare-metal infrastructure
-
Create and manage instances across private and public clouds
-
Group your hosts and manage them in bulk, regardless of location
-
Review historical changes for auditing or troubleshooting
-
Extend as needed via a robust plug-in architecture
-
Build images (on each platform) automatically per system definition to optimize deployment
Puppetboard (https://github.com/nedap/puppetboard) is primarily a web interface to PuppetDB. Puppet Dashboard has been around for a while as well, although it does not have an install base as large as Foreman. You can find out more about it athttps://github.com/sodabrew/puppet-dashboard.
Certificate Management
Puppet master and client communicate securely using encryption. This encryption is done with the help of certificates. Both the master and client have a certificate. When a client contacts the master, it presents its certificate request to the master. As a Puppet administrator, you have two options. One option is to set up autosigning, in which case the master automatically accepts the client certificate request. The other option is a more manual process, in which you, as a Puppet administrator, have to sign the client certificate. For safety, manual signatures might be OK; however, some enterprises chose to enable autosigning for the sake of convenience.
The Puppet server is normally the Certificate Authority (CA) server as well. For additional security you can create another CA server and get Puppet to use that instead of having the CA server on the same server as the Puppet server.
You do not need to purchase certificates for Puppet because Puppet generates its own, and does not require a public key infrastructure that it can use.
To list the client certificates from the Puppet server, run the following:
# puppet cert list --all + "puppet.example.com" (SHA256) 84:68:8B:32:BE:F8:CA:1D:09:80:E7:38:5D:36:EE:46:9A:DC:... + "www.example.com" (SHA256) AA:A2:7A:EE:1B:FA:80:65:2C:97:DD:EB:5B:E1:FD:60:AA:DD:... + "mysql.example.com" (SHA256) 06:B4:C4:8B:C2:7E:97:27:03:57:FF:55:69:C1:2F:1D:7A:FC:...
If you want to enable autosigning of certificate requests by the server, then in/etc/puppet/autosign.conf, type in the either the individual hostnames for which you want autosigning enabled, or the wildcard for the domain:
# cat /etc/puppe/autosign.conf *.example.com
If you have autosigning disabled, then after you install each Puppet client, on the server you must sign the certificate request of the client manually:
# puppet cert sign web.example.com notice: Signed certificate request for web.example.com notice: Removing file Puppet::SSL::CertificateRequest agent1.localdomain at '/etc/puppet/ssl/ca/ requests/web.example.com'
Note |
Time has to be synchronized between the server and client for Puppet to work, because certificate-based authentication and encryption are time sensitive. Make sure you have NTP installed and enabled on both master and client. |