In this chapter, we’re going to discuss how to document your manifests well. Good documentation ensures that others can use your module. It also enables people to build modules that depend on yours in a manner that you can support.
Don’t skimp on documentation if it’s an internal module that will never be published. Your own users in the company are far more important than random people on the Internet. Give your own users the very best.
Don’t forget that you are creating this module based on active needs you understand today. Make notes to remind yourself of the requirements you are trying to solve, so that you can recall what you were thinking when you come back to refactor the class a year later. You’ll have a whole new set of requirements in your head, and what you did last year won’t make any sense. Trust me, this happens.
Puppet is moving away from RDoc format to the widely used Markdown format. Markdown is much easier to learn than RDoc, and is utilized today by the Puppet Forge, GitHub, Sourceforge, Stack Exchange, and many other code repositories and forums.
While you should absolutely read the Markdown documentation, it is entirely possible to build a valid and working README document with just the following simple rules:
```
.#
sign for each level: #heading1 ##heading2
.*single asterisks*
for italic text.**double asterisks**
for bold text.These nine rules provide more than enough syntax to create valid README documents.
Markdown supports a lot more syntax than this. You can find complete documentation of the format at Markdown: Syntax.
An initial README.md template is generated by the puppet module generate
command. Replace the example content with details specific to your module.
This is documentation for users of your module, so be clear and unambiguous about how to use it. A well-written README will inform a user what your module does, and how to use it without reading its manifests. In particular, make sure you include:
You can find Puppet Labs’ latest recommendations for style on its site at “README Style Notes”. These are well worth reading, and will greatly improve the usability of your module.
Each class and defined type in your module should be documented in the manifest.
Puppet 4 has deprecated RDoc syntax documentation in favor of using Markdown for class documentation. Therefore you’ll need to use the formats shown in this book instead of the RDoc format you may find in older modules.
Puppet 4 has moved away from RDoc in favor of Markdown format documentation for consistency. You can use Markdown format even if your module is used by Puppet 3 users, as Markdown is easy to read. Puppet 3 users can also install the puppet-strings
module to generate HTML and PDF documentation.
puppet doc
no longer generates module documentation in Puppet 4. Module documentation is generated by puppet strings
, which you can make available by installing the puppetlabs-strings
module.Install the Yard gem as a gem used by the AIO puppet installation:
$
sudo
puppet
resource
package
yard
provider
=
puppet_gem
Notice:
/Package
[
yard
]
/ensure:
created
package
{
'yard'
:
ensure
=
>
[
'0.8.7.6'
]
,
}
Install the Puppet Labs strings
module. You can install this in the production
environment, or within your personal Puppet directory:
$
puppet
module
install
puppetlabs-strings
Notice:
Preparing
to
install
into
/home/vagrant/.puppetlabs/etc/code/modules
...
Notice:
Created
target
directory
/home/vagrant/.puppetlabs/etc/code/modules
Notice:
Downloading
from
https://forgeapi.puppetlabs.com
...
Notice:
Installing
--
do
not
interrupt
...
/home/vagrant/.puppetlabs/etc/code/modules
└──
puppetlabs-strings
(
v0.3.0
)
Finally, ensure that the .yardopts file exists in the root of each module. If you are updating older modules not generated from a recent skeleton, you may need to add this file:
$
echo
'--markup markdown'
>
.yardopts
At the time this book was written, the default Puppet module skeleton included headers tagged with a valid but less common Markdown header. If these don’t appear as headers in your documentation, then you may be missing the aforementioned .yardopts file:
#
# Authors
# -------
It is also completely valid to replace those headers with the same Markdown headers used in the README file. Use the proper number of #
characters for your header depth and leave no spaces before the start of the header:
#
##Authors
#
When updating an older module, change the heading for Examples to a comment, as Yard includes its own special heading for examples in the generated output.
Parameters are documented with the @param
meta tag. Follow this tag with the name of the parameter and a description of its use. The following lines should document the default and expected values, as shown here:
# @param [Type] sample_parameter Description of parameter
# * `sample parameter`
# Description of the parameter
@param
in the manifest documentation.I have found that it displays better if you put two spaces after the parameter bullet so the description falls on the following line. It is no longer necessary to document the type or default value, as this is shown clearly in the Parameter Summary lower on the page.
Here is an example for documenting the puppet::client
class we built earlier in the book:
# @param [Enum['running','stopped']] status Whether Puppet runs as a daemon
# * `status`
# Whether Puppet agent should be running as a daemon
#
# @param [Boolean] enabled Whether Puppet client should start at boot
# * `enabled`
# Whether Puppet agent should start at boot
#
# @param [Hash] config Hash of configuration options for the [agent] section
# * `config`
# Hash containing key/value pairs of Puppet configuration directives
class
puppet
:
:agent
(
Enum
[
'running'
,
'stopped'
]
$status
=
'running'
,
Boolean
$enabled
=
true
,
Hash
$config
=
{
}
,
)
{
Every parameter listed in the class invocation must be listed with a @param
option, or the documentation will reflect the discrepancy with marked-out lines and (TBD)
flags.
In the Variables section of the documentation, list any variables declared by this class that are not input parameters. This can include:
Variables are documented with the same syntax used for parameters, but without the @param
tag. Follow the bullet line with a freeform description of how the variable is used. As before, adding two spaces after the bullet line provides the best display.
Here is an example for documenting the puppet::agent
class we built earlier in the book:
##Variables
#
# * `puppet::package_version`
# This uses the common $package_version variable provided by the base class
#
class
puppet
:
:agent
(
.
.
.
)
{
include
'::puppet'
package
{
'puppet-agent'
:
version
=
>
$:
:puppet
::
package_version
,
}
Examples are documented with the @examples
meta tag. Follow this tag with the name of the parameter and a description of its use. The following lines should document how to use the module, the required values, and the most likely use case.
Here is an example for documenting how to use the puppet::agent
class we built earlier in the book:
# Examples here (no header required)
#
#
@examples Hiera data for using puppet::agent
# classes:
# - puppet::agent
#
# puppet::agent::status = 'running'
# puppet::agent::enabled = true
As always in Markdown, add two spaces to any empty line you wish to preserve in the output.
The Authors section of the documentation is self-explanatory. List your name and the names of your coauthors. Include an email address where they can contact you with questions. If you intend to publish this module on the Puppet Forge and don’t feel comfortable giving out your email, list the address of your issue tracking system as a contact method.
Here’s an example:
##Authors
# Jo Rhett, Net Consonance
# - report issues at http://github.com/jorhett/puppet4-module/issues
For the copyright section of the documentation, identify the module copyright. If this is a module that you created for an employer, you should probably use Employer Name, All Rights Reserved
.
If you’re publishing the module with an intent for others to use it, include a reference to the license in question:
##Copyright
# Copyright © 2015 Acme Products
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this module except in compliance with the License.
# http://www.apache.org/licenses/LICENSE-2.0
Only the copyright owner can assign rights to others, so you or your organization must retain copyright for the license to be valid. See Open Source Initiative: Frequently Asked Questions for more details.
Refer to “Identifying the License” for instructions about selecting the correct license for your needs.
Document function parameters using the @param
meta tag followed by the input value type. If the function returns any values they should be defined using @returns
followed by the type.
The following lines provide the format for parameter and return values:
# @param [Type] sample_parameter - Description of parameter
# @returns [Type] - Description of the result
# @since [String] - Description of when the function became available
It is not necessary to document a default value, as this is shown in the Parameter Summary lower on the page.
Here is an example for documenting the get_subnet
class built earlier in the book:
##Function: get_subnet
#
# @param [String] address - an IP address
# @param [String] netmask - the subnet mask
# @returns [String] - the network address of the subnet
# @since 1.2.2
Puppet
:
:Functions
.
create_function
(
:'mymodule::get_subnet'
)
do
Every parameter listed in the class invocation must be listed with a @param
option, or the documentation will reflect the discrepancy with marked-out lines and (TBD)
markers.
Functions can include @example
blocks exactly the same as Puppet classes.
If the function is private and you want to warn others against making use of it, you can mark it as such and a big warning label will be included in the documentation alerting others not to use it:
# @api private
Puppet
:
:Functions
.
create_function
(
:'mymodule::get_subnet'
)
do
Generate module documentation by changing into the module path and running puppet strings
. strings
reads each manifest and plugin, and creates the final documentation from tags in them. It places the HTML documentation in the doc/ folder within the module.
If you run this against your Puppet module, you should see output like this:
$
cd
/etc/puppetlabs/code/environments/test/modules/puppet
$
puppet
strings
Files:
1
Modules:
0
(
0
undocumented
)
Classes:
0
(
0
undocumented
)
Constants:
0
(
0
undocumented
)
Methods:
0
(
0
undocumented
)
Puppet
Classes:
3
(
0
undocumented
)
Puppet
Types:
1
(
0
undocumented
)
100.00%
documented
true
If it points out that any of your classes or defined types aren’t documented, you know what to do.
You can read the documentation for a module you are using by opening up the index.html file within the module’s doc/ directory.
You can generate documentation for all modules by running puppet strings
from the modules directory:
$
cd
/etc/puppetlabs/code/environments/test/modules
$
puppet
strings
An initial metadata.json file is generated by the puppet module generate
command. You should go through each of these sections and replace the example content with details specific to your module.
You can find Puppet Labs’ latest recommendations for names and values at “Writing a metadata.json File” on its site. However, we will review some important guidelines next.
If you are publishing on the Puppet Forge, you must select a license that allows others to use the module, such as one of the following:
If you need help finding the right license for your needs, the Choose an OSS License website provides a choose-your-path approach to finding a license that meets your needs. Once you have chosen the license, find the appropriate identifier on the SPDX License List and use that for the value, like so:
"license"
:
"Apache-2.0"
,
Generate a license for your module by following the instructions included with the license selected. This generally involves adding a copyright year, a copyright owner, and perhaps the project name to the boilerplate text provided. Place this in a file named LICENSE.
An important thing to get right is the source
, project
, and issues
tags. These are used to create links in the Puppet Forge and private forges. They inform users how to find documentation and submit issues related to the project:
"source"
:
"https://github.com/exampleorg/exampleorg-puppet"
,
"project_page"
:
"https://forge.puppetlabs.com/exampleorg/puppet"
,
"issues_url"
:
"https://github.com/exampleorg/exampleorg-puppet/issues"
,
Operating system compatibility informs the viewer which operating systems and versions your module is known to work on. This is defined as an array of hashes in the metadata. A match for any one hash indicates success.
Each hash contains two values:
operatingsystem
$facts['os']['name']
operatingsystemrelease
$facts['os']['release']['major']
or "$facts['os']['release']['major'].$facts['os']['release']['minor']
"Here’s an example of compatibility that supports recent Enterprise Linux and Debian/Ubuntu versions:
"operatingsystem_support"
:
[
{
"operatingsystem"
:"RedHat"
,
"operatingsystemrelease"
:
[
"6"
,
"7"
]
},
{
"operatingsystem"
:"CentOS"
,
"operatingsystemrelease"
:
[
"6"
,
"7"
]
},
{
"operatingsystem"
:"Amazon"
,
"operatingsystemrelease"
:
[
"2017.09"
,
"2017.03"
,
"2016.09"
]
},
{
"operatingsystem"
:"OracleLinux"
,
"operatingsystemrelease"
:
[
"6"
,
"7"
]
},
{
"operatingsystem"
:"Scientific"
,
"operatingsystemrelease"
:
[
"6"
,
"7"
]
},
{
"operatingsystem"
:"Debian"
,
"operatingsystemrelease"
:
[
"6"
,
"7"
,
"8"
]
},
{
"operatingsystem"
:
"Ubuntu"
,
"operatingsystemrelease"
:
[
"17.10"
,
"16.10"
,
"16.04"
,
"14.04"
]
}
]
,
Requirements are an array of hashes, each item of which identifies a valid Puppet version. A match for any one hash indicates success:
name
puppet
and pe
(Puppet Enterprise) are supported.version_requirement
This is a comparison expression that can utilize both <=
and >=
operators:
"requirements"
:
[
{
"name"
:
"pe"
,
"version_requirement"
:
">= 2015.0.0"
},
{
"name"
:
"puppet"
,
"version_requirement"
:
">= 4.0.0"
}
]
,
A module that requires Puppet 4/PE 2015 or greater might use the preceding example, whereas a module that supports only Puppet 3 might use >= 3.7.0 < 4.0.0
.
version_requirement
attribute requires two periods in the version number. Append an extra .0
to the end of the Puppet Enterprise version number.I wish there were a way to suggest that Puppet 3 versions that utilized the future parser (the prototype for Puppet 4’s parsing engine) were compatible, but I’ve found no way to express that.
List all modules required by this module in Dependencies. Indicate the range of acceptable versions with comparison operators. puppet module
uses this information to automatically install dependencies when the module is installed:
name
/
).version_requirement
This expression can utilize multiple >=
and <=
operators to indicate a valid range of matching versions:
"dependencies"
:
[
{
"name"
:
"puppetlabs/stdlib"
,
"version_requirement"
:
">= 3.2.0"
},
{
"name"
:
"puppetlabs/inifile"
,
"version_requirement"
:
">= 1.0.2"
}
]
,
}
If the module uses a data provider as documented in “Binding Data Providers in Modules”, list the data provider type (valid values are hiera
or function
):
"data_provider"
:
"function"
You can leave out this variable or use none
to indicate that there is no data provider specific to this module.
The CHANGELOG.md file isn’t generated by default. Create this file and update it with every version change in your module. For each version change, include something like this:
##YYYY-MM-DD - Release X.Y.Z ###Summary This release ... ####Features - Added new... - Revised... ####Deprecations - Removed support for... ####Bugfixes - Fixed bug where...
Maintaining CHANGELOG.md will save you a tremendous amount of bug reports and frustrated users. Consider this a worthwhile investment.
The migration to Markdown format is an evolving effort that has iterated a few times during the development of this book, and will continue after the book has gone to press.
You can find the latest updates for recommended style at the puppetlabs-strings module on GitHub.
It can be useful to refer to the YARD Documentation Guides and Resources for how-to guides and other resources. The following tags are supported at this time:
@example
@param
@returns
@since
Keep in mind that not all YARD tags are parsed by Puppet strings
. The following YARD tags were not supported when this book was last updated:
Let’s review some of the best practices for module documentation covered in this chapter:
You can find more detailed guidelines in “Documenting Modules” on the Puppet docs site.