ruby whenever gem: scoping :mailto to specific tasks - ruby

Is it possible to scope :mailto to specific tasks in schedule.rb? I'm thinking something like the following:
# pseudo-code
env MAILTO=address_1#mail.com
#task_1
env MAILTO=address_2#mail.com
#task_2
#etc
The only reference I can find in the source code is in the following test case:
should "output MAILTO environment variable" do
assert_match "MAILTO=someone#example.com", #output
end

Per this question on server overflow as well as research into cron it appears that most cron daemon packages would not support having task-relative email addresses.

Related

Run arbitrary ruby code in a chef cookbook

I have a simple chef cookbook and all it does is it sets the MOTD on a CentOS machine. It takes the content of the /tmp/mymotd.txt and turns it into the MOTD.
I also have a simple ruby script (a full-fledged ruby script) that simply reads the text from the web-server and puts in into the /tmp/mymotd.txt.
My questions are:
how do I run this ruby script from within the cookbook?
how do I pass some parameters to the script (e.g. the address of the web-server)
Thanks a lot beforehand.
Ad 1.
You can use libraries directory in scripts to place there your ruby script and declare it in a module. Example:
# includes
module MODULE_NAME
# here some code using your script
# Example function
def example_function (text)
# some code
end
end
You can use then
include MODULE_NAME
in your recipe to import those functions and just use it like
example_function(something)
What's good - you can use there also Chef functions and resources.
IMPORTANT INFO: Just remember that Chef has 2 compilation phases. First will be all of Ruby code, second all of Chef resources. This means, that you have to remember priority of code. I won't write here more info about it, since you haven't asked for this, but if you want, you can find it here.
Ad 2.
You can do this in several ways, but it seems to me, that the best option for you would be to use environments. You can find more info in here. Basically, you can set up environment for script before it will run - this way you can define some variables you would use later.
Hope this helps.

Pass a variable from bash into chef template

In my chef recipe, I have a bash ruby block. I need some value generated from it passed to my templates. For example, the joke variable in here:
bash "create opal user" do
code <<-EOH
joke='my funny joke'
echo $joke
EOH
end
template '/joke.txt' do
source 'joke.txt.erb'
variable(joke:"#{joke}")
end
Answered in IRC, Chef doesn't work like this in general. The shell_out helper can sometimes be used but overall you should try to not structure your recipes this way.

Passing variables between chef resources

i would like to show you my use case and then discuss possible solutions:
Problem A:
i have 2 recipes, "a" and "b".. "a" installs some program on my file system (say at "/usr/local/bin/stuff.sh" and recipe "b" needs to run this and do something with the output.
so recipe "a" looks something like:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh"
(the script just echo(es) "stuff" to stdout)
and recipe "b" looks something like:
include_recipe "a"
var=`/usr/local/bin/stuff.sh`
(note the backquotes, var should contain stuff)
and now i need to do something with it, for instance create a user with this username. so at script "b" i add
user "#{node[:var]}"
As it happens, this doesn't work.. apparently chef runs everything that is not a resource and only then runs the resources so as soon as i run the script chef complains that it cannot compile because it first tries to run the "var=..." line at recipe "b" and fails because the "execute ..." at recipe a did not run yet and so the "stuff.sh" script does not exist yet.
Needless to say, this is extremely annoying as it breaks the "Chef runs everything in order from top to bottom" that i was promised when i started using it.
However, i am not very picky so i started looking for alternative solutions to this problem, so:
Problem B: i've run across the idea of "ruby_block". apparently, this is a resource so it will be evaluated along with the other resources. I said ok, then i'd like to create the script, get the output in a "ruby_block" and then pass it to "user". so recipe "b" now looks something like:
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`
end
end
user "#{node[:var]}"
However, as it turns out the variable (var) was not passed from "ruby_block" to "user" and it remains empty. No matter what juggling i've tried to do with it i failed (or maybe i just didn't find the correct juggling method)
To the chef/ruby masters around: How do i solve Problem A? How do i solve Problem B?
You have already solved problem A with the Ruby block.
Now you have to solve problem B with a similar approach:
ruby_block "create user" do
block do
user = Chef::Resource::User.new(node[:var], run_context)
user.shell '/bin/bash' # Set parameters using this syntax
user.run_action :create
user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
end
end
You could also solve problem A by creating the file during the compile phase:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
action :nothing
end.run_action(:run)
If following this course of action, make sure that:
/usr/local/bin exist during Chef's compile phase;
Either:
stuff.sh is executable; OR
Execute it through a shell (e.g.: var=`sh /usr/local/bin/stuff.sh`
The modern way to do this is to use a custom resource:
in cookbooks/create_script/resources/create_script.rb
provides :create_script
unified_mode true
property :script_name, :name_property: true
action :run do
execute "creating #{script_name}" do
command "echo 'echo stuff' > #{script_name}"
not_if { File.exist?(script_name) }
end
end
Then in recipe code:
create_script "/usr/local/bin/stuff.sh"
For the second case as written I'd avoid the use of a node variable entirely:
script_location = "/usr/local/bin/stuff.sh"
create_script script_location
# note: the user resources takes a username not a file path so the example is a bit
# strange, but that is the way the question was asked.
user script_location
If you need to move it into an attribute and call it from different recipes then there's no need for ruby_blocks or lazy:
some cookbook's attributes/default.rb file (or a policyfile, etc):
default['script_location'] = "/usr/local/bin/stuff.sh"
in recipe code or other custom resources:
create_script node['script_location']
user node['script_location']
There's no need to lazy things or use ruby_block using this approach.
There are actually a few ways to solve the issue that you're having.
The first way is to avoid the scope issues you're having in the passed blocks and do something like ths.
include_recipe "a"
this = self
ruby_block "a_block" do
block do
this.user `/usr/local/bin/stuff.sh`
end
end
Assuming that you plan on only using this once, that would work great. But if you're legitimately needing to store a variable on the node for other uses you can rely on the lazy call inside ruby to do a little work around of the issue.
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`.strip
end
end
user do
username lazy { "#{node[:var]}" }
end
You'll quickly notice with Chef that it has an override for all default assumptions for cases just like this.

Mercurial HG_NODE hook variable on windows

I'm currently testing mercurial hooks on windows and it seems like I cannot access hook variables....
here's hgrc content :
[hooks] prechangegroup = ruby prechangegroup.rb test1 test2 $HG_NODE
I also tried with %HG_NODE%
Here's prechangegroup.rb content
ARGV.each do|a|
puts "Argument: #{a}"
end
It prints out:
Argument: test1
Argument: test2
Argument: $HG_NODE$
Followed by the normal push output...
Any idea? (probably something stupid but, I can't seem to find it)
Thanks
HG_NODE is an environmental variable. You don't have to use it as arguments on the command line. Instead, you should be able to use it as puts ENV['HG_NODE'] (found through search engine as I'm not a ruby guy)
OK, I found a good documentation right on mercurial's website.
http://www.selenic.com/mercurial/hgrc.5.html#hooks
I tried with a variable other than %HG_NODE% like %HG_URL% and the variable worked.
So it probably means that the variable is inaccessible from that hook.

Redirect Output of Capistrano

I have a Capistrano deploy file (Capfile) that is rather large, contains a few namespaces and generally has a lot of information already in it. My ultimate goal is, using the Tinder gem, paste the output of the entire deployment into Campfire. I have Tinder setup properly already.
I looked into using the Capistrano capture method, but that only works for the first host. Additionally that would be a lot of work to go through and add something like:
output << capture 'foocommand'
Specifically, I am looking to capture the output of any deployment from that file into a variable (in addition to putting it to STDOUT so I can see it), then pass that output in the variable into a function called notify_campfire. Since the notify_campfire function is getting called at the end of a task (every task regardless of the namespace), it should have the task name available to it and the output (which is stored in that output variable). Any thoughts on how to accomplish this would be greatly appreciated.
I recommend not messing with the Capistrano logger, Instead use what unix gives you and use pipes:
cap deploy | my_logger.rb
Where your logger reads STDIN and STDOUT and both records, and pipes it back to the appropriate stream.
For an alternative, the Engineyard cap recipies have a logger – this might be a useful reference if you do need to edit the code, but I recommend not doing.
It's sort of a hackish means of solving your problem, but you could try running the deploy task in a Rake task and capturing the output using %x.
# ...in your Rakefile...
task :deploy_and_notify do
output = %x[ cap deploy ] # Run your deploy task here.
notify_campfire(output)
puts output # Echo the output.
end

Resources