Multiple providers for a particular resource & Chef 12.4.x?

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

you can probably do this:

provides :software_install do |node|
node[:software][:install_type] == "source"
end

provides :software_install, platform_family: [ "debian", "ubuntu" ] do
|node|
node[:software][:install_type] != "source"
end

then if its set to "source" it'll force the source installs, if its not
"source" (nil or doesn't exist) then it'll select one of the package
installs.

On 07/08/2015 06:16 AM, Martin Smith wrote:

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

if i understand the question correctly, the way i’ve seen this done for resources is as an attribute, like resource.installation_method, and a provider that knows how to do both. if you want to separate the logic of package and source install, i’d move that out into helper modules or mixins, and use them appropriately based on inspection of the resource from the provider.

then to provide users the ability to use you’re resource-powered cookbook and still be able to control it via attributes, provide a recipe they can include with an attribute-driven instance of your resource, like

# recipes/default.rb
resource “name” do
  installation_method node[‘my_software’][‘installation_method’]
  version node[‘my_software’][‘version’]
  ...
end

HTH

On Jul 8, 2015, at 6:16 AM, Martin Smith martin.smith@RACKSPACE.COM wrote:

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