Ohai chefs,
With the recent release of 12.4 (see blog post for specifics), a lot of changes have been happening around resolving resources to providers. I’m writing because, while I’ve seen a lot of examples for providers that are platform-specific, or completely override another provider, I haven’t seen any examples where a user might want to select between providers using an attribute, where the providers aren’t overriding one another.
My example is for software installation (names changed to protect the innocent). I have a resource, software_install, and it has an option for installation by package or by tarball. I have written two providers, one for package installs, and one for installation by tarball. I was using this snippet inside each provider to get the resolve to choose the correct one:
class << self
def supports?(resource, action)
resource.type == :package
end
end
That snippet was inside the provider that supports package installs, and I’ve got a similar one for the tarball option. To complicate matters, due to this software not being in a repo, it has to be downloaded by deb or rpm for the package install, so I’ve also got provides:
statements that control which provider is picked when the package install method is selected:
provides :software_install (in the provider that installs from source)
provides :software_install, platform_family: [ “debian”, “ubuntu” ] (in the provider that installs from deb)
provides :software_install, platform_family: [ “rhel”, “fedora” ] (in the provider that installs from rpm)
I had to make a distinction between deb/rpm because the package provider for debian/ubuntu is apt-based, which can’t directly install debs (I had to use the dpkg_package resource there, and the package resource everywhere else).
So I’m wondering… what’s the best way to still have platform-specific providers while still steering chef to a provider based on an attribute like ‘type’ (one of ‘package’, ‘source’). Should I really have written two resources, instead of giving an attribute to control how the software is installed? Or should I write one huge provider that can install from source or package?
Or is it reasonable to have two providers that perform the same work in different ways, but don’t override each other? In the last case, Chef is now warning that one of them is masking the selection of the other – but I want the user to be able to choose using an attribute (overriding using ‘provider Chef::Provider::Foo’ is marginally worse, but the same problem).
Thanks in advance for any design advice you might have. I feel like provider selection is becoming more & more focused on platforms and platform families and platform versions, making it harder for me to write providers that all map to the same resource, but encapsulate different, but equally valid ways to perform the same task.
Cheers,
- Martin Smith