2

I have the following Ruby block:

ruby_block "Validate" do
  block do
    require "mixlib/shellout"
    begin
      cmd = Mixlib::ShellOut.new("/usr/local/bin/someScript.py", :timeout => 3600)
      cmd.live_stream = STDOUT
      cmd.run_command
      cmd.error!
    rescue Exception => e
      puts "Action failed..."
      return 168
    end
  end
  action :create
  notifies :create, "ruby_block[Validated]", :immediately
  not_if { node[:state][:validated] == true }
end

I want to log the result of the script into both STDOUT and a file called "/tmp/xml_diff_results.txt".

The first thing I did was change:

cmd=Mixlib::ShellOut.new("/usr/local/bin/someScript.py", :timeout => 3600)

to:

cmd=Mixlib::ShellOut.new("/usr/local/bin/someScript.py > /tmp/xml_diff_results.txt", :timeout => 3600)

however, that did not do what I expected.

Then I noticed the cmd.live_stream variable. Is there a way I can tap into that and do something like this?:

cmd.live_stream = (STDOUT > /tmp/xml_diff_results.txt)

SOLUTION:

The solution to my problem was simple and inspired by @tensibai.

log_file = File.open('/tmp/chef-run.log', File::WRONLY | File::APPEND)
LOG = Logger.new(log_file)

def shell(command)
  LOG.info "Execute: #{command}"
  cmd = Mixlib::ShellOut.new(command, :timeout => 1800)
  cmd.run_command
  LOG.info "Returned: #{cmd.stdout}"
  cmd.error!
  cmd
end
Jakir00
  • 2,023
  • 4
  • 20
  • 32

2 Answers2

5

This isn't a Ruby or even Chef question. It's more like a Bash question

One way to run a command, and redirect its output to stdout and a file, could be using tee

echo 'Hello World!' | tee output.log

So, with your example it could be like this

cmd=Mixlib::ShellOut.new("/usr/local/bin/someScript.py | tee /tmp/xml_diff_results.txt", :timeout => 3600)
Mauro Baraldi
  • 6,346
  • 2
  • 32
  • 43
2

Another option in Ruby (just the inner part) in case tee is not available (windows):

  cmd = Mixlib::ShellOut.new("/usr/local/bin/someScript.py", :timeout => 3600)
  cmd.live_stream = STDOUT
  cmd.run_command
  # new part
  log=::Tempfile.new(["xml_diff_results",".txt"])
  errlog=::File.open(log.path.gsub(".txt",".err")
  log.write(cmd.stdout)
  errlog.write(cmd.stderr)
  log.close
  errlog.close
  Chef::Log.info("Log results are in #{log.path}")
  # end of new part 
  cmd.error!

Change the Chef::Log level to warn if you run chef-client without -l info and really want the path to be printed in chef log.

Main advantage is that it's platform independent, drawback is that the log files will only be written once the command has ended its execution.

Tensibai
  • 15,557
  • 1
  • 37
  • 57
  • Actually this is more like what I'm looking for. I am going to test this now. – Jakir00 Nov 03 '16 at 17:11
  • @Jakir00 I forgot to say it is untested code, I'll be happy if you can confirm this is ok (it should be as far as I know `shell_out`, but I may have missed a case) – Tensibai Nov 04 '16 at 08:47
  • Using this as psudo-code worked. I didn't copy/paste this but I ended up using this methodology to write the logs from the command output. Thanks! – Jakir00 Nov 18 '16 at 01:34