<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:admin="http://webns.net/mvcb/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
<channel>
    
    <title>till's blog</title>
    <link>http://till.klampaeckel.de/blog/</link>
    <description>OMG -- it's not vox.com</description>
    <dc:language>en</dc:language>
    <generator>Serendipity 1.5.5 - http://www.s9y.org/</generator>
    
    

<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/till" /><feedburner:info uri="till" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
    <title>Bento and VirtualBox</title>
    <link>http://feedproxy.google.com/~r/till/~3/nr9tuDQbsTU/200-Bento-and-VirtualBox.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/200-Bento-and-VirtualBox.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=200</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=200</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>Last week <a href="http://till.klampaeckel.de/blog/archives/199-Wanderlust.html">I blogged some Vagrant tips</a> and <a href="https://twitter.com/klimpong/status/325238429451440130">pretty much jinxed the run I had</a> in the past months.</p>

<p>Here's how:</p>

<ul>
<li><p>I decided to upgrade to Vagrant 1.1, which broke bento: the current bento master is incompatible with Vagrant 1.1. But selecting the right rbenv env and installing the latest available Vagrant gem (inside the rbenv environment) fixed it.</p></li>
<li><p>My base box build, but for some reason, the guest addition setup broke and while it worked on Mac OSX, it broke the image completely on Ubuntu. Don't ask me why.</p></li>
</ul>

<p>Especially the last bit reminded me to share another small tip, or work-around.</p>

<h2>Fixing up base boxes</h2>

<p>So every once in a while something doesn't work as expected. In my case, the guest additions were installed but not loaded and also failed to load when I started a VM. This in itself wasn't so bad, but it broke the vboxfs shares and while there may be cases where you don't need that (e.g. for a database VM), a VM without your code mounted into it is pretty useless.</p>

<p>The fix wasn't too hard:</p>

<ul>
<li>I created a blank Vagrantfile (no recipes, just a simple box definition).</li>
<li><code>vagrant up</code> and wait for the box to fail.</li>
<li><code>vagrant ssh</code> to enter the box, or start with the GUI option and login through it.</li>
<li>execute <code>sudo /etc/init.d/vboxadd setup</code></li>
</ul>

<p>Then, exit the VM and execute the following: <code>vagrant package vm_name --output box_name.box</code>.</p>

<p>Import the box again with <code>vagrant box add etc.</code>, update your <code>Vagrantfile</code> and test &mdash; then distribute.</p>

<h2>Thoughs</h2>

<p>Let me use this to reiterate on how critical a fixed set of versions are.</p>

<p>VirtualBox 4.2.x and bento (or veewee) seem to be incompatible somewhere and even though an error doesn't surface while the box is build or validated, it's still happening. It's paramount that there's always a rollback of some kind before you end up breaking too many things and stall your team for long.</p>

<h2>Fin</h2>

<p>That would be all for today.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=nr9tuDQbsTU:-x6Bay1lG-Y:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=nr9tuDQbsTU:-x6Bay1lG-Y:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=nr9tuDQbsTU:-x6Bay1lG-Y:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=nr9tuDQbsTU:-x6Bay1lG-Y:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=nr9tuDQbsTU:-x6Bay1lG-Y:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=nr9tuDQbsTU:-x6Bay1lG-Y:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/nr9tuDQbsTU" height="1" width="1"/>]]></content:encoded>

    <pubDate>Tue, 23 Apr 2013 11:44:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/200-guid.html</guid>
    <category>bento</category>
<category>devops</category>
<category>php</category>
<category>vagrant</category>
<category>virtualbox</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/200-Bento-and-VirtualBox.html</feedburner:origLink></item>
<item>
    <title>Wanderlust</title>
    <link>http://feedproxy.google.com/~r/till/~3/tAdnpFdv0pU/199-Wanderlust.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/199-Wanderlust.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=199</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=199</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>At the last meetup of the <a href="http://bephpug.de">Berlin PHP Usergroup</a>, <a href="https://twitter.com/chluehr">Christoph</a> gave <a href="http://www.bephpug.de/folien/chluehr-vagrant_for_php-bephpug_201304.pdf">a talk about Vagrant</a>.</p>

<p>Good enough of a reason to write down or re-cap some things I've learned with or about Vagrant over the last two years.</p>

<h2>Base boxes</h2>

<p>There are lots of <a href="http://www.vagrantbox.es/">base boxes</a> available, but don't be tempted to rely on them (e.g. via <code>config.vm.box_url</code>).</p>

<ol>
<li>Vagrantbox.es doesn't actually mirror images and that is a huge pain.</li>
<li>Available base boxes tend to be outdated. (Think kernel, packages, etc.) Running updates each time you provision is painful.</li>
<li>Available base boxes use U.S. mirrors only/mostly/always &mdash; because we all live in the U.S. of A..</li>
<li>Your VirtualBox guest additions may not match with your system and this may create random issues. </li>
</ol>

<p>Your best bet is to create your own base box and establish a workflow e.g. using <a href="https://github.com/jedi4ever/veewee">veewee</a> or <a href="https://github.com/opscode/bento">bento</a>.</p>

<p>At EasyBib, we use bento and we created a definition which replaces the sources with <a href="https://github.com/easybib/bento/blob/master/definitions/easybib-ubuntu-10.04.4/update.sh">Ubuntu's nifty mirror syntax</a> (since we're pretty distributed at times, everyone appreciates this) and upgrades the base system. Either one of these tools introduce more Ruby into your organization and you may think "WTF &mdash; why do I need this?!", but the clear advantage is that no one has to write down a lot of steps how to recreate these boxes and <em>anyone</em> can do it.</p>

<p>In bento's case, the requirement is Ruby 1.9.1+ (getting this installed is IMO the hardest) and bundler. <code>bundle install</code> in your bento-clone gets you everything needed and then the three commands require build, validate and export a box which is ready to use. Ensure to put whatever you need into the definition &mdash; for example in the <code>update.sh</code>. Avoid too many manual steps before you export because the next person will have to know and repeat them. Bento serves as documentation as well.</p>

<p>I version our boxes with like <em>easybib-something-10.04.4_vbox-4.1.8_0.1.box</em> and upload them to an S3 bucket. The first number is the Ubuntu release and the second is the version of the VirtualBox guest additions. Simple. The third version is our internal iteration &mdash; typically a base box isn't perfect from the beginning while e.g. the Ubuntu and VirtualBox part are settled, there might be other improvements. With an extra version you avoid conflicts and extra work like <code>vagrant box delete</code> etc. and ensure the latest box is always used.</p>

<p>Also &mdash; in case the software stack is very different across your projects, it also helps to to create different boxes which come with different software pre-installed.</p>

<h2>Standardize on versions</h2>

<p>Vagrant and VirtualBox have frequent releases. I suggest to standardize on one so members of your team don't have random issues at hand and fires to fight.</p>

<p>Even for a small team of up to ten developers this makes a lot of sense. Because people tend to add a lot of randomness anyway &mdash; different hardware, operating systems and so on. Fight only the battles you want to fight, and deploy otherwise.</p>

<p>Vagrant also recently went from <em>being a rubygem</em> to providing installers. I haven't had the time to roll this out yet, but I expect this to help as well since at least as far as ruby is concerned all the dependencies are bundled.</p>

<p>This of course still implies that testing is required so you and your team don't walk into a stupid little regression and waste away the day trying to figure out what went wrong. And of course even if Vagrant is smoother, it still leaves you with VirtualBox and tools like bento and plenty of potential breakage.</p>

<h3>Chef Versions</h3>

<p>On a side-note &mdash; Chef 10 and 11 may also introduce a lot of breakage in recipes. It helps to roll your base box with a specific version as well. With bento the work-around was pretty straight forward: <a href="https://github.com/easybib/bento/blob/master/definitions/easybib-ubuntu-10.04.4/chef-client.sh">I replaced the <code>chef-client.sh</code> and installed Chef 10</a> (instead of 11 &mdash; or whatever the <em>latest</em> is).</p>

<h3>VirtualBox and guest additions</h3>

<p>In theory, it's alright to run with different guest additions in a box than the version of VirtualBox you have installed on the host. It should at least match the <em>main</em> release &mdash; for example: 4.1.8 guest additions and 4.1.12 VirtualBox should do fine. That's a big should though, because it also may cause random issues like crashes and hangs. </p>

<p>If you don't want or cannot rebuild the base box for some reason, you can also use <a href="https://github.com/dotless-de/vagrant-vbguest">vbguest</a> which is a Vagrant plugin to update the guest additions when you start the virtual machine. Keep in mind that this adds a couple minutes to the bootstrapping.</p>

<h2>Learn some Ruby</h2>

<p>There are little things where it helps to know a little Ruby. And by Ruby, I don't mean Rails. A <code>Vagrantfile</code> itself is Ruby code &mdash; this implies that it is fully customizable.</p>

<p>An example of something we as a team couldn't agree on is the location of where projects (and essentially cookbooks) are located on your local disk. Every other team member has a different preference:</p>

<pre class="sh_ruby">
case ENV['USER']
when 'till'
  local_cookbook_dir = "~/Documents/workspaces/easybib-cookbooks"
when 'someonelse'
  local_cookbook_dir = "~/dev/till/easybib-cookbooks"
else
  local_cookbook_dir = "~/Sites/easybib/cookbooks"
end

if not File.directory?(File.expand_path(local_cookbook_dir))
  raise "You need to checkout your cookbooks into #{local_cookbook_dir}"
end

# ...

web_config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = local_cookbook_dir
  chef.add_recipe "ohai"
  # ...
end
</pre>

<p>It's as simple as that.</p>

<p>Another example &mdash; setting VirtualBox options for everyone but a certain user:</p>

<pre class="sh_ruby">
web_config.vm.boot_mode = :gui unless ENV['USER'] == 'mr_I_dont_run_X'
</pre>

<p>Bonus tip: Once you made changes, make sure to at least re-provision. Commit and push after!</p>

<h2>Learn Chef or Puppet</h2>

<p>I often see projects where developers end up writing a lot of shell script to bootstrap VMs, but learning Chef or Puppet is not really that hard.</p>

<p>I find it harder to validate exit codes (again and again and again) in bash than using a DSL (which is what Chef and Puppet essentially are). The code in your cookbooks (Chef) or manifests (Puppet) is certainly not faster than a shell script but a lot easier to read and more maintainable in the end.</p>

<p>Bash-scripting is not hard either, but in order to produce a set of scripts which can be ran again and again (not just to bootstrap a fresh VM but e.g. also to run updates on one that is running), defensive coding is paramount. And while that is certainly not impossible, it's often a waste of time when frameworks like Chef or Puppet have that covered.</p>

<p>But let's skip on the benefits of using identical tools to bootstrap Vagrant, staging and production because I find them more than obvious.</p>

<h2>Learn some Linux</h2>

<p>Every once in a while you will run into weird issues with the VMs. These may include one of your VMs losing connectivity (<code>sudo restart networking</code> to the rescue) or weird behavior like assets not refreshing (<code>sendfile off;</code> in nginx). Take it as an opportunity to learn some about the system that is run in production.</p>

<p>In the end all required configuration changes will go back into your provisioning and make sure to share your experience with at least the people on your team.</p>

<h2>Backup everything</h2>

<p>Whatever you find and use &mdash; make a copy of it and put it on Amazon S3 or the local network. With larger teams even a local Ubuntu mirror (or whatever you use) can come in handy.</p>

<p>This includes base boxes, packages, etc.. Nothing is more annoying than waking up and not being able to bootstrap your VMs because someone decided to remove something in order to force you to upgrade.</p>

<h2>Don't dumb it down!</h2>

<p>Typically, PHP applications are developed on a single host &mdash; Apache, PHP and MySQL on localhost. With Vagrant it becomes surprisingly easy to mimic production.</p>

<p>Not to say that I have to run 20 virtual machines to copy my cluster of application servers, but it's perfectly acceptable to set up an environment with four VMs where one is a loadbalancer, two are application servers and then a database server.</p>

<h2>Networking and port forwards</h2>

<p>Unless you regulary let others use your VMs, don't add port forwards &mdash; or at least install a firewall.</p>

<p>For networking, I suggest you either use static IPs (and keep track of them in a sheet) or DHCP. I prefer static IPs though since that makes configuration (e.g. of an application to connect to the database) easier.</p>

<p>It also doesn't hurt to assign names, so you know which VM you're dealing with when GUI is enabled:</p>

<pre class="sh_ruby">
    db_config.vm.customize [
      "modifyvm", :id,
      "--name", "DB",
    ]
</pre>

<h2>Hardware</h2>

<p>It doesn't hurt to have lots of CPU and RAM, but also configure the VMs accordingly. I run up to four virtual machines on a Macbook Air &mdash; usually configured with 256 to 512 MB. I imagine this would go smoother with VMWare Fusion, but since our team contains Mac and Linux as well, we haven't moved on this.</p>

<p>Here's an example how to give 512 MB RAM to a virtual machine:</p>

<pre class="sh_ruby">
    db_config.vm.customize [
      "modifyvm", :id,
      "--memory", "512"
    ]
</pre>

<h2>Fin</h2>

<p>That's all I can think of right now. Happy development!</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=tAdnpFdv0pU:L8KIYM6vt9E:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=tAdnpFdv0pU:L8KIYM6vt9E:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=tAdnpFdv0pU:L8KIYM6vt9E:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=tAdnpFdv0pU:L8KIYM6vt9E:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=tAdnpFdv0pU:L8KIYM6vt9E:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=tAdnpFdv0pU:L8KIYM6vt9E:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/tAdnpFdv0pU" height="1" width="1"/>]]></content:encoded>

    <pubDate>Tue, 16 Apr 2013 19:37:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/199-guid.html</guid>
    <category>chef</category>
<category>chef-solo</category>
<category>devops</category>
<category>php</category>
<category>ruby</category>
<category>vagrant</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/199-Wanderlust.html</feedburner:origLink></item>
<item>
    <title>From Unfuddle (svn) to git</title>
    <link>http://feedproxy.google.com/~r/till/~3/_mhWzDaXFnY/197-From-Unfuddle-svn-to-git.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/197-From-Unfuddle-svn-to-git.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=197</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=197</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>I've blogged about <a href="http://till.klampaeckel.de/blog/plugin/tag/subversion">converting a Subversion repository to git</a> a couple times. While it was a tedious process at first, I've made my peace with it and now cannot count the code repositories I have migrated successfully anymore. The migration usually works, except for when I deal with our old provider unfuddle.</p>

<p>For some reason, sometimes it didn't work right away and I had to re-run <code>git svn clone</code> a couple of times to get it right.</p>

<h2>git svn clone</h2>

<p>Here's a snippet to make it work for you:</p>

<pre class="sh_sh">
$ git svn clone --authors-file=./authors.txt --no-metadata \
--prefix=svn/company_repositoryname/ \
--tags=tags \       
--trunk=trunk \
--branches=branches \
http://company.unfuddle.com/svn/company_repositoryname \
./new-git-repo
</pre>

<p>Make sure to replace the <em>company</em> and the <em>company_repositoryname</em> part in above scripts.</p>

<p>For some reason, I was never able to get prefix and all that completely right from the start. That is, until now &mdash; needless to say the above works. And for the sake of documenting and not re-learning each time (e.g. today, as a migrate another Subversion repository to git (Github :-)), here's my documentary blog post.</p>

<h2>Tags and branches</h2>

<p>Tags and branches are slightly different concepts in Subversion and git.</p>

<p>In Subversion, we usually ran pre-processing on tags before we deployed them (because doing this in a branch was a huge pita due to size of the repository and the overall joy of merging commits in Subversion. So in the end, a tag we created in Subversion, is not a tag in git because we modified the tag &mdash; which makes it a branch.</p>

<p>So as a follow up to my prior snippet to convert a repostitory, I've used this script to convert the branches it created to proper tags in git:</p>

<pre class="sh_bash">
#!/bin/sh

branches=(`git branch -r`)

for branch in "${branches[@]}"
do
    case $branch in
    *tag*)
        tag=${branch//svn\/company_repositoryname\/tags\//}
        remote="remotes/${branch}"
        echo "$tag from $branch, remote: $remote"
        git checkout -b "tag-$tag" $remote &&amp; git tag -a $tag -m "SVN tag: $tag"
        ;;
    *)
        echo "Skipping: $branch"
    esac
done
</pre>

<p><em>Again</em>, you will have to adjust <em>company_repositoryname</em> in this piece. :-)</p>

<p>Once the script completes, I verify the tags with <code>git tag -l</code> and delete the branches with <code>git branch -D foo</code>.</p>

<p>If all looks ok and the tests confirm this, I add an <em>origin</em>, push branches and also <code>git push --tags</code>.</p>

<h2>Fin</h2>

<p>That's all &mdash; happy migrating. Just in case: the code is BSD licensed, which means, you can do whatever you want with it.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=_mhWzDaXFnY:IWQQmFCA5cs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=_mhWzDaXFnY:IWQQmFCA5cs:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=_mhWzDaXFnY:IWQQmFCA5cs:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=_mhWzDaXFnY:IWQQmFCA5cs:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=_mhWzDaXFnY:IWQQmFCA5cs:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=_mhWzDaXFnY:IWQQmFCA5cs:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/_mhWzDaXFnY" height="1" width="1"/>]]></content:encoded>

    <pubDate>Sat, 08 Dec 2012 19:30:00 +0100</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/197-guid.html</guid>
    <category>administration</category>
<category>git</category>
<category>github</category>
<category>migration</category>
<category>subversion</category>
<category>unfuddle</category>
<category>version-control</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/197-From-Unfuddle-svn-to-git.html</feedburner:origLink></item>
<item>
    <title>Continuous Integration: Automated database setup with Doctrine on Travis-CI</title>
    <link>http://feedproxy.google.com/~r/till/~3/8EYi9v41Q34/192-Continuous-Integration-Automated-database-setup-with-Doctrine-on-Travis-CI.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/192-Continuous-Integration-Automated-database-setup-with-Doctrine-on-Travis-CI.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=192</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=192</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>Testing is important &mdash; most people understand that by now. A lot of people write tests for their open source code already, but in-house testing is still hard. For example, many of us had an encounter with Jenkins: it runs well to a point where it becomes harder to maintain the Jenkins than it is to write tests. </p>

<p>Another obstacle is test setup and environments: When I write and run tests, there is sometimes only so much I can do to mock and avoid actual calls to my storage backend. While I prefer to run my database tests against a SQLite in memory database, there are these edge cases, where I work with multiple database or I write a direct query (and by-pass the <em>ORM-magic</em>).</p>

<p>In these cases I need to have that database server available in my test environment!</p>

<p>The following blog posts explains how to solve these things with Travis-CI. I will walk you through the setup on Travis-CI's business service. But most of this applies to their open source offering as well.</p>

<h2>Step by step</h2>

<p>I'll try to break it up into small steps.</p>

<h3>Travis-CI</h3>

<p>The first step is to login at <a href="http://travis-ci.com">http://travis-ci.com</a> and add the repository tests should be run for. To be able to add repositories of an organization, you have to be the owner of the organization. The easiest way to get access to the service right now is donating to these guys or in case you have done that already: email them. ;-)</p>

<p>The second step is setting up a <code>.travis.yml</code> file.</p>

<p>Mine looks like this:</p>

<pre class="sh_yaml">
language: php
php:
  - 5.3
  - 5.4
before_script:
  - ./composer.phar -v install --dev
  - psql -c 'create database testdatabase;' -U postgres
</pre>

<p>Run-down:</p>

<ul>
<li>run the tests against PHP 5.3 <strong>and</strong> 5.4</li>
<li><code>before_script</code> defines your test setup (outside PHP)</li>
<li>I omitted the <code>script</code> stanza because the default (<code>phpunit</code>) works for me </li>
</ul>

<h3>Composer</h3>

<p>I am using <a href="http://getcomposer.org/">composer</a> to manage my dependencies and you should too. I don't want to go into details here, but a short example of my <code>composer.json</code> is the following:</p>

<pre class="sh_js">
{
    "name": "mycompany/project",
    "description": "A super cool project.",
    "require": {
        "doctrine/orm": "2.2.2"
    },
    "autoload": {
        "psr-0": {
            "MyCompany\\Project\\Test": "tests/",
            "MyCompany\\Project": "src/"
        }
    }
}
</pre>

<p><strong>Side-note</strong>: We also currently commit a <code>composer.phar</code> into each repository for two reasons:</p>

<ol>
<li>To ensure a change in composer won't break our setup.</li>
<li>Downtime of (or connectivity issues to) their website don't break our deployments and test runs.</li>
</ol>

<h3>Test framework setup</h3>

<p>There is not a whole lot to setup since Travis-CI installs phpunit already. Just make sure you have a <a href="http://www.phpunit.de/manual/current/en/appendixes.configuration.html"><code>phpunit.xml</code></a> in the root of your repository and you are good to go.</p>

<h3>Database schema</h3>

<p>The next step would be to generate your schema and check in some <code>.sql</code>, right? I'm not a huge fan of this, because I hate running through a lot of manual steps when I need to update something. Manual steps means that they might be forgotten or people make a mistake. So the objective is to avoid any manual labour as much as you can.</p>

<p>Instead of maintaining these files, I use Doctrine's <code>SchemaTool</code>. It takes care of this just fine because <a href="http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html">I annotated all my entities</a> already.</p>

<p>To make use of this, I suggest to add the following to your test case:</p>

<pre class="sh_php">
&lt;?php 
namespace MyCompany\Project\Test;

use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\ORM\Tools\SchemaTool;

class MyTestCase extends \PHPUnit_Framework_Testcase
{
    protected $em, $tool;

    public function setUp()
    {
        $this->setUpDatabase(); // wrap this again
        $this->setUpSchema();
        /* more setup here */
    }

    public function tearDown()
    {
        $classes = array(
            $this->em->getClassMetadata('MyCompany\Project\Entity\SomethingImportant'),
        );
        $this->tool->dropSchema($classes);
        unset($tool);
        unset($em);
    }

    public function setUpDatabase()
    {
        $isDevMode      = true;
        $doctrineConfig = Setup::createAnnotationMetadataConfiguration(
            array('path/to/Entity'),
            $isDevMode
        );

        // database configuration parameters
        $dbConfig = array(
            'host'     => '127.0.0.1',
            'user'     => 'postgres',
            'password' => '',
            'dbname'   => 'testdatabase',
        );

        $this->em = EntityManager::create($dbConfig, $doctrineConfig);
        PersistentObject::setObjectManager($this->em);
    }

    public function setUpSchema()
    {
        $this->tool = new SchemaTool($this->em);
        $classes = array(
            $this->em->getClassMetadata('MyCompany\Project\Entity\SomethingImportant'),
        );
        $this->tool->createSchema($classes);
    }
}
</pre>

<p>Assumptions made (aka, <em>room for improvement</em>):</p>

<ul>
<li>your entities are in <code>path/to/Entity</code></li>
<li>PostgreSQL is used and runs on <code>127.0.0.1</code></li>
<li>you're using a database called <code>testdatabase</code></li>
</ul>

<h3>Tests</h3>

<p>Once the setup is complete, I add the usual <code>testFoo()</code> methods into my test case class.</p>

<p>From within the <code>testFoo()</code> methods I have Doctrine's <code>EntityManager</code> available via <code>$this-&gt;em</code>. The entity manager allows me to do whatever I need or want within a test run.</p>

<p>After a test completes, the <code>tearDown()</code> method is invoked and destroys the tables in your <code>testdatabase</code> and then <code>setUp()</code> re-creates it. This will take some time but the side-effects of stale data are not to be neglected. Add to that, your tests should not rely on the order they are executed in anyway.</p>

<p>Another benefit of this setup are updated SQL tables each time a commit changes the annotations. No extra <code>.sql</code> files to maintain. :)</p>

<h2>Fin</h2>

<p>That's really all there is to running your test suite with Travis-CI and while you did all the above, you just added <strong>continuous integration</strong> to your toolkit because these tests run each time a pul request is opened or commits are pushed. :-)</p>

<p>As I mentioned early on, these steps apply to the open source offering as well &mdash; all but the link to login.</p>

<p>If PostgreSQL is not your game, have a look at <a href="http://about.travis-ci.org/docs/user/database-setup/">the list of currently supported databases on Travis-CI</a>. And if your database server is not on the list of supported applications, you might as well install it with a <code>before_script</code> (as long as it runs on Ubuntu). ;-)</p>

<p>Happy testing!</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=8EYi9v41Q34:P2cNKtbGrj4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=8EYi9v41Q34:P2cNKtbGrj4:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=8EYi9v41Q34:P2cNKtbGrj4:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=8EYi9v41Q34:P2cNKtbGrj4:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=8EYi9v41Q34:P2cNKtbGrj4:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=8EYi9v41Q34:P2cNKtbGrj4:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/8EYi9v41Q34" height="1" width="1"/>]]></content:encoded>

    <pubDate>Mon, 13 Aug 2012 15:47:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/192-guid.html</guid>
    <category>ci</category>
<category>composer</category>
<category>database</category>
<category>development</category>
<category>doctrine</category>
<category>php</category>
<category>sql</category>
<category>testing</category>
<category>travis-ci</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/192-Continuous-Integration-Automated-database-setup-with-Doctrine-on-Travis-CI.html</feedburner:origLink></item>
<item>
    <title>Mind blown: svn:ignore</title>
    <link>http://feedproxy.google.com/~r/till/~3/76dKrji7b8Y/191-Mind-blown-svnignore.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/191-Mind-blown-svnignore.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=191</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=191</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>As we migrate away from Subversion, I ran into this little issue today.</p>

<p>Since it's something which popped up time and time again and I never got around to figuring out why this happened in the first place, I decided to put it into a blog post.</p>

<h2>Problem?</h2>

<p>What I did was, I setup an <code>svn:ignore</code> in the root of my Subversion repository, in order to ignore modules which are installed in the <code>app/modules</code> structure:</p>

<pre class="sh_sh">
dev% svn propedit svn:ignore .
</pre>

<p>My editor opened, and I put in <code>app/modules/foo</code> for the path I want to ignore.</p>

<p>Turns out this does not work:</p>

<pre class="sh_sh">
dev% svn st
?       app/modules/foo
</pre>

<p>After trying out various things like <code>app/modules/f*</code> and <code>*/foo</code>, I finally figured it out:</p>

<pre class="sh_sh">
dev% cd app/modules
dev% svn propedit svn:ignore .
</pre>

<h2>Solution</h2>

<p>The revalation of the day is: <code>svn:ignore</code> does not work recursively &mdash; like <code>svn:externals</code> would.</p>

<h2>Fin</h2>

<p>And this is after almost eight years with Subversion. I am just glad to move on.</p>

<p>Apologies if this is too basic or written down somewhere &mdash; I did try to read the page on <a href="http://svnbook.red-bean.com/en/1.6/svn.advanced.props.special.ignore.html">svn:ignore</a> in the documentation, but decided to spend my time on something else after the first couple paragraphs.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=76dKrji7b8Y:gOZaqbep5QE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=76dKrji7b8Y:gOZaqbep5QE:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=76dKrji7b8Y:gOZaqbep5QE:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=76dKrji7b8Y:gOZaqbep5QE:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=76dKrji7b8Y:gOZaqbep5QE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=76dKrji7b8Y:gOZaqbep5QE:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/76dKrji7b8Y" height="1" width="1"/>]]></content:encoded>

    <pubDate>Thu, 09 Aug 2012 16:55:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/191-guid.html</guid>
    <category>subversion</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/191-Mind-blown-svnignore.html</feedburner:origLink></item>
<item>
    <title>Too much abstraction: Doctrine and PHP</title>
    <link>http://feedproxy.google.com/~r/till/~3/oHVC-O3DzTE/190-Too-much-abstraction-Doctrine-and-PHP.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/190-Too-much-abstraction-Doctrine-and-PHP.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=190</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=190</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>Personally &mdash; I feel like I'm really, really late to get on the Doctrine train.</p>

<p>I've tried Doctrine 1 a few years back and found it mostly too complicated for my needs. For all I know, I still find PEAR::DB and PEAR::MDB2 the perfect database abstraction layers (DBAL). Though of course they may or may not be totally up to date in terms of PHP etc.. The overall concept of an ORM never stuck with me.</p>

<p>Of course Doctrine has a DBAL too and it would be unfair to compare its ORM to another DBAL, but it seems like almost no one uses the Doctrine DBAL by itself. I haven't either.</p>

<p>The primary use-case for Doctrine seems to be ORM and here is what I think about it.</p>

<h2>Little steps</h2>

<p><em>RTFM</em> is especially hard with Doctrine: Google usually points to older documentation, then there are a lot of out-dated links and the classic <em>has not been written yet</em>.</p>

<p>In case there is documentation I compare the experience to RPM (Redhat Package Manager):</p>

<ul>
<li>I need to learn about X</li>
<li>But have to read about Y first.</li>
<li>To understand Y I need to dive into Z.</li>
</ul>

<p>Sounds familiar? Dependency resolution in documentation. However, of course there's always also light at the end of the tunnel &mdash; it's when it all works &mdash; I conquered a problem and defeated the proxies and caches. ;-)</p>

<h2>Entities</h2>

<p>Entities can be incredibly simple &mdash; take the persistent object:</p>

<pre class="sh_php">
&lt;?php
use Doctrine\Common\Persistence\PersistentObject;

/**
 * @Entity
 * @Table(
 *   name="db.users"
 * )
 */
class Row extends PersistentObject
{
  /**
   * @Id
   * @Column(type="integer")
   * @GeneratedValue
   */ 
  protected $id
}
</pre>

<p>So what does this look like:</p>

<ul>
<li>database "db"</li>
<li>table: "users"</li>
<li>columns: "id" (most likely integer and auto_increment with MySQL) </li>
</ul>

<p>I feel like that is feature a hidden gem because I could not find much about it in official documentation &mdash; but that's basically <em>all</em> you have to do.</p>

<p>Annotations are not my thing and XML or YAML or neither &mdash; I can live with either of the three though. What seemed annoying is that usually people start off by writing half a dozen <code>getFoo()</code> and <code>setFoo()</code> methods which of course depends on how many columns in your table are. With the persistent object, this is not necessary.</p>

<h2>Abstraction</h2>

<p>Abstraction is Doctrine's biggest strength and largest drawback in my opinion. Abstraction is nice within reason, but I think with Doctrine it's way over the top. </p>

<p>My personal opinion on SQL is that <code>SELECT * FROM table WHERE id = 1</code> never hurt anyone. Not to suggest to do this in the wrong layer of your application, but actually understanding the tools I work with, made me a better developer today. A <code>JOIN</code> does not hurt either because then you and I know how to write one and what it costs to execute the sucker. <code>LEFT JOIN</code>, <code>STRAIGHT JOIN</code> and <code>INNER JOIN</code> &mdash; most developers don't know the difference. Doctrine also likes cascades but I bet not many people ever watched a busy database server while it's trying to process all them while a couple 1000 writes happen at the same time.</p>

<p>With Doctrine, SQL is so far away that developers really don't need to have an idea about what is going on and I think that is bad in particular.</p>

<p>My problem is that most of the time, the developer does not see their SQL at all and also doesn't analyze what it is doing. I have yet to figure out how to make the application log all statements produced. And yeah, it seems to be impossible because with the use of prepared statements no SQL is actually produced in Doctrine &mdash; it all happens on the server. But sure, prepared statements are pretty awesome because we are all idiots and don't know how to escape data. But I should leave this for another blog post.</p>

<p>About abstraction &mdash; <em>we</em> had a similar discussion at this month's <a href="http://bephpug.de/">PHP Usergroup in Berlin</a> and usually someone will counter my argument that the benefits of <em>faster</em> development outweigh all the potential screw ups. And maybe they are right &mdash; for a while. And then they upgrade their database server to have more RAM, right? What if for a while is already past tense?</p>

<h3>Example</h3>

<p>I just found the following code in one of our projects and I think it's a good example of what I mean.</p>

<p>The basics (broken down):</p>

<ul>
<li>SQL:
<ul><li>a <code>user</code> table</li>
<li>columns: <code>id</code>, <code>email</code>, <code>password</code>, <code>site_id</code></li>
<li>indices: <code>PRIMARY</code> (on <code>id</code>), <code>UNIQUE</code> (on <code>email</code> and <code>site_id</code>) </li></ul></li>
<li>an entity <code>Doctrine\Model\User</code> class describing the above </li>
<li>a repository <code>Doctrine\Model\UserRepository</code> class to add convenience calls to the entity</li>
</ul>

<p>So here is what I found:</p>

<pre class="sh_php">
&lt;?php
// in class UserRepository
public function getUserById($id, $site_id = 1)
{
    $qb = $this->_em->createQueryBuilder();
    $qb->select(array('u'))
        ->from('Doctrine\Model\User', 'u')
        ->where('u.id = :id')
        ->andWhere('u.site_id = :site_id')
        ->setParameter('id', $id)
        ->setParameter('site_id', $site_id);

        /* ... */
}
</pre>

<p>And to the defense of all the people who worked on this (including me), it takes a while to see. :-)</p>

<p>Question to my reader: Take the setup into account, do you see the issue? <strong>Leave a comment if you figure out what the problem is.</strong></p>

<p>So despite having an entity which defines the table setup, it's still possible to do something like that and there is no error/warning what so ever to let the developer know what they are doing. Not at run-time and not in the Doctrine CLI either. I'd argue that with the level of abstraction Doctrine adds to hide SQL from the developer, I expect that it would do enough analysis on what the developer is doing.</p>

<p>Or maybe the level of abstraction added here is actually counter-productive?</p>

<h2>Fin</h2>

<p>Food for thoughts. Hope it was not too ranty.</p>

<p>Disclaimer: This is with no offense meant to the people contributing to Doctrine or using Doctrine. I just had to raise my concerns somewhere.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=oHVC-O3DzTE:hZ8c4mH_An0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=oHVC-O3DzTE:hZ8c4mH_An0:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=oHVC-O3DzTE:hZ8c4mH_An0:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=oHVC-O3DzTE:hZ8c4mH_An0:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=oHVC-O3DzTE:hZ8c4mH_An0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=oHVC-O3DzTE:hZ8c4mH_An0:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/oHVC-O3DzTE" height="1" width="1"/>]]></content:encoded>

    <pubDate>Fri, 06 Jul 2012 17:38:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/190-guid.html</guid>
    <category>abstraction</category>
<category>development</category>
<category>doctrine</category>
<category>php</category>
<category>sql</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/190-Too-much-abstraction-Doctrine-and-PHP.html</feedburner:origLink></item>
<item>
    <title>Vagrant: ShellProvisioner vs. Chef</title>
    <link>http://feedproxy.google.com/~r/till/~3/CcOMp9ADGuc/189-Vagrant-ShellProvisioner-vs.-Chef.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/189-Vagrant-ShellProvisioner-vs.-Chef.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=189</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=189</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>In <a href="http://till.klampaeckel.de/blog/archives/188-Vagrant-sans-Ruby.html">my last blog entry</a>, I demo'd how to get started with Vagrant and the ShellProvisioner.</p>

<p>To further illustrate how amazingly simple it is to get started on some Ruby, I'll convert the shell script from my last blog post to a little recipe for chef. Same objective, we install a PEAR package &mdash; but it could be anything really.</p>

<p>Follow me.</p>

<h2>Shell</h2>

<p>This is the shell script from before:</p>

<pre class="sh_sh">
#!/bin/sh

apt-get update

apt-get install -y php5 php5-cli php-pear
hash -r

pear upgrade-all
pear install -f HTTP_Request2
</pre>

<h2>Ruby</h2>

<p>Create a cookbooks directory and create structure for your first cookbook in it:</p>

<pre class="sh_sh">
$ mkdir -p my-cookbooks/first/recipes/
</pre>

<p>Create a <code>default.rb</code> file with the following content:</p>

<pre class="sh_ruby">
# my-cookbooks/first/recipes/default.rb
execute "apt-get update"

packages = ["php5", "php5-cli", "php-pear"]

packages.each|p| do
  package p
end

execute "pear upgrade-all"
execute "pear install -f HTTP_Request2"
</pre>

<p>The recipe is later referred to as <code>first</code> or <code>first::default</code> (name of the recipe directory, name of the .rb).</p>

<p>It's so simple it hurts. ;)</p>

<h3>Step by step</h3>

<ol>
<li>I run <code>apt-get update</code> using Chef's execute resource.</li>
<li>I create an array of the packages (Arrays are ordered in Ruby, hashes are not (up until Ruby 1.9.x). Order is important here.)</li>
<li>I run <code>pear upgrade-all</code> using the execute resource.</li>
<li>I run <code>pear install</code> using the execute resource.</li>
</ol>

<h3>Vagrantfile</h3>

<p>The <code>Vagrantfile</code> looks slightly different when you provision with chef-solo:</p>

<pre class="sh_ruby">
Vagrant::Config.run do |config|

  config.vm.define :web do |web_config|
    web_config.vm.box       = "lucid64"
    web_config.vm.host_name = "web"

    web_config.vm.provision :chef_solo do |chef|
      chef.cookbooks_path = "PATH-TO-YOUR-COOKBOOKS"
      chef.add_recipe "first"
      chef.log_level = :debug
    end
  end

end
</pre>

<p>The important bit: the path to the location of your cookbooks &mdash; could be an <code>/absolute/path</code> or <code>./../relative/path</code>.</p>

<p>Because a Vagrantfile is essentially Ruby code, anything goes here.</p>

<h2>Further reading</h2>

<p>Getting started with Chef and Ruby can be intimidating or even frustrating at times. Google "chef cookbooks" and you know what I mean.</p>

<p>These links are what you need:</p>

<ul>
<li><a href="http://wiki.opscode.com/display/chef/Resources">Opscode's wiki with all available Chef resources</a></li>
<li><a href="http://apidock.com/ruby">A decent Ruby reference</a></li>
<li><a href="http://till.klampaeckel.de/blog/plugin/tag/chef">My blog</a> ;)</li>
</ul>

<h2>Fin</h2>

<p>Save, and enjoy &mdash; <code>vagrant up</code>.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=CcOMp9ADGuc:kzwU9e0v84E:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=CcOMp9ADGuc:kzwU9e0v84E:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=CcOMp9ADGuc:kzwU9e0v84E:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=CcOMp9ADGuc:kzwU9e0v84E:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=CcOMp9ADGuc:kzwU9e0v84E:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=CcOMp9ADGuc:kzwU9e0v84E:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/CcOMp9ADGuc" height="1" width="1"/>]]></content:encoded>

    <pubDate>Wed, 20 Jun 2012 11:45:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/189-guid.html</guid>
    <category>chef</category>
<category>chef-solo</category>
<category>devops</category>
<category>operations</category>
<category>opscode</category>
<category>pear</category>
<category>php</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/189-Vagrant-ShellProvisioner-vs.-Chef.html</feedburner:origLink></item>
<item>
    <title>Vagrant sans Ruby</title>
    <link>http://feedproxy.google.com/~r/till/~3/DBUD2TcGK6c/188-Vagrant-sans-Ruby.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/188-Vagrant-sans-Ruby.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=188</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=188</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>Development, testing, staging and production &mdash; this is how most people devide up different environments for application development.</p>

<p>Maintenance and setup of these environments is often not a trivial goal to achieve. Having worked with a couple different code bases and setups over the last decade, I often noticed things like environment specific hacks (<code>if ($env == 'testing') { ... }</code>) in application code and service configurations and a lot of manual labour all around. It's still very common that code deployed to staging does not work &mdash; but it worked for you right?</p>

<p>And it's also too common that members of a <em>team</em> do not know how something works because whoever set it up is currently out sick or on vacation.</p>

<p>My opinion is that the best setup currently available is something like: chef(-solo) on the server and Vagrant on the desktop. <em>Something like</em> because aside from chef, there is also puppet, cfengine and a couple others. Leaving specific projects aside, it really just boils down to <strong>automation</strong> (within reason).</p>

<p>Automation in my opinion is not just the easiest but the only viable way to setup development, testing, staging and production environments. Without some automation in place it's required that all team members know how all of it works when maybe that is not yet important and the end goal is that all environments actually resemble each other.</p>

<h2>Enter Vagrant</h2>

<p><a href="http://vagrantup.com">Vagrant</a> is a very nifty toolkit to bootstrap Virtualbox images. Bootstrapping means installing your application stack into one or multiple virtual machines in order to resemble production a lot better.</p>

<p>So a lot of times when I rave about how useful Vagrant and chef are, I get the crazy eye from PHP developers:</p>

<blockquote>
  <p>You want me to learn Ruby to setup my local development environment?</p>
</blockquote>

<p>My response:</p>

<ol>
<li>Do not fear the Ruby.</li>
<li>You don't have to. (Well, not a whole lot!)</li>
</ol>

<h2>Real talk</h2>

<p>First off, of course you need Virtualbox &mdash; get a download from <a href="https://www.virtualbox.org/wiki/Downloads">their website</a> or use your package manager.</p>

<p>Then, it's not possible to avoid Ruby a 100% &mdash; after all Vagrant is written in Ruby.</p>

<p>When you're on a Linux or a Mac, this is usually enough (you may need sudo unless you <acronym title="Ruby Version Manager"><em>RVM</em></acronym>):</p>

<pre class="sh_sh">
$ sudo gem install vagrant
</pre>

<p>For a sudo-less install, use RVM:</p>

<pre class="sh_sh">
$ sudo gem install rvm
...
$ rvm install
...
$ rvm use 1.8.7
...
</pre>

<p>When set up, this is what you do:</p>

<pre class="sh_sh">
$ gem install vagrant
...
</pre> 

<p>RVM allows me to run multiple versions of ruby side by side and also leverage local gem installs &mdash; per Ruby version. Think of it as a nifty way to run multiple PHP versions and have a PEAR install per version &mdash; similar projects exist for PHP today:</p>

<ul>
<li><a href="https://github.com/CHH/php-build">php_build</a></li>
<li><a href="http://sourceforge.net/p/phpfarm/wiki/Home/">phpfarm</a></li>
</ul>

<p>To learn more about RVM, visit <a href="http://www.rubyinside.com/rvm-ruby-version-manager-2347.html">their website</a>.</p>

<p>Once vagrant is installed, we can continue!</p>

<h2>ShellProvisioner for fun and profit</h2>

<p>So, in case you are more comfortable writing some shell script for the time being &mdash; Vagrant got you covered!</p>

<p>First off, create a new <code>Vagrantfile</code> for your project:</p>

<pre class="sh_sh">
$ mkdir -p Documents/workspaces/blog-example/
$ cd Documents/workspaces/blog-example
$ vagrant init lucid64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
</pre>

<p><code>lucid64</code> (Ubuntu Lucid 10.04, 64bit) is the name of one of my local box files for Vagrant. In case you haven't got a box yet, head over to <a href="http://vagrantbox.es">vagrantbox.es</a>. There are a couple images you (aka box files) you can download. To get the <code>lucid64</code> box, use the following command:</p>

<pre class="sh_sh">
$ vagrant box add lucid64 http://files.vagrantup.com/lucid64.box
</pre>

<p>Once you made it past <code>vagrant init</code>, you should have a <code>Vagrantfile</code> with a lot of stuff in there.</p>

<p>While it's not important for my tutorial, I recommend you review it some (other time). The created <code>Vagrantfile</code> contains examples for all the provisioners (puppet, chef and shell) and a couple other configuration options, etc..</p>

<p>Let's skip over this and get to the <code>ShellProvisioner</code>.</p>

<h3>This is all you need</h3>

<p>First off, let's create a shell script <code>setup.sh</code> in the same directory and put something like this in it:</p>

<pre class="sh_sh">
#!/bin/sh

apt-get update

apt-get install -y php5 php5-cli php-pear
hash -r

pear upgrade-all
pear install -f HTTP_Request2
</pre>

<p>What does it do?</p>

<ol>
<li>Update local package sources.</li>
<li>Install php5, cli interpreter and PEAR installer</li>
<li>reload environment</li>
<li>upgrade all installed PEAR packages</li>
<li>install PEAR <code>HTTP_Request2</code></li>
</ol>

<p>When you're done editing, make sure to <code>chmod +x</code> it.</p>

<p>Simple as that.</p>

<h3>Putting your shell script to use</h3>

<p>Replace your <code>Vagrantfile</code> with the following:</p>

<pre class="sh_ruby">
Vagrant::Config.run do |config|

  config.vm.box = "lucid64"
  config.vm.provision :shell, :path => "./setup.sh"

end
</pre>

<p>Then, run <code>vagrant up</code> and watch it provision. :)</p>

<p>Enter your virtual machine with <code>vagrant ssh</code> and verify <code>HTTP_Request2</code> is installed:</p>

<pre class="sh_sh">
$ vagrant ssh                                                                                                                                                               [14:16:08]
Linux lucid64 2.6.32-33-server #70-Ubuntu SMP Thu Jul 7 22:28:30 UTC 2011 x86_64 GNU/Linux
Ubuntu 10.04.3 LTS

Welcome to the Ubuntu Server!
 * Documentation:  http://www.ubuntu.com/server/doc
Last login: Thu Jul 21 14:08:15 2011 from 10.0.2.2
vagrant@lucid64:~$ pear list -c pear
Installed packages, channel pear.php.net:
=========================================
Package          Version State
Archive_Tar      1.3.10  stable
Console_Getopt   1.3.1   stable
HTTP_Request2    2.1.1   stable
Net_URL2         2.0.0   stable
PEAR             1.9.4   stable
Structures_Graph 1.0.4   stable
XML_Util         1.2.1   stable
</pre>

<p>Yay &mdash; your first steps into automation!</p>

<h2>Fin</h2>

<p>That's all for today &mdash; Vagrant with almost no Ruby. I hope this gets many more started!</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=DBUD2TcGK6c:Rg2ozsQdGgo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=DBUD2TcGK6c:Rg2ozsQdGgo:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=DBUD2TcGK6c:Rg2ozsQdGgo:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=DBUD2TcGK6c:Rg2ozsQdGgo:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=DBUD2TcGK6c:Rg2ozsQdGgo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=DBUD2TcGK6c:Rg2ozsQdGgo:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/DBUD2TcGK6c" height="1" width="1"/>]]></content:encoded>

    <pubDate>Tue, 05 Jun 2012 14:45:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/188-guid.html</guid>
    <category>chef</category>
<category>chef-solo</category>
<category>development</category>
<category>pear</category>
<category>php</category>
<category>ruby</category>
<category>vagrant</category>
<category>virtualbox</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/188-Vagrant-sans-Ruby.html</feedburner:origLink></item>
<item>
    <title>Composer and chef updates</title>
    <link>http://feedproxy.google.com/~r/till/~3/I3pBZYM66Mg/187-Composer-and-chef-updates.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/187-Composer-and-chef-updates.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=187</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=187</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>Here are some updates on my chef resource for <a href="http://getcomposer.org">composer</a>:</p>

<p>It started off with a <acronym title="Pull Request">PR</acronym> where I was asked to include code that would download (and <em>install</em>) a <code>composer.phar</code> if none existed. Not a bad idea in general, but also not something I want to impose on anyone, so I decided to create a new action (or in chef-lingo <em>goal</em>) instead.</p>

<h2>setup</h2>

<p>Up until now, my <code>php_composer</code> resource supported <code>:install</code> which runs <code>php composer.phar install</code> when a <code>composer.phar</code> is in the designated directory:</p>

<pre class="sh_ruby">
php_composer "/var/www/app" do
  action :install
end
</pre>

<p>Since an hour or so ago, it also supports <code>:setup</code>. And <code>:setup</code> will download composer for you if requested:</p>

<pre class="sh_ruby">
php_composer "/var/www/app" do
  action [:setup, :install]
end
</pre>

<p>Note: I'm not sure if this is a good idea for a couple of reasons &mdash; the most obvious one: getcomposer.org could be unavailable when you run your deployment. And then there may or may not be changes in the latest <code>composer.phar</code> which require you to make adjustments to your local <code>composer.json</code>. I prefer my deployments rock-solid to minimize side-effects! :)</p>

<h2>Getting started</h2>

<p>My resource will use composer but it's generally a chef-no-no to also install the tools required by the resource.</p>

<p>A small list of items you will need:</p>

<ul>
<li>php 5.3+</li>
<li>the Phar extension</li>
<li>git-core (and/or subversion)</li>
<li>unzip (or PHP's zip extension)</li>
</ul>

<p>For an idea how to set these up with chef, see my <a href="https://github.com/till/easybib-cookbooks/blob/master/vagrant-test/recipes/default.rb"><code>vagrant-test::default</code> recipe</a>.</p>

<h2>Roadmap</h2>

<p>I haven't really decided on a roadmap, but here are a few things I want to implement in the coming weeks:</p>

<ul>
<li>If you're feeling frisky: support for <code>php composer.phar self-update</code>.</li>
<li>Support for <code>php composer.phar update</code>!</li>
<li>An attribute to provide the location of a phar (e.g. local or network file-system, CDN, etc.).</li>
<li>Support for <code>--install-dir</code>.</li>
</ul>

<p>I'm open to contributions on any of these items, my requirements are:</p>

<ul>
<li>Code must be robust.</li>
<li>Code must be robust.</li>
<li>Your additions should work on Ubuntu and Linux in general. :)</li>
</ul>

<h2>Fin</h2>

<p>That's all. Check it out on <a href="https://github.com/till/easybib-cookbooks/tree/master/php">Github</a>!</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=I3pBZYM66Mg:0knRB9SvqdE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=I3pBZYM66Mg:0knRB9SvqdE:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=I3pBZYM66Mg:0knRB9SvqdE:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=I3pBZYM66Mg:0knRB9SvqdE:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=I3pBZYM66Mg:0knRB9SvqdE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=I3pBZYM66Mg:0knRB9SvqdE:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/I3pBZYM66Mg" height="1" width="1"/>]]></content:encoded>

    <pubDate>Sun, 03 Jun 2012 18:20:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/187-guid.html</guid>
    <category>chef</category>
<category>composer</category>
<category>php</category>
<category>vagrant</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/187-Composer-and-chef-updates.html</feedburner:origLink></item>
<item>
    <title>Zend Framework: CRUD</title>
    <link>http://feedproxy.google.com/~r/till/~3/Z6e0vv1sYT4/183-Zend-Framework-CRUD.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/183-Zend-Framework-CRUD.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=183</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=183</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p><strong>Update, 2012-05-24</strong>: Fixed some typos and added the screenshot (eyecandy!).</p>

<p>I think it took me (or us) a couple attempts to get this <em>right</em> &mdash; let me introduce you to Zf_Crud, a CRUD controller for the Zend Framework.</p>

<h2>What's CRUD?</h2>

<p>CRUD is an acronym and stands for:</p>

<ul>
<li>Create</li>
<li>Read</li>
<li>Update</li>
<li>Delete</li>
</ul>

<p>A general purpose for CRUD are administrative interfaces &mdash; view records, create them, update them or delete them. Think of phpMyAdmin as a very general purpose CRUD interface. One you need some SQL-fu for.</p>

<p>In my experience, such interfaces are most likely or often the very last item on a project. Not too many people (myself included) like to build these interfaces primarily because we have build them before. They are necessary non-the-less: not everybody on a project is a developer and feels comfortable writing SQL queries into phpMyAdmin to get data out (or in).</p>

<h2>Zf_Crud</h2>

<p>Zf_Crud aims to provide you with an interface for any table possible &mdash; think of it as a phpMyAdmin more tailored towards your data and (thanks to Twitter Bootstrap and the <a href="https://github.com/easybib/EasyBib_Form_Decorator">Easybib_Form_Decorator</a>) prettier!</p>

<h3>Example</h3>

<p>Note: We haven't setup a PEAR package yet. I'll get to it <em>soon</em>, but feel free to submit a PR with a <code>package.xml</code>.</p>

<h4>Install</h4>

<p>The only technical dependencies are PHP 5.3+ and the <code>Easybib_Form_Decorator</code>:</p>

<pre class="sh_sh">
$ pear channel-discover easybib.github.com/pear
...
$ pear install easybib/Easybib_Form_Decorator
...
</pre>

<p>The other dependency is a working Zend Framework (1) application and an idea what you're doing.</p>

<h5>Setup</h5>

<p>To setup <code>Zf_Crud</code>, clone (or export) it into your local vendor library:</p>

<p>Our Zend Framework projects have the following structure:</p>

<ul>
<li>app/modules/&lt;module&gt;/library/vendor</li>
</ul>

<p>To export with git, just do the following:</p>

<pre class="sh_sh">
$ cd app/modules/foo/library/vendor
$ git clone git://github.com/lagged/Zf_Crud.git ./Zf_Crud
...
$ cd Zf_Crud &&amp; rm -rf .git
</pre>

<p>Then check it into your own VCS.</p>

<p>If you don't have git (and cannot install it), get <a href="https://github.com/lagged/Zf_Crud/downloads">a download of <code>Zf_Crud</code></a> from Github.</p>

<h5>Composer, you ask?</h5>

<p>With the initial public release, we also added composer:</p>

<pre class="sh_json">
{
  "require": {
    "php": ">=5.3.0",
    "lagged/Zf_Crud": "0.5.1"
  }
}
</pre>

<h4>Code</h4>

<p>Working with <code>Zf_Crud</code> should be super-simple and easy!</p>

<p>In your module (e.g. foo), create a controller in <code>app/modules/foo/controllers/AdminController.php</code>.</p>

<pre class="sh_php">
&lt;?php
use Lagged\Zf\Crud\Autoload as CrudAutoload;
use Lagged\Zf\Crud\Controller as CrudController;

require_once dirname(__DIR__) . '/library/vendor/Zf_Crud/library/Autoload.php';
CrudAutoload::register();

class Foo_AdminController extends CrudController
{
    protected $model = 'Foo_Model_SomeTable';
    protected $title = 'My Interface';
}
</pre>

<p>Next, create a plain model using <code>Zend_Db_Table_Abstract</code>:</p>

<pre class="sh_php">
&lt;?php
class Foo_Model_SomeTable extends Zend_Db_Table_Abstract
{
    protected $_name = 'some_table';
}
</pre>

<p>The (naming convention in ZF1 is not PSR-0 and a little weird. In case it's not obvious: the) model should live in: <code>app/modules/foo/models/SomeTable.php</code>.</p>

<p>Last but not least: <code>Zend_Db_Table</code> means RDBMS. <code>Zf_Crud</code> expects a <code>Zend_Registry</code>-key called <code>dbAdapter</code> to work. If your's is called differently, skip to the "Convention over Configuration" section.</p>

<p>Anyway &mdash; once these two files created (and assuming the rest is setup correctly), you should see something like the following:</p>

<p><a href="http://www.flickr.com/photos/till/7261150958/" title="screenshot-zf_crud by illtillwillkillbill, on Flickr"><img src="http://farm8.staticflickr.com/7220/7261150958_7abc7c6949_c.jpg" width="800" height="390" alt="screenshot-zf_crud"></a></p>

<h2>Convention over configuration</h2>

<p>I'm a fan of convention over configuration and it's the approach we selected when we build <code>Zf_Crud</code>.</p>

<p>The idea is that it should work out of the box without setting up a huge application.ini or DIC, in case you want to tinker with it when you're up and running, here is how.</p>

<p>Since you're extending from the <code>Lagged\Zf\Crud\Controller</code>, this controller has a few configuration options. Configuration is probably too advanced since these are essentially <a href="https://github.com/lagged/Zf_Crud/blob/master/library/Controller.php#L27-L96">a bunch of class-properties</a> you can overwrite in your class or via <code>init()</code>:</p>

<pre class="sh_php">
&lt;?php
// setup here
class Foo_AdminController extends CrudController
{
    protected $model = 'Foo_Model_SomeTable';
    protected $dbAdapter = 'db';
}
</pre>

<p>Some of the gems are:</p>

<ul>
<li><code>$where</code>: a <code>WHERE</code>-clause for the data query</li>
<li><code>$order</code>: column to order by</li>
<li><code>$hidden</code>: hide these columns from display</li>
<li><code>$count</code>: number of items per page</li>
</ul>

<h2>Fin</h2>

<p>We've been using this code for a couple months now for various items. We recently tagged an early 0.5.1 which suggests that this code is still a <acronym title="Work In Progress">WIP</acronym> and a moving target. The configuration bits are not too <strike>great</strike> elegant yet. So there are a lot of rough edges to be aware of.</p>

<p>The bottom line is that Zf_Crud has been good for us since it allows us to take the pain out of building administrative interfaces. In most cases it's setup in an hour tops and then we can move on to build something more interesting than a couple forms and views to display and edit data.</p>

<p>If you have anything to add &mdash; comments and pull-requests welcome!</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=Z6e0vv1sYT4:ZtvodOqCHrM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=Z6e0vv1sYT4:ZtvodOqCHrM:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=Z6e0vv1sYT4:ZtvodOqCHrM:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=Z6e0vv1sYT4:ZtvodOqCHrM:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=Z6e0vv1sYT4:ZtvodOqCHrM:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=Z6e0vv1sYT4:ZtvodOqCHrM:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/Z6e0vv1sYT4" height="1" width="1"/>]]></content:encoded>

    <pubDate>Mon, 21 May 2012 17:25:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/183-guid.html</guid>
    <category>bootstrap</category>
<category>php</category>
<category>twitter</category>
<category>zend-framework</category>
<category>zend_db</category>
<category>zend_form</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/183-Zend-Framework-CRUD.html</feedburner:origLink></item>
<item>
    <title>Expose services via an ssh tunnel</title>
    <link>http://feedproxy.google.com/~r/till/~3/LzkSxsBjqsU/186-Expose-services-via-an-ssh-tunnel.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/186-Expose-services-via-an-ssh-tunnel.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=186</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=186</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p><em>Ever since I remember, I have this inability to learn the most basic things, until I actually write down a couple notes or instructions somewhere. This is one of these <strike>notes</strike> blog posts &mdash; so in case it's too basic, just skip over it. Or bear with me.</em></p>

<p>ssh tunnels &mdash; useful and powerful. They can help me with all kinds of trickery &mdash; e.g. usually for remoting through a tight firewall setup to access remote resources. So point taken there are a lot of GUIs for this, but if you spend a couple minutes with the ssh man page, you will realize how amazingly simple they are.</p>

<p>My example for an ssh tunnel put to good use is our production gearman setup: The gearmands runs isolated on EC2. On the upside: <em>pretty</em> secure, but the downside is that the service is unavailable when you need to a little live data for some local tests. When we added an interface to get more visibility into the processes we push through gearman, of course we couldn't access it.</p>

<h2>A tunnel to the rescue!</h2>

<p>Consider this:</p>

<pre class="sh_sh">
till@macbook$ screen -S gearman-ssh-tunnel
till@macbook$ ssh -L 2222:127.0.0.1:4730 production-gearmand
Linux Ubuntu SMP AMD64

Last login: Sat May  5 16:22:47 2012 from YYY.YYY.YYY.YYY
till@production-gearmand:~$
</pre>

<p>("ctrl + a + d", to detach from screen.)</p>

<p>So what does that do?</p>

<p>First off, we are starting a session in screen: it's called "gearman-ssh-tunnel". You could use tmux as well, but screen works just as nice.</p>

<p>The consecutive command maps port <code>2222</code> on my (local) <em>macbook</em> to a service running on the server <code>production-gearmand</code> (this is via <code>.ssh/config</code>) but only listens on <code>127.0.0.1:4730</code>.</p>

<p>Your <code>.ssh/config</code> could look like this:</p>

<pre class="sh_sh">
Host production-gearmand
HostName ec2-XXX-XXX-XXX-XXX.compute-1.amazonaws.com
User till
IdentityFile ~/.ssh/gearman.pem
</pre>

<p>If I wanted to connect to this server in a PHP-script on my <em>macbook</em>, I would use the following configuration:</p>

<pre class="sh_php">
&lt;?php
$client = new GearmanClient;
$client->addServer('127.0.0.1', 2222);
var_dump($client->echo('ehlo'));
?>
</pre>

<p>Run it from the terminal:</p>

<pre class="sh_sh">
till@macbook$ php gearman-ping.php
bool(true)
</pre>

<p>Success!</p>

<h3>Need to stop the tunnel?</h3>

<p>The following command lets you resume your session with screen:</p>

<pre class="sh_sh">
till@macbook$ screen -r gearman-ssh-tunnel
...
</pre> 

<p>Type <code>exit</code> twice, or hit "ctrl + d" to log off the server and "ctrl + d" again to kill the screen. Done.</p>

<h2>Fin</h2>

<p>Bonus points if you use this from your VM in vagrant. But otherwise, that's all for today.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=LzkSxsBjqsU:qnJGxl57CNk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=LzkSxsBjqsU:qnJGxl57CNk:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=LzkSxsBjqsU:qnJGxl57CNk:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=LzkSxsBjqsU:qnJGxl57CNk:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=LzkSxsBjqsU:qnJGxl57CNk:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=LzkSxsBjqsU:qnJGxl57CNk:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/LzkSxsBjqsU" height="1" width="1"/>]]></content:encoded>

    <pubDate>Mon, 07 May 2012 16:52:00 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/186-guid.html</guid>
    <category>gearman</category>
<category>php</category>
<category>screen</category>
<category>ssh</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/186-Expose-services-via-an-ssh-tunnel.html</feedburner:origLink></item>
<item>
    <title>Your own personal opinion</title>
    <link>http://feedproxy.google.com/~r/till/~3/atbnnEC0Z98/185-Your-own-personal-opinion.html</link>
            <category>More than 140 characters</category>
    
    <comments>http://till.klampaeckel.de/blog/archives/185-Your-own-personal-opinion.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=185</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=185</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>I guess most of you have probably come across blogs which have one of these disclaimers:</p>

<blockquote>
  <p>This is my personal opinion, and not my employers.</p>
</blockquote>

<p>Here are my thoughts on these.</p>

<h2>Perspective</h2>

<p>I think there is always a certain point of view when you read these things.</p>

<ol>
<li><p>If you have to put this on your personal blog because your opinion and your employer's opinion differ so much, it might be time to find another job. For a second, let's focus on other areas in life. We are clearly not referring to the technical details of an implementation, programming languages or your open source license of choice. These are all non-issues where these kind of disclaimers are not necessary. So if you catch my drift &mdash; I don't even remotely understand how anyone can possibly spend eight hours a day working for someone who you disagree with on the really important issues.</p></li>
<li><p>If you happen to be the employer yourself or hold a management position in that company and still try to distance yourself from your <em>own</em> shop, then who are you kidding really? I find it hard to believe that for example people oppose war when they are at home, but during the work day they manage to run a company who's a weapon supplier.</p></li>
</ol>

<h2>Fin</h2>

<p>There is absolutely nothing wrong with expressing your opinion. You can even donate all your money if you think that is really necessary. I really don't care if you're in favour or against something.</p>

<p>But please get real: your own statements will always reflect on the place you work at. In one way or another.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=atbnnEC0Z98:t6K1KbJ7jtA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=atbnnEC0Z98:t6K1KbJ7jtA:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=atbnnEC0Z98:t6K1KbJ7jtA:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=atbnnEC0Z98:t6K1KbJ7jtA:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=atbnnEC0Z98:t6K1KbJ7jtA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=atbnnEC0Z98:t6K1KbJ7jtA:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/atbnnEC0Z98" height="1" width="1"/>]]></content:encoded>

    <pubDate>Fri, 06 Apr 2012 15:50:37 +0200</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/185-guid.html</guid>
    <category>opinion</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/185-Your-own-personal-opinion.html</feedburner:origLink></item>
<item>
    <title>Hosted MySQL: Amazon RDS (and backups)</title>
    <link>http://feedproxy.google.com/~r/till/~3/cWeIhU90jZM/179-Hosted-MySQL-Amazon-RDS-and-backups.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/179-Hosted-MySQL-Amazon-RDS-and-backups.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=179</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=179</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>Among all the different technologies in our stack, we also use MySQL. While we still run MySQL (or Percona-Server) ourselves, we selected a managed solution to power parts of our production infrastructure: a Multi-AZ setup with Amazon's RDS.</p>

<p>AZ is Amazon-speak for "availability zone", essentially a datacenter. RDS stands for: Relational Database Service.</p>

<p>Judging from my experience with our own setups where EBS is in the mix, I have to say that Amazon does an outstanding job hiding these potential issues with RDS from us. Looking at the price tag of the setup can be intimidating at first, but as far as <acronym title="Total Cost of Ownership">TCO</acronym> is concerned, RDS is the complete package: managed from every which way and painless for the most part.</p>

<h2>RDS in a nutshell</h2>

<p>RDS is pretty awesome &mdash; it's basically a highly available MySQL setup with backups and optional goodness like read-slaves. RDS is one of the best services as far as Amazon Webservices are concerned: 90% of what anyone would need from RDS, Amazon allows you to do with a couple clicks. For tuning, it gets a little less shiny and maybe even messy, but even changing parameters is possible.</p>

<p>Another pretty important feature is growing and shrinking RDS. Change your storage and either apply the change <em>right away</em> or wait for your next maintenance window. It should be noted that these changes are rarely instant (or "right away"), which doesn't make it any less awesome. So even though for example resizing the storage is not an instant operation (of course), it still puts a whole new definition into the word elastic.</p>

<h4>The package</h4>

<p>A standard RDS setup gives you a managed database instance with a DNS hostname and database credentials to log in.</p>

<p>Access from instances is granted using database security groups, which work just like the regular security groups (on AWS). In non-AWS-language, this translates to firewall policies.</p>

<h3>Pricing</h3>

<p>As far as pricing is concerned, AWS is always a little tough to understand: the baseline is 0.88 USD per hour for a multi-az deployment which totals to 633.6 USD a month (large instance class). Since we opted for reservation (a 1,200 USD one time fee for a three (3) year term), we were able to drop that price to 0.56 USD per hour.</p>

<p>Aside from instance costs there are storage costs as well: 0.20 USD per GB (100 GB will cost you and me about 20 USD) and 0.10 USD per million I/O requests (aka the "i/o rate"). On our multi-az RDS we selected 100 GB for total storage initially but since we currently use only about 60 GB, we just end up paying about 12 USD per billing period.</p>

<p>While storage costs are somewhat easy to predict, the "i/o rate" is not. But it's also not a major factor. I'm unable to provide exact numbers currently because we have three RDS servers (1 multi-az deployment, 1 read-slave and another single-az deployment) and the numbers are aggregated on the billing statement but our total is 368,915,692 IOs which runs at roughly 36 USD per month.</p>

<h3>Vendor lockin</h3>

<p>Anyway &mdash; if RDS is awesome, what's the catch? A closed environment.</p>

<p>The primary advantage and disadvantage of RDS is that we don't get access to the server <strong>and our own backups</strong>.</p>

<p>Of course there are backups and we can use them to restore (or rollback) our RDS setup from <em>within</em> AWS. There are options using the AWS console and I believe using their API as well. But in the end there is no way to export this backup and load it into a non-RDS-setup. And add to that: replicating from or into RDS is not possible either. Which makes migrations and backups an unnecessary pain in the butt.</p>

<p>Aside from not getting access to our own backup, we also don't get access to the actual instances. Which makes sense for AWS, but it means we need to rely on in my opinion questionable metrics like Cloudwatch. Questionable because there is no way for the customer to verify the data. AWS uses their own metrics (and scale) and it's often not obvious to me how well Cloudwatch works even on regular AWS EC2 instance.</p>

<p>I've seen instances which became unavailable, but Cloudwatch is reporting A-OK (<em>green</em>). I'm not sure how beta Cloudwatch is, but we decided on <a href="http://silverline.librato.com">Silverline</a> (more on that in another blog post) for instance monitoring. Since Silverline requires a local client, it's unfortunately not an option for RDS.</p>

<h3>What's pain?</h3>

<p>Aside from the monitoring and backup quirks, one of the real pain points of Amazon RDS is that a lot of the collective MySQL knowledge is not available to us. The knowledge which is manifested in books, blogs, various monitoring solutions and outstanding tools like Percona's backup tools are not available to people who run Amazon RDS setups.</p>

<!--
[pt-online-schema-change][online-schema]
[online-schema]: http://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html
-->

<p>Whatever the (technical) reasons for all this may be, they pain me at least a little and should be discussed when Amazon RDS is evaluated to replace MySQL infrastructure.</p>

<h2>MySQL and backups</h2>

<p>I mentioned backups! First off, I hope ya'll are doing them! ;-)</p>

<p>For many, the preferred way to do MySQL-backups is <code>mysqldump</code>. It's pretty simple to use:</p>

<pre class="sh_sh">
$ mysqldump -u root -pPASS -A > my_backup.sql
</pre>

<p>This command essentially dumps all (<code>-A</code>) databases from your MySQL server.</p>

<p>mysqldump is an <em>OK</em> solution as long as you take a few things in mind:</p>

<ul>
<li>Create your backups during a period where there is the least activity &mdash; typically the night.</li>
<li>There will be a slight bump, but hope that your database is small enough so no one notices.</li>
</ul>

<p>With a larger database or most databases with lots of read and write activity, this is almost impossible to achieve. For a snapshot to be consistent, table locks are used and that usually stalls access to any application which relies on your database.</p>

<p>Of course there is <a href="http://www.percona.com/doc/percona-xtrabackup/">Percona's xtrabackup</a> (which is outstanding), but with RDS, that is not an option either.</p>

<h2>Read-slave to the rescue</h2>

<p>Typically people will use a read-slave with MySQL to offload read queries from the master. I haven't done any tests on how far these typically lag behind with Amazon RDS, but I am going to use my RDS read-slave for something else: backup.</p>

<p>Adding a read-slave is easy:</p>

<ol>
<li>Log into the AWS Console and go to RDS.</li>
<li>Select your RDS server and click 'add read replica' above</li>
</ol>

<p>The operation will likely take a while. The timeframe depends on the type of instance and the amount of storage provisioned. In my case I selected a small instance and it assigned 100 GB of storage to match my RDS. Small instances boot notoriously long &mdash; the entire operation completed in a little over ten minutes.</p>

<p>On a side-note: read-replicas allow you to scale RDS beyond availability zones (AZ). But you should be aware that traffic across different AZ is billed to the customer.</p>

<h3>Costs</h3>

<p>A small instance costs roughly 76 USD/month (excluding storage, I/O rate and bandwidth), which by itself is not bad for a fully managed server which I basically setup with two or three clicks. Since we plan to do backup on a regular basis, we will buy a coupon to reserve the instance which cuts down costs tremendously and generally makes the AWS cloud very affordable.</p>

<h2>Amazon RDS, quo vadis?</h2>

<p>I mentioned a little vendor-lockin with the service and the little visibility from the outside.</p>

<p>In theory, this should not matter &mdash; however there are <a href="https://forums.aws.amazon.com/thread.jspa?threadID=86553&amp;tstart=25">more</a> <a href="https://forums.aws.amazon.com/thread.jspa?threadID=86790&amp;tstart=75">than</a> <a href="https://forums.aws.amazon.com/thread.jspa?threadID=88248&amp;tstart=0">a</a> <a href="https://forums.aws.amazon.com/thread.jspa?messageID=267526#267526">few</a> <a href="https://forums.aws.amazon.com/thread.jspa?threadID=87269&amp;tstart=25">issues</a> you should be aware of. I don't want to mention them to stomp on Amazon &mdash; RDS is still in beta after all. But you should be aware of them to get a complete picture.</p>

<p>Pretty questionable is the way some of these issues are handle: not at all or in private messages. AWS is not always at fault here since I imagine pretty often the customer forgets to update the ticket when the issue is only temporary because their focus shifts to other areas.</p>

<p>But one of the core problem with customer service problem all over AWS is that customers have to resort to posting on a forum with no guaranteed response or have to buy a support contract which includes answers like "we fixed it". The first response is usually that more details are needed (Maybe customer accounts on the forum are not linked to AWS accounts on the <em>inside</em>?) and off it goes into private mode.</p>

<p>My wish is that these situations across all AWS services are handled more transparent in the future so people see development and evolution of the service which means that a trust-worthy platform is be build.</p>

<h2>Fin</h2>

<p>I've been thinking about my final statement for a while. If anything right now, I would be more in favour of Amazon RDS.</p>

<p>Amazon RDS is an extremely interesting product &mdash; the beta-tag is even more impressive. It'll be interesting to see what it will offer once Amazon pronounces it stable.</p>

<p>As for the future of our RDS-setups: they are not gonna go away soon. One of our objectives for 2012 is stabilizing across all products and infrastructure underneath. I think this will include some sort of integration with our external monitoring system to at least try to make sense of things like Cloudwatch and to be able to correlate with other events from all over production.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=cWeIhU90jZM:A9KdE4-Gntg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=cWeIhU90jZM:A9KdE4-Gntg:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=cWeIhU90jZM:A9KdE4-Gntg:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=cWeIhU90jZM:A9KdE4-Gntg:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=cWeIhU90jZM:A9KdE4-Gntg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=cWeIhU90jZM:A9KdE4-Gntg:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/cWeIhU90jZM" height="1" width="1"/>]]></content:encoded>

    <pubDate>Sat, 17 Mar 2012 17:53:00 +0100</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/179-guid.html</guid>
    <category>amazon</category>
<category>aws</category>
<category>backup</category>
<category>database</category>
<category>mysql</category>
<category>php</category>
<category>rds</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/179-Hosted-MySQL-Amazon-RDS-and-backups.html</feedburner:origLink></item>
<item>
    <title>Deploying PHP applications: PEAR and composer resources for chef</title>
    <link>http://feedproxy.google.com/~r/till/~3/fFclOFO7q5M/180-Deploying-PHP-applications-PEAR-and-composer-resources-for-chef.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/180-Deploying-PHP-applications-PEAR-and-composer-resources-for-chef.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=180</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=180</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>This is something <em>experimental</em> I have been working on for our chef deployments. So the objective was/is to find a sane way to install PEAR packages and install dependencies with composer.</p>

<h2>execute in chef recipes</h2>

<p>In chef recipes, almost everything is a resource. In case you're just getting started with Chef, a list of current resources is available on the <a href="http://wiki.opscode.com/display/chef/Resources">Opscode Wiki</a>. It's a link I put in my browser bar since I frequently work on chef recipes.</p>

<p>Some examples for resources are:</p>

<ul>
<li>package (to install software)</li>
<li>cron (setup a crontab)</li>
<li>directory (create directories)</li>
<li>template (install <em>customized</em> configuration files)</li>
<li>user and group (to create users and groups)</li>
<li>mdadm (to setup a RAID)</li>
</ul>

<p>The above list are examples &mdash; so there is more. But if there isn't a designated resource, you can always use an <code>execute</code> block.</p>

<p>An example for an execute block is the following:</p>

<pre class="sh_ruby">
execute "discover a pear channel" do
  command "pear channel-discover easybib.github.com/pear"
end
</pre>

<p>This works pretty well, but it is also not very robust.</p>

<h3>Fail hard</h3>

<p>By default whenever a command fails, chef fails hard.</p>

<p>To illustrate what I'm talking about, let's test and execute the command from our execute block multiple times on the shell to see its exit status (<code>$?</code>):</p>

<pre class="sh_sh">
till:~/ $ pear channel-discover easybib.github.com/pear
Adding Channel "easybib.github.com/pear" succeeded
Discovery of channel "easybib.github.com/pear" succeeded
till:~/ $ echo $?
0
till:~/ $ pear channel-discover easybib.github.com/pear
Channel "easybib.github.com/pear" is already initialized
till:~/ $ echo $?
1
</pre>

<p>So whenever a command returns not <code>0</code>, chef will bail.</p>

<p>One solution is to brute-force your way through these things with <code>ignore_failure true</code> in your execute block. But that's usually not a great idea either because it hides other issues from you (and me) when we need to debug this later on.</p>

<p>For example, if this PEAR channel is unavailable during your next chef-run, it would be much, much harder to find the root cause as of why the install commands failed.</p>

<p>Another solution is using the <code>not_if</code> or <code>only_if</code> options with <code>execute</code>:</p>

<pre class="sh_ruby">
execute "discover a pear channel" do
  command "pear channel-discover easybib.github.com/pear"
  not_if do
    `pear channel-info easybib.github.com/pear`
  end
end
</pre>

<p>If the command wrapped in <code>not_if</code> succeeds (success is exit status), we would skip the execute block.</p>

<h3>Optimize?</h3>

<p>Since I discovered <code>not_if</code> and <code>only_if</code>, it allows me write recipes which work in most cases. More robust code, which allows me to re-execute recipes on already running instances. So for example when I update a recipe or configuration file which is distributed through a recipe I can re-run the entire recipe and it will not fail but instead complete successfully.</p>

<p>One problem remains with this approach I end up doing the same checks again and again.</p>
 <br /><a href="http://till.klampaeckel.de/blog/archives/180-Deploying-PHP-applications-PEAR-and-composer-resources-for-chef.html#extended">Continue reading "Deploying PHP applications: PEAR and composer resources for chef"</a>
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=fFclOFO7q5M:YdfrBhFyZE8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=fFclOFO7q5M:YdfrBhFyZE8:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=fFclOFO7q5M:YdfrBhFyZE8:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=fFclOFO7q5M:YdfrBhFyZE8:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=fFclOFO7q5M:YdfrBhFyZE8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=fFclOFO7q5M:YdfrBhFyZE8:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/fFclOFO7q5M" height="1" width="1"/>]]></content:encoded>

    <pubDate>Thu, 23 Feb 2012 17:52:00 +0100</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/180-guid.html</guid>
    <category>chef</category>
<category>chef-solo</category>
<category>composer</category>
<category>deployment</category>
<category>open-source</category>
<category>opscode</category>
<category>pear</category>
<category>php</category>
<category>vagrant</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/180-Deploying-PHP-applications-PEAR-and-composer-resources-for-chef.html</feedburner:origLink></item>
<item>
    <title>Wordpress and disqus and post loops</title>
    <link>http://feedproxy.google.com/~r/till/~3/kIJhRBcWHUs/177-Wordpress-and-disqus-and-post-loops.html</link>
    
    <comments>http://till.klampaeckel.de/blog/archives/177-Wordpress-and-disqus-and-post-loops.html#comments</comments>
    <wfw:comment>http://till.klampaeckel.de/blog/wfwcomment.php?cid=177</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://till.klampaeckel.de/blog/rss.php?version=2.0&amp;type=comments&amp;cid=177</wfw:commentRss>
    

    <author>nospam@example.com (Till Klampaeckel)</author>
    <content:encoded><![CDATA[
    <p>First off &mdash; I don't have too much experience with disqus on Wordpress but when I looked at the code(-quality), disqus is one of the best plugins for Wordpress to date. I looked only very briefly, but I found it to be very clean, well documented and well <em>architected</em>. Good job, disqus!</p>

<p>In case you happen to dive into Wordpress plugins in 2012, you will see that this is unfortunately not a given. Even (or maybe especially) the commercially available plugins to extend Wordpress are a source for PHP worst practices and contain all kinds of examples how not to write PHP. Part of it, is the platform, the rest is unexperienced developers or general lazyness.</p>

<p>Anyhow! Enough the rant!</p>

<h2>Disqus and the loop</h2>

<p>In Wordpress, <a href="http://codex.wordpress.org/The_Loop">almost everything is a loop</a>. If you'd like to display a list of posts somewhere (for whatever reason), a loop is executed. The advantage (vs. a straight SQL query) range from all kinds all things like caching to convenience. When said hook is used different actions (plugin hooks) and filters are run. So even when the objective is to just collect data (and not even display it) &mdash; for example for an Ajax request/response &mdash; all these things happen in the back.</p>

<p>So in this case, whenever a loop is used, disqus will attempt to add their JavaScript to it. Usually, to display a comment form, counts and what not.</p>

<h3>Problem?</h3>

<p>The problem is, that the JavaScript code is then added all over and especially with Ajax you end up with the same code multiple times but add to that the cost of transport.</p>

<h3>Solution!</h3>

<p>Here's a simple workaround to prevent that.</p>

<p>Stick the following code into your template when you do not want the code to appear:</p>

<pre class="sh_php">
&lt;?php
if (has_action('loop_end', 'dsq_loop_end')) {
    remove_action('loop_end', 'dsq_loop_end');
}
</pre>

<p>Yay! Happyness!</p>

<h2>Fin</h2>

<p>Filters and hooks are really what make Wordpress worth while. Period. In case you happen to do development for it, I urge you to take a look at them. I have a couple more ideas for small posts &mdash; stay tuned.</p>
 
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/till?a=kIJhRBcWHUs:kRNEqf2IRng:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/till?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=kIJhRBcWHUs:kRNEqf2IRng:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/till?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=kIJhRBcWHUs:kRNEqf2IRng:DTerMVx4NQQ"><img src="http://feeds.feedburner.com/~ff/till?i=kIJhRBcWHUs:kRNEqf2IRng:DTerMVx4NQQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/till?a=kIJhRBcWHUs:kRNEqf2IRng:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/till?i=kIJhRBcWHUs:kRNEqf2IRng:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/till/~4/kIJhRBcWHUs" height="1" width="1"/>]]></content:encoded>

    <pubDate>Mon, 09 Jan 2012 02:36:00 +0100</pubDate>
    <guid isPermaLink="false">http://till.klampaeckel.de/blog/archives/177-guid.html</guid>
    <category>disqus</category>
<category>php</category>
<category>plugin</category>
<category>qa</category>
<category>wordpress</category>

<feedburner:origLink>http://till.klampaeckel.de/blog/archives/177-Wordpress-and-disqus-and-post-loops.html</feedburner:origLink></item>

</channel>
</rss>
