Infrastructure – News

Puppet Test Driven Development part III: create a custom type and provider

26 March 2015

Now that we can install our OpenLDAP server and ensure that it is running, we want to be able to manage OpenLDAP databases.
For that, we will create an OpenLDAP Puppet type and a provider to manage databases using OpenLDAP’s live configuration API. We will do all this using TDD, of course!

Write the acceptance tests

First, let’s create an acceptance test in spec/acceptance/openldap_database_spec.rb for our new type “openldap_database”

Write the custom type

Let’s write a custom Puppet type that has these properties:

  • is ensurable
  • has suffix as namevar
  • has backend and directory properties

Write the unit tests for the openldap_database type

Unit tests for Puppet types live in spec/unit/puppet/type, so let’s create this directory first.

Then we can create our unit tests for our openldap_database type in spec/unit/puppet/type/openldap_database_spec.rb.

The first thing to do is to require our spec_helper.rb file:

First, let’s validate the attributes of our custom type. We have to be sure that it accepts suffix and provider parameters and ensure, backend and directory properties:

Line 1:

Load spec/spec_helper.rb

Line 4:

Loop over all supported operating systems (in metadata.json)

Line 5:

Create a new rspec context for the operating system currently being tested

Line 7 – 11:

Stub all facts before each test

Lines 14 – 18:

Loop over each desired parameter and validate it

Line 19 – 23:

Loop over each desired property and validate it
Let’s also make sure that suffix is the namevar of our type:

Write the openldap_database type

The puppet custom types live in lib/puppet/type, so let’s create this directory first:

We can then write our puppet type to manage OpenLDAP databases in lib/puppet/type/openldap_database.rb:

Let’s test:

It Works!

Now let’s validate the attributes value.
We want to ensure that ensure matches present or absent and that backend matches either bdb or hdb (the 2 current supported OpenLDAP database backends) and defaults to hdb:

And now we can adapt our custom type:

And launch the unit tests again:

We should ideally validate more things:
the suffix should be /dc=[^,]+(,dc=[^,]+)*/
the directory should be an absolute path
…but this will be left as an exercise for you.

Write the custom provider for openldap_database type

Now let’s write a provider for our custom type. As said, we will use the OpenLDAP configuration API to manage the databases. So we’ll use slapcat to read the configuration and ldapmodify to update it. Since we’ll not have a real OpenLDAP server running on our workstation where we run the tests, we’ll have to mock the commands.

Let’s start, as usual, by writing the unit tests.

Write the unit test for the openldap_database’s olc provider

Unit tests for providers lives in spec/unit/puppet/provider/<type>, so let’s create this directory first:

Next, we can create the unit tests for openldap_database’s olc provider in spec/unit/puppet/provider/openldap_database/olc_spec.rb:

Again, the first thing to do is to require our spec_helper.rb file:

The first thing we want to do is to list the current instances of the OpenLDAP databases and use this list as a prefetch cache for the resources. So our provider must respond to the self.instances and self.prefetch methods:

Write the openldap_database’s olc provider

The puppet providers for the openldap_database type lives in lib/puppet/provider/openldap_database, so let’s create this directory first:

Then we can create the olc provider for the openldap_database type in lib/puppet/provider/openldap_database/olc.rb with a self.instances and a self.prefetch methods plus some things we’ll need:

Line 3:

Declares commands we will use (used by confinement) and creates helper methods dynamically for each command.

Line 5:

Creates accessors (getters and setters) for each property using the @property_hash instance variable created by the self.prefetch method

Line 13 – 15:

DefineS the exists? method which returns a boolean base on whether the ressource is prefetch.

And let’s test:

Write the unit tests for the self.instances method

Now that everything is set up, let’s write the next unit test. We want to make sure that the self.instances method returns the right resources:

We’ll test with no database:

Then, with one database:

And finally with two databases:

If you launch the unit tests now, it will obviously fails because self.instances returns nil and we try to call the size method on it.

Write the provider’s self.instances method

Let’s write the provider code that populates the instances Array:

And if we test now:

Now that we have the self.instances method, we want to be able to create a database. We will now code the self.prefetch method, to pass the discovered instances to the catalog resources.

The prefetch method

The prefetch method is used to build a cache of the resources which can be used to easily assert the existence and synchronisation state of the resource, using the @property_hash instance variable.

Line 1:

The method takes a list of catalog resources as argument, and is expected to associate RAL resources to each of them, if they already exist.

Line 2:

Retrieve all the discovered resources from the self.instances method. This is the most common way to prefetch resources when resources can all be automatically discovered by self.instances.

Line 4:

For each catalog resource passed to the method, look into the discovered instances to find a RAL resource with a matching name.

Line 5:

If a matching resource is found, associate it to the catalog resource. This will set the values of the @property_hash instance variable for this resource based on the values set in the self.instances method for this resource.

The unit test for the create method

Now let’s check that, when we want to create a resource, it generates a valid ldif and that the resource exists. In spec/unit/puppet/provider/openldap_database/olc_spec.rb:

The create method

Finally, let’s run the acceptance test to see if it really works.

On RedHat 7

Yes, it does!

On Debian 7

Now that we have a functional type and provider to manage an OpenLDAP database, we will create a function to manage the database’s Root Password in the coming part IV.

Leave a comment

Your email address will not be published. Required fields are marked *