Hi,
is it possible to overwrite the default provider for a built-in resource (say service
or package
) for an entire chef run?
How would I do that?
Probably I missed existing answers to this question, my apologies if so.
Best
Christopher
Hi,
is it possible to overwrite the default provider for a built-in resource (say service
or package
) for an entire chef run?
How would I do that?
Probably I missed existing answers to this question, my apologies if so.
Best
Christopher
It's technically possible, but highly brittle, a severe footgun and begs the question of why you want to do it instead of providing a PR upstream ?
Could you ellaborate on the reasons so we may better help/give advice on how to achieve that ?
Right.
The scenario is as follows:
We use a bunch of cookbooks to configure "normal" machines (say Linux desktops).
We also have nodes that mount their root fs from NFS read-only. Their setup is split in two parts:
It would be nice to configure stand-alone and netboot nodes with the same cookbooks, but:
service { action :start }
should be a no-op inside the OS images on the NFS server,
while package
will fail on the read-only netboot nodes.
Therefore I was thinking about replacing the affected resources and actions with no-ops.
That's why the action :nothing
exists.
You'll need to have a way to differenciate which machine the cookbook is running on, but that could be something like (using an attribute of nfs_server being true or false)
package 'xxx' do
action node['nfs_server'] ? :install : :nothing
end
service 'xxx' do
action node['nfs_server'] ? :nothing : :start
end
Recipes are evaluated as ruby code, so you can do logic within the recipe code based on attributes or something else.
Hm.
If I don't want the provider to do :nothing
but :something_else
the functional equivalent would be:
package 'asdf' do
provider MyCustomProvider if node['nfs_server']
end
In either case I would need to change dozens, maybe hundreds of resources, hence my question if this could be tweaked centrally.
But your suggestion is indeed very helpful as this may be a promising approach:
Chef::Resource::Package.default_action = %i[nothing] if node['nfs_server']
I'll look further into that direction.
Many thanks
Christopher
I think we'd all need code to understand what you're willing to do here.
Conditional action on resource are pretty common, like for files or services we can use the ternary operator to say which action we want based on attribute as recipes are evaulated as pure ruby, like this:
file '/path/to/file' do
content 'A content to update'
action node['my_namespace']['do_not_overwrite'] ? :create_if_missing : :create
end
service 'my_service' so
action node['my_namespace']['start_enable'] ? [:start, :enable] : :nothing
end
Or for something more complex based on distribution type (pseudo code, just to give an idea):
if node['platform'] == 'redhat'
package 'httpd'
service 'httpd' do
action [:start, :enable]
end
template '/etc/httpd/....' do
source 'your_template.erb'
action xx
notifies :reload, 'service[httpd]', :immediately
end
else
package 'apache2'
service 'apache2' do
action [:start, :enable]
end
template '/etc/apache2/conf.d/xxx.conf' do
source "XXX.erb"
notifies :restart, 'service[apache2]'
end
end
Hi Tensibai
I think get your point. My use case is to reuse existing cookbooks in this new scenario with as little modification as possible.
For now I found a working solution monkey-patching Chef::Resource.provider
in a wrapper-cookbookslibraries/….rb
.
This is my custom service provider:
# use systemd provider but turn :start and :restart into no-ops
class Chef::Provider::Service::Netboot < Chef::Provider::Service::Systemd
def start_service
Chef::Log.info("Stubbing systemctl start #{new_resource}")
end
def restart_service
Chef::Log.info("Stubbing systemctl restart #{new_resource}")
end
end
And this is the monkey patch:
class Chef::Resource::Service
def provider(arg = nil)
set_or_return(:provider, Chef::Provider::Service::Netboot, kind_of: [ Class ])
end
end
That's nowhere near an optimal solution and it surely breaks
service 'my-service' do
provider Chef::Provider::Service::Foo
end
A better solution would be to inject my provider into the first place of Chef::ProviderResolver.prioritized_handlers
(via Chef.set_provider_priority_array :service, …
?) but that seems to be even uglier and I haven't managed to get this to work.
Cheers
Christopher
P.S.: In my use case the custom service provider is required anyhow as Chef does not properly detect systemd inside the server-side chroot
s but falls back to Chef::Provider::Service::Debian
which is completely wrong.