Check environment variable in Chef only-if guard - ruby

I am trying to add a conditional guard to a Chef recipe action, so that it only runs if a certain environment variable is set (CLEAN_DB).
execute "create databases" do
Chef::Log.info("Cleaning/creating the database")
command "psql --file #{node['sql-directory']}/sql/db_create.sql"
cwd node['sql-directory']
action :run
user node['postgresql-user']
only-if (ENV['CLEAN_DB'] == "create")
end
The line right before the end is giving me trouble. It gives the following syntax error:
...
SyntaxError
-----------
/home/centos/.jenkins/workspace/test1__Deploy/cache/cookbooks/recipes/app_linux.rb:157: syntax error, unexpected end-of-input, expecting keyword_end
...
I've tried all the following:
only-if ENV['CLEAN_DB'] == "create"
only-if { ENV['CLEAN_DB'] == "create" }
only-if '("#{ENV[\'CLEAN_DB\']}" == "create")'
only-if 'if [[ "$CLEAN_DB" == "create" ]] ; then echo 0 ; else echo 1; fi;'
only-if 'if [[ "$CLEAN_DB" == "create" ]] ; then echo 0 ; else echo 1; fi'
And none of them seem to work. The documentation (https://docs.chef.io/resource_common.html) seems to suggest that all I need is a shell script that outputs 0 (in the shell case, it gives an error about a string literal in the condition) or a Ruby block (shown in the examples between {}). I am out of ideas at this point.

If you would have correctly spelled it only_if, then you should at least have succeeded with the second try (of the last block).
The correct syntax would be:
execute "create databases" do
# Logging here makes IIRC no sense because it's emitted at compile time
# Chef::Log.info("Cleaning/creating the database")
command "psql --file #{node['sql-directory']}/sql/db_create.sql"
cwd node['sql-directory']
action :run
user node['postgresql-user']
only_if { ENV['CLEAN_DB'] == "create" }
end
Alternatively, you can use
only_if do ENV['CLEAN_DB'] == "create" end

Related

Failed to validate arguments being parsed to Expect Script

I am writing an Expect script for first time, and I would like to have a checker to validate user arguments. But I am getting errors as following, please do let me know if you know what has went wrong. Thanks
#!/usr/bin/expect
if {$argc != 1}
{
puts 'Insert IP address of tftp server'
exit 1
}
This is the erro
jeff#mymachine:/home/jeff~$ ./test.sh
wrong # args: no script following "$argc != 1" argument
while executing
"if {$argc != 1}"
(file "./test.sh" line 3)
Expect is based on Tcl. As your code is written, the Tcl parser does not know that the if statement is supposed to continue on the next line. You can fix this by writing either
if {$argc != 1} \
{
puts 'Insert IP address of tftp server'
exit 1
}
or
if {$argc != 1} {
puts 'Insert IP address of tftp server'
exit 1
}

Execute psql query in bash

I have a problem with executing a psql-query in a bash script.
Below is my code.
run_sql(){
sql_sel="$1;";#sql select
table=$2;#table name
for i in "${!GP_SERVER_NAMES[#]}"
do
logmsg "Executing [$sql_sel] on "${GP_SERVER_NAMES[$i]}": " $loglvl;
result_host[$i]=`${PSQL_HOST[$i]}${sql_sel}`;
#result_host[$i]=cleanresult "`$(${PSQL_HOST[$i]} "$tx_fix $sql_sel" 2>&1`");
if [[ `checkresult "${result_host[$i]}"` != 'true' ]]; then
logmsg "Error occured during sql select: Result for "${GP_SERVER_NAMES[$i]}" '${table}': ${result_host[$i]};" '1' '1';
raise_alarm "${GP_SYNC_SQL_ERR}" "${i}" "${table}" "${result_host}";
fi
logmsg "Result for" ${GP_SERVER_NAMES[$i]} " '${table}': ${result_host[$i]}";
done
final_result='true';
for i in "${!result_host[#]}"
do
if [[ `checkresult "${result_host[$i]}"` = 'true' ]]; then
final_result='false';
I am trying to executing the query on many different servers, with the following command
result_host[$i]=${PSQL_HOST[$i]}${sql_sel};
The above variables have the following meaning:
1. result_host[$i] : is an array that holds the i-th result of the sql query.
2. PSQL_HOST[$i] : is the command line psql-statement, including the IP address which is of the form
psql -t -q -h -U password -d database -c
3. $sql_sel : is the sql_statement
When I run the script, I get the following output.
scriptname.sh: line 168: SELECT: command not found
ERROR: syntax error at end of input
LINE 1: SELECT
^
Why is that? Any help, comments or suggestions would be appreciated.

How to get the real line number of a failing Bash command?

In the process of coming up with a way to catch errors in my Bash scripts, I've been experimenting with "set -e", "set -E", and the "trap" command. In the process, I've discovered some strange behavior in how $LINENO is evaluated in the context of functions. First, here's a stripped down version of how I'm trying to log errors:
#!/bin/bash
set -E
trap 'echo Failed on line: $LINENO at command: $BASH_COMMAND && exit $?' ERR
Now, the behavior is different based on where the failure occurs. For example, if I follow the above with:
echo "Should fail at: $((LINENO + 1))"
false
I get the following output:
Should fail at: 6
Failed on line: 6 at command: false
Everything is as expected. Line 6 is the line containing the single command "false". But if I wrap up my failing command in a function and call it like this:
function failure {
echo "Should fail at $((LINENO + 1))"
false
}
failure
Then I get the following output:
Should fail at 7
Failed on line: 5 at command: false
As you can see, $BASH_COMMAND contains the correct failing command: "false", but $LINENO is reporting the first line of the "failure" function definition as the current command. That makes no sense to me. Is there a way to get the line number of the line referenced in $BASH_COMMAND?
It's possible this behavior is specific to older versions of Bash. I'm stuck on 3.2.51 for the time being. If the behavior has changed in later releases, it would still be nice to know if there's a workaround to get the value I want on 3.2.51.
EDIT: I'm afraid some people are confused because I broke up my example into chunks. Let me try to clarify what I have, what I'm getting, and what I want.
This is my script:
#!/bin/bash
set -E
function handle_error {
local retval=$?
local line=$1
echo "Failed at $line: $BASH_COMMAND"
exit $retval
}
trap 'handle_error $LINENO' ERR
function fail {
echo "I expect the next line to be the failing line: $((LINENO + 1))"
command_that_fails
}
fail
Now, what I expect is the following output:
I expect the next line to be the failing line: 14
Failed at 14: command_that_fails
Now, what I get is the following output:
I expect the next line to be the failing line: 14
Failed at 12: command_that_fails
BUT line 12 is not command_that_fails. Line 12 is function fail {, which is somewhat less helpful. I have also examined the ${BASH_LINENO[#]} array, and it does not have an entry for line 14.
For bash releases prior to 4.1, a special level of awful, hacky, performance-killing hell is needed to work around an issue wherein, on errors, the system jumps back to the function definition point before invoking an error handler.
#!/bin/bash
set -E
set -o functrace
function handle_error {
local retval=$?
local line=${last_lineno:-$1}
echo "Failed at $line: $BASH_COMMAND"
echo "Trace: " "$#"
exit $retval
}
if (( ${BASH_VERSION%%.*} <= 3 )) || [[ ${BASH_VERSION%.*} = 4.0 ]]; then
trap '[[ $FUNCNAME = handle_error ]] || { last_lineno=$real_lineno; real_lineno=$LINENO; }' DEBUG
fi
trap 'handle_error $LINENO ${BASH_LINENO[#]}' ERR
fail() {
echo "I expect the next line to be the failing line: $((LINENO + 1))"
command_that_fails
}
fail
BASH_LINENO is an array. You can refer to different values in it: ${BASH_LINENO[1]}, ${BASH_LINENO[2]}, etc. to back up the stack. (Positions in this array line up with those in the BASH_SOURCE array, if you want to get fancy and actually print a stack trace).
Even better, though, you can just inject the correct line number in your trap:
failure() {
local lineno=$1
echo "Failed at $lineno"
}
trap 'failure ${LINENO}' ERR
You might also find my prior answer at https://stackoverflow.com/a/185900/14122 (with a more complete error-handling example) interesting.
That behaviour is very reasonable.
The whole picture of the call stack provides comprehensive information whenever an error occurs. Your example had demonstrated a good error message; you could see where the an error actually occurred and which line triggered the function, etc.
If the interpreter/compiler can't precisely indicate where the error actually occurs, you could be more easily confused.

checking a bash command in a if statement in Ruby

How can I check the return value (true/false) of bash command in an if statement in Ruby. I want something like this to work,
if ("/usr/bin/fs wscell > /dev/null 2>&1")
has_afs = "true"
else
has_afs = "false"
end
It complains with the following error meaning, it will always return true.
(irb):5: warning: string literal in condition
What's the correct syntax ?
UPDATE :
/usr/bin/fs wscell
looks for afs installed and running condition. It will throw a string like this,
This workstation belongs to cell <afs_server_name>
If afs is not running, the command exits with status 1
You want backticks rather than double-quotes. To check a program's output:
has_afs = `/usr/bin/fs wscell > /dev/null 2>&1` == SOMETHING ? 'true' : 'false'
Where SOMETHING is filled in with what you're looking for.
You should probably use system() or Backticks and then check the exit status of the command ($?.exitstatus):
Heres a good quicktip read: http://rubyquicktips.com/post/5862861056/execute-shell-commands)
UPDATE:
system("/usr/bin/fs wscell > /dev/null 2>&1") # Returns false if command failed
has_afs = $?.exitstatus != 1 # Check if afs is running

How can you check to see if a file exists (on the remote server) in Capistrano?

Like many others I've seen in the Googleverse, I fell victim to the File.exists? trap, which of course checks your local file system, not the server you are deploying to.
I found one result that used a shell hack like:
if [[ -d #{shared_path}/images ]]; then ...
but that doesn't sit well with me, unless it were wrapped nicely in a Ruby method.
Has anybody solved this elegantly?
In capistrano 3, you can do:
on roles(:all) do
if test("[ -f /path/to/my/file ]")
# the file exists
else
# the file does not exist
end
end
This is nice because it returns the result of the remote test back to your local ruby program and you can work in simpler shell commands.
#knocte is correct that capture is problematic because normally everyone targets deployments to more than one host (and capture only gets the output from the first one). In order to check across all hosts, you'll need to use invoke_command instead (which is what capture uses internally). Here is an example where I check to ensure a file exists across all matched servers:
def remote_file_exists?(path)
results = []
invoke_command("if [ -e '#{path}' ]; then echo -n 'true'; fi") do |ch, stream, out|
results << (out == 'true')
end
results.all?
end
Note that invoke_command uses run by default -- check out the options you can pass for more control.
Inspired by #bhups response, with tests:
def remote_file_exists?(full_path)
'true' == capture("if [ -e #{full_path} ]; then echo 'true'; fi").strip
end
namespace :remote do
namespace :file do
desc "test existence of missing file"
task :missing do
if remote_file_exists?('/dev/mull')
raise "It's there!?"
end
end
desc "test existence of present file"
task :exists do
unless remote_file_exists?('/dev/null')
raise "It's missing!?"
end
end
end
end
May be you want to do is:
isFileExist = 'if [ -d #{dir_path} ]; then echo "yes"; else echo "no"; fi'.strip
puts "File exist" if isFileExist == "yes"
I have done that before using the run command in capistrano (which execute a shell command on the remote server)
For example here is one capistrano task which will check if a database.yml exists in the shared/configs directory and link it if it exists.
desc "link shared database.yml"
task :link_shared_database_config do
run "test -f #{shared_path}/configs/database.yml && ln -sf
#{shared_path}/configs/database.yml #{current_path}/config/database.yml ||
echo 'no database.yml in shared/configs'"
end

Resources