# tiredpixel ☮

programmer; mathematician; musician; vegetarian;
moral-relativist; philosopher.

04 Mar 2014

## puppet masterless server automation on a very small scale

23 Feb 2014

The benefits of server automation are well-known; the ability to swiftly build and maintain a group of servers provides a competitive advantage, reducing overhead of setups, minimising likelihood of errors, and generally keeping the bill for headache-tablets low. One brilliant and popular such tool is Puppet, which for a group of servers can be run with a ‘Puppet master’ controlling related servers. But what about automation on a very small scale—say just a couple of boxes? Puppet can also be run in ‘masterless’ or ‘local’ mode, in which approach manifests are applied individually without centralisation. In these notes I highlight the main steps I tend to take when setting up an isolated server to run using this method.

Throughout, I assume a target distribution of Ubuntu Server 12.04 LTS; instructions for other environments should be similar.

### bootstrapping the server

As the scale is small, the new server can be bootstrapped by hand. Create the server using your host of choice. If possible, avoid a situation where a root password is sent to you and go straight for an SSH key—but this might not be possible, depending on your host. If you have to take an unsecure route, I recommend following these instructions and establishing a process and manifest that you’re happy with, then destroying the instance, changing the passwords, and building all over again; the build is supposed to be repeatable, after all, and this way you’ll minimise the time that your box is live with security settings which upset you.

Set up your DNS as required.

Update packages and reboot the system, to load any new kernel. I like to do this as the first step so I know I’m working from an up-to-date distribution.

apt-get update && apt-get upgrade -y && reboot


Install up-to-date puppet (not puppetmaster), altering as needed if not using Ubuntu Server 12.04 LTS (‘Precise’).

wget http://apt.puppetlabs.com/puppetlabs-release-precise.deb && dpkg -i puppetlabs-release-precise.deb && apt-get update && apt-get install -y puppet


That’s it; everything else is configuring your manifest and applying it on the server.

### creating the puppet manifest

For even a single server, I store the Puppet manifest in a private Git repository; that way, I can track changes over time, and also use it as part of the automation process. The basic repository structure can be something as simple as

puppet.conf
manifests/
manifests/site.pp
modules/


puppet.conf is the basic configuration file for your Puppet installation; I lift one of these from the installed Puppet, and version that with the rest. modules/ holds any Puppet modules used by your manifest; I tend to use Git Submodules for these. manifests/site.pp is the manifest for the box you are automating. I tend to use a mixture of third-party and private modules, meaning I can easily set up security as required and include SSH keys and such on the box.

# manifests/site.pp

# Firewall

class { 'tp_firewall': }

# Apt

class { 'apt': }

# Users

user { 'root':
ensure     => 'present',
}

user { 'eguser':
ensure     => 'present',
groups     => ['sudo'],
managehome => true,
shell      => '/bin/bash',
}

# SSH

class { 'tp_ssh': }

tp_ssh::ssh_authorized_key_tp { 'root': }
tp_ssh::ssh_authorized_key_tp { 'eguser': }


tp_firewall is a private module which locks down ports using Puppet Firewall. apt is set up with unattended upgrades, so security patches get applied automatically (this might not be best for you if you want to vet things more carefully). user sets up accounts using the Puppet user type. PASSWORD_HASH_* are set using openssl passwd -1 locally; I change the root password on the first run. tp_ssh is a private module which applies my custom /etc/ssh/sshd_config locking down the SSH installation, and places SSH keys using the Puppet ssh_authorized_key type.

### applying the puppet manifest

All that remains is to apply the Puppet manifest itself. For this, use your deployment tool of choice, deploying the manifest with any submodules, and triggering the Puppet run after-deploy. I usually deploy straight to /etc/puppet/, and log to both stdout and a file.

sudo bash -c "puppet apply --logdest /dev/stdout /etc/puppet/**/*.pp | tee -a /var/log/puppet/puppet.auto.log"


Obviously, this is merely a basic automation. But for situations where you find yourself running a couple of isolated servers on a very small scale, something along these lines should enable you to gain many benefits whilst keeping things simple and cost-effective.

Peace,
tiredpixel ☮

## tunefl 1.4.4 LilyPond Ruby released

16 Feb 2014

tunefl: LilyPond mini-score engraving and sharing service for musicians.

I am pleased to announce tunefl 1.4.4 – a release upgrading small things. As a general celebration and overhaul of the tunefl project, I also today announce upgraded tunefl servers. :)

tunefl now has 5 GitHub stars, 1 GitHub fork, and has engraved 2694 valid mini-scores, which are easily shareable on your social network of choice.

As ever, I render grateful thanks to the LilyPond project contributors (http://www.lilypond.org/), whose continued hard and excellent work makes such a simple, yet useful, service as tunefl possible.

Changelog:

• start of support for Ruby 2.1.0; end of support for other versions (please wave if this upsets you :) )
• timeout long-running compile jobs

Peace,
tiredpixel ☮

## ryespy 1.0.0 Ruby gem released

26 Jan 2014

ryespy: Redis Sidekiq/Resque IMAP, FTP, Amazon S3, Google Cloud Storage, Rackspace Cloud Files listener.

I’m pleased to announce ryespy 1.0.0 – the first major release, adding support for Amazon S3, Google Cloud Storage, Rackspace Cloud Files, fixing various things, and freezing the Redis key structure. (Thank you, Fog, for making this support so straightforward. :) )

Changelog:

• first major release; Redis key structure frozen
• Redis key structure backwards-incompatible with 0.x.x (sorry! :( )
• start of support for Ruby 2.1.0
• end of support for Ruby 1.9.2
• new Amazon S3 listener (--listener amzn-s3)
• new Google Cloud Storage listener (--listener goog-cs)
• new Rackspace Cloud Files listener (--listener rax-cf)
• change of --verbose mode to --debug mode
• broader error-catching, in case weird things happen when --eternal
• missing FTP listener (--listener ftp) --ftp-port fix
• dynamic requiring of listeners (some have their own dependencies)
• comprehensive README with lots of examples
• major refactoring and improvement of code throughout
• a plethora of tests; most of the core is now covered

Peace,
tiredpixel ☮

## chocolate muffin

11 Jan 2014

i ponder the chocolate muffin’s demise
it feels a little sad :(
but it strikes me that
it is being converted into energy and fulfilling its fate
much like people?
(the allegorical muffin ↦ the allegorical muffin’s energy field)
(*dedicated to T.)

## o song, from ancient wells eternal sprung

09 Jan 2014

o song, from ancient wells eternal sprung
through wandering time a coloured thread
the her, the him, the echoed chord
be ever-present, ever-sung
as we do journeys slowly make

and may there listeners always be
to soundless resonance of form
the shade of light, the breath of day
a half-dreamed thought within a dream
forget us not who wearied be

and when at last our shape we lose
now woven back within the core
defined again but as the whole
forgive us for the troubled notes
we cast within the sacred scores

## curses! conditional ruby gem installation within a gemspec

05 Jan 2014

From Ruby 2.1.0 onwards, curses has been removed from the Ruby standard library and extracted to the curses Ruby gem. For sidekiq-spy, the Ruby gem providing Sidekiq monitoring in the console, that necessitated some enthusiastic dancing using conditional Ruby gem installation within a gemspec, to maintain support for Ruby 1.9.3 and Ruby 2.0.0.

To use curses in Ruby 1.9.3 and Ruby 2.0.0, all that is needed is require 'curses'. In Ruby 2.1.0, specifying the dependency gem 'curses' lets you carry on unharmed. But the curses gem does not appear to work properly in Ruby 1.9.3. Thus, the Ruby gem can be installed conditionally, perhaps for Ruby 2.1.0 onwards. This same approach could be used to install different gems or versions based on which way the wind is blowing, or the Ruby version used. In a non-gem project, this could be accomplished using

# Gemfile

gem 'curses', :platform => :ruby_21


or, because the :ruby_21 platform does not appear to exist in Ruby 1.9.3, maybe something like

# Gemfile

gem 'curses' if RUBY_VERSION >= '2.1'


For a gem, however, this would only be evaluated at build-time, not install-time. The Wikibook Ruby Programming has an excellent chapter entitled RubyGems. This describes an approach where the gem is installed within the umbrella of native extensions building. I found that not all of this appeared to be necessary for my use-case, but the approach seems to work well.

First, register the extension in the gemspec:

# sidekiq-spy.gemspec

spec.extensions << 'ext/mkrf_conf.rb'


Then, create the extension file itself. In my case, I wanted to install curses only for Ruby 2.1.0 or later, but this can easily be adapted.

# ext/mkrf_conf.rb

require 'rubygems/dependency_installer'

di = Gem::DependencyInstaller.new

begin
if RUBY_VERSION >= '2.1'
puts "Installing curses because Ruby #{RUBY_VERSION}"

di.install "curses", "~> 1.0"
else
puts "Not installing curses because Ruby #{RUBY_VERSION}"
end
rescue => e
warn "#{$0}: #{e}" exit! end puts "Writing fake Rakefile" # Write fake Rakefile for rake since Makefile isn't used File.open(File.join(File.dirname(__FILE__), 'Rakefile'), 'w') do |f| f.write("task :default" +$/)
end


The puts messages won’t usually show up during installation, but are useful for gem install using the -V flag. This should be all that’s necessary for handling the dependency at install-time, in this case installing the curses gem for Ruby 2.1.0 or later and not otherwise.

Next, ensure that the gem is loaded before being required. I found a variety of information online saying to use a variant of gem 'curses' whilst rescuing Gem::LoadError, but this did not appear to be necessary in my case. Merely requiring as before seems to be working fine (spaghetti crossed).

However, this still doesn’t handle the dependency for development-time, without which, curses isn’t available with Ruby 2.1.0. For this, a conditional development-dependency declaration ensures that the curses gem is available, whilst having no effect on install-time (at least, with the usual bundle install --without development test). Alternatively, the Gemfile could be used similarly. As mentioned, I did not have success with using the :platform => ruby_21 approach for Ruby 1.9.3.

# sidekiq-spy.gemspec

if RUBY_VERSION >= '2.1'
end


Of course, it’s very possible that there are better ways of doing all this. If you know of one, I would be very interested to hear it. :)

Peace,
tiredpixel ☮

05 Jan 2014

29 Dec 2013

## emmental

28 Dec 2013

—so you’re saying, the dog was basically wearing glow sticks? however did you work that out?!
—emmental, my dear watson.
—pardon?
—you see, whenever i eat it, i get odd dreams.
—so you didn’t deduce it?
—how? there was also this guy called ramanujan; he kept talking a lot about blood.
—can i just write it as ‘elementary’?

26 Dec 2013

26 Dec 2013

26 Dec 2013

22 Dec 2013

## replacing the keyboard of a macbook air

21 Dec 2013

I dislike dirty keyboards. It’s quite a thing, for me—so much so, I regularly clean my keyboards with tissue and a healthy dose of the liquid created by the magical creatures who live at the bottom of gardens. This has been my wont since time unmemorable. The other day, I instead used a sponge. It turns out that laptops do not like sponges. Allow me to tell you why.

Without the wish to spoil any surprises, sponges absorb water. Wipe-wipe, wipeity-wipe, accidental-squeeze, hmm-maybe-that’s-too-much-soapy-water, it’ll-probably-be-fine. Except it wasn’t fine. No-no. (shake) It’ll dry out overnight, thought I. It didn’t. The next morning, most keys didn’t work, and I started to consider that I might have a problem.

I tried all the usual fixes: vacuuming the keys; putting it in the oven (Gas Mark 1, door open, timed carefully so as not to spoil the taste, of course); leaving it on top of a radiator; compiling Ruby on loop; letting it watch its choice of foreign-language films on its own all day (a two-pronged attack of getting the graphics card toasty and letting it have some laptop-time and thus hopefully feeling better about life). It typed a few messages to itself, but no Shakespeare. Imagine my surprise when none of these cure-alls helped.

Perhaps I should point out that my laptop was out of warranty. Perhaps I should also warn the tempted reader by pointing out that my laptop has subsequently become so far out of warranty as to probably not be allowed to use the name MacBook Air anymore.

I tripped into the large store of forbidden fruits, feeling a little woebegone. I feared the bill would be expensive; clearly I couldn’t pretend it had always been like that, and a couple of online stories detailed such a repair as costing hundreds of dollars. What I wasn’t expecting, however, was that there were no available appointments. It seems Geniuses are much in demand. Fear not; a hair-dryer to the rescue. With added rice.

I opened my laptop and disconnected the internal keyboard, after which an external keyboard worked without having its polite requests shouted down by the aqueous party within. I removed every single key except for the Caps Lock (which didn’t approve of what the other keys were doing, and kept itself to itself throughout). This took some hours. There was ample time for reflection. I thought I’d managed to remove the keys without damage except to one tiny bit which hopefully didn’t matter too much anyway. I left the keys in a box of rice overnight to dry out.

This didn’t seem to help, however, so I tried a hair-dryer, carefully styling both keys and frame. This didn’t help, either. In fact, all these approaches did were to lead to an unpleasant game of one-player Scrabble, at the conclusion of which I realised I’d damaged the keyboard frame probably beyond reasonable repair and really had to get it replaced.

I ordered a keyboard from the electronic seaside inlet. This arrived on metaphorical wings of eagles. I compared the keyboards quizically. Most keys matched. Most—because I’d mistakenly ordered a US, rather than a UK, layout. For most keyboards, this would simply be a matter of keys having identity issues. But in this case, the keyboard wouldn’t even fit; the US layout has a horizontal Return key, and the UK layout has a vertical. I tried again, with greater success.

You can skip the rest if you don’t want a screw-by-screw account of my activities. I would say just look at the pretty pictures, but I’m concerned they might give bad dreams. Like most electrical products, the MacBook Air has a mixture of screw types: magical, and impossible. The magical fitting is a small, star-shaped screw, a bit like CR-VT5 but not quite. I purchased an expensive ‘Mac’ screwdriver which fitted. I removed the rear case screws—8 short, 2 long. I removed the battery pack—3 long, 2 short. Somebody (for once, not me) had already damaged the corners of the battery pack by over-tightening the screws. No matter. I disconnected what are probably the speakers, but they didn’t say anything so I can’t be sure. This was an anxious time, as the long, black mounts (I’m guessing also the antennas?) were stuck on. I carefully levered them up. I’d already disconnected the keyboard data ribbon. I disconnected the backlight ribbon. I disconnected the screen data connector—patiently, thankfully, as I didn’t intially realise it’s locked by a bar and then is removed vertically. I disconnected the broad flat cable connecting the two main boards—another vertical connector. I disconnected the screen power ribbon. I disconnected what is probably the microphone but might be an alien listening device for all I know.

Then to remove the motherboard. I disconnected the SSD—so impressively small! It slides out, in case you’re wondering and following along at home (which I seriously hope you’re not). Underneath the SSD is a screw—I found this after a game of let’s-apply-gentle-pressure-oh-no-this-doesn’t-feel-right. There’s a screw near the USB ports. There’s a screw on the power connector board underneath the broad flat cable. The fan was tricksy; there are 5 screws, some of them hidden under cables. The screws are also of inconsistent lengths, which later made me wish I’d written this down at the time instead of reconstructing it afterwards when reassembling.

With a few rubber washers, the keyboard was finally exposed. I proceeded to remove the 39 screws surrounding the keyboard. Yes, 39. It turns out these were of type impossible, which required a diversion to some shops. At one of these I found what I needed: coffee. Oh, and I also found a CR-VPH00 fitting (but not in the same shop). It first seemed that CR-VPH000 fitted—until I destroyed the thread of one of the screws, that is. If you’re already squirming, don’t read this next bit. I removed the keyboard. It doesn’t lift out; it’s riveted in or something. Also by this point, the case is nowhere near as tough as it looks when it’s stuffed with innards. Also, the screen was very exposed, so care was needed with anything sharp. One crack at a time, I broke the rivets of the keyboard and removed it from the shell, bending the keyboard in the process. I decided this was acceptable, so long as I didn’t bend the shell or scratch the screen. For the broken screw, I broke the edge of the keyboard, and then used pliers. An ugly job. It can probably be done a lot better. But I do not know how; by this point, I was gaining the distinct impression that it is not intended that customers replace their keyboards themselves.

But finally the keyboard was removed, and work to install the new keyboard could begin.

I could not anchor the keyboard anywhere near as well as it had been done previously, because of the missing rivets. But I hoped the 39 screws would be sufficient once the rest of the innards were in, and this turned out to be the case. The keyboard I installed was used, so the seal around the edges wasn’t quite daisy-fresh. Electrical tape to the rescue (black tape is best, so it matches; I wouldn’t like to have a laptop which had poor colour co-ordination inside). With the rubber washers replaced, it actually looked quite fetching.

After this, everything was much easier. All that remained was to attach the various cables, try to remember which screws went where, add the SSD, and attach the speakery things back onto the case. For this, I used a crafty glue gun. It probably wasn’t necessary, but I didn’t want to risk anything breaking. Of course.

Although I suspect very few people will find themselves treading this road of tool-juggling and patience-or-else-you’ll-snap-it, I would encourage those who do to remember that no matter how it feels at times, it is possible to put it back together again. Neatly, even—though I say so myself.

I’d hate to bore the two readers who have made it thus far, so I’ll let you use your imagination for the battery pack and rear case. When I powered on the laptop, I discovered… it works! In fact, you’ll be delighted to learn that I’m using the fully-functional keyboard to write this endearing tale. And clearly, everyone will be delighted that the keyboard was fixed and this delightful story could be written.

The only real problem is that the keyboard, as I have mentioned, was used. This means I will shortly feel compelled to clean it.

21 Dec 2013