Using virtualenvwrapper to start processes and swap config files

I do my Django development work locally on OS X, so I have several different daemons installed on my machine. I used to keep Postgres, MySQL, lighttpd, memcached, and more set up to autostart and run continuously, but I didn't like the burden on performance (real or imagined). Also, I switch between projects frequently and I often need to switch config files based on the project I'm about to work on.

My solution is to use the end-user customization hooks that Doug Hellmann's excellent virtualenvwrapper provides. If you're not familiar with virtualenvwrapper, it makes it easy to manage your virtualenvs in one place and switch between them quickly and conveniently. The project provides several scripts that are meant to be added to by the end-user to customize behavior.

Configuration

For my configuration files, I create multiple versions with the changes needed for each project, and I then create a symlink to the version I want at the moment. For example, with lighttpd I'll create an /etc/lighttpd/project-a.conf and an /etc/lighttpd/project-b.conf, and then create a symlink called /etc/lighttpd/lighttpd.conf that points to the one I need.

In OS X, I use launchd .plist files that have the OnDemand option set to True. I've posted the .plists I'm using currently to Github. Put these in your /Library/LaunchDaemon/ folder and then load them using the command:

:::bash
sudo launchctl load /Library/LaunchDaemon/[plist_filename]

This registers the processes with OS X's launchd system. Afterwards, you can start and stop them using the commands:

:::bash
sudo launchctl start [label]
sudo launchctl stop [label]

Note that, when starting and stopping, you don't have to use the path to the .plist file. Just use the daemon's label, which is typically the filename without the path or the .plist part.

Env-specific virtualenvwrapper scripts

I use the project-specific postactivate and postdeactivate scripts to swap in the right config and start the daemons that I need for a particular project. You'll find these in the $WORKON_HOME/[project_name]/bin directory. If your virtualenv is a little old, those scripts may not already be present. As long as you're up-to-date with a recent version of virtualenvwrapper, you can simply add those scripts to your virtualenv's bin directory yourself.

For $WORKON_HOME/project-a/bin/postactivate, I use a script similar to this:

#!/bin/bash
# This hook is run after this virtualenv is activated.

# Postgres
sudo launchctl start org.postgresql.postmaster

# Memcached
sudo launchctl start com.danga.memcached

# Lighttpd
sudo mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.bak
sudo ln -s /etc/lighttpd/project-a.conf /etc/lighttpd/lighttpd.conf
sudo launchctl start net.lighttpd

cd ~/code/webapps/project-a-dir/project-a

I like having the cd command there so that I can immediately run manage.py commands.

For $WORKON_HOME/project-a/bin/postdeactivate, I use:

#!/bin/bash
# This hook is run after this virtualenv is deactivated.

# Postgres
sudo launchctl stop org.postgresql.postmaster

# Memcached
sudo launchctl stop com.danga.memcached

# Lighttpd
sudo launchctl stop net.lighttpd

So now, when I run workon project-a, postgres, memcached, and lighttpd all come to life. When I run workon project-b, the postdeactivate script for project-a runs, and then the postactivate script for project-b runs. This effectively switches my lighty conf and restarts the process so it's immediately ready for me. When I deactivate, the processes shut down and my computer can take advantage of the free memory.

General virtualenvwrapper scripts

Virtualenvwrapper also provides global hooks directly in $WORKON_HOME. Brian Rosner suggested making the config switching generic so you wouldn't have to create scripts for each environment. I haven't tried this yet, but it definitely wouldn't be hard to do. The environment variable $VIRTUAL_ENV is available within postactivate scripts, so $WORKON_HOME/postactivate could include a block that looks something like this:

:::bash
if [ -f /etc/lighttpd/$VIRTUAL_ENV.conf ]; then
    sudo ln -s /etc/lighttpd/$VIRTUAL_ENV.conf /etc/lighttpd/lighttpd.conf
fi

All you would have to do is name your config files after your environments, and they would automatically be symlinked into place when you activate the environment.