You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix broken links and update community resources in bgtm.md
- Fix all http:// → https:// for Forge, rspec-puppet, Vagrant, and Serverspec URLs
- Fix section TOC anchors to match actual heading IDs
- Change code fences from 'ruby' to 'puppet' for Puppet code examples; add 'ruby' hint to RSpec/Rakefile examples
- Fix extra backtick on closing rspec code fence
- Fix containment code block indentation
- Remove Puppet 3.4 anchor pattern paragraph (not relevant for OpenVox 8)
- Fix missing space in documenting section prose
- Update community resources: replace Puppet Slack/IRC with Vox Pupuli Slack and #voxpupuli on Libera.Chat
- Remove all {:.concept}/{:.section} kramdown markers unused by theme
Signed-off-by: Michael Harp <mike@mikeharp.com>
Learn how to create fantastic modules by following module best practices [standards and architecture](./style_guide.html).
11
9
12
10
Contributors to this guide have spent years creating Puppet modules, falling into every pitfall, trap, and mistake you could hope to make. This guide is intended to help you avoid our mistakes through an approachable introduction to module best practices.
13
11
14
12
Before you begin, you should be familiar with Puppet such that you have a basic understanding of the Puppet [language](./lang_summary.html), you know what constitutes a [class](./lang_classes.html), and you understand the basic module [structure](./modules_fundamentals.html).
15
13
16
-
{:.concept}
17
14
## Giving your module purpose
18
15
19
16
Before you begin writing your module, you must define what it will do. Defining the range of your module's work helps you create concise modules that are easy to work with.
@@ -30,23 +27,21 @@ To help plan your module appropriately, consider:
30
27
31
28
It is standard practice for Puppet users to have 200 or more modules in an environment. Simple is better. Each module in your environment should contain related resources that enable it to accomplish a task. Create multiple modules for more complex needs. The practice of having many small, focused modules promotes code reuse and turns modules into building blocks.
32
29
33
-
As an example, let's take a look at the [`puppetlabs-puppetdb`](http://forge.puppet.com/puppetlabs/puppetdb) module. This module deals solely with the the setup, configuration, and management of PuppetDB. However, PuppetDB stores its data in a PostgreSQL database. Rather than having the module manage PostgreSQL, the author included the [`puppetlabs-postgresql`](http://forge.puppet.com/puppetlabs/postgresql) module as a dependency, leveraging the postgresql module's classes and resources to build out the right configuration for PuppetDB. Similarly, the `puppetdb-module` needs to manipulate the `puppet.conf` file in order to operate PuppetDB. Instead of having the `puppetdb-module` handle `puppet.conf` changes internally, the author used the [`puppetlabs-inifile`](http://forge.puppet.com/puppetlabs/inifile) module to enable `puppetlabs-puppetdb` to make only the required edits to `puppet.conf`.
30
+
As an example, let's take a look at the [`puppetlabs-puppetdb`](https://forge.puppet.com/puppetlabs/puppetdb) module. This module deals solely with the the setup, configuration, and management of PuppetDB. However, PuppetDB stores its data in a PostgreSQL database. Rather than having the module manage PostgreSQL, the author included the [`puppetlabs-postgresql`](https://forge.puppet.com/puppetlabs/postgresql) module as a dependency, leveraging the postgresql module's classes and resources to build out the right configuration for PuppetDB. Similarly, the `puppetdb-module` needs to manipulate the `puppet.conf` file in order to operate PuppetDB. Instead of having the `puppetdb-module` handle `puppet.conf` changes internally, the author used the [`puppetlabs-inifile`](https://forge.puppet.com/puppetlabs/inifile) module to enable `puppetlabs-puppetdb` to make only the required edits to `puppet.conf`.
34
31
35
-
{:.concept}
36
32
## Structuring your module
37
33
38
34
The ideal module manages a single piece of software from installation through setup, configuration, and service management.
39
35
40
36
This section covers:
41
37
42
-
*[2a. How to design your module's classes](#a-class-design).
43
-
*[2b. How to develop useful parameters](#b-parameters).
44
-
*[2c. How best to order your classes (rather than resources)](#c-ordering).
45
-
*[2d. How to leverage and utilize dependencies](#d-dependencies).
38
+
*[How to design your module's classes](#class-design).
39
+
*[How to develop useful parameters](#parameters).
40
+
*[How best to order your classes (rather than resources)](#ordering).
41
+
*[How to leverage and utilize dependencies](#dependencies).
46
42
47
-
To demonstrate a real-world best practices standard module, we will walk through the structure of the [puppetlabs-ntp](http://forge.puppet.com/puppetlabs/ntp) module.
43
+
To demonstrate a real-world best practices standard module, we will walk through the structure of the [puppetlabs-ntp](https://forge.puppet.com/puppetlabs/ntp) module.
48
44
49
-
{:.concept}
50
45
### Class design
51
46
52
47
A good module is comprised of small, self-contained classes that each do only one thing. Classes within a module are similar to functions in programming, using parameters to perform related steps that create a coherent whole.
@@ -57,14 +52,13 @@ In terms of class structure we recommend the following (more detail below):
57
52
58
53
![module class structure][structure]
59
54
60
-
{:.section}
61
55
#### `module`
62
56
63
57
The main class of any module must share the name of the module and be located in the `init.pp` file. The name and location of the main module class is extremely important, as it guides the [autoloader](./lang_namespaces.html#autoloader-behavior) behavior. The main class of a module is its interface point and ought to be the only parameterized class if possible. Limiting the parameterized classes to just the main class allows you to control usage of the entire module with the inclusion of a single class. This class should provide sensible defaults so that a user can get going with `include module`.
64
58
65
59
For instance, the main `ntp` class in the `ntp` module looks like this:
66
60
67
-
```ruby
61
+
```puppet
68
62
class ntp (
69
63
Boolean $broadcastclient,
70
64
Stdlib::Absolutepath $config,
@@ -81,14 +75,13 @@ class ntp (
81
75
...
82
76
```
83
77
84
-
{:.section}
85
78
#### `module::install`
86
79
87
80
The install class must be located in the `install.pp` file. It should contain all of the resources related to getting the software that the module manages onto the node.
88
81
89
82
The install class must be named `module::install`, as in the `ntp` module:
90
83
91
-
```ruby
84
+
```puppet
92
85
class ntp::install {
93
86
94
87
if $ntp::package_manage {
@@ -101,14 +94,13 @@ class ntp::install {
101
94
}
102
95
```
103
96
104
-
{:.section}
105
97
#### `module::config`
106
98
107
99
The resources related to configuring the installed software should be placed in a config class. The config class must be named `module::config` and must be located in the `config.pp` file.
108
100
109
101
For example, see the `module::config` class in the `ntp` module:
110
102
111
-
```ruby
103
+
```puppet
112
104
class ntp::config {
113
105
114
106
#The servers-netconfig file overrides NTP config on SLES 12, interfering with our configuration.
@@ -143,14 +135,13 @@ class ntp::config {
143
135
...
144
136
```
145
137
146
-
{:.section}
147
138
#### `module::service`
148
139
149
140
The remaining service resources, and anything else related to the running state of the software, should be contained in the service class. The service class must be named `module::service` and must be located in the `service.pp` file.
150
141
151
142
For example:
152
143
153
-
```ruby
144
+
```puppet
154
145
class ntp::service {
155
146
156
147
if ! ($ntp::service_ensure in [ 'running', 'stopped' ]) {
@@ -171,14 +162,12 @@ class ntp::service {
171
162
}
172
163
```
173
164
174
-
{:.concept}
175
165
### Parameters
176
166
177
167
Parameters form the public API of your module.
178
168
179
169
They are the most important interface you expose, and you should take care to balance to the number and variety of parameters so that users can customize their interactions with the module. Below, we walk through best practices for naming and developing parameters.
180
170
181
-
{:.section}
182
171
#### Naming parameters
183
172
184
173
Naming consistency is imperative for community comprehension and assists in troubleshooting and collaborating on module development.
@@ -187,7 +176,7 @@ Best practices recommend the pattern of `thing_property` for naming parameters.
187
176
188
177
For example, in the `ntp` module
189
178
190
-
```ruby
179
+
```puppet
191
180
class ntp::install {
192
181
193
182
if $ntp::package_manage {
@@ -204,7 +193,6 @@ If you have a parameter that toggles an entire function on and off, the naming c
204
193
205
194
Consistent naming across modules helps with the readability and usability of your code.
206
195
207
-
{:.section}
208
196
#### Number of parameters
209
197
210
198
To maximize the usability of your module, make it flexible by adding parameters. Parameters enable users to customize their use of your module.
@@ -213,16 +201,15 @@ You must not hardcode data in your modules, and having more parameters is the be
213
201
214
202
Avoid adding parameters that allow you to override templates. When your parameters allow template overrides, users can override your template with a custom template that contains additional hardcoded parameters. Hardcoded parameters in templates inhibits flexibility over time. It is far better to create more parameters and then modify the original template, or have a parameter which accepts an arbitrary chunk of text added to the template, than it is to override the template with a customized one.
215
203
216
-
For an example of a module that capitalizes on offering many parameters, please see [puppetlabs-apache](http://forge.puppet.com/puppetlabs/apache).
204
+
For an example of a module that capitalizes on offering many parameters, please see [puppetlabs-apache](https://forge.puppet.com/puppetlabs/apache).
217
205
218
-
{:.concept}
219
206
### Ordering
220
207
221
208
Best practice is to base all order-related dependencies (such as `require` and `before`) on classes rather than resources. Class-based ordering allows you to shield the implementation details of each class from the other classes.
222
209
223
210
For example:
224
211
225
-
```ruby
212
+
```puppet
226
213
file { 'configuration':
227
214
ensure => present,
228
215
require => Class['module::install'],
@@ -231,7 +218,6 @@ For example:
231
218
232
219
Rather than making a `require` to several packages, the above ordering allows you to refactor and improve `module::install` without adjusting the manifests of other classes to match the changes.
233
220
234
-
{:.section}
235
221
#### Containment and anchoring
236
222
237
223
To allow other modules to form ordering relationships with your module, ensure that your main classes explicitly _contain_ any subordinate classes they declare.
@@ -240,76 +226,67 @@ Classes do not _automatically_ contain the classes they declare. This is because
240
226
241
227
For example, the `ntp` module uses containment in the main `ntp` class:
242
228
243
-
```
229
+
```puppet
244
230
contain ntp::install
245
-
contain ntp::config
246
-
contain ntp::service
247
-
Class['::ntp::install'] ->
248
-
Class['::ntp::config'] ~>
249
-
Class['::ntp::service']
231
+
contain ntp::config
232
+
contain ntp::service
233
+
Class['::ntp::install'] ->
234
+
Class['::ntp::config'] ~>
235
+
Class['::ntp::service']
250
236
```
251
237
252
-
Containment is supported in Puppet 3.4 and later. To support versions prior to Puppet 3.4 (or Puppet Enterprise 3.2), you must use the [anchor pattern][anchor] to hold those classes in place. Anchoring requires [puppetlabs-stdlib](http://forge.puppet.com/puppetlabs/stdlib).
253
-
254
-
{:.concept}
255
238
### Dependencies
256
239
257
240
If your module's functionality depends on another module, then you must list these dependencies and include them directly.
258
241
259
242
This means you must `include x` in the main class to ensure the dependency is included in the catalog. You must also add the dependency to the module's [metadata.json](./style_guide.html#module-metadata) and `.fixtures.yml`. (`.fixtures.yml` is a file used exclusively by RSpec to pull in dependencies required to successfully run unit tests.)
260
243
261
-
{:.concept}
262
244
## Testing your module
263
245
264
246
Ensure that the module works in a variety of conditions, and that the options and parameters of your module work together to an appropriate end result.
265
247
266
-
We recommend several testing frameworks available to help you write unit and acceptance tests. Some of these tools are already included in the Puppet Development Kit (PDK; see the [PDK][pdk] documentation for details.
248
+
We recommend several testing frameworks available to help you write unit and acceptance tests.
267
249
268
-
{:.section}
269
250
### rspec-puppet
270
251
271
252
The `rspec-puppet` gem provides a unit-testing framework for Puppet. It extends RSpec to allow the testing framework to understand Puppet catalogs, the artifact it specializes in testing. You can write tests, as in the below example, to test that aspects of your module work as intended.
272
253
273
-
```
254
+
```ruby
274
255
it { should contain_file('configuration') }
275
-
````
256
+
```
276
257
277
258
RSpec lets you provide facts, like `osfamily`, in order to test the module in various scenarios.
278
259
279
260
A typical use of RSpec is to iterate over a list of operating systems, asserting that the package and service should exist in the catalog for every operating system your module supports.
280
261
281
-
To learn more, see [http://rspec-puppet.com/](http://rspec-puppet.com/).
262
+
To learn more, see [https://rspec-puppet.com/](https://rspec-puppet.com/).
282
263
283
-
{:.section}
284
264
### puppetlabs-spec-helper
285
265
286
266
The [puppetlabs-spec-helper](https://github.com/puppetlabs/puppetlabs_spec_helper) gem automates some of the tasks required to test modules.
287
267
288
268
This is especially useful in conjunction with `rspec-puppet`, as `puppetlabs-spec-helper` provides default Rake tasks that allow you to standardize testing across modules. It also provides some code to connect `rspec-puppet` with modules. Add it to the Gemfile of the project, and then add the following line to the Rakefile:
289
269
290
-
```
270
+
```ruby
291
271
require'puppetlabs_spec_helper/rake_tasks'
292
272
```
293
273
294
-
{:.section}
295
274
### Beaker-rspec
296
275
297
276
[Beaker-rspec](https://github.com/puppetlabs/beaker-rspec) is an acceptance/integration testing framework.
298
277
299
-
It provisions one or more virtual machines on various hypervisors (such as [Vagrant](http://www.vagrantup.com/)) and then checks the result of applying your module in a realistic environment.
278
+
It provisions one or more virtual machines on various hypervisors (such as [Vagrant](https://www.vagrantup.com/)) and then checks the result of applying your module in a realistic environment.
300
279
301
-
{:.section}
302
280
#### serverspec
303
281
304
-
[Serverspec](http://serverspec.org/) provides additional testing constructs (such as `be_running` and `be_installed`) for beaker-rspec. It allows you to abstract away details of the underlying distribution when testing. It lets you write tests like:
282
+
[Serverspec](https://serverspec.org/) provides additional testing constructs (such as `be_running` and `be_installed`) for beaker-rspec. It allows you to abstract away details of the underlying distribution when testing. It lets you write tests like:
305
283
306
284
describe service('httpd') do
307
285
it { should be_running }
308
286
end
309
287
310
288
It then knows how to translate `be_running` into shell commands for different distributions.
311
289
312
-
{:.concept}
313
290
## Versioning your module
314
291
315
292
Modules, like any other piece of software, must be versioned and released when changes are made. Use semantic versioning, which sets out specific rules for when to increment major and minor versions.
@@ -318,38 +295,29 @@ After you've decided on the new version number, adjust the version number in the
318
295
319
296
This allows you to create a list of dependencies in the `metadata.json` file of your modules with specific versions of dependent modules, which ensures your module isn't used with an old dependency that won't work. Versioning also enables workflow management by allowing you to easily use different versions of modules in different environments.
320
297
321
-
{:.concept}
322
298
## Documenting your module
323
299
324
300
We recommend that you document your module with a README explaining how your module works and a Reference section detailing information about your module's classes, defined types, functions, and resource types and providers.
325
301
326
-
For guidance, see our modules documentation [guide](./modules_documentation.html)and the [documentation](./style_guide.html#module-documentation) section of the Puppet Language Style Guide.
302
+
For guidance, see our modules documentation [guide](./modules_documentation.html)and the [documentation](./style_guide.html#module-documentation) section of the OpenVox Language Style Guide.
327
303
328
-
{:.concept}
329
304
## Releasing your module
330
305
331
-
We encourage you to publish your modules on the [Puppet Forge](http://forge.puppet.com).
306
+
We encourage you to publish your modules on the [Puppet Forge](https://forge.puppet.com).
332
307
333
308
Sharing your modules allows other users to write improvements to the modules you make available and contribute them back to you, effectively giving you free improvements to your modules.
334
309
335
310
Additionally, publishing your modules to the Forge helps foster community among Puppet users, and allows other Puppet community members to download and use your module. If the Puppet community routinely releases and iterates on modules on the Forge, the quality of available modules increases dramatically and gives you access to more modules to download and modify for your own purposes. Details on how to publish modules to the Forge can be found [here](./modules_publishing.html).
336
311
337
-
{:.concept}
338
312
## Community Resources
339
313
340
314
For beginning module authors, a variety of community resources are available.
341
315
342
316
[Module basics](./modules_fundamentals.html)
343
317
344
-
[Puppet Development Kit][pdk]
345
-
346
-
[Puppet Language Style Guide](./style_guide.html)
347
-
348
-
[The Forge](http://forge.puppet.com)
349
-
350
-
The [puppet-users mailing list](https://groups.google.com/forum/#!forum/puppet-users)
318
+
[OpenVox Language Style Guide](./style_guide.html)
351
319
352
-
`#puppet` on IRC
320
+
[The Forge](https://forge.puppet.com)
353
321
354
-
[Puppet Community on Slack](https://slack.puppet.com/)
322
+
[Vox Pupuli community channels](https://voxpupuli.org/connect/)
0 commit comments