Shell Script to Upgrade Ruby Enterprise Edition while Maintaining Directory Naming Sanity
As you're likely already aware, a denial of service (DoS) vulnerability in Ruby's BigDecimal library was uncovered, fixed and reported on June 9, 2009. Patching options include:
- Replacing your current ruby installation with a patched version
- Installing a gem to patch the code and then updating your application to load the patch.
The latter approach only postpones the inevitable, so I opted for the former. If you're using the debian packages, it's a simple call to aptitude update. However, if you've installed from source, this presents a few challenges.
The upgrade is actually quite straightforward. According to the REE Documentation, you simply need to run the new installer over top of your previous installation. This includes specifying the exact same directory name that you used to install the previous version.
As you may know, REE installs by default into /opt/ruby-enterprise-1.8.6-<year_month_day_release>. However, after completing this, you're left with a directory name that doesn't reflect what's actually installed in there. If you've a little neurotic like I am, you're going to want to rename that directory. However, be careful. Your Passenger configuration is likely pointing to the directory that you want to rename, so you'll have to remember to update your apache/nginx configuration to reflect this.
When we install REE we create a symlink at /opt/ruby which points to the version of ruby that we want passenger to run. This allows us to quickly swap in/out different versions of ruby for testing/upgrading/etc.
Considering we have dozens of client applications running on distinct servers each with their own versions of REE installed, I decided to write a script to minimize the manual effort required to upgrade several production servers.
#!/bin/sh
# Author: John Trupiano
# Script to upgrade an REE installation on a hot server and maintain sane directory names
if ["$(whoami)" != "root"]; then
echo "You need to be root to run this!"
exit 2
fi
RF_RELEASE=58677
REE_VERSION=20090610
REE=ruby-enterprise-1.8.6-$REE_VERSION
URL=http://rubyforge.org/frs/download.php/$RF_RELEASE/$REE.tar.gz
# Determine what the most recent version of REE is that is installed
MOST_RECENT_REE_VERSION=`ls /opt | awk -F"-" '$4 > max && $2 == "enterprise" { max=$4; maxline=$0 }; END { print max }'`
MOST_RECENT_REE=ruby-enterprise-1.8.6-$MOST_RECENT_REE_VERSION
WORKING_DIR=/root/src
echo "Going to update $MOST_RECENT_REE to $REE"
echo "Back up previous release"
cp -R /opt/$MOST_RECENT_REE /opt/$MOST_RECENT_REE.bak
echo "Download new release"
mkdir -p $WORKING_DIR
cd $WORKING_DIR && wget $URL
echo "Untar and install over the previous release for 'upgrade' according to REE manual"
tar xzf $REE.tar.gz
./$REE/installer --auto /opt/$MOST_RECENT_REE
echo "Shuffle folder names to remain sane"
mv /opt/$MOST_RECENT_REE /opt/$REE && mv /opt/$MOST_RECENT_REE.bak /opt/$MOST_RECENT_REE
echo "Actually symlink in the new version of REE"
rm /opt/ruby && ln -s /opt/$REE /opt/ruby
echo "Clean up after ourselves"
rm -f $WORKING_DIR/$REE
Simply upload this script to your server, su - up to root and run it. Then restart apache/nginx so that passenger will pick up the new version of ruby. All of your gems will be maintained, and your most recent version of REE will remain in tact at /opt/ruby-enterprise-1.8.6-<old_version> in case you need it again.