Reification

"Beautiful as poetry; ludicrous as science."

Sail Away Selah’s Way

Sorry for my absence. I’m sailing to Australia at the moment.

Scrobble to Last.fm with Pianobar

Pianobar is an excellent command-line Pandora client. It has support for calling external commands when songs play, pause, skip, et cetera via the event_command directive. With this, we can add a little scrobbling script and get our listening tracks sent to Last.fm.

First, cd to ~/.config/pianobar (or mkdir if it doesn’t exist) and paste https://gist.github.com/833064 in as scrobble.py, and chmod +x it. You’ll have to open scrobble.py and add your Last.fm API key (get one from http://www.last.fm/api) and username/password.

Next, go to pylast and get pylast.py and put it in the same directoy as scrobble.py.

Finally, open or create ~/.config/pianobar/config and put in these lines:

user = email@domain.com
password = somethinglong
event_command = /Users/username/.config/pianobar/scrobble.py

Bonus: If you’re a Pandora One subscriber, you can get the high-quality stream by adding the audio_format = mp3-hifi directive.

(Note: Due to shortcomings in pianobar, your password can’t have funny characters like & in it.)

When you start pianobar, it should start showing up on Last.fm after the first song finishes. If there are problems then pianobar should print them to stderr.

Edit 2011-06-02: The gist has been updated to support Now Playing. Update your scrobble.py with the new version and pianobar should automatically start updating last.fm with Now Playing.

Puppet bootstrap

When starting a brand-new puppet environment, this is the procedure I usually follow to get a client/server bootstrap. First get puppet installed on two boxes, one will be a master and one (or both) will be clients.

(Note: You need some kind of name-resolution infrastructure, either via DNS or /etc/hosts. Puppet clients by default try to contact your master via the short name puppet then the long name puppet.domain.name.)

To start, do the following (I’m assuming Puppet 2.6 commands. For 0.25 use puppetd for puppet agent and puppetmasterd for puppet master):

  • On the master: touch /etc/puppet/manifests/site.pp and puppet master --no-daemonize --verbose to watch for connections.
  • On the client: puppet agent --test You should see it generate and submit a cert to the master to be signed.
  • On the master: puppet cert --list to get the list, then puppet cert --sign <certname> to sign it.
  • On the client: puppet agent --test and it should get it's signed cert, then a catalog from the master if you have a node definition for it.

If that messes up and you want to start over:

  • On the master: puppet cert --clean <certname> to clean the client's cert.
  • On the agent: puppet agent --configprint ssldir then rm -rf that dir to remove its cert.

Once server<->client is working fine fine, ^C the puppet master and run:

  • On the master: service puppetmaster start
  • On the agent: service puppet start

Now you should be able to put modules in /etc/puppet/modules and node definitions in /etc/puppet/manifests/site.pp and the master will stat these every 2 seconds for changes. You can wait for the client to automatically contact the master for its catalog, or run puppet agent --test to see immediate results.

GTD Gmail Filter Tricks

One of my favorite blog posts of all time on Gmail GTD techniques is this one by Neil Dixon (Edit: Link is dead. Use this blog post instead). Using this technique means that if I want to manage a todo list in combination with my email GTD workflow, I have to check two places to see my task list (even if I’m using the task list built-in to Gmail). How can I combine my email GTD with my todo GTD into one place?

A quick tip about a gmail feature that is not well known. You can append +anything to the end of your username for any Gmail or Google Apps domain and it will still be sent to you. For example, h.haugen@gmail.com or hunter@puppetlabs.com can be turned into h.haugen+gpg@gmail.com or hunter+haugen@puppetlabs.com.

To integrate my Gmail GTD workflow with an email-driven todo list, I should theoretically be able to have any single email sent “from me to me and only me” be archived and labeled @action. Gmail filters don’t currently have a way to filter based on “to me and only me,” so I improvised.

Create an email filter for “to:hunter+todo@puppetlabs.com” (or whatever) to auto-archive and apply your @action label (and optionally a “Todo” label via a second filter), and send an email to this address with your one-line todo sentence in the email subject line. Thereafter, when composing an email you should be able to type “todo” into the To: line and hit <tab> and have it complete for your Todo list.

Protip: If you have keyboard shortcuts enabled and are viewing your @action emails, you can use ‘y’ to remove the @action label without removing the Todo label, essentially marking it “complete.”

Fullscreen Cocoa Emacs on OS X 10.6

The age old battle of editors rages on, but I have a feeling that it’s largely due to misunderstandings of purpose and ignorance of the opposing party. I have been a vi/vim user for 10+ years and found myself strongly advocating vim’s qualities of being lighter and more prevalence than emacs without having so much of an idea of how to quit emacs other than with pkill. Then I found myself with 3 months of free evenings and a slow-to-nonexistent internet connection and decided to fill some of that time and try emacs.

Long story short, I really like emacs for some tasks, and I really like vim for others. I love hacking on my .emacsrc but had been mostly constrained to running it in the terminal over ssh connections until I got a Mac. Now Macs are funny in that they are very GUI-gooey, but they manage to pull it off in a way that doesn’t make me, a hardcore terminal-only tiling-window-manager user, want to take a hammer to it.

Subsequently, I have been using the gui version of Emacs more and more, and it’s really something special on OS X. One feature I feel it’s lacking is fullscreen support. When working with vim, I toggle to fullscreen terminals when I really want to focus, so Emacs should allow me to do the same thing.

Shamelessly adapted from citizen428′s guide, guide, here is the step-by-step way to get fullscreen support written by typester on OS X vanilla emacs without having to rely on someone else’s compiled Emacs.app:

# Get the official Emacs repo:
git clone git://repo.or.cz/emacs.git

# Add typester as a remote and fetch his branches
git remote add typester git://github.com/typester/emacs.git
git fetch typester

# From a3585f6c2a to typester/feature/fullscreen are the 14
# commits we want to apply to our tree. Create a patch and apply to
# HEAD for the most cutting-edge Emacs
git checkout origin/master
git diff a3585f6c2a typester/feature/fullscreen | patch -p1

# Create a commit so you can just maintain your git tree for later
git commit -a -m "Typester's fullscreen patch" && git clean -d -f

# Alternatively, you could cherry pick all of typester's commits
# and not have to commit anything.
git cherry-pick 329d3eeab6e1997eed97e1fe40e5489a4c68edae \
                592a51bc2938e7c40a3c60b5a5301ea1510a0e74 \
                b9ff1ae683cf453da0415f5c83e329bbff35b9fa \
                000208df01f5c3af7d6f584304bc1a898701f87b \
                d8cf14d0243e5046f77051d05af31af77125d583 \
                df1e25370854fc04e4c217c214e9c02084c1ddae \
                41a9a1d946061c87ab6a6faf484a5b2e5812229f \
                c586de33d8e9ed6c94887526e9891de1788ba3f8 \
                663de471ea537083a86832c46984eab08407be90 \
                d1e183b01bd9f4608203aec5ae06518c37932a98 \
                b549c85507a1f52230c16ace217fe4e66308fc97 \
                5f2a5f555f475d570e6db2ffea4b259f0a90c302 \
                94512b4fd984146a62cde7d9c925058d0ebd0be3 \
                65343cea007dc56fa6c9e79ecd78f6fe538374e8

# Done with typester's remote
git remote rm typester

# build emacs
./configure --with-ns
make && make install
mv nextstep/Emacs.app /Applications/

# Later, to update emacs and keep fullscreen just do:
git pull origin master

I then bound “M-RET” to ns-toggle-fullscreen in my .emacsrc.

Suppress iTunes invocation on Pause/Play key

On my Macbook I have the F1-12 keys accessible under the Fn key, and instead by default they have various actions such as changing the volume or brightness, activating exposé, or other various functions. F7-F9 are shortcut keys for Next Track, Play/Pause, and Previous Track for music or other media.

When I got my mac I knew I didn’t want iTunes for various reasons, not the least of which is that it can’t play flac. I’m still looking for a good alternative for heavy usage of listening to my own music, but between last.fm‘s app and Pandora‘s awesome console program called pianobar (you can `port install pianobar`, I’ve only been listening to my own music about 50% of the time.

The other 50% of the time I’ve been using VLC, and it works out of the box with the keyboard media shortcuts! Except for one hitch… any time I press the Play/Pause button OS X tries to start iTunes, and there are more than one angry threads about Apple turning a deaf ear to making this configurable/disabled short of deleting iTunes. But there is a hack if you want both iTunes and VLC to co-exist.

Essentially I rename the iTunes binary and replace the original with a small script that first checks if VLC is running, and if not then launches iTunes. Just paste this into Terminal:

cd /Applications/iTunes.app/Contents/MacOS
sudo mv iTunes iTunesX
sudo cat > iTunes << EOF
#!/usr/bin/env python
import sys, os, subprocess
launch = True
blocker = ""
apps = ["Spotify", "Songbird", "VLC"] # Add more blocking programs here
ps = subprocess.Popen("/bin/ps -x", shell=True, stdout=subprocess.PIPE)
for line in ps.stdout.read().split("\n"):
    for app in apps:
        if app in line:
            launch = False
            blocker = app
ps.stdout.close()
if launch :
    os.spawnvp(os.P_WAIT, '/Applications/iTunes.app/Contents/MacOS/iTunesX', sys.argv)
else :
    print "Not launching iTunes while %s is running." % blocker
EOF
sudo chown root:admin iTunes #To match the perms of the other iTunes files
sudo chmod 775 iTunes
exit

If you want to "reset" your iTunes, you can paste this:

cd /Applications/iTunes.app/Contents/MacOS
sudo mv iTunesX iTunes

Dynamic Git-branch Puppet Environments

When working with puppet, eventually you’ll arrive at any one of several conclusions; you have a mass of code and want to refactor it, you have a cool idea that you want to prototype, or you want to add some code but don’t want to push it to production until it’s done. Puppet’s answer to this is environments. The documentation says that you can have arbitrary environments and redefine your configuration parameters in puppet.conf based on which environment you wish your clients to use. The general idea is to have several environments like “Production,” “Development,” and “Testing.”

Now, for me this falls down when I want to break away from more standard development cycles that no longer fit in the dev->test->prod workflow. Or if I have groups of developers working on disjointed projects but don’t want to support completely separate puppetmasters with their own clients. Or when I want to follow git’s mentality of “branching is cheap; branch often.”

I’d like to be able to say `git checkout -b newfeature`, add some resources and templates, `git push`, and instantly use it on my clients. No reconfiguration of the puppetmaster needed. When I finish, `git checkout production ; git merge newfeature ; git branch -d newfeature` will put all aright again.

In puppet.conf I can use $environment to reference the current environment for setting modulepath and manifest, but I don’t actually need the [newfeature] environment declaration section in the puppet.conf. This allows me to create a config thus:

[master]
  environment = production
  manifest    = $confdir/environments/$environment/manifests/site.pp
  modulepath  = $confdir/environments/$environment/modules

[agent]
  environment = production

This says that the master will base the manifest and module path on the environment, which is passed by the agent to the master. If I changed the [agent] section on a puppet agent to have environment = newfeature instead, then the catalog compiled by the master would be from /etc/puppet/environments/newfeature. If an agent does not pass the environment variable then the default production will be used. (Big Note is that your files and templates really only work cross-environment if you’re using modules. You can either clone all of your modules per environment, or you can have some modules be per environment and some be global. Check out Volcane’s blogpost on using your modules in this manner.)

Now that I have puppet set up to take arbitrary environments, lets make this useful. To implement it so that git branches are recognized as separate environments, I have to keep your puppet manifests and modules in git. (You’re already doing that, right?) Starting without any environments, I’ll rearrage my puppet configs to look like this:

/etc/puppet/
     | puppet.conf
     | fileserver.conf
     - environments/
       - production/
         + manifests/
         + modules/

In the production directory I `git init ; git add .` (then considering that I have a gitosis instance set up and an ssh key ready to be used by the puppet user,) `git remote add origin git@gitosis.host:puppet-environments.git ; git push origin master`. Now for the git hook I go open puppet-environments.git/hooks/post-receive and put this bourne code in, configured with my git repo url and the key by which my puppet master can pull from gitosis:

#!/bin/sh
read oldrev newrev refname

REPO="git@gitosis.host:puppet-environments.git"
BRANCH=`echo $refname | sed -n 's/^refs\/heads\///p'`
BRANCH_DIR="/etc/puppet/environments"
SSH_ARGS="-i /var/lib/puppet/.ssh/id_rsa"
SSH_DEST="puppet@puppetmaster.host"

if [ "$newrev" -eq 0 ] 2> /dev/null ; then
  # branch is being deleted
  echo "Deleting remote branch $BRANCH_DIR/$BRANCH"
  ssh $SSH_ARGS $SSH_DEST /bin/sh <<-EOF
    cd $BRANCH_DIR && rm -rf $BRANCH
EOF
else
  # branch is being updated
  echo "Updating remote branch $BRANCH_DIR/$BRANCH"
  ssh  $SSH_ARGS $SSH_DEST /bin/sh <<-EOF
    { cd $BRANCH_DIR/$BRANCH && git pull origin $BRANCH ; } \
    || { mkdir -p $BRANCH_DIR && cd $BRANCH_DIR \
         && git clone $REPO $BRANCH && cd $BRANCH \
         && git checkout -b $BRANCH origin/$BRANCH ; }
EOF
fi

Now after I’ve cloned puppet-environment.git to my laptop for hacking, I can `git checkout -b newfeature` and end up with an exact representation of my production code, but free to be hacked on until it’s ready to be pushed and tested. Pushing will instantiate it in a new environment. After I’ve pushed, I can run `puppet agent --test --environment newfeature` on a puppet client and get a one-time run inside your newfeature environment, and it will revert back to production after that run.

Finally, what if I want my hosts to remember which --environment I last ran on it? Or how about being able to configure the nodes’ environment right from puppet? For that I can make a small module with a parameterised class called “environment.” Here’s my init.pp:

class environment($env = $environment) {
  file { '/etc/puppet/puppet.conf':
    owner   => 'puppet',
    group   => 'puppet',
    mode    => '644',
    content => template("environment/puppet.conf.erb"),
  }
}

As the final step, I copy my clients’ puppet.conf to modules/environment/files/puppet.conf.erb and replace the environment = production section in [agent] with environment = <%= env %>.

Now I can add include environment in my base node or any node that you want to “remember” it’s environment given via --environment. If I’d like to configure a node to always use a specific environment then I can instead use class { environment: env => "production" } and it will always revert to the production environment.

Thanks to Marut and the guys at PSU for the inception!

Joining the Tautology Club

Hunter Haugen is a sys-admin/hacker of 10 years turned DevOps in an attempt to understand, play, and ultimately win The Game. Sorry, I mean The Game. My current battleground is a little open source company called Puppet Labs in the home of open source, Portland Oregon. My theatre is the world (it’s in my job description) — as of last Monday I have signed on as a consultant with Puppet’s Professional Services team to offer training seminars around the world for those who want to understand and use the configuration management software called ‘Puppet.’ I also have aspirations of being a successful entrepreneur one day, but so far haven’t gotten the hang of the ‘successful’ part.

As with all good Portlanders, I relish the experience of engaging in the most efficient form of mechanized transportation and often go on long (60-600 mile) bicycle rides with my family of 6 (3 are siblings). I also enjoy the motorized version, which is also my primary form of transportation at the moment, rain or shine. Quick trivia: I was run over by a tractor while driving a motorcycle in India. My other interests are keyboards, ultralight backpacking, languages, and a collection of things that a large percentage of the population also find captivating.

Coming up next: technical and non-technical things about OS X, productivity, coding, and things relating to but not entirely 42.