Ohai Chefs!
Last week, we started looking at how to create the Provider part of the LWRP, the code which creates, deletes, or changes the Resource on the managed node. We looked at implementing Action methods (:create
, :delete
), using the load_current_resource
method to read in attributes of existing resources, and how to support Chef’s why-run
mode.
This week, we’ll complete the Provider by looking at the port_exists?
, create_printer_port
, and delete_printer_port
methods. The load_current_resource
method uses the port_exists?
method to determine if the printer port already exists. The latter two methods leverage Windows PowerShell to create and delete printer ports respectively. Collectively, these are the private methods in this class, meant to be called only by the :create
and :delete
Action methods and load_current_resource
.
OOP (there it is)…
We’ve moved some of the code which might otherwise go into our :create
and :delete
Action methods into separate methods which create and delete printer ports using PowerShell. Breaking up our methods into smaller, logical chunks follows the Composed Method technique which I first learned about in by Russ Olsen.
“The composed method technique advocates dividing your class up into methods that have three characteristics. First, each method should do a single thing—focus on solving a single aspect of the problem. By concentrating on one thing, your methods are not only easier to write, they are also easier to understand.
Second, each method needs to operate at a single conceptual level: Simply put, don’t mix high-level logic with the nitty-gritty details. A method that implements the business logic around, say, currency conversions, should not suddenly veer off into the details of how the various accounts are stored in a database.
Finally, each method needs to have a name that reflects its purpose.”
Olsen, Russ (2011-02-07). Eloquent Ruby (Addison-Wesley Professional Ruby Series) (Kindle Locations 2102-2107). Pearson Education (USA). Kindle Edition.
The :create
action
Let’s look at the :create
Action to see how we applied the Composed Method technique.
1 2 3 4 5 6 7 8 9 |
|
Even without comments it should be pretty clear what this method does; it almost reads like plain English. If the printer port already exists, it logs a message and does nothing, otherwise it creates a printer port. We have made it so readable by extracting out the code which checks for a pre-existing printer port into the load_current_resource
method, and the code which creates the printer port into the create_printer_port
method.
By composing our :create
method this way, it’s become a template. You should be able to use the code above for almost any :create
Action in any LWRP you will write, just by changing the name of the create_printer_port
method to something more suitable.
Now, let’s look at the private create_printer_port
method, which actually creates the printer port.
create_printer_port
method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
The create_printer_port
method starts off by setting a local variable, port_name
which is set to the new_resource.port_name
if the user set the port_name
attribute, or IP_<ipv4_address>
if the user didn’t set port_name
.
After that, it’s just a straightforward PowerShell resource block which creates the printer port using the attributes passed in from the windows_printer_port
resource in the Recipe.
One thing to note is how we are using Ruby String Interpolation to pass our Resource Attributes in to the PowerShell script.The "#{ new_resource.foo }"
sections are how we can pass Ruby variables into a PowerShell or batch script. Pretty handy.
port_exists?
method
port_exists?
is a simple method which queries the Windows Registry to see if the printer port has already been created. The load_current_resource
method calls port_exists?
, and if the printer port exists, it sets to
true
.
One thing you should notice is that the question mark is part of the port_exists?
method name. In Ruby, it is perfectly acceptable, and expected that methods which return true or false have a ?
appended to the method name.
1 2 3 4 5 6 7 8 |
|
This code block creates a Ruby Constant, the PORTS_REG_KEY
, which is the key we’ll query to determine if the printer port already exists.
In our port_exists?
method, we call the Windows Cookbook Registry.key_exists?
method which returns true
or false
, telling us whether the printer port is already in the Windows Registry or not. Notice that ?
in the method name again?
delete_printer_port
method
The delete_printer_port
is much the same as the create_printer_port
method so I won’t cover it here.
Wrap-up
So now we’ve completed our look at how to create LWRPs. We’ve covered both the Reource and Provider and looked at structuring your Ruby methods using the Composed Methods pattern. Certainly, my printer port LWRP isn’t perfect. In writing these blog posts, I’ve already come up with some changes to make it better, but the biggest glaring omission is the complete lack of test coverage!
Next Steps
We’ll probably take a break from LWRPs next week but look for a blog post in the near future on testing LWRPs using my absolute favorite Ruby testing libarary, RSpec. Huge shout out to to whom I’m indebted for great example RSpec tests in the Runit cookbook.