Access Node Attributes in Chef Library

Access Node attributes in Chef Library

You don't have access to the node object in a library unless you pass it into the initializer:

class MyHelper
def self.get_inputs_for(node)
# Your code will work fine
end
end

Then you call it with:

inputs = MyHelper.get_inputs_for(node)

Alternative, you can to create a module and mix it into the Chef Recipe DSL:

module MyHelper
def get_inputs
# Same code, but you'll get "node" since this is a mixin
end
end

Chef::Recipe.send(:include, MyHelper)

Then you have access to the get_inputs method right in a recipe:

inputs = get_inputs

Notice this is an instance method versus a class method.

In short, libraries don't have access to the node object unless given as a parameter. Modules will, if they are mixed into the Recipe DSL. Additionally, the node object is actually an instance variable, so it's not available at the class level (i.e. self.).

How can I access the current node from a library in a Chef cookbook?

What you can do is to include the module in your recipe. That way, your module functions get access to the methods of the recipe, including node.

I normally do this for my library modules:

# my_cookbook/libraries/helpers.rb
module MyCookbook
module Helpers
def foo
node["foo"]
end
end
end

Then, in the recipe, I include the module into the current instance of a recipe:

# my_cookbook/recipes/default.rb
extend MyCookbook::Helpers

That way, only the current recipe gets the module included, not all of them in the whole chef run (you thus avoid name clashes).

Alternatively, you could pass the current node as a parameter to the function. That way, you don't need to include the module (which has the upside of keeping the module namespaces) but has the downside of a more convoluted method call.

Chef - ServerSpec - Accessing Node attributes

According to the code there's no override_attrs method.

Here you'll have the cookbook's attributes under node.override and the environment's attributes in node.env_override, the node.combined_override gives you the resulting attributes after deep merge.

The blog post is quite old, you should better use attrs = node.merged_attributes to write the json file and get the resulting attributes from cookbook, roles and environments, using merged_attributes should avoid the ohai attributes to, keeping the json size low.

Accessing template attributes in Chef

Here are two of my favourite ways to do that:

  • Use chef attributes.

Define the default for the attribute in an attribute file. so in <cookbook_name>/attributes/default.rb file add this line:

default['instance_region'] = 'us-west-1'

and then in your recipe where you are adding the template:

variables(region: node['instance_region'])

you can access that in you template as you mentioned:

region =<%= @region %>
  • Using chef library.

For wider usage you can define such value within a chef library. so in <cookbook>/libraries/common.rb add:

module Common
def instance_region
# This will return the name of AWS region that the nodes is in.
shell_out!('curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone/').stdout
end
end

and then in your recipe you can use it just by calling plain instance_region

Chef node attributes

With newer Chef, you can use the node.read method:

s3_bucket = node.read('elastic', 's3', 'bucket')

It will be nil if any intervening key does not exist.

Chef - access new_resource from library

Use a normal mixin:

# libraries/default.rb
module MyLibrary
def script_path
case new_resource.server_type
when 'admin'
'/admin_server/bin/admin_cntl.sh'
when 'managed'
'/managed_server/bin/managed_cntl.sh'
end
end
end

# providers/default.rb
include MyLibrary

action :start do
execute 'my_script' do
command "./#{script_path} start"
end
end

action :remove do
execute 'my_script' do
command "./#{script_path} stop"
end
end

Also remember you can define methods directly in the provider if they are only useful for that one provider.

Chef::Node available in library called from recipe, but not when called through Singleton Class

I changed my approach to create a custom resource.

resource_name :otbo_service
actions [:stop, :start, :restart, :nothing]
default_action :nothing

property :service, Hash, required: true

action_class do
def control(commands)
handle(new_resource.service, commands)
end
end

def handle(service, commands)
name = service[:name]

for command in commands
service[command].call()
end
end

action :nothing do
Chef::Log.info('Nothing to do.')
end

action :stop do
control([:stop])
end

action :start do
control([:start])
end

action :restart do
control([:stop, :start])
end

I added a library function that can access the node variable.

module Common
module Util
def _WLStart()
Chef::Log.info("<< Starting WebLogic Server on Host: #{node['hostname']} >>")
end

def _WLShutdown()
Chef::Log.info("<< Stopping WebLogic Server on Host: #{node['hostname']} >>")
end
end
end

Chef::Recipe.send(:include, Common::Util)
Chef::Resource.send(:include, Common::Util)

In the recipe I can then call the custom resource via code something like the following.

extend Common::Util

weblogic = {
:name => 'weblogic',
:start => lambda { _WLStart() },
:stop => lambda { _WLShutdown() }
}

otbo_service 'Restart WebLogic' do
action :restart
service weblogic
end

How to access node attributes and recipe variables in ruby_block

You need double quotes. Single quotes in Ruby do not support the #{foo} interpolation syntax.



Related Topics



Leave a reply



Submit