Chef Template - Timestamping - ruby

While implementing a templated config file using chef 11.x I'd like to insert the current date/time into the file whenever it is updated.
For example:
# Created By : core::time-settings
# On : <%= Time.now %>
Obviously this evaluates on each recipe run and constantly updates the target file even when the other attributes are OK - which is not desired.
Therefore is anyone aware of a solution? I'm not aware of any built-in logic within Chef to achieve this and I don't know of a built-in chef variable that I could evaluate within a ruby block that would only be true if the other attributes are out of compliance (as that would provide a potential workaround).
I know that I could run an execute type operation which only gets run after the template resource has been fired, and it expands a variable in the file to achieve this, but I don't like the concept or idea of doing that.
Thanks,

While I agree with Tensibai that what you expect is not what Chef is made for.
What I want to add (because some time ago I searched pretty long for that) is how to include the current time stamp in a file, once it was modified through Chef (somehow you have to circumvent that it always updates the time stamp).
The result can be found here, simplified, untested version:
time = Time.new.strftime("%Y%m%d%H%M%S")
template "/tmp/example.txt" do
source "/tmp/example.txt.erb" # the source is not in a cookbook, but on the disk of the node!
local true
variables(
:time => time
)
action :nothing
end
template "/tmp/example.txt.erb" do
variables(
variable1 => "test"
)
notifies :create, resources(:template => "/tmp/example.txt"), :immediately
end
Everytime, when the content of /tmp/example.txt.erb changes, it triggers /tmp/example.txt to be written - taking /tmp/example.txt.erb as template from the local disk instead of from the cookbook (because local true) and replacing the time variable with the current time.
So the only variable that has to be replaced when writing /tmp/example.txt is the time, thus the example.txt.erb template looks like this:
# my template
time: <%%= #time %>
other stuff here.. <%= #variable1 %>

That's the way chef works, it makes a diff between rendered template and actual file, as the timestamp is not the same it replace it.
Your alternate solution won't work either for the same reason, a placeholder will be different than the datetime of the replacements.
The best you can do is write a file aside named 'myfile-last-update' for exemple with a text inside describing the last update of it.
But last question: Why would you want to have the time inside the file as it's already present in the file attribute (ls -l should give you this information) ?

Related

Chef - if node.name is in template list, tag server

We have an issue where we don't have admin privileges to tag servers simply with knife. How would I tell Chef to read the template and if the template includes the node.name of the server to tag it.
I know I can tag servers with tag('tagnamehere') But the code surrounding that... I don't know if that will work. Or if "Template.readlines" is a search function instead of "File.readlines".
if Template.readlines('template1.erb').grep(/#{node.name}/).any?
tag('mytag')
end
Not sure how to accomplish this feat. But trying very hard to understand as an Ops person.
If I understand it correctly reading the static template erb file and you are looking for #{node.name} variable used or not.
In this case solution would be skipping string interpolation by using \ in grep
if Template.readlines('template1.erb').grep(/\#{node.name}/).any?
tag('mytag')
end

How to get the name of an instance of Tempfile in Ruby?

When creating a Tempfile in ruby, it takes the basename you pass it, and then it appends a random string to the end.
From the docs: http://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/Tempfile.html
file = Tempfile.new('hello')
file.path # => something like: "/tmp/hello2843-8392-92849382--0"
You can see it starts with hello and then adds 2843-8392-92849382--0. Though this ending will change every time you create an instance.
This makes it difficult (at least for me) to lookup in the directory its saved in.
Question:
Is there any method (like file.fullName) that could be run on the instance to just get the hello2843-8392-92849382--0, in order to look it up in the directory where its saved?
Thoughts:
You could take the path and parse it but that seems excessive.
Basically you're asking for:
File.basename(file.path)
There's rarely a reason to need that exposed as a method, but if you want you could subclass Tempfile to add it in:
class SuperTempfile < Tempfile
def basename
File.basename(path)
end
end

Guard, how to temporally track specific file?

I'm using Guard gem
At some time of development I need to track only a specific file or several files but not an entire project.
Is there some handy way to temporarily track a particular file?
I know it can be done by modifying guard file but I don't think it's a neat solution.
Actually you can just use focus: true in the it statement for instance:
In your Spec file.
it "needs to focus on this!", focus: true do
#test written here.
end
Then in spec/spec_helper you need to add the config option.
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
end
Then Guard will automatically pick up test that is focused and run it only. Also the config.run_all_when_everything_filtered = true tells guard to run all of the tests when there is nothing filtered even though the name sounds misleading.
I find Ryan Bate's rails casts to be very helpful on Guard and Spork.
Perhaps groups will work for you?
# In your Guardfile
group :focus do
guard :ruby do
watch('file_to_focus_on.rb')
end
end
# Run it with
guard -g focus
I know you mentioned you don't want to modify the Guardfile, but adding a group would just need to be done once, not every time you switch from watching the project to a focused set and back.
Of course if the set of files you need to focus on changes, you'll need to change the args to watch (and maybe add more watchers), but I figure you'll have to specify them somewhere and this seems as good a place as any.
Alternately, if you really don't want to list the files to focus on in the Guardfile, you could have the :focus group read in a list of files from a separate file or an environment variable as suggested by David above. The Guardfile is just plain ruby (with access to the guard DSL), so it's easy to read a file or ENV variable.
If you use guard-rspec. You can do this.
Change your Guardfile rspec block so that is has something like this:
guard 'rspec', :cli => ENV['RSPEC_e'].nil? ? "": "-e #{ENV['RSPEC_e']}") do
# ... regular rspec-rails stuff goes here ...
end
Start Guard. And set RSPEC_e before. Like so...
RSPEC_e=here guard
Then whenever you change something only specs that have text "here" (set by RSPEC_e) in their description will be re-run.
You could permanently modify the guard file to check an environment variable of your choosing and behave differently if it is present. For example, access the variable ENV['FILE']. Then you can prepend your command for running guard with FILE=foo.rb whenever you want.

GridFS in Ruby: How to upsert?

Does GridFS have an upsert?
For example, if i want to save an image with a specified _id, and one with that same _id already exists, i want it to overwrite (update) it. Otherwise, insert it.
The spec isn't really designed to support upserts, since you're technically modifying more than one document, and certainly tricky race conditions can arise. So we recommend what Matt has done, which is to delete first and then put.
I looked at the mongo ruby gem source code and found this:
# Store a file in the file store. This method is designed only for writing new files;
# if you need to update a given file, first delete it using #Grid#delete.
# ...
def put(data, opts={})
So, I did this in the code:
grid.delete(id) # if exists
grid.put(tmp_file.read, :_id => id, :content_type => file_type)
See the working sinatra script here:
http://github.com/acani/acani-sinatra/blob/master/acani.rb#L97

Rails File I/O: What works in Ruby doesn't work in Rails?

So, I wrote a simple Ruby class, and put it in my rails /lib directory. This class has the following method:
def Image.make_specific_image(paths, newfilename)
puts "making specific image"
#new_image = File.open(newfilename, "w")
puts #new_image.inspect
##blank.each(">") do |line|
puts line + "~~~~~"
#new_image.puts line
if line =~ /<g/
paths.each do |p|
puts "adding a path"
puts p
#new_image.puts p
end
end
end
end
Which creates a new file, and copies a hardcoded string (##blank) to this file, adding custom content at a certain location (after a g tag is found).
If I run this code from ruby, everything is just peachy.
HOWEVER, if I run this code from rails, the file gets CREATED, but is then empty. I've inspected each line of the code: nothing I'm trying to write to the file is nil, but the file is empty nonetheless.
I'm really stumped here. Is it a permissions thing? If so, why on EARTH would Rails have the permissions necessary to MAKE a file, but then not WRITE to the file it made?
Does File I/O somehow work differently in rails?
Specifically, I have a model method that calls:
Image.make_specific_image(paths, creature.id.to_s + ".svg")
which succesfully makes a file of the type "47.svg" that is empty.
Have you tried calling close on the file after you're done writing it? (You could also use the block-based File.open syntax, which will automatically close once the block is complete). I'm guessing the problem is that the writes aren't getting flushed to disk.
So.
Apparently File I/0 DOES work in Rails...just very, very slowly. In Ruby, as soon as I go to look at the file, it's there, it works, everything is spiffy.
Before, after seeing blank files from Rails, I would get frustrated, then delete the file, and change some code and try again (so as not to be full of spam, since each file is genearted on creature creation, so I would soon end up with a lot of files like "47.svg" and "48.svg", etc.
....So. I took my lunch break, came back to see if I could tell if the permissions of the rails generated file were different from the ruby generated file...and noticed that the RAILS file is no longer blank.
Seems to take about five minutes for rails to finally write to the file, even AFTER it claims it's done processing that whole call. Ruby takes a few seconds. Not really sure WHY they are so different, but at least now I know it's not a permissions thing.
Edit: Actually, on some files take so long, others are instant...

Resources