Fact file was parsed but returned an empty data set - ruby

For my current module, I need to check if php version 5 or 7 is installed and created a fact for this. The fact file is stored in the modules directory in facts.d/packageversion.rb and has the following content:
#!/usr/bin/ruby
require 'facter'
Facter.add(:php_version) do
setcode do
if File.directory? '/etc/php5'
5
else
if File.directory? '/etc/php7'
7
else
0
end
end
end
end
But I can't use it in my module. In Puppet agent log, i get this error:
Fact file /var/lib/puppet/facts.d/packageversion.rb was parsed but
returned an empty data set
How can I solve this?

facts.d is the module directory for external facts. You could place this file into the external facts directory, but the expected output would need to be key-value pairs. This is not happening, so Puppet is not finding a data set for the fact. https://docs.puppet.com/facter/3.6/custom_facts.html#executable-facts-----unix
You have written this fact as a custom fact and not an external fact. Therefore, it needs to be placed inside the lib/facter directory in your module instead. Then it will function correctly. I notice this important information seems to have been removed from the latest Facter documentation, which probably lends to your confusion.
Also, consider using an elsif in your code for clarity and optimization:
if File.directory? '/etc/php5'
5
elsif File.directory? '/etc/php7'
7
else
0
end

What Matt Schuchard said.
Also, you might consider that the Approved Vox Populi Puppet module uses this code for PHP version:
Facter.add(:phpversion) do
setcode do
output = Facter::Util::Resolution.exec('php -v')
unless output.nil?
output.split("\n").first.split(' ').
select { |x| x =~ %r{^(?:(\d+)\.)(?:(\d+)\.)?(\*|\d+)} }.first
end
end
end
Note that Facter::Util::Resolution.exec is deprecated in favour of Facter::Core::Execution.exec.
Aside from that, you might consider this a better way of getting the PHP version.

Related

Keeping files updated with a Chef recipe

The challenge prompt is above, and my latest attempt is below. The directories and files are created as expected, and the read-out after executing chef-apply multipleCopies.rb tells me the files are linked, but when I update any one of the files, the others do not follow suit. Any ideas? Here is my code:
for x in 1..3
directory "multipleCopy#{x}" do
mode '0755'
action :create
end
end
file "multipleCopy1/secret.txt" do
mode '0755'
action :create
end
for x in 2..3
link "multipleCopy#{x}/secret.txt" do
to "multipleCopy1/secret.txt"
link_type :hard
subscribes :reload, "multipleCopy1/secret.txt", :immediately
end
end
Note: For less headache, I am testing the recipe locally before uploading to the ubuntu server referenced in the prompt, which is why my file paths are different and why I have not yet included the ownership properties.
So a file hard link doesn't seem to be what the question is going for (though I would say your solution is maybe better since this is really not what Chef is for, more on that later). Instead they seem to want you to have three actually different files, but sync the contents.
So first the easy parts, creating the directories and the empty initial files. It's rare to see those for loops used in Ruby code, though it is syntactically valid:
3.times do |n|
directory "/var/save/multipleCopy#{n+1}" do
owner "ubuntu"
group "root"
mode "755"
end
file "/var/save/multipleCopy#{n+1}/secret.txt" do
owner "root
group "root"
mode "755"
end
end
But that doesn't implement the hard part of sync'ing the files. For that we need to first analyze the mtimes on the files and use the most recent as the file content to set.
latest_file = 3.times.sort_by { |n| ::File.mtime("/var/save/multipleCopy#{n+1}/secret.txt") rescue 0 }
latest_content = ::File.read("/var/save/multipleCopy#{latest_file+1}/secret.txt") rescue nil
and then in the file resource:
file "/var/save/multipleCopy#{n+1}/secret.txt" do
owner "root
group "root"
mode "755"
content latest_content
end
As for this not being a good use of Chef: Chef is about writing code which asserts the desired state of the machine. In the case of files like this, rather than doing this kind of funky stuff to check if a file has been edited, you would just say that Chef owns the file content for all three and if you want to update it, you do it via your cookbook (and then usually use a template or cookbook_file resource).

optimising reading id3 tags of mp3 files

I'm trying to read mp3 files using 'mp3info' gem and by going through each file which ends with .mp3 in its file name in a directory and going inside a directory using Dir.chdir() and repeating the process and storing these tags in database. But I have 30gb of music collection and it takes around 6-10 mins for the whole scan to complete. Is there any way I can optimise this scan?
def self.gen_list(dir)
prev_pwd=Dir.pwd
begin
Dir.chdir(dir)
rescue Errno::EACCES
end
counter = 0
Dir[Dir.pwd+'/*'].each{|x|
#puts Dir.pwd
if File.directory?(x) then
self.gen_list(x) do |y|
yield y
end
else if File.basename(x).match('.mp3') then
begin
Mp3Info.open(x) do |y|
yield [x,y.tag.title,y.tag.album,y.tag.artist]
end
rescue Mp3InfoError
end
end
end
}
Dir.chdir(prev_pwd)
end
This is the method which generates list and sends the tags to &block where data is stored in database..
Have you tried setting the parse_mp3 flag to false? by default it is on which means you are going to pull in the entire file for each scan when all you care about is the info. I don't know how much time this will save you. See the github source for more info.
https://github.com/moumar/ruby-mp3info/blob/master/lib/mp3info.rb#L214
# Specify :parse_mp3 => false to disable processing of the mp3
def initialize(filename_or_io, options = {})
You can:
Run several processes (for each directory in the base dir, for example)
Use threads with rubinius or JRuby.
You can try taglib-ruby gem which is unlike mp3info wrapper over C library and it could give you little bit more performance. Otherwise you have to stick to JRuby and run multiple threads (4 if you have 4 cores).
You may also benefit from a more direct way of retrieving the mp3 files.
Dir['**/*.mp3'].each |filepath|
Mp3Info.open(filepath) do |mp3|
...
end
rescue Mp3ErrorInfo
...
end
This will find all .mp3 files at any depth from the current directory and yield the relative path to the block. It is approximately equivalent to find . -name '*.mp3' -print

Trouble Creating Directories with mkdir

New to Ruby, probably something silly
Trying to make a directory in order to store files in it. Here's my code to do so
def generateParsedEmailFile
apath = File.expand_path($textFile)
filepath = Pathname.new(apath + '/' + #subject + ' ' + #date)
if filepath.exist?
filepath = Pathname.new(filepath+ '.1')
end
directory = Dir.mkdir (filepath)
Dir.chdir directory
emailText = File.new("emailtext.txt", "w+")
emailText.write(self.generateText)
emailText.close
for attachment in #attachments
self.generateAttachment(attachment,directory)
end
end
Here's the error that I get
My-Name-MacBook-2:emails myname$ ruby etext.rb email4.txt
etext.rb:196:in `mkdir': Not a directory - /Users/anthonydreessen/Developer/Ruby/emails/email4.txt/Re: Make it Brief Report Wed 8 May 2013 (Errno::ENOTDIR)
from etext.rb:196:in `generateParsedEmailFile'
from etext.rb:235:in `<main>'
I was able to recreate the error - it looks like email4.txt is a regular file, not a directory, so you can't use it as part of your directory path.
If you switched to mkdir_p and get the same error, perhaps one of the parents named in '/Users/anthonydreessen/Developer/Ruby/emails/email4.txt/Re: Make it Brief Report Wed 8 May 2013' already exists as a regular file and can't be treated like a directory. Probably that last one named email.txt
You've got the right idea, but should be more specific about the files you're opening. Changing the current working directory is really messy as it changes it across the entire process and could screw up other parts of your application.
require 'fileutils'
def generate_parsed_email_file(text_file)
path = File.expand_path("#{#subject} #{date}", text_file)
while (File.exist?(path))
path.sub!(/(\.\d+)?$/) do |m|
".#{m[1].to_i + 1}"
end
end
directory = File.dirname(path)
unless (File.exist?(directory))
FileUtils.mkdir_p(directory)
end
File.open(path, "w+") do |email|
emailText.write(self.generateText)
end
#attachments.each do |attachment|
self.generateAttachment(attachment, directory)
end
end
I've taken the liberty of making this example significantly more Ruby-like:
Using mixed-case names in methods is highly irregular, and global variables are frowned on.
It's extremely rare to see for used, each is much more flexible.
The File.open method yields to a block if the file could be opened, and closes automatically when the block is done.
The ".1" part has been extended to keep looping until it finds an un-used name.
FileUtils is employed to makes sure the complete path is created.
The global variable has been converted to an argument.

Using yaml files within gems

I'm just working on my first gem (pretty new to ruby as well), entire code so far is here;
https://github.com/mikeyhogarth/tablecloth
One thing I've tried to do is to create a yaml file which the gem can access as a lookup (under lib/tablecloth/yaml/qty.yaml). This all works great and the unit tests all pass, hwoever when I build and install the gem and try to run under irb (from my home folder) I am getting;
Errno::ENOENT: No such file or directory - lib/tablecloth/yaml/qty.yaml
The code is now looking for the file in ~/lib/tablecloth... rather than in the directory the gem is installed to. So my questions are;
1) How should i change line 27 of recipe.rb such that it is looking in the folder that the gem is installed to?
2) Am I in fact approaching this whole thing incorrectly (is it even appropriate to use static yaml files within gems in this way)?
Well first of all you should refer to the File in the following way:
file_path = File.join(File.dirname(__FILE__),"yaml/qty.yaml")
units_hash = YAML.load_file(filepath)
File.dirname(__FILE__) gives you the directory in which the current file (recipe.rb) lies.
File.join connects filepaths in the right way. So you should use this to reference the yaml-file relative to the recipe.rb folder.
If using a YAML-file in this case is a good idea, is something which is widely discussed. I, myself think, this is an adequate way, especially in the beginning of developing with ruby.
A valid alternative to yaml-files would be a rb-File (Ruby Code), in which you declare constants which contain your data. Later on you can use them directly. This way only the ruby-interpreter has to work and you save computing time for other things. (no parser needed)
However in the normal scenario you should also take care that reading in a YAML file might fail. So you should be able to handle that:
file_path = File.join(File.dirname(__FILE__),"yaml/qty.yaml")
begin
units_hash = YAML.load_file(filepath)
rescue Psych::SyntaxError
$stderr.puts "Invalid yaml-file found, at #{file_path}"
exit 1
rescue Errno::EACCES
$stderr.puts "Couldn't access file due to permissions at #{file_path}"
exit 1
rescue Errno::ENOENT
$stderr.puts "Couldn't access non-existent file #{file_path}"
exit 1
end
Or if you don't care about the details:
file_path = File.join(File.dirname(__FILE__),"yaml/qty.yaml")
units_hash =
begin
YAML.load_file(filepath)
rescue Psych::SyntaxError, Errno::EACCES, Errno::ENOENT
{}
end

Can't delete Dir/Files after using File.open block

When trying to delete a directory (+ contents) and after reading the files inside, FileUtils.rm_rf(path) will not delete all the folders, although it does delete all the files and some of the folders.
After some experimentation it seems to be related to a File.open block. (I actually do a regex match inside the block, but I'm just using a puts here to keep things clear)
File.open(file).each do |line|
puts line
end
From what I've read, the above should automatically close the file but when using this, FileUtils fails to complete its task.
However, if I use the following code, FileUtils works as desired.
open_file = File.open(file)
open_file.each do |line|
puts line
end
open_file.close
It's no big deal to use the code in the second example, but I do prefer the cleanliness of the first.
Is there any reason why that first example breaks FileUtils?
P.S. I'm new to both Ruby and Stack Overflow....Hi. My system is Ubuntu 11.04 (64bit), running RVM with Ruby 1.9.2-p180
You should use something like this:
File.open(file) do |f|
f.each{|line| puts line}
end
In your example the block is supplied to the each method and the version of open without a block is executed returning an IO object on which the each method is called.

Resources