How do you modularize a Chef recipe?
Asked Answered
T

1

7

Here is an example of a working recipe that loops through an array of website names and creates them in IIS using the function createIisWebsite().

def createIisWebsite(websiteName)
    iis_site websiteName do
      protocol :http
      port 80
      path "#{node['iis']['docroot']}/#{websiteName}"
      host_header  "#{websiteName}.test.kermit.a-aws.co.uk"
      action [:add,:start]
    end
end
In our actual solution this data is stored elsewhere and accessed via a web API.
websiteNames = ["website-2", "website-3", "website-4"]

for websiteName in websiteNames do
    createIisWebsite websiteName
end

Now I want to be able to call the function createIisWebsite() from multiple recipes within this Cookbook.

I have tried throwing it into a helper module (library). There I cannot get the reference to iis_site to work.

I have tried moving the function to default.rb and then doing include_recipe "::default". That does not seem to work either.

I get a "Cannot find a resource for createIisWebsite on windows version 6.2.9200"

The reason I am taking this approach is because I want to have a recipe containing the list of websites per cluster of web servers. I get the feeling I am not taking the best practice route.

Any ideas?

Terrier answered 7/5, 2013 at 17:27 Comment(1)
Ohai! Did you ever find a solution to this problem? Did any of the answers help? Please don't forget to mark one as correct :)Scar
B
6

The problem is that the function is being defined inside a recipe, and can only be used within that recipe. The include_recipe method ensures that a given recipe is loaded, but it doesn't import anything into the recipe doing the including.

Since your function is being used to declare a Chef resource with some calculated parameters, the closest thing to look at is the Definition (Chef Docs). Definitions look similar to Resources, having a name and a set of optional parameters, but are actually simple macros that are expanded into the recipe when it is compiled.

In your cookbook directory, create definitions/my_iis_website.rb containing something like:

define :my_iis_website do
    iis_site websiteName do
        protocol :http
        port 80
        path "#{node['iis']['docroot']}/#{websiteName}"
        host_header  "#{websiteName}.test.kermit.a-aws.co.uk"
        action [:add,:start]
    end
end

Then, replace the loop in your recipe with:

for websiteName in websiteNames do
    my_iis_website websiteName
end

If your recipes for each cluster of server would be identical but for the list of sites, you might want to consider storing this data in attributes or data bags instead. This helps you to avoid repeating yourself in your recipes, and will also allow you to add sites without updating your cookbook.

Blouse answered 8/5, 2013 at 10:1 Comment(5)
Of course you could also check out and create an LWRP. There has been a discussion on removing Definitions from chef on the chef mailing list.Moonshiner
Different segments of the Chef community have different opinions on the use of Chef. While the value of Definitions (and Roles) have been debated on the mailing list more than once, I'm not aware of any plans to remove either. If you happen to know otherwise, please let me know!Blouse
Thanks, will give this a shot. To get things working I just left the function in the recipe. And yes, the list of website is somewhere else available via a web API rather than a data bag as other systems need to access and the Chef web API doesn't have a .Net client lib and the auth seemed somewhat bespoke.Terrier
@GiannisNohj could you do the same thing with a chef library? and if so any advice?Gap
The Chef documentation on definitions says that custom resources are intended to replace both definitions and LWRPs, and encourages people to migrate to custom resources. However, definitions are not deprecated.Apiculate

© 2022 - 2024 — McMap. All rights reserved.