Thursday, January 30, 2014

vmconnect

I wrote a script called _vmconnect that either simply logs into a VirtualBox guest, or starts it headlessly first and then logs in.  With _vmconnect set as the command for an iTerm2 profile, I can open the session without caring whether the machine is running or not.

The full script became a bit unwieldy for a blog post (and I didn't want to maintain it forever within Blogger), so I published it to github.  You'll find it in my bin repository: https://github.com/sapphirecat/bin

Enjoy!

Update: _vmconnect version 1.0.3 fixes a possible data loss: if the VM was booted by _vmconnect, terminating the ssh connection it logged in with would also abort the VM.  You SHOULD pull if you are using _vmconnect 1.0.2 or prior (check your _vmconnect --version output!)

Friday, January 17, 2014

Brief notes on Packer

packer.io says they build identical images for multiple platforms such as EC2, VirtualBox, VMWare, and others (with even more on the way via plugins).  However, it's more about customizing a machine already running than it is about fabricating a new machine out of whole cloth and distributing the result to disparate services.  That is, the AMI builder starts from another AMI; the VirtualBox builder starts from an installer ISO (with commands to make the install unattended) or an OVF/OVA export and customizes that to produce an output OVF/OVA export.  This is, as I understand it, in contrast to Vagrant or Docker images, which build one image that runs on many systems.

Packer is about customizing existing machines that already exist in some form, then building the result as a new image for the same system.  If you're really interested in taking "one base image" to many environments, then Docker is probably the better choice there.  It's built on Linux Containers, where the virtualization happens inside the host kernel instead of a dedicated hypervisor, which makes it possible to run a bit-for-bit identical container both  inside EC2 (even PV) and inside VirtualBox.

In another contrast to vagrant, packer supports a shell provisioner that lets you upload a shell script and run wild.  Last I knew, which is probably woefully out of date, vagrant supported puppet by default and chef-solo as an option.  Since I happen to have an existing build system constructed around shell scripts for provisioning (with a dash of perl or php for the trickier parts), albeit specifically for EC2 instances, packer might suit my codebase and philosophy a little better.

Put another way, and beating the dead horse a bit more, Vagrant and Docker are about creating One True Image and later instantiating that single image on different underlying services (including EC2 and VirtualBox).  Packer is about coordinating the construction of native images for those services; it's about making predictable, repeatable changes to a running instance in the service and exporting the result for later use.

Unrelated to the above, packer is written in go 1.2 and distributes binaries for various platforms.  Docker is also written in go (but I'm not sure of the version target), leaving Vagrant in Ruby as the odd man out.  Having been subjected to rvm to install octopress at one point, I'm much more amenable to smaller dependency footprints... like static binaries produced by Go.  (Then again, I heard later that everyone hates rvm.)

Update 14 Feb 2014: I thought of one more useful contrast among the projects.  Docker and Packer are aimed toward building immutable, self-contained images.  Vagrant is focused on setting up (repeatable) development environments--particularly, in how it includes only the VirtualBox provider by default, and uses shared folders to persist changes as you develop.

Monday, January 13, 2014

Event Handler Optimization

I've been coding with jQuery for a long time.  I'm pretty sure 1.3 was new when I started.

One of the things that had always been "good enough" for me was to attach event handlers en masse directly to the elements involved: $('a.history').click(popupHistory);

It so happens that if the page is a table of 50 customers in the search result, each with a history link, then 50 handlers get bound.  jQuery takes care of all the magic, but it can't really optimize your intent.  Newer versions of jQuery—that is, 1.7 and up—support a more intelligent event-binding syntax which is a beautiful combination of two things:
  1. A single handler on an element containing all of the interesting ones, invoked as the event bubbles.
  2. A selector for the handler to filter interesting children.  The handler is only called if the event occurs within a child matching that selector.
These features are part of .on().  For an idea how this works:

<div id="box"></div>
<script type="text/javascript">
$('#box').on('click', 'a.history', function (e) {
popupHistory($(e.target).closest('[data-customer]').attr('data-customer'));
});
// populate the #box with HTML somehow, e.g.:
$('#box').html('<div data-customer="3">Customer! <a class="history" href="#">View Order History</a></div>');
</script>

Not only does this attach one event handler under the hood, but that event handler will be run for any present or future children.  The parent must be there for .on() to bind the handler to (otherwise, you'd have an empty jQuery set, and nothing would happen), but children will be detected by that handler when the event fires.

In the presence of dynamically added/removed elements, then, it's a double optimization: not only does only one handler get bound in the first place, but the handlers don't have to be changed on a per-element basis when children are added or removed.

Thursday, January 9, 2014

Smart Clients and Dumb Pipes for Web Apps

After something like 8 years of coding for the web, both in new and legacy projects, across four companies and some personal projects, I've come to a philosophy on web application layout:
If you require that the client has JavaScript, then you may as well embrace the intelligence of your client-side code.  Let your AJAX calls return simple messages, to be converted to a visible result for the user by code that shipped alongside that UI.