
Breaking old practices
When Puppet Labs decided to work on the parser and on the new features, they also decided to remove some features that had already been deprecated for a couple of releases.
Converting node inheritance
Node inheritance has been considered good practice during older Puppet releases. To avoid too much code on the node level, a generic, nonexistent host was created (basenode
) and the real nodes inherited from basenode
:
node basenode { include security include ldap include base } node 'www01.example.net' inherits 'basenode' { class { 'apache': } include apache::mod::php include webapplication }
This node classification is no longer supported by Puppet 4.
As of 2012, the Roles and Profiles pattern became increasingly popular, bringing new methods on how to allow smart node classification. From a technical point of view, roles and profiles are Puppet classes. The profile module wraps technical modules and adapts their usage to the existing infrastructure by providing data such as ntp servers and ssh configuration options. The role module describes system business use cases and makes use of the declared profiles:
class profile::base { include security include ldap include base } class profile::webserver { class { 'apache': } include apache_mod_php } class role::webapplication { include profile::base include profile::webserver include profile::webapplication } node 'www01.example.net' { include role::webapplication }
Dealing with bool algebra on Strings
A minor change with a huge impact is the change of empty string comparison. Prior to Puppet 4, one could test for either an unset variable or a variable containing an empty string by checking for the variable:
class ssh ( $server = true, ){ if $server {...} }
The ssh
class behaved similar ($server evaluates to true) when used within the following different declarations:
include ssh class { 'ssh': server => 'yes', }
Disabling the server section in the ssh
class could be achieved by the following class declarations:
class { 'ssh': server => false, } class { 'ssh': server => '', }
The behavior of the last example (empty string) changed in Puppet 4. The empty string now equals a true value in Boolean context just as in Ruby. If your code makes use of this way of variable checking, you need to add the check for empty string to retain the same behavior with Puppet 4:
class ssh ( $server = true, ){ if $server and $server != '' {...} }
Using strict variable naming
Variables sometimes look like constants and exhibit the following features:
- Variables cannot be declared again
- In the scope of one node, most variables are static (
hostname
,fqdn
, and so on)
Sometimes developers prefer to write the variables in capital letters due to the previously mentioned items, to make them look like Ruby constants.
With Puppet 4, variable names must not start with a capital letter:
class variables { $Local_var = 'capital variable' notify { "Local capital var: ${Local_var}": } }
Declaring this class will now produce the following error message:
root@puppetmaster:/etc/puppetlabs/code/environments/production/modules# puppet apply -e 'include variables' Error: Illegal variable name, The given name 'Local_var' does not conform to the naming rule /^((::)?[a-z]\w*)*((::)?[a-z_]\w*)$/ at /etc/puppetlabs/code/environments/production/modules/variables/manifests/init.pp:3:3 on node puppetmaster.example.net
Learning the new reference syntax
Due to the type system and due to the reason that Puppet 4 now takes everything as an expression, one has to name references on other declared resources properly. References now have some strict regulations:
- No whitespace between reference type and opening bracket
- The reference title (used without quotes) must not be spelled with a capital letter
The following will produce errors on Puppet 4:
User [Root] User[Root]
Starting with Puppet 4, references have to be written in the following pattern:
Type['title']
Our example needs to be changed to:
User['root']
Cleaning hyphens in names
Many modules (even on the module Forge) have used the hyphen in the module name. The hyphen is now no longer a string character, but a mathematical operator (subtraction). In consequence, hyphens are now strictly forbidden in the following descriptors:
- The module name
- Any class name
- Names of defined types
When using modules with hyphens, the hyphen needs to be removed or replaced with a string character (for example, the underscore).
This is possible with older versions as follows:
class syslog-ng {...} include syslog-ng
Now, the new style is as follows:
class syslog_ng { ... } include syslog_ng
No Ruby DSL anymore
Some people used the possibility to put .rb
files as manifests inside modules. These .rb
files contained Ruby code and were mostly needed for working with data. Puppet 4 now has data types that make this obsolete.
The support for these Ruby manifests has been removed in Puppet 4.
Relative class name resolution
With Puppet 3 and older, it was required to specify absolute class names in case that a local class name was identical to another module:
# in module "mysql" class mysql { ... } # in module "application" class application::mysql { include mysql }
Within the scope of the application::
namespace, Puppet 3 would search this namespace for a mysql
class to be included. Effectively, the application::mysql
class would include itself. But this was not what we intended to do. We were looking for the mysql
module instead. As a workaround, everybody was encouraged to specify the absolute path to the mysql
module class:
class application::mysql { include ::mysql }
This relative name resolution no longer applies in Puppet 4. The original example works now.
Dealing with different data types
Due to the reason that Puppet 3 was not aware of the different data types (mostly everything was dealt with as being of type string) it was possible to combine several different data types.
Puppet 4 now is very strict when it comes to combining different data types. The easiest example is dealing with float and integer; when adding a float and an integer, the result will be of type float.
Combining actions on different data types such as string and bool will now result in an error.
The following code will work:
case $::operatingsystemmajrelease { '8': { include base::debian::jessie } }
On the other hand, the following code will not work:
if $::operatingsystemmajrelease > 7 { include base::debian::jessie }
You will receive the following error message:
Error: Evaluation Error: Comparison of: String > Integer, is not possible. Caused by 'A String is not comparable to a non String'
Review comparison of different Facter variables carefully. Some Facter variables such as operatingsystemmajrelease
return data of the type string, whereas processorcount
returns an integer value.