Book Image

Mastering Puppet 5

By : Ryan Russell-Yates, Jason Southgate
Book Image

Mastering Puppet 5

By: Ryan Russell-Yates, Jason Southgate

Overview of this book

Puppet is a configuration management system and a language written for and by system administrators to manage a large number of systems efficiently and prevent configuration drift. The core topics this book addresses are Puppet's latest features and mastering Puppet Enterprise. You will begin by writing a new Puppet module, gaining an understanding of the guidelines and style of the Puppet community. Following on from this, you will take advantage of the roles and profiles pattern, and you will learn how to structure your code. Next, you will learn how to extend Puppet and write custom facts, functions, types, and providers in Ruby, and also use the new features of Hiera 5. You will also learn how to configure the new Code Manager component, and how to ensure code is automatically deployed to (multiple) Puppet servers. Next, you will learn how to integrate Puppet with Jenkins and Git to build an effective workflow for multiple teams, and use the new Puppet Tasks feature and the latest Puppet Orchestrator language extensions. Finally, you will learn how to scale and troubleshoot Puppet. By the end of the book, you will be able to deal with problems of scale and exceptions in your code, automate workflows, and support multiple developers working simultaneously.
Table of Contents (19 chapters)
Title Page
Dedication
Packt Upsell
Contributors
Preface
Index

Using good module and class structure


This section contains a set of recommendations surrounding good module and class design. Bear in mind that Puppet development is, in principle, just like any other type of software development, and we've learned over many years in software development, and especially at O&O software, that certain modular and class design principles make our development better. I also feel that part of our journey toward infrastructure as code is making our Puppet code just as well-designed, structured, and tested as any other application code.

 

 

Following the class-naming conventions

There's a certain class-naming convention that has developed over time within the Puppet community, and it's really worth taking these into account when structuring your classes:

  • init.pp: init.pp contains the class named the same as the module, and is the main entry point for the module.
  • params.pp: The params.pp pattern (more on this later in the chapter) is an elegant little hack, taking advantage of Puppet's class inheritance behavior. Any of the other classes in the module inherit from the params class, so have their parameters set appropriately.
  • install.pp: The resources related to installing the software should be placed in an install class. The install class must be named <modulename>::install and must be located in the install.pp file.
  • config.pp: The resources related to configuring the installed software should be placed in a config class. The config class must be named <modulename>::config and must be located in the config.pp file.
  • service.pp: The resources related to managing the service for the software should be placed in a service class. The service class must be named <modulename>::service and must be located in the service.pp file.

For software that is configured in a client/server style, see the following:

  • <modulename>::client::install and <modulename>::server::install would be the class names for the install.pp file placed in the client and server directories accordingly
  • <modulename>::client::config and <modulename>::server::install would be the class names for the config.pp file placed in the client and server directories accordingly
  • <modulename>::client::service and <modulename>::server::service would be the class names for the service.pp files placed in the client and server directories accordingly

Having a single point of entry to the module

init.pp should be the single entry point for the module. In this way, someone reviewing the documentation in particular, as well as the code in init.pp, can have a complete overview of the module's behavior.

 

 

If you've used encapsulation effectively and used descriptive class names, you can get a very good sense just by looking at init.pp of how the module actually manages the software.

Note

Modules that have configurable parameters should be configurable in a single way and in this single place. The only exception to this would be, for example, a module such as the Apache module, where one or more virtual directories are also configurable.

Ideally, you can use your module with a simple include statement, as follows:

include mymodule

You can also use it with the use of a class declaration, as follows:

class {'mymodule':
  myparam => false,
}

The Apache virtual directory style of configuring a number of defined types would be the third way to use your new module:

mymodule::mydefine {‘define1':
  myotherparam => false,
}

The anti-pattern to this recommendation would be to have a number of classes other than init.pp and your defined types with parameters expecting to be set.

Using high cohesion and loose coupling principles

As far as possible, Puppet modules should be made up of classes with a single responsibility. In software engineering, we call this high, functional cohesion. Cohesion in software engineering is the degree to which the elements of a certain module belong together. Try to make each class have a single responsibility, and don't arbitrarily mix together unrelated functionalities in your classes.

 

 

Using the encapsulation principle

As far as possible, these classes should use encapsulation to hide the implementation details from the user; for example, users of your module don't need to be aware of individual resource names. In software engineering, we call this encapsulation. For example, in a config class, we can use several resources, but the user doesn't need to know all about them. Rather, they just simply know that they should use the config class for the configuration of the software to work correctly.

Having classes contain other classes can be very useful, especially in larger modules where you want to improve code readability. You can move chunks of functionality into separate files, and then use the contain keyword to refer to these separated chunks of functionality.

Note

See https://puppet.com/docs/puppet/5.3/lang_containment.html website for a reminder about the contain keyword.

Providing sensible, well-thought-out parameter defaults

If the vast majority of the people using your module will use the module with a certain parameter set, then of course it makes sense to set that parameter with a default.

Carefully think through how your module is used, and put yourself in the position of a nonexpert user of your own module.

Present the available module parameters in a sensible order, with more often accessed settings before least accessed settings, as opposed to some arbitrary order, such as alphabetical order.

Strongly typing your module variables

In versions of Puppet proper to the new language features which came out in version 4, we would create class parameters with undefined data types, and then, if we were being very nice, we would use the stdlib validate_<datatype> functions to check appropriate values for those variables:

class vhost (
  $servername,
  $serveraliases,
  $port
)
{ ...

Puppet 4 and 5 have an in-built way of defining the data type that a parameterized class accepts. See the following example:

class vhost (
  String  $servername,
  Array   $serveraliases,
  Integer $port
)
{ ...