Ohai Chefs!
If you’ve written any Chef recipes at all, you’ve almost certainly used Lightweight Resources and Providers (LWRPs). LWRPs enable you start/stop services, install packages, manage firewalls, deploy apps and many other common configuration tasks. LWRPs combine a simple interface (Resource) with one or more usually OS-specific implementations (Providers). For example this resource installs Windows packages:
1 2 3 4 |
|
The windows_package
provider, which comes with the Windows cookbook, is 250 lines of code. It handles five different kinds of package types, e.g. msi, inno, nullsoft, etc. LWRPs make it very easy for sysadmins to write Chef recipes with a minimal amount of code because someone has already done the hard work of writing the Resource and Provider.
Even though Opscode has provided many LWRPs “out of the box”, you will still need to write your own at some point. This week we’ll look at how to write an LWRP starting with the Resource part. Next week, we’ll complete the two-part series by learning how to write the corresponding Provider.
Example, please…
Your first step should be to determine if the resource you need already exists. Read and bookmark this page. It provides a good introduction to LWRPs and lists the Opscode provided LWRPs. Don’t reinvent the wheel.
Here’s an example resource we’ll be looking at. It allows you to create Windows TCP/IP printer ports.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Let’s take it line by line. The first line requires the resolv
library, “a thread-aware DNS resolver library written in Ruby”. It provides a very good IPv4 regex we will use to verify the user has passed in a valid IPv4 address for the :ipv4_address
attribute. It’s easy to forget, but Resources are just Ruby and you can require
libraries and use any other Ruby to help you out.
The next line specifies the allowed actions. Actions are what your resource can do, e.g. start, stop, create, delete, etc. In this case, you can :create
, or :delete
printer ports.
Line four defines the default_action
for our resource, in this case :create
. If you don’t specify an action when you use the resource in a recipe, it will default to creating a printer port, which is what you probably want. A general philosophy of Chef is to define intelligent or “sane” defaults.
Lines 6 – 13 define attributes, or properties of the printer port resource we are creating. Let’s look at each of these attributes in turn.
Line 6 defines an :ipv4_address
attribute. Its :name_attribute
is true, which means that this attribute will be set to the string between windows_printer_port
and do
:
1 2 3 4 5 6 |
|
In the second example above, the printer_port :ipv4_address
attribute wll be set to ‘10.2.32.47’.
Also, on line 6, we are definining the kind_of
validation parameter to tell the resource which kind of data we should expect (in this case, a string), whether this attribute is required (yes), and setting a validation regex (Resolv::IPv4::Regex). Instead of attempting to write a regex to validate IPv4 addresses, I am using a pre-defined regex supplied by the Resolv
Ruby library.
Line 8 defines a port_name
attribute, which is an optional string with no default. Line 9 defines a port_number
attribute, a Ruby Fixnum (i.e. an integer) with a default of 9100, which is the default when you create a printer port in Windows.
Line 10 defines a port_description
attribute, an optional string.
Line 11 defines an snmp_enabled
attribute, a boolean which defaults to false.
Line 13 defines a port_protocol
attribute, a Ruby Fixnum, which defaults to 1. The equal_to
constraint limits the possible values to 1 or 2.
It’s important to note that the constraints and defaults in the windows_printer_port
Resource are very carefully chosen based on knowlege of how the Win32_TCPIPPrinterPort class in Windows works. You can’t write a Resource and Provider unless you really understand the underlying resource you are modeling.
I’ll explain the attr_accessor :exists
in more detail next week, but in short, it defines an exists
property on the Resource so we can test whether a given printer port already exists, so we don’t create it again.
So that’s it for this week. Tune in next week for an overview of writing Providers.