Wordpress and Vagrant

I wanted to test a new version of Wordpress on $WORK's website, and ran into an interesting set of problems. I figured it would be worth setting them down here.

First: I set up a Vagrant box, got it to forward port 8000 to port 80 on the VM, told Cfengine to set it up as a webserver, then copied over the files and database. Turned out I'd forgotten a few things, like installation of modreverseproxy. I really need to make that into an RPM, especially since it should be pretty trivial, but for now I settled for documenting and scripting my instructions. There were a few other things like that; it's always a good exercise to do this and see what you've left out. Eventually I got it down to a Makefile that I could run on the box itself:

OLD=wordpress-3.4.2
NEW=wordpress-3.5.2

CF=/var/cfengine/bin/cf-agent -f /vagrant/cfengine/masterfiles/promises.cf -KI

go: /var/cfengine/bin /etc/firstrun /www/www.example.com-wordpress /usr/bin/mysql /var/lib/mysql/chibiwp /etc/httpd/modules/mod_proxy_html.so
    sudo $(CF)
/var/cfengine/bin:
    sudo rpm -ivh cfengine-community-3.3.0-1.x86_64.rpm
/etc/firstrun:
    sudo $(CF) -Dinstall_now_please
    sudo touch /etc/firstrun
/www/www.example.com-wordpress:
    sudo tar -C /www -xvzf /vagrant/wordpress.tgz
/var/lib/mysql/example-wordpress:
    mysql -u root -e"create database example-wordpress; grant all on example-wordpress.* to 'wordpress'@'localhost' identified by 's33kr1t'; flush privileges; use example-wordpress; source /vagrant/example-wordpresswp.sql;"
/usr/bin/mysql:
    sudo /var/cfengine/bin/cf-agent  -f /vagrant/cfengine/masterfiles/promises.cf -KI -Dinstall_now_please
/etc/httpd/modules/mod_proxy_html.so:
    tar -C /tmp -xvjf /vagrant/mod_proxy_html.tar.bz2
    sudo bash -c 'cd /tmp/mod_proxy_html ; /usr/sbin/apxs -I /usr/include/libxml2 -I . -c -i mod_proxy_html.c'
disable_plugins:
    mysql -B -u root chibiwp -e "select option_value from wp_options where option_name='active_plugins';" | sed -e's/^/update wp_options set option_value=QQQ/;s/$/QQQ where option_name="active_plugins";/;' | tail -1 | sed -e"s/QQQ/'/g" > /tmp/restore
    mysql -u root chibiwp -e'update wp_options set option_value="a:0:{}" where option_name="active_plugins";'
enable_plugins:
    mysql -u root chibiwp < /tmp/restore
unpack_wp:
    sudo tar -C /www/www.example.com-wordpress -xvzf /vagrant/wordpress-3.5.2.tar.gz
    sudo mv /www/www.example.com-wordpress/wordpress $(NEW)
    -sudo rm -r $(OLD)/wp-includes
    -sudo rm -r $(OLD)/wp-admin
    -sudo mv $(NEW)/wp-includes $(OLD)
    -sudo mv $(NEW)/wp-admin $(OLD)
    sudo find $(NEW) -type f -maxdepth 1 -exec cp -v {} $(OLD) \;
force_upgrade:
    wget "http://localhost/wp-admin/upgrade.php?step=1&backto=%2Fwp-admin%2F"
upgrade: disable_plugins unpack_wp force_upgrade enable_plugins


However, when I browsed to localhost:8000 it tried to redirect me to the actual URL for the work website (http://work.example.com), rather than simply showing me the page and serving it all locally. Turns out this is a known problem, and the solution is to use one of Wordpress' many ways to set the site URL. The original poster used the RELOCATE method, but I had better luck setting the URL manually:

define('WP_HOME','http://localhost:8000');
define('WP_SITEURL','http://localhost:8000');

I can do this manually, but it's better to get Cfengine to do this. First, we have an agent bundle to edit the file:

bundle agent configure_wp_for_vagrant_testing {
  files:
vagrantup_com::
  "/var/www/wordpress/wp-config.php"
    edit_line => vagrant_testing_wpconfig;
}

We specify the lines to add. Rather than install the lines in two passes, which is non-convergent, we add just one line that happens to have an embedded newline:

bundle edit_line vagrant_testing_wpconfig {
  insert_lines:
"define('WP_HOME','http://localhost:8000');
define('WP_SITEURL','http://localhost:8000');" location => wp_config_thatsallfolks;
}

(I found that on the Cfengine mailing list, but I've lost the link.) And finally, we specify the location. This depends on having the default comment in wp-config that indicates the end of user-settable vars, but it seems a safe bet:

body location wp_config_thatsallfolks {
  select_line_matching => "^/\* That's all, stop editing. Happy blogging. \*\/.*$";
  before_after => "before";
}

Second, the production webserver actually hosts a bunch of different sites, and we have separate config files for each of them. Since I was getting Cf3 to configure the VM just as if it was production, the VM got all these config files too. Turned out that browsing to http://localhost:8000 gave me what Apache thought was the default site -- which is the VirtualHost config listed first, which in our case was not our main site. I got around that by renaming our main site's config file to 000-www.example.com.conf (a trick I stole from Debian). Now I could see our main website at http://localhost:8000.

Third, testing: normally I rely on Nagios to do this sort of thing, but it's kind of hard to point it at a VM that might be only around for a few minutes. I could add tests to Cfengine, and that's probably a good idea; however, right now I wanted to try out serverspec, a Ruby-based test suite that lets you verify server attributes.

The serverspec docs say they can run tests on a Vagrant machine, and that all you have to do is tell it so when running "serverspec init." However, I had problems with this; it asked me for a VM name, and I didn't have one...there was only one machine set up, and it didn't seem to like "default". I didn't spend a lot of time on this, but instead went to running the serverspec tests on the Vagrant box itself. That brought its own problems, sinc installing gems in CentOS 5 via the default Ruby (1.8.5) causes buffer overflows. A better person would build a newer RPM, rather than complain about non-schedule repos. However, this Gist does the trick rather nicely (though I also removed the stock Ruby and didn't bother installing Chef).

Okay, so: running "serverspec init" on the Vagrant box created a nice set of default tests for a website. I modified the test for the website config file to look for the right config file and server name:

describe file('/etc/httpd/conf.d/000-www.example.com.conf') do
  it { should be_file }
  it { should contain "ServerName www.example.com }
end