System Provisioning with Vagrant

3 minute read

In my last blog post, I explained how to build a Vagrant box for PS2 development from scratch. While the steps to create such a virtual machine are quite simple and only take about 20 minutes to complete, our innate desire to automate things tells us that entering all those commands for every new Ubuntu release is probably a bad idea. Fortunately, Vagrant really shines at setting up virtual machines with everything we need – in an automated and repeatable way – through the use of provisioners.

At the point of writing, Vagrant supports five different provisioners out of the box:

  • Chef Solo
  • Chef Server
  • Puppet Standalone
  • Puppet Server
  • Shell

For this post, I’ve chosen the shell provisioner to introduce you to the wonderful world of system provisioning. In particular, I’ll show you how to create a VM for PS2 development (yes, again!); only that this time we’re going the lazy, fully automated way.

The Very Basic Basics

Here are a handful of things you need to know about provisioning with Vagrant:

  • In the Vagrantfile, the configuration directive config.vm.provision is used to enable one or more provisioners for the VM.
  • The command vagrant up creates and boots the VM, and then runs all provisioners. The latter can be skipped with --no-provision.
  • When the VM is already running, vagrant provision can be used to just run the provisioners.
  • In case you screwed up your VM and want to start all over again:
    vagrant destroy --force && vagrant up

(You may want to read the full documentation for all the dirty details.)

The Shell Provisioner

Of all the provisioners that come with Vagrant, the shell provisioner is the most basic and simplest one. It allows to upload and execute a shell script as the root user in the VM. So if you’re familiar with shell scripting, you already have what it takes to set up custom virtual machines with it.

Now let’s put the shell provisioner to use. First off, wrap up all commands required to create a Vagrant box for PS2 development into a single script. The result, ps2dev.sh, can be seen below. Read the code and maybe the few comments describing what’s going on:

#!/bin/sh

set -e # Exit script immediately on first error.
set -x # Print commands and their arguments as they are executed.

# Abort provisioning if toolchain is already installed.
# This checks for basic PS2 tools and the SDK.
which ee-gcc iop-gcc ps2client >/dev/null &&
test -d /usr/local/ps2dev/ps2sdk &&
{ echo "PS2 toolchain already installed."; exit 0; }

# Update Debian package index.
sudo apt-get update -y

# Install required Debian packages.
sudo apt-get install -y gcc git-core make patch wget

# Clone, build, and install toolchain.
tmp_dir="/tmp/toolchain"
sudo rm -rf "$tmp_dir"
git clone --depth 1 git://github.com/ps2dev/ps2toolchain.git "$tmp_dir"
( cd "$tmp_dir"; sudo ./toolchain-sudo.sh )

# Clean up build products.
sudo rm -rf "$tmp_dir"

# Set up environment variables, adding the new tools to PATH.
sudo sh -c "cat > /etc/profile.d/ps2dev.sh" <<'EOF'
export PS2DEV=/usr/local/ps2dev
export PS2SDK=$PS2DEV/ps2sdk
export PATH=$PATH:$PS2DEV/bin:$PS2DEV/ee/bin:$PS2DEV/iop/bin:$PS2DEV/dvp/bin:$PS2SDK/bin
EOF

That script will actually transform any recent Debian or Ubuntu OS into a full-fledged PS2 development environment. You may call it multiple times, but the provisioning process is stopped immediately in case the toolchain is already installed (lines 8-10).

With ps2dev.sh in our hands, the only thing left is to tell Vagrant about it. As mentioned earlier, that’s what config.vm.provision is for:

# Vagrantfile
Vagrant::Config.run do |config|
  config.vm.box = "lucid64"
  config.vm.box_url = "http://files.vagrantup.com/lucid64.box"
  config.vm.provision :shell, :path => "ps2dev.sh"
end

The next time you bring up the VM, the provisioner will dutifully upload and execute our script:

$ vagrant up
[default] Importing base box 'lucid64'...
...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Mounting shared folders...
[default] -- v-root: /vagrant
[default] Running provisioner: Vagrant::Provisioners::Shell...
output of ps2dev.sh ...

$ vagrant ssh
vagrant@lucid64:~$ which ee-gcc
/usr/local/ps2dev/ee/bin/ee-gcc

Last but not least, let’s prove that the toolchain isn’t built twice, saving us a lot of time:

$ vagrant provision
[default] Running provisioner: Vagrant::Provisioners::Shell...
...
PS2 toolchain already installed.

Works.

Wrapping Up

To be honest, this blog post was supposed to be much longer. I also wanted to tell you about Chef – the awesome systems integration framework by Opscode – and the merits of Vagrant’s Chef Solo provisioner. I, however, decided to to leave it at that and delight you with more in the future, so be sure to check back later on.

If there’s anything I want you to take away from this, it’s that provisioning with Vagrant is simple and fun – go and try it yourself!

Updated: