Caius Theory

Now with even more cowbell…

Find dependencies blocking rails upgrades

The initial pain point when upgrading a rails app is figuring out which of your dependencies are blocking you upgrading the actual rails gem (& immediate dependencies, actionpack, etc.). One way to start this is to update the rails dependency in your Gemfile and run bundle update rails. Then check the error output (it never works first time…) to see which gems are blocking the upgrade. Repeat, rinse until it works.

I figured I'd cheat a little and eyeball the Gemfile.lock to see which gems had an explicit dependency pinning rails (or actionpack, activejob, etc) to a version lower than I want to upgrade to, so I could get an idea of what needs to be upgraded without having to do them all one-by-one.

Then instead of eyeballing Gemfile.lock, I wrote an awk script to pull out the interesting dependencies (ie, anything that depends on rails gems) so I just have to check which versions they depend on by hand.

# Reads a Gemfile.lock and outputs all dependencies that depend on rails

  parent = 0
  parent_printed = 0
  rails_gems = "^(rail(s|ties)|action(mailer|pack|view)|active(job|model|record|support))$"

# We only want the specs from the GEM section
NR == 1, $1 ~ /GEM/ { next }
$1 == "" { exit }

# Skip parent gems we don't care about (rails itself…)
$0 ~ /^ {4}[^ ]/ &&
$1 ~ rails_gems {
  parent = 0
  parent_printed = 0

# Parent gems that aren't part of rails core
# Store the name to be printed if we match below
$0 ~ /^ {4}[^ ]/ {
  parent = $0
  parent_printed = 0

# If the nested gem (6 space prefix) matches rails-names and we have a parent value
# set then we print them out - making sure to only print the parent once
$0 ~ /^ {6}[^ ]/ &&
$1 ~ rails_gems &&
parent != 0 {
  if (parent_printed == 0) {
    parent_printed = 1
    print parent

  print $0

Run it against your Gemfile.lock for the app you're upgrading:

awk -f rails5.awk Gemfile.lock

And you'll get output like this, to run through and see if any of the dependencies are pinning to lower versions than you need.

    coffee-rails (4.0.1)
      railties (>= 4.0.0, < 5.0)
    factory_girl (4.4.0)
      activesupport (>= 3.0.0)
    factory_girl_rails (4.4.1)
      railties (>= 3.0.0)
    globalid (0.3.7)
      activesupport (>= 4.1.0)
    google-api-client (0.8.6)
      activesupport (>= 3.2)
    jquery-rails (3.1.4)
      railties (>= 3.0, < 5.0)
    jquery-ui-rails (5.0.5)
      railties (>= 3.2.16)
    rails-deprecated_sanitizer (1.0.3)
      activesupport (>= 4.2.0.alpha)
    rails-dom-testing (1.0.7)
      activesupport (>= 4.2.0.beta, < 5.0)
    rspec-rails (3.4.2)
      actionpack (>= 3.0, < 4.3)
      activesupport (>= 3.0, < 4.3)
      railties (>= 3.0, < 4.3)
    sass-rails (4.0.5)
      railties (>= 4.0.0, < 5.0)
    sprockets-rails (2.3.3)
      actionpack (>= 3.0)
      activesupport (>= 3.0)

In this case, I'm trying to take this app to rails 5.0, so all the ones specifying < 5 and < 4.3 need upgrading beforehand.

SmartOS Recovery mount /usbkey

Recently I managed to hose a box in a perfectly self-inflicted storm of idiocy. Imagine a SmartOS server with the following issues:

Needless to say, this caused a tiny issue in the server doing what it's supposed to. Luckily I had access to a KVM remote console for the box and the following worked.

I brought the machine up, choosing the second option for recovery at the grub menu. Waited for a login prompt, then logged in with root/root.

Realised quite quickly that /usbkey must be persisted on the zones zfs pool otherwise the configuration would be lost after shutdown, so imported the correct pool, created a directory to mount into and then mounted the zfs share.

zpool import zones
mkdir /usbkey
mount -F zfs zones/usbkey /usbkey

Much Wenlock Triathlon 2016

500m Pool Swim: 00:11:34

Felt good setting off, within a couple of lengths felt like I had no energy. Didn’t have enough to eat/drink during the morning before starting, really makes a huge difference once you set off. Must remember to replace my goggles at some point too, the strap comes loose without warning.

Transition 1: 00:03:06

Rough time for transition. Legs felt good as soon as I was out the pool. Got my kit on without too much trouble, although taking a t-shirt style cycling top instead of a full zip jacket was a mistake—forgot I’d be putting it on wet. Had a gel in transition in the hope it would give me some energy.

19km Cycle: 00:48:03

Lovely start to the cycle route, massive downhill on recently resurfaced roads for the most part. Best part of the cycle ride, with the rest of the route consisting of climbing back up to the start. Couple of nasty steep hills I had to walk sections of, for all I fitted lower gears they still weren't low enough given my lack of bike fitness currently.

Transition 2: 00:06:57

Bike racked easily, shoes swapped, gel necked and a couple of Lucozade bottles clutched and off to run. Took on another gel.

7km Run: 01:04:37

Needed all the fluid I carried, didn't take anything else during the run and felt no less energetic than the other disciplines. Legs didn't really hurt, just felt like lead and had no power. Route was basically a mini trail run then up a tarmac road to the turning point then head back via trail run to finish. Adopted a walk/run approach.

Total: 02:14:17

Certainly the hardest triathlon I've done, and I'm probably at my least fittest compared to any of the others I've done to boot. The location was lovely, the weather was pretty decent (sunny but not too hot). Don't feel like I was beaten by it, but definitely haven't given it my best. One to redo next year and train towards.

Setup DHCP interface in FreeBSD

Given a FreeBSD instance without a configured network interface that you'd like to configure, first check what the name of the interface you want to configure is with ifconfig. (Mine is em0 in this instance.)

Then we need to add the configuration telling services that we want to use DHCP for this interface, and setting up our default router (use your IP, not mine!) too:

cat >> rc.conf <<CONF

And then we need to start dhclient on the given interface:

service dhclient start em0

Hey presto, you should see dhclient finding a DHCP server and being handed an IP address for em0.

SoundCloud RSS Feeds

SoundCloud appears to have gained popularity in recent times for hosting podcasts on. As a consumer of their service they're pretty good at everything except having a visible RSS feed on a profile page for a show! If I want to listen to a show in my podcast app of choice, an RSS feed is the easiest way for me to achieve that.

Turns out SoundCloud do have RSS feeds, they're just well hidden and unfindable from the profile page itself. Thankfully, you can construct the URL for it from information on the profile page, and here's a bookmarklet that will do it for you:


Or as a handy link to copy to your bookmarks bar. Simply click/run that when on a SoundCloud profile page and you'll be taken to the RSS Feed URL.

exec(3) in Go

Using exec(3) from Go is simple enough, once you figure out to look in the syscall package and how to pass arguments to the new command.

As a simple example, I'm going to exec /bin/echo with a hardcoded string from the go binary. The program built here is in the gecho (Gecko, geddit?) git repo, which each stage as a commit.

In our main function lets setup some variables we're going to need for arguments to syscall.Exec:

func main() {
  cmdPath := "/bin/echo"
  cmdArgs := []string{"Hello", "World"}
  cmdEnv := []string{}    

(We could use os.Environ() for cmdEnv to take the ENV from the go binary, but we don't require anything from the environmnt here so it doesn't matter that we aren't.)

Now we have the arguments for syscall.Exec, lets add that in and see what happens:

err := syscall.Exec(cmdPath, cmdArgs, cmdEnv)
if err != nil {

And running the file (go run gecho.go compiles & runs for us) gives the following output:


Err, say what now? Where's "Hello" gone?!

Took me a while to figure this out when I originally ran into this. The answer is staring us right in the face if we go look at the syscall.Exec docs. Lets have a look at the function signature, argument names and all:

func Exec(argv0 string, argv []string, envv []string) (err error)

Hmm. The first argument is argv0 (and a string), rather than binaryPath or something similar. The second argument is then argv and an array of strings.

At this point I remember that the first element of argv in other runtimes is the name of the binary or command invoked - $0 in a bash script is the name of the script for example.

The answer is simple. cmdArgs in our script should have /bin/echo as the first element, and then we pass cmdArgs[0], cmdArgs as the first two arguments to syscall.Exec. Lets give that a go:

func main() {
  cmdArgs := []string{"/bin/echo", "Hello", "World"}
  cmdEnv := []string{}

  err := syscall.Exec(cmdArgs[0], cmdArgs, cmdEnv)
  if err != nil {

And running it (go run gecho.go remember) gives the expected output:

Hello World

Excellent. Now I just need to remember argv contains the command name as argv[0] and we're golden.

There is also the os/exec package in the stdlib, which is intended for executing other binaries as child processes from what I can tell. Tellingly, when you create a exec.Cmd struct with exec.Command() you give it the name as first argument, and args as following arguments. Then it has the following snippet in the documentation:

The returned Cmd's Args field is constructed from the command name followed by the elements of arg, so arg should not include the command name itself. For example, Command("echo", "hello")

So cmd := exec.Command("echo", "hello"); cmd.Args would return []string{"echo", "hello"} - which is recognisable as what we have to pass to syscall.Exec!

Changing hostname in SmartOS Zone

Given a non-global zone in SmartOS that we want to change the hostname of, we need to make sure to edit the following files to change it:

A quick way to do that is with sed (renaming "fred" to "beth" here):

sed -e 's/fred/beth/g' -i /etc/hosts /etc/nodename

Then shutdown & start the zone (from my testing a restart doesn't apply it).

Install Rubinius on OS X

Using ruby-install, homebrew building it for use with chruby, here's how I install Rubinius under Yosemite (works for Mavericks as well.)

  1. Make sure llvm is installed

     $ brew install llvm
  2. Prepend the homebrew-installed llvm tools to your path

     $ export PATH="$(brew --prefix llvm)/bin:$PATH"
     # Or, for ZSH
     $ path=( $(brew --prefix llvm)/bin $path )
  3. Install rubinius, v2.3.0 at the time of writing

     $ ruby-install rbx 2.3.0
  4. Open a fresh shell once that's built, and you should be able to switch to rbx!

     $ chruby rbx
     $ ruby -v
     rubinius 2.3.0 (2.1.0 9d61df5d 2014-10-31 3.5.0 JI) [x86_64-darwin14.0.0]

There is also a homebrew tap for rubinius which should also work instead of the above. I couldn't get it working on one of my laptops though, which is why I was installing by hand using the above instead. The tap is at and explains install steps.

Changing root password in global zone

SmartOS mounts /etc/shadow from /usbkey/shadow so we can change the root password for the global zone after install. Here's how:

  1. Fire up a console or ssh session as root in the global zone
  2. Check the existing permissions on the file

     $ ls -l /usbkey/shadow
     -r--------   1 root     root         560 Oct 19 16:45 /usbkey/shadow
  3. Make the file writable

     $ chmod 600 /usbkey/shadow
  4. Fire up vi to edit the file

     $ vi /usbkey/shadow
  5. Edit the line containing root to change the crypted password. See shadow(4) if you need help with the format of /etc/shadow & use /usr/lib/cryptpass to generate a hash for the password you desire. (Remember to clean the bash history!)

  6. Save the file and exit vi

  7. Make the file readonly again

     $ chmod 400 /usbkey/shadow
  8. Double check permissions are correct on the file again

     $ ls -l /usbkey/shadow
     -r--------   1 root     root         560 Oct 19 16:49 /usbkey/shadow

Job done. Verify by logging in as root (invoking /usr/bin/login from an ssh session makes this easy to verify.)

Compiling SmartOS for AMD processors

There's a few community-provided patches for SmartOS that enable KVM on AMD processors amongst other things, and given the HP Microserver has an AMD processor, that's quite useful for turning it into a better lab server. The main list of so called "eait" builds was hiccuping when I tried to download the latest, and all I could find was a 20140812T062241Z image here.

The source code for the eait builds is maintained at, and you can see the patches applied on top of the normal SmartOS master by going to

So here's how to use SmartOS to compile a more up to date AMD-friendly Smartos!

  1. Grab the latest multiarch SmartOS image (which has to be used, or the compile will fail.) The latest at the time of writing was 4aec529c-55f9-11e3-868e-a37707fcbe86, so that's what I'll use.

     imgadm import 4aec529c-55f9-11e3-868e-a37707fcbe86
  2. Spin up a zone for us to build in (the Building SmartOS on SmartOS page has extra info about this):

     echo '{
       "alias": "platform-builder",
       "brand": "joyent",
       "dataset_uuid": "4aec529c-55f9-11e3-868e-a37707fcbe86",
       "max_physical_memory": 32768,
       "quota": 0,
       "tmpfs": 8192,
       "fs_allowed": "ufs,pcfs,tmpfs",
       "maintain_resolvers": true,
       "resolvers": [
       "nics": [
           "nic_tag": "admin",
           "ip": "dhcp",
           "primary": true
       "internal_metadata": {
         "root_pw": "password",
         "admin_pw": "password"
     }' | vmadm create
  3. Login to the created zone:

     zlogin <uuid from `vmadm create` output>
  4. Update the image to the latest packages, etc:

     pkgin -y update && pkgin -y full-upgrade
  5. Install a few images we'll need to compile & package SmartOS:

     pkgin install scmgit cdrtools pbzip2
  6. Grab the source code of the fork containing the patches we want, from arekinath/smartos-live

     git clone
     cd smartos-live
  7. Optional: Edit src/Makefile.defs and change PARALLEL = -j$(MAX_JOBS) to PARALLEL = -j8 to do less at once. (Microserver only has a dual core CPU!)

  8. Copy the configure definition into the right place and start configuration:

     cp {sample.,}configure.smartos

    (You'll probably get asked to accept the java license during configuration, so keep half an eye on it)

  9. Once configure has completed (which doesn't take too long, 15 minutes or so), start building:

     gmake world && gmake live
  10. Once the build is successfully finished, time to package an iso & usb image:

    export LC_ALL=C

Hey presto, you've a freshly built AMD-friendly SmartOS build to flash to a USB key / put on your netboot server and boot your Microserver from!