[chef] Re: Re: Re: Re: Re: Re: Introducing the httpd cookbook


Chronological Thread 
  • From: Phil Dibowitz < >
  • To:
  • Subject: [chef] Re: Re: Re: Re: Re: Re: Introducing the httpd cookbook
  • Date: Sat, 06 Sep 2014 18:37:44 -0700

On 09/06/2014 06:17 AM, Bráulio Bhavamitra wrote:
> Thanks Phil for excelent discussion and examples of how to use node 
> attributes.
> 
> When LWRP are coded, how do you think both ways to configure can the 
> connected?

Well I think that LWRPs are often the wrong way to provide an API. Not because
LWRPs are themselves bad - they're awesome - but for the same reason we don't
use most of the standard built-in providers. We don't use 'cron' for example,
we built a template and provide a data-api as I described in the last email.
You can see this here:

  https://github.com/facebook/chef-utils/tree/master/cookbooks/fb_cron

But there are times when an LWRP is what you need. The most common thing we
use LWRPs for is when the data we're pulling from the node object cannot be
put in a single file, and represents some dynamic set of files - and we won't
know what that set of files is until after everyone has munged the node
object. We avoid this wherever possible, because it makes system-level
idempotency more difficult, but sometimes you don't have a choice. So the
example here might be if the example software I was using *required* every vip
to be in a different file. Now again - often times it's how they set it up,
but it's not required. But lets just say it was absolutely required.

In this modified example the software requires /etc/foo/foo.conf and then each
site goes in a file like /etc/foo/vips/www.sample.com.conf. And lets assume,
just to make it more interesting that the main configuration doesn't take that
"[global]" header but the other ones do so they know the site name.

In this case I'd tweak the attributes slightly and put all the VIPs under
their own section:

  default['foo']['config_file'] = '/etc/foo/foo.conf'
  default['foo']['config']['global'] => {
    'logdir' => '/var/log/foo',
    'loglevel' => 'DEBUG',
  }
  default['foo']['config']['vips']['www.sample.com'] => {
    'root' => '/var/foo/sample.com',
  }

Then I'd the recipe to do:

  template '/etc/foo/foo.conf' do
    source 'foo.conf.erb'
    owner 'root'
    group 'root'
    mode '0644'
  end
  foo_gen_site_files 'doit'

And that would call a 'gen_site_files' LWRP. Notice I'm not passing anything
to it and notice it's not meant for anyone else to call. Now inside the
provider I'd have:

  action :run do
    node['foo']['config']['vips'].keys do |url|
      template "/etc/foo/vips/#{url}.conf" do
        source 'vip.conf.erb'
        user 'root'
        group 'root'
        mode '0644'
        variables({'site' => url})
      end
    end

    # Be idempotent - clean up crap we no longer own
    # We have to do this because Foo is too dumb to allow
    # all sites in a single file. I hate Foo.
    Dir.glob('/etc/foo/vips/*.conf').each do |cfile|
      site = File.basename(cfile, '.conf')
      next if node['foo']['config']['vips'][site]
      file cfile do
        action :delete
      end
    end
  end

And actually since all this is doing is using built-in resources I'd actually
use notifying_action so I didn't have to deal with notifications myself... but
that's orthogonal to this point.

The vip.conf.erb template would now look like:

  <% config = 
node['foo']['config']['vips'
 %>
  [<%= @site %>]
  <% config.each do |key, val| %>
  <%=  key %> = <%= val %>
  <% end %>

And the foo.conf.erb template would now look like:

  <% config = node['foo']'config']['global'].to_hash %>
  <% config.each do |key, val| %>
  <%=  key %> = <%= val %>
  <% end %>

There's a a few things to note here: variables to templates are evil and
should be avoided at all costs... rather use the node object from within your
template. In this case, I passed in static data that told the template where
to look in the node object and that's it.

The other use-case we have for LWRPs is in the relatively-rare case where we
have cookbooks that allow you to both enable *or* disable something. We do
this with autofs, so our fb_autofs cookbook's default recipe is included
everywhere and looks like this:

  fb_autofs 'enable it' do
    only_if { node['fb']['fb_autofs']['enable'] }
    action :enable
  end
  fb_autofs 'disable it' do
    not_if { node['fb']['fb_autofs']['enable'] }
    action :disable
  end

Since 'lazy' doesn't work on actions, we need two invocations of the LWRP
using only_if and not_if which are always lazily-evaluated.

The final use-case at FB for LWRPs is basically just a modification of the
first one in this email. When we want to deal with things like hardware or
mounts or things that require a complicated set of running command, looking at
output, running more commands. But even in these cases it works like the first
example above - the LWRP is just scheduled by it's own cookbook and it's
*purely* configured by tweaking the node object and it looks at that at
runtime inside the LWRP to decide what to do. This is basically just
abstracting what would otherwise be a long ruby_block or some such with
bunches of Mixlib::ShellOut calls.


-- 
Phil Dibowitz                             

Open Source software and tech docs        Insanity Palace of Metallica
http://www.phildev.net/                   http://www.ipom.com/

"Be who you are and say what you feel, because those who mind don't matter
 and those who matter don't mind."
 - Dr. Seuss


Attachment: signature.asc
Description: OpenPGP digital signature




Archive powered by MHonArc 2.6.16.

§