output of unix command in chef recipe - ruby

I have an execute command in a chef recipe, and I'd like to set the cwd property as the output of a unix command.
execute 'run dynamically generated install file' do
command 'make install'
cwd '' # would like the output of `ls -Adrt /tmp/unixODBC.* | tail -n 1`
end
Is this possible?

Okay, so finally at a keyboard and can write this out in full.
The literal translation of what you have there would be:
execute 'run dynamically generated install file' do
command 'make install'
cwd lazy { shell_out!('ls -Adrt /tmp/unixODBC.* | tail -n 1').stdout.strip }
end
However that is going to be much slower than needed and more failure prone so I would recommend writing it in Ruby instead:
execute 'run dynamically generated install file' do
command 'make install'
cwd lazy { Dir['/tmp/unixODBC.*'].first }
end
This avoids having to spawn a bunch of processes and instead just does the same (I think) logic directly.

You should be able to do it just like this:
execute 'run dynamically generated install file' do
command 'make install'
cwd `ls -Adrt /tmp/unixODBC.* | tail -n 1`
end

That seems like it's out of the scope of an execute block.
Maybe just use a ruby_block?
ruby_block 'run dynamically generated install file' do
require 'mixlib/shellout'
block do
cmd = Mixlib::ShellOut.new('make install')
cmd.run_command
cwd = cmd.stdout
# Do more stuff with cwd...
end
end

Related

Mix input/output with Ruby IO?

I am hoping to write a small method that can interact with a subprocess (bash in this case) and should be able to both write commands and have those commands print their outback back to my shell when running the Ruby file.
So far, I can do something similar with this code:
require 'io/console'
#shell = IO.popen('/bin/bash', 'w')
def run(command)
puts command
#shell.puts command
puts 'Done'
end
run 'var=3'
run 'echo $var'
run 'sleep 2'
run 'ls docs'
#shell.close
And then when I run this code all of the Ruby code is printed first, and only later does any of the shell code get printed:
var=3
Done
echo $var
Done
sleep 2
Done
ls docs
Done
3
<ls output>
I was trying to read some of the tests for io/console as I'm almost certain there exists a really straightforward way to interact with a subprocess like this and get the output inline with the commands being run:
https://github.com/ruby/io-console/blob/master/test/io/console/test_io_console.rb

Strange behavior with ruby_block resource in Chef

I have two ruby blocks at the end of a recipe:
ruby_block 'set permissions for app dir' do
block do
require 'fileutils'
FileUtils.chown_R 'user01', 'user01', '/mnt/app/'
end
action :run
end
ruby_block 'configure node app session' do
block do
cmd = "sudo su - user01 -c \"/mnt/app/http-app-/bin/app create /mnt/app/http-app/#{node['hostname']}\" && sudo su -c 'systemctl enable app' && sudo su -c 'systemctl start app'"
exec(cmd)
end
action :run
not_if "stat -c %U /mnt/app/#{node['hostname']} |grep app"
end
A couple strange things are happening. One, I cannot add any code after the last block... it will not run if added. Two, when the cookbook runs the recipe never ends with if the run failed or was successful. Bootstrapping the system a second time will prove to finish successful... but ssh'ing to the box and running chef-client comes back with an empty run list.
Can anyone explain this behavior? How can i fix it?
exec() is not what you think. That's a Ruby core method which calls the actual exec() syscall, which replaces the current process with something new. What you want is our shell_out!() helper which runs a subcommand and returns and object with the results.

How to achieve idempotence when we execute bash script in ruby/knife environment?

I am trying to execute bash script in knife/Ruby environment. For example:
cookbook_file "test.sh" do
path "/tmp/test.sh"
mode "755"
action :create
end
bash "execute test.sh on #{nodeName}" do
code <<-EOH
sh test.sh arg1 arg2
EOH
#only_if { false }
end
How can I use only_if or not_if? So when we execute second time and the contents of "test.sh" is not changed, it should skip execution. I am getting this:
* cookbook_file[test.sh] action create (up to date)
but it still executes second time, third time...
You wouldn't use a guard, instead you would use a notification:
execute 'run test' do
action :nothing
command 'bash /tmp/test.sh arg1 arg2'
end
cookbook_file "test.sh" do
path "/tmp/test.sh"
mode "755"
notifies :run, 'execute[run test]', :immediately
end
Notifications trigger when a resources updates, so any time the cookbook file changes, it will run the execute. Also you want to be using execute instead of bash or script because you are running a command rather than an inline script file.

Chef - execute vs bash resource

I used both the execute resource or the bash resource.
Both achieve the same result:
bash 'Execute my script' do
user 'root'
cwd '/mydir'
code <<-EOH
./myscript.sh
EOH
end
execute 'Execute my script' do
user 'root'
cwd '/mydir'
command './myscript.sh'
end
The only difference I see is that bash actually creates a shell script (named /tmp/chef-script#{date}{#id}) where code is written.
What is the best practice to execute a shell script with Chef between execute or bash resource ?
For a single script, use an execute. The bash resource is for including the script contents inline in the recipe code.
In bash & execute block we need to write code to catch the error as if you add more than one command & the chef takes the status of the last command.
To make it more clear - when the Bash/execute block has only one command chef catches the issue ,if the next command is successful then it takes the last command status.
bash 'Execute my script' do
user 'root'
cwd '/mydir'
code <<-EOH
./myscript.sh
ls srini ##This will fail
ls ## this will be successful
EOH
end
execute 'Execute my script' do
user 'root'
cwd '/mydir'
command './myscript.sh'
command 'ls srini' #will fail
command 'ls' # will be successful
end

Chef shell script not being run

I am using Chef on Scalarium to download an agent and run various commands on it. What I'm attempting is to write a shell script in the recipe to perform this.
file "/etc/profile.d/blah.sh" do
content <<-EOH
sudo -sH
<Retrieve file and run some commands>
EOH
end
When I run the recipe in Scalarium, no errors occur, but the commands aren't run either. There's no errors in the commands themselves, as I've run them on my computer.
The recipe is definitely read, as the Chef logs contain Processing file[/etc/profile.d/blah.sh] on blah.localdomain.
I've never used Chef before, do I need to do something else to tell it to execute the shell script?
Perhaps you want something like:
file "/etc/profile.d/blah.sh" do
mode 0500
content <<-EOH
sudo -sH
<Retrieve file and run some commands>
EOH
end
execute "/etc/profile.d/blah.sh"
Or, you can put the file retrieval and running of commands directly into your chef recipe:
remote_file "/path/to/where/the/file/should/be/saved" do
source "https://example.com/path/to/where/the/file/comes/from"
end
execute "first command"
execute "second command"

Resources