Writing custom helper methods for resource to evaluate resource state


#1

=> How to inject custom methods inside existing resource to decide whether resource is allowed to execute or not ?

e.g. sensu__ckeck resource is provided by community cookbook “sensu”

sensu_check "apache-service" do
# provider Chef::Provider::MySensuCheck
  command "date"
  handlers ["remediator"]
  subscribers ["k2-service"]
  standalone false
  publish false
  action [:create]
# not_if {SensuHelper.isCheckValid?}
end

Hash: - ( Hash containing different checks, applicable servers category and active status)

default['cookbook_name']['checks'] = [
	{
		'checks' =>['apache','apache-service'],
		'applicable_servers' => ['httpd','apache-server'],
		'active' => true
	},
    {
         ....
         ....
    }
]

Intension: -

I’m trying to invoke custom method (isCheckValid?) inside community cookbook resource (sensu_check) which is LWRP based.

isCheckValid? method must determine current resource state i.e. resource.name (apache-service) and check against provided hash to determine whether this check.name is applicable on current host or not.


Tried Solution commented statements in above resource: -

In order to achieve this below are few scenarios are tried but not working efficiently as aspected -

  1. Helper Method -
    Created SensuHelper.isCheckValid? method but unable to get current resource information (new_resource.name) as load_current_resource is able to be invoked in provider.

  2. Custom Provider -
    We are able to get current resource information in custom provider but need to rewrite whole Provider actions in HWRP overriding community cookbook provider which is not desired.


Help needed on to solve above problem: -

  1. How to inject such custom methods in LWRP or HWRP Providers (either by subclassing way) which can be called from resources.
  2. How to write custom helper methods where we can retrieve current resource information and check desired resource state.
  3. Any other solution to achieve such filtration either with guard condition or custom classes.

Note: - Main Objective here is to use any Helper methods where we should be able to get current resource state i.e. what we get from Chef::Provider#load_current_resource and write helper logic.


#2

@Franklin @sigje


#3

[qHello mg_devstack

In my opinion your existing code for the guard not_if {SensuHelper.isCheckValid?} is the best way

  • it is explicit
  • easy to read and understand
  • conforms to same practice as used by other resources

However, with Ruby you can modify code at runtime (monkey patching), occasionally we have done this and with guards.

For example, the Windows community cookbook contains windows_helper which has a method names is_package_installed?
To add this to ALL resources we use the Ruby include method (via a send message)

Chef::Resource.send(:include, Windows::Helper)

The use it in our resource like so

directory 'c:\temp' do
  not_if { is_package_installed?('product_a') }
end

You can also target specific resources

Chef::Resource::Directory.send(:include, Windows::Helper)

You can also “open the class” and make changes but that has lots of caveats and as a Rubyist it can be avoided.

One thing to note, if you add a method in a library file that is not namespace

def my_method
 do stuff ...
end

The method is added as a private method to the Ruby base Object and becomes available to call in all Objects that inherit from Object, example below

def say_hello
  puts 'Hello World'
end

x = String.new 'Goodbye World'
x.respond_to?(:say_hello) #=> true
x.say_hello #=> 'Hello World'

Adding the code to a provider is fairly easy but you would have to get your code executed which requires it to be called from an existing method or hook. So in answer to your question Providers CAN be modified but I’d argue against that.

What is your reason for wanting to add the check in the provider?


#4

@chris_sullivan, Whole reason of using check in Provider was to have accessibility of current_resource state. as we wanted to check resource_name against provided hash. Resource state was not available inside helper Methods
Note :- I’m not sure whether we can inherit resource state inside helper module.

We load current resource in HWRP like -

class Chef
	class Provider
		class MySensuCheck < Chef::Provider
            @@current_rn = ''

			def why_run_supported?
				true
			end

			def load_current_resource
				@current_resource = Chef::Resource::SensuCheck.new(new_resource.name)  // issue-1
				@@current_rn = @current_resource.name
				@current_resource
			end

			def self.isCheckValid?
				return true if @@current_rn == node.hostname
			end
         end
    end
end

Issue-1: - Here when we are loading current_resource by SensuCheck.new, it throws an error as (probably because community cookbook resource sensu__check is LWRP based)

    NameError

       uninitialized constant Chef::Resource::SensuCheck

Issue-2: - Need to rewrite whole action behaviour inside Chef::Provider::MySensuCheck, which is not desired.


Definitely I’m looking Helper module function but how to load resource state inside that function ? (This can solve whole issue)


#5

I think your solution would be a bit messy.

The sensu_check code is an older style LWRP.

The order that Chef will load your cookbooks is

  • libraries
  • child cookbook attributes
  • your cookbook attributes
  • lwrp
  • resource definitions
  • recipes

Your code needs to inherit from a lwrp so you I don’t think you can write your code in a library file (could be wrong) … your code would have to reside in a recipe.

Therefore your (default) recipe would look like

class Chef
  class Provider
   class MySensuCheck < ::Chef::Provider::SensuCheck
      def load_current_resource
	    Chef::Log.warn 'In load current resource'
	  end
   end
  end
end

And your recipe that uses the resource something like this

sensu_resource = sensu_check 'do_stuff' do
   provider Chef::Provider::MySensuCheck
end

I don’t know the cookbook in question so there might be a better check