Tutorial: Setting up a Vagrant dev environment with a script in OS X

Vagrant Vagrant is a fantastic development tool: however, when we started developing our ad server AwesomeAds, it started to become a problem.

I am writing this with 10 VMs running, which is what is required in order to have the full test environment for AwesomeAds running locally. Each of these machines ideally wants their own terminal window so they can be restarted independently as well as execute any scripts that may need to be run during the process of development. On top of that, the machines want to be started in a specific order.

Put both of these together and it’s a complete pain to start up the dev environment in the morning. However we are programmers – we solve problems. I are programmer

Scripting this start up process in Bash is actually a rather simple process. I fully recommend anyone with a similar development stack to write similar scripts for their own environment (if you haven’t already) as it saves so much time every morning. Simply turn on the computer, run the script, get a coffee and come back to your dev environment all set up and ready to work. No fiddling with terminal tabs or other things that aren’t programming, so you can get straight to the problem solving.

To start off, we are going to be using ttab to handle the terminal windows, which requires we have node.js installed. If you don’t want to install ttab you can use other programs to handle this such as xterm. However, for this tutorial we will use ttab.

Without further delay, the script:

#!/bin/bash -ex

echo "need admin permissions"

sudo echo "lets do this"

command -v ttab >/dev/null 2>&1 || sudo npm install -g ttab

path=`pwd`

dir[1]="$path/server1"
dir[2]="$path/server2"
dir[3]="$path/server3"
dir[4]="$path/server4"
dir[5]="$path/server5"
dir[6]="$path/server6"
dir[7]="$path/server7"
dir[8]="$path/server8"

for ((i=1;i<9;i++)); do
	echo "${dir[i]}"

	cd "${dir[i]}"

	vagrant up default

done

for ((i=1;i<3;i++)); do
	# server 1 and 2 need to be run first in order

	ttab -t "${dir[i]}" -d ${dir[i]} sh run.sh &
	sleep 20
done

for ((i=4;i<9;i++)); do
	ttab -t "${dir[i]}" -d ${dir[i]} sh run.sh &
	sleep 1
done

This script can then be run with:

sh the_script_name.sh

…in the directory above all your servers. See? I told you it was simple. Still, for those who aren’t familiar with Bash I will break things down so you can understand and modify this to suit your own stack.

echo "need admin permissions"

sudo echo "lets do this"

Vagrant requires admin permissions in order to use shared folders. This is just a neat way of ensuring this:

command -v ttab >/dev/null 2>&1 || sudo npm install -g ttab

This then checks if ttab is installed, and if not, installs it globally:

path=`pwd`

dir[1]="$path/server1"
dir[2]="$path/server2"
dir[3]="$path/server3"
dir[4]="$path/server4"
dir[5]="$path/server5"
dir[6]="$path/server6"
dir[7]="$path/server7"
dir[8]="$path/server8"

We then assign a bunch of variables. The ‘path’ variable is set to the current directory. Surrounding a bash command in ‘ (in this case pwd) in a variable definition sets the variable to be the commands output (very useful to know)!

dir[1-8] are then set to the names of the directories with the machines we want to start in them. We will be looping through these next:

for ((i=1;i<9;i++)); do
	echo "${dir[i]}"

	cd "${dir[i]}"

	vagrant up default
done

This should be simple to understand to anyone using a language for loops – it just iterates through the directories we defined earlier and runs vagrant up as default in all of them. It should be noted that changing this section to:

for ((i=1;i<9;i++)); do
	echo "${dir[i]}"

	cd "${dir[i]}"

	vagrant up default &
done

wait

…will run each command in parallel (and in Bash this means running as a background process), and then wait until they all finish before continuing.

However, this isn’t advised for 2 reasons:

1) Vagrant can’t actually boot multiple machines at the same time due to SSH limitations, so it runs the bulk of the start up one at a time regardless.

2) This seems to cause infrequent crashes on my machine. However, you might find slightly better performance with this optimisation –  I encourage you to experiment!

for ((i=1;i<3;i++)); do
	ttab -t "${dir[i]}" -d ${dir[i]} sh run.sh &
	sleep 20
done

for ((i=4;i<9;i++)); do
	ttab -t "${dir[i]}" -d ${dir[i]} sh run.sh &
	sleep 1
done

And finally we have these two for loops. Firstly you will notice the calls to ttab. These just open a new terminal tab, go to the directory after the -d and then run the command:

sh run.sh &

The run.sh file is rather simple and I would assume most people running node environments would have something similar:

vagrant ssh default -- "sudo killall node; sudo killall gulp; cd /vagrant/; sudo gulp nodemon"

However, what’s more important are the trailing & and the sleep calls.

The & is required to run the run.sh command as a background process – however, this command obviously never exits as it’s running the server, so we call the sleep function to wait for it to boot up before starting the next machine.

The first loop starts the first two servers, which in the SuperAwesome environment need to be run first, as all the other machines depend on these 2. The third server then doesn’t have a run.sh command, so we skip that one.

The rest of the machines can all be started at the same time – the sleep call here is just so the tabs always appear in the same order. Without the sleep 1 the tabs would be random on each boot.

And that’s it! We also use similar scripts for our integration tests (the main difference being we don’t open new tabs for each machine) so this script has proven to be more useful than just fulfilling my laziness.

But the main benefit here is that I don’t have to sit and watch each machine boot to then run the run.sh file each morning – instead I simply enter the one command and go make a drink.

[Max is a Software Developer at SuperAwesome]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s