Setting attributes from a custom resource in compile phase


#1

I am looking for a way to set a default attribute from a custom resource in compile phase.

While using the older LWRP notation, I am able to do this by overriding getter/setter in the resource file. For example, in resources/foo.rb:

attribute :bar, kind_of: Integer

def bar(arg = nil)
  if arg
    node.default['some_attribute'] = arg * 1024
  end
  set_or_return(:bar, arg, kind_of: [Integer])
end

This looks like a hack, and I have not been able to find a similar way to do something in compile phase (rather than in converge phase) while using the new single-file custom resource syntax.

Any advice or examples I could look at?


#2

Can you describe what you’re actually trying to achieve? Because in general modifying a node attribute in a resource should not be necessary.


#3

Sure, happy to describe the use case in more detail. In short, some of the cookbooks that a given custom resource needs to use provide a “data-driven API” and need to be configured via attributes.

Taking the fb_cron example from the link above, imagine that I am adding a provider to configure web hosts, and each host needs to have a separate user, and a temporary directory which gets cleaned up regularly. I would like my resource to be used like this:

web_host 'foobar' do
  user 'foobar-user'
  ...
end

My resource code will have the following:

user new_resource.user do
  home "/home/www/#{new_resource.user}"
  manage_home true
end

directory "/home/www/#{new_resource.user}/tmp"
  owner new_resource.user
end

node.default['fb_cron']['jobs']["cleanup tmp for #{new_resource.user}"] = {
  'command' => "find /home/www/#{new_resource.user}/tmp -mtime +2 -delete",
  'time' => '0 0 * * *',
  'user' => new_resource.user,
}

Now this will make the cronjob added to default attributes during converge phase, which I believe means that the cronjob will only be configured if template '/etc/cron.d/fb_crontab' (part of fb_cron cookbook) is later in the run list than my web_host 'foobar'. I would like this to work independently on run list order, which means I need the default attribute to be set during compile phase rather than converge phase.

Does this make sense? I am not using fb_cron specifically, but my other cookbooks use similar attribute-driven configuration pattern.


#4

Any advice from someone familiar with Chef internals?
Or is overriding getter/setter still the best way to do this?


#5

I could imagine several ways to achieve this. I’ll start by naming one and you let me know if that meets your needs.

The Chef::Resource class (which every resource inherits from) has a hook (#after_created) that is called after the resource is created (this is in the compile phase). In that hook you should be able to modify node attributes. That should be effectively the same-ish thing as setting node attributes in a recipe.

See this: https://github.com/chef/chef/blob/master/lib/chef/resource.rb#L928-L934
The hook is actually called here: https://github.com/chef/chef/blob/master/lib/chef/resource_builder.rb#L72-L73

To be clear: I’ve never done this personally, but I can’t imagine why it wouldn’t work for your use case.

Good luck and let me know how it goes!


#6

Chef uses the hook in the chef_gem resource here: https://github.com/chef/chef/blob/master/lib/chef/resource/chef_gem.rb#L45-L53


#7

Thanks, jasonwbarnett! after_created seems to work great.


#8

Just posting updated links that are pinned to a specific commit (05f63ca) so the intent isn’t lost, no matter how old this thread is.