Understand chef lwrp (Heavy version)
Recently I am mainly working on devops things, including system admin and chef.
We are refactoring our old chef recipes into a more modulize shape with tests,
So I think it’s a good time to share some experience in this refactor!
Resource and Provider in Chef
In chef, we use resource to describe the state of our system.
And cookbook is a series of resources that describe the server state.
For example, the cookbook to install nginx on server is like this:
1 | package 'nginx' do |
describe 3 resources, nginx package, nginx service and nginx config file.
Than the provider will take the action in resource, execute the corresponding action,
which will install nginx package, create nginx config file, start and enable nginx service.
So provider provide methods to achieve the state of resource.
Take a look at the install action in package provider (simplfied for read):
1 | def action_install |
The provider will check current installed version and install package by install_package method,
install_package method is implemented by different provider like Yum and Rpm.
Which will run command like yum install nginx
to install package.
Resource and Provider (Heavy ver)
Sometime we want to define specific resources and providers for better describe the state of our server.
For example like ruby '2.0.0-p247'
or nginx_site 'www.example.com'
We have two way to implement it. One is using definition, which is like a helper method in chef.
Another is writing custom resource and provider.
In our cookbook, we use custom resorce and provider to upload our ssh key to github
1 | github key_name do |
We can create resource and provider by inherit the Chef::Resource and Chef::Provider:
1 | class Chef |
Above code extend the chef to build custom resource and provider.
Put the code under /libraries directory in cookbook, and then the custom resource and provider will be avaliable in cookbook!
Resource and Provider (Light version - LWRP)
However, the full class implementation is too complex for system admins who don’t have ruby background.
So chef provide a resource and provider DSL, called light weight resource provider (LWRP)
Using LWRP DSL, previous resource and provider can be written as:
1 | # resources/github.rb |
Basically the DSL use dynamic programming to construct the method and create new resource and provider class.
The dsl syntax will generate into full resource and provider code same as the heavy version.
Testing LWRP
The reason we dig into how LWRP generate, is mainly because we want to know how to test LWRP better in our refactor
We use chefspec to test the logic in our custom resource and provider
1 | require 'spec_helper' |
For testing provider part, it’s much harder because provider depends on node, resource and run_context
However, we can either throw the checspec run context to provider or mock everything, and we choose the later one:
1 | describe 'github provider' do |
Conclusion
Resource and provider is the foundamental concept of Chef.
while we refactor the cookbook, we found a lot of recipes is written like bash script.
Recipe should describe the state of our server but not the action taken,
and the action logic need to be seperate into provider.
This is also the target we want to achieve in the refactor. I will share more experience on how to test and build the infrastructure with chef later.
Comments