Serverspec doesn't check package version correctly - ruby

I have a problem with serverspec. I'm trying to check installed package version on ubuntu.
I use this code:
describe 'java packages' do
it 'package openjdk-9-jre should be installed with the correct version' do
expect(package('openjdk-9-jre')).to be_installed.with_version('9~b114-0ubuntu1')
end
end
Serverspec run dpkg-query command to check package but escapes tilda character and it doesn't work.
serverspec runs:
dpkg-query -f '${Status} ${Version}' -W openjdk-9-jre | grep -E '^(install|hold) ok installed 9\\~b114-0ubuntu1$'
instead of
dpkg-query -f '${Status} ${Version}' -W openjdk-9-jre | grep -E '^(install|hold) ok installed 9~b114-0ubuntu1$'
How can I fix this problem?

The problem is here: https://github.com/mizzy/specinfra/blob/92ccc19714ead956589127c40e3cd65faf38cb8b/lib/specinfra/command/debian/base/package.rb#L6.
Specinfra is escaping the characters in the with_version chain as #{Regexp.escape(escape(version))} instead of #{Regexp.escape(version)). This would require a PR to Specinfra to fix due to the Specinfra/Serverspec contribution policy. I can put this on my list of things to do and notify you when finished, since I keep an up-to-date Specinfra fork around and am a contributor to both so I know the codebase.
In the meantime, you would have to do a command matcher workaround.
describe 'java packages' do
it 'package openjdk-9-jre should be installed with the correct version' do
describe command("dpkg-query -f '${Status} ${Version}' -W openjdk-9-jre") do
its(:stdout) { is_expected.to match('^(install|hold) ok installed 9\~b114\-0ubuntu1$') }
end
end
end
Specinfra PR: https://github.com/mizzy/specinfra/pull/608

Related

Debian: "command -v <command>" still returns path after removing the package?

I've uninstalled the stylus package on my Debian by sudo apt-get remove --purge node-stylus.
Now it says when I try to run the stylus command: stylus: command not found. So it works as it should.
But in my scripts I check whether Stylus is installed or not by:
if ! command -v sudo stylus &> /dev/null; then
echo "ERROR: Stylus is not installed!"
exit 1
fi
And for some reason command -v stylus still returns /usr/bin/stylus thus the script won't fail.
I checked /usr/bin/ and there is no stylus there.
Could someone explain to me please why does this work like this?
Bash maintains a cache for lookups; you want to do
hash -r stylus
to force it to forget the old value.
Separately, of course, don't use command -v sudo when you actually want command -v stylus, as already pointed out in a comment.

Installing cpanm in a bash script

I'm writing a script that installs and configures Nagios to my requirements. It requires cpanm and some perl modules.
It's using the step/try/next function from here: https://stackoverflow.com/a/5196220
step "Downloading cpanm installer"
try `wget -q http://cpanmin.us -O $swrepo/cpanm.install`
next
step "Installing cpanm"
try echo '{ exec </dev/tty; cat $swrepo/cpanm.install | perl - App::cpanminus; }' | bash
# try bash -c "$(cat $swrepo/cpanm.install | perl - App::cpanminus)"
# try cat $swrepo/cpanm.install | perl - App::cpanminus
next
step "Installing Perl module Nagios Config"
try `cpanm Nagios::Config`
next
My problems here are:
whichever way I attempt to run the install for cpanminus, it fails the script, and won't install properly. I can't seem to make it function outside of the step/try/next functions (not that I want it to.)
The cpanm command fails too. If I isolate and run only this part of the script, it still fails, with "cpanm command not found." I can run it manually at the command line.
Any pointers for the slightly frustrated?
Update
I pulled the cpanm setup out to a separate file:
step "Installing cpanm"
try sh conf_cpanm.sh
next
Which works, and I'll probably try and pull it back in at a later date, but so far that functions. So it can stay.
However, doing the same for
try cpanm Nagios::Config
won't work. The file looks like this:
#!/bin/bash
cpanm Nagios::Config
...and if I run that by calling sh conf_nagcpanm.sh it works fine.
I think using backticks
try `cpanm Nagios::Config`
is a mistake. bash will take an expression in backticks, execute it, and substitute the output of the command for the expression. The output of cpanm is not going to be shell commands, so this will not work. It should simply be
try cpanm Nagios::Config

Automatic RVM installation

I am trying to have RVM and ruby installed in an Ubuntu 12 virtual machine without human interaction apart from the password prompts.
I created a shell script to do this that works pretty fine until I need to use RVM itself.
I am using multi-user installation.
#!/bin/bash -l
mainUser=`whoami`
echo "Installing as '${mainUser}'"
echo "Installing git..."
sudo -S apt-get install --yes curl git-core
echo "Installing RVM..."
\curl -L https://get.rvm.io | sudo bash -s stable
echo "Adding ${mainuser} to RVM group..."
sudo adduser $mainUser rvm
newgrp rvm
From here things get weird.. I need to load dvm as a source. I want both my script to have this source and my user's bash_profile / bashrc. Anyway.. I know how to do it manually, but I can't have this done from the script. This is the last code I tried:
. "/usr/local/rvm/bin/rvm"
rvm use ruby-head
rubyVersion=`rvm list | awk '/ruby-head/{print x;print};{x=$0}' | sed -n '/ruby-head/{g;1!p;};h' | awk -F ' ' '{print $1}'`
rubyTest=${rubyVersion}#test
rvm use $rubyTest --create --default
The error I get is this:
test.sh: 7: /usr/local/rvm/bin/rvm: Syntax error: "(" unexpected (expecting "fi")
If I simply try to use the full path, like this:
rvm=/usr/local/rvm/bin/rvm
$rvm use ruby-head
rubyVersion=`$rvm list | awk '/ruby-head/{print x;print};{x=$0}' | sed -n '/ruby-head/{g;1!p;};h' | awk -F ' ' '{print $1}'`
rubyTest =${rubyVersion}#test
$rvm use $rubyTest --create --default
I get this error instead:
RVM is not a function, selecting rubies with 'rvm use ...' will not work.
I am clueless. Why can't I use /usr/local/rvm/bin/rvm?
Is there a way to execute source /etc/profile.d/rvm.sh for this user from the script?
I am not so good at shell script and Linux, so I appreciate any references and examples you could give.
Thanks!
UPDATE
I also tried:
source "/usr/local/rvm/scripts/rvm"
... and all it's variants. Same error: "RVM is not a function".
rvm is actually implemented as a shell function rather than an executable, which is why you can't just call /usr/local/rvm/bin/rvm itself.
Quoting you, "Is there a way to execute source /etc/profile.d/rvm.sh for this user from the script?"
Have you tried doing that? I had a somewhat similar install once where it didn't work properly from crontab (they have instructions on the site for that scenario, but we couldn't make them work), and I had to do almost exactly that -- source part of the profile.d for rvm.
RVM is not a function, selecting rubies with 'rvm use ...' will not work.
Above error is generated when rvm is not running and hence your terminal is not able to recognize it, as it tries to run it as a system command.
You may want to try this to run rvm through your shell script before calling rvm methods:
source ~/.rvm/scripts/rvm
I found out what is causing the issue.
I realised before that I would get errors in the lines with conditions in the script files, so I came across this page:
https://superuser.com/questions/552016/bash-script-not-found
As it happens, I was executing the script with the following command:
sh script.sh
Which means I was getting Dash instead of Bash.
To fix the issue I changed my code to have this:
PATH=$PATH:/usr/local/rvm/bin # Add RVM to PATH for scripting
[[ -s "/usr/local/rvm/scripts/rvm" ]] && . "/usr/local/rvm/scripts/rvm" # Load RVM function
And then I executed like this:
bash script.sh
And voilĂ ... RVM works again!

How to access secondary travis logs

I know the primary travis build logs are available on the web and with the logs command in the travis command line client, but I was wondering if there is a way to access other files generated as part of the build process, such as the file /home/travis/.rvm/log/1391454748_rbx-2.2.4/rubygems.install.log referenced in https://travis-ci.org/rspec/rspec-its/jobs/18148204
Those files are lost once the build is finished. If you want to read them, you should add a cat command to print out to the log you see.
before_script: cat /home/travis/.rvm/log/*_rbx-2.2.4/rubygems.install.log
If the install command is failing, then you should override install to install the gem for which the installation is failing:
install: gem install XXX || cat /home/travis/.rvm/log/*_rbx-2.2.4/rubygems.install.log
banzaiman's answer is good (it helped me!). But if you use:
install: gem install XXX || cat /home/travis/.rvm/log/*_rbx-2.2.4/rubygems.install.log
then the cat command will likely succeed, and so the line above will count as as success, and the build will continue. If you want the build to fail when the install fails, then you need to make sure the line has a non-zero exit status. So do something like this:
install: gem install XXX || { cat /home/travis/.rvm/log/*_rbx-2.2.4/rubygems.install.log && 1; }
The expression in curly braces will be run only if the gem install XXX fails (i.e., has a non-zero exit status). cat will presumably succeed, so the command after the && will be run. That 1 ensures a non-zero exit status for the whole line, causing the build to stop at that point.
Note the necessary whitespace around the curly braces.

Drop database of Rails application from Ruby script, outside of application

I need to run 'rake db:drop' console command from ruby script (outside of Rails app). Generally it looks simple:
system("cd /my/path && rake db:drop")
But the problem is I'm under rvm hell, and when I'm doing cd /my/path it doesn't load correct gemset and environment, that's why as result I have
Could not find activesupport-3.1.12 in any of the sources
Run `bundle install` to install missing gems
Additionally, I have open connections problem, as Rails application is running.
How it could be done?
(my global task is to close existing connections to the database (we may have some, as I need to drop running rails app database), drop it, and then recreate again)
Thank you!
To kill active connections, I use this in a rake task. That should take care of one problem.
task :kill_postgres_connections => :environment do
db_name = "#{File.basename(Rails.root)}_#{Rails.env}"
sh = <<EOF
ps xa \
| grep postgres: \
| grep #{db_name} \
| grep -v grep \
| awk '{print $1}' \
| xargs kill
EOF
puts `#{sh}`
end
task "db:drop" => :kill_postgres_connections
To drop PostgreSQL db in Rails 4 you can also patch ActiveRecord drop method. Based on https://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-rails-4/
# config/initializers/postgresql_database_tasks.rb
module ActiveRecord
module Tasks
class PostgreSQLDatabaseTasks
def drop
establish_master_connection
connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{configuration['database']}' AND state='idle';"
connection.drop_database configuration['database']
end
end
end
end

Resources