How to assign file content to chef node attribute - ruby

I have fingreprint.txt at the location "#{node['abc.d']}/fingreprint.txt"
The contents of the file are as below:
time="2015-03-25T17:53:12C" level=info msg="SHA1 Fingerprint=7F:D0:19:C5:80:42:66"
Now I want to retrieve the value of fingerprint and assign it to chef attribute
I am using the following ruby block
ruby_block "retrieve_fingerprint" do
block do
path="#{node['abc.d']}/fingreprint.txt"
Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
command = 'grep -Po '(?<=Fingerprint=)[^"]*' path '
command_out = shell_out(command)
node.default['fingerprint'] = command_out.stdout
end
action :create
end
It seems not to be working because of missing escape chars in command = 'grep -Po '(?<=Fingerprint=)[^"]*' path '.
Please let me know if there is some other way of assigning file content to node attribute

Two ways to answer this: first I would do the read (IO.read) and parsing (RegExp.new and friends) in Ruby rather than shelling out to grep.
if IO.read("#{node['abc.d']}/fingreprint.txt") =~ /Fingerprint=([^"]+)/
node.default['fingerprint'] = $1
end
Second, don't do this at all because it probably won't behave how you expect. You would have to take in to account both the two-pass loading process and the fact that default attributes are reset on every run. If you're trying to make an Ohai plugin, do that instead. If you're trying to use this data in later resources, you'll probably want to store it in a global variable and make copious use of the lazy {} helper.

Related

How i can Insert contents of text file with heredoc and variable with ruby

There was a problem when im creating the CLI. I'm want to give the user the opportunity to insert their data into a text file, for this I created a file and added a heredoc to it
I'm trying to get data from a text document that has a heredoc inside of it with a function that is supposed to interpolate
When I try to display the result of the file, I get the entire contents of the file, including the heredoc
an example will be below
I tried to solve my problem through File class
variable_name = File::open("path_directory/file_with_heredoc.txt", "r+")::read
Next, I decided to give the value of the variable to the terminal via
exec("echo #{variable_name}")
The terminal displays
file = <<-EOM
single text with def result: #{upcase_def("Hello")}
EOM
Tried to give through struct, but result is unchanged
exec("echo #{variable_name.strip}")
What do I need to do to get only data, no HEREDOC syntax?
I want to get this result
"single text with def result: HELLO"
I think this is what you are trying to do but I recommend you to first do some research why 'eval() is evil'. If the file is a user (or hacker) input you definitely want some sanitization there or a completely different approach.
def upcase_def(str)
str.upcase
end
data = File.read('file_with_heredoc.txt')
eval(data)
# => " single text with def result: HELLO\n"

Chef - Read out node attribute and store it in another node attribute in same chef client run fails

I try to read out the current recipe name while chef-client run and to store it in a variable or node attribute wihtin an recipe. Until yet i just found a way storing it into a node attribute but it always fails. This is my code:
ruby_block "Fetch Recipe Name From Run List" do
block do
Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
s = shell_out("echo \"#{node['expanded_run_list']}\" | awk -F '::' '{print substr($3, 1, length($3)-1)}'" )
node.default['sftp-selfmade']['extracted_recipe'] = s.stdout
end
end
extracted_recipe = node['sftp-selfmade']['extracted_recipe']
# To debug the output of the node attribute.
execute 'TEST' do
command "echo \"TEST #{extracted_recipe}\""
end
Output:
* execute[TEST] action run
[execute] TEST
- execute echo "TEST "
Output should be:
- execute echo "TEST <Name-Of-Extracted-Recipe>"
I tried lot's of things as also storing the s.stdout output in a variable but this throws an NoMethodError during compiling stage. Also tried to use stronger values like node.override - this works but only by setting node.normal first and the setting it to node.override but this is not a satisfying solution to do this everytime within cookbook code again for deploying to new hosts. Tried also a solution reloading OHAI. But this also did not work. On a completely new host it also doesn't work after a 2nd chef-client run in case of attributes have been set then after first run.
Is there somebody who can help me out?
Found out the following solution:
expandedrecipe = node['expanded_run_list'].select{ |e| e.include? 'sftp-selfmade' }.first.split('::').last
That does the trick.

Chef - get variable from ruby block

I try to get a variable from my ruby block, but Chef doesn't recognise my variable outside of this block. How can I retrieve any variable out of ruby block? Thanks in advance.
ruby_block 'fetch_path' do
block do
current_path = `sudo cat /var/chef/cache/revision`
new_path = current_path.to_s.split(',').last.split('"').drop(1).first
Chef::Log.info("### Your Current Directory: '#{new_path}' ###")
end
end
Chef::Log.info("### Your Current Directory: '#{new_path}' ###")
Within the block I can get a value, but, out of block - no.
There's two thing here.
First, your second Chef::Log.info will be run at compilation phase, at this time your ruby_block has not been converged. See here about it. You can prefix your logs with 1) and 2) to witch one runs first.
Second, there's a scoping problem, when you define a variable in a block, it is available only within this block.
In chef you can use node.run_state['variable'] as a global variable usable in all recipes, without an use case it's hard to showcase this.
Side note: you should not use the backticks `` construction to execute commands and prefer using shell_out from the recipe DSL.

How to use a Ruby block to assign variables in chef recipe

I am trying to execute Ruby logic on the fly in a Chef recipe and believe that the best way to achieve this is with a block.
I am having difficulty transferring variables assigned inside the block to the main code of the chef recipe. This is what I tried:
ruby_block "Create list" do
block do
to_use ||= []
node['hosts'].each do |host|
system( "#{path}/command --all" \
" | awk '{print $2; print $3}'" \
" | grep #{host} > /dev/null" )
unless $? == 0
to_use << host
end
end
node['hosts'] = to_use.join(',')
end
action :create
end
execute "Enable on hosts" do
command "#{path}/command --enable -N #{node['hosts']}"
end
Rather than try to re-assign the chef attribute on the fly, I also tried to create new variables in the block but still can't find a way to access them in the execute statement.
I was originally using a class and calling methods however it was compiled before the recipe and I really need the logic to be executed at runtime, during the recipe.
Your problem is compile time versus converge time. Your block will be run at converge time but at this time the node['host'] in the execute resource has already been evaluated.
The best solution in this case would be to use lazy evaluation so the variable will be expanded when the resource is converged instead of when it is compiled:
execute "Enable on hosts" do
command lazy { "#{path}/command --enable -N #{node['hosts']}" }
end
Here's a little warning though, the lazy operator has to include the full command attribute text and not only the variable, it can work only as first keyword after the attribute name.

How can I create a custom :host_role fact from the hostname?

I'm looking to create a role based on host name prefix and I'm running into some problems. Ruby is new to me and although I've done extensive searching for a solution, I'm still confused.
Host names look like this:
work-server-01
home-server-01
Here's what I've written:
require 'facter'
Facter.add('host_role') do
setcode do
hostname_array = Facter.value(:hostname).split('-')
first_in_array = hostname_array.first
first_in_array.each do |x|
if x =~ /^(home|work)/
role = '"#{x}" server'
end
role
end
end
I'd like to use variable interpolation within my role assignment, but I feel like using a case statement along with 'when' is incorrect. Please keep in mind that I'm new to Ruby.
Would anybody have any ideas on how I might achieve my goal?
Pattern-Matching the Hostname Fact
The following is a relatively DRY refactoring of your code:
require 'facter'
Facter.add :host_role do
setcode do
location = case Facter.value(:hostname)
when /home/ then $&
when /work/ then $&
else 'unknown'
end
'%s server' % location
end
end
Mostly, it just looks for a regex match, and assigns the value of the match to location which is then returned as part of a formatted string.
On my system the hostname doesn't match either "home" or "work", so I correctly get:
Facter.value :host_role
#=> "unknown server"

Resources