Devops

YT channel recommendation for neovim and zsh users

I don’t think I ever copied more vim or zsh configuration from anyone than from ChrisAtMachine. Looks like I found a soulmate. 😊 His YouTube channel is well worth watching!

Making my own Nerd Font

Well-structured status lines in vim and shell prompts with version control symbols are a nice quality-of-life improvement. Unfortunately, not all monospace fonts come with the necessary PowerLine glyphs. For example, my favourite font is Operator Mono, and it too doesn’t have PowerLine symbols built in.

Thanks to the NerdFonts font patcher, I was able to generate a font variant that has the necessary symbols. I simply ran the tool as a Docker container in my fonts directory:

$ mkdir HCo_OperatorMonoSSmNF
$ docker run --rm -v $PWD/HCo_OperatorMonoSSm/OpenType:/in \
  -v $PWD/HCo_OperatorMonoSSmNF:/out \
  nerdfonts/patcher --windows --powerline --powerlineextra

Now my spaceship shell prompt sparks even more joy.

dotenv and direnv for better developer QoL

The latest RubyTapas tutorial taught me the difference between the dotenv and direnv tools. While they have significant overlap, they complement each other quite nicely. dotenv simplifies both development and production ops by importing environments variables either from a .env file or the application hosting platform. direnv on the other hand augments the dev environment even further; not only can it add dev-only variables but also modify shell settings like extending $PATH on a per-project basis.

Verify your assumptions

That’s what I keep preaching to my team but still fail at myself every so often.

This week, I wasted a whole day implementing a fix for a bug that wasn’t there. My task was to add one line to a configuration file in a Chef cookbook. When I ran its test suite, it surprisingly failed and it was clear that my simple change couldn’t be the cause. I assumed it was due to recent changes in software packages installed by the cookbook. So I started adapting our old code to these changes, and one change led to another, and another, until I finally realised that over the course of multiple hours, my one-line bugfix had turned into a full code overhaul. How did I get here?

I decided to start from scratch. git reset --hard HEAD. I quickly fixed my config file and ran the tests. When they failed again, closer inspection revealed that it was actually just the same test failing twice. The cause was a bug that had found its way into the main branch without breaking the CI pipeline (finding the reason for this will be interesting). It had been there all along! After another one-line fix, my change was ready for review. Round 2 had taken me less than half an hour.

The lesson: Don’t rush into battle blindly. Look closely. Identify assumptions and verify them. It might just be windmills!

Clean Chef code: Depend on public cookbook interfaces

For about a year, we’ve been cleaning up the Chef Infra code for freistilbox to make updating dependencies, Chef versions and even operating systems easier. It’s a lot of work because our early code is functional but not pretty. There have been many instances of “we didn’t know better”, and that’s what refactoring is for. But I also came to realise that we were missing a critical fact: Common software engineering principles and practices apply to infrastructure code like they do to any other type of code.

Or as early Chef developer Joshua Timberman puts it:

“Hey, ya’ll remember when devops really just meant you knew how to write all your bash in ruby instead?”

Ouch. Making this connection earlier would have saved us weeks of work. That’s why I’m going to share my findings in a series of posts.

In this post, I’m going to advocate for treating a Chef cookbook as a unit of software that provides explicit interfaces instead of tempting its users to depend on implementation details.

Chef Infra cookbooks use node attributes as variable parameters for system configuration. In software engineering terms, node attributes are global variables. They’re implementation details of a cookbook. In consequence, using a cookbook’s node attributes for any purpose other than defining setup parameters creates a dependency on implementation details which can change at any time.

For example, it’s common practice to use a search on node attributes for service discovery. As node attributes are global variables, any cookbook can do this:

service_nodes = search(:node, "webservice_id:myservice")

From this code, we can tell that web service nodes are identified by a node attribute named webservice_id; nodes sharing the same webservice_id value belong to the same web service.

The problem with using this information outside the cookbook which provides it is that this particular implementation can change at any time. This kind of tight coupling is a liability. For example, a second attribute webservice_status might get introduced, reducing the node set by adding AND webservice_status:active to the query. Since this change in semantics is not necessarily a breaking change, there’s no simple way like semantic versioning to inform everyone who depends on this unofficial interface.

How about we provide an public API instead? Our web service cookbook could for example provide a class we can use for service discovery:

webservice = Company::Cookbook::Webservice::Discovery.new("myservice")
service_nodes = webservice.nodes

This is easy to implement as a cookbook library. By using namespaces, we make sure that method names don’t conflict. In my practice, I tend to use the camel-cased cookbook name under the namespace Cookbook and the company namespace.

With service discovery encapsulated and hidden behind a public interface, we could even reimplement the cookbook’s service discovery using a different technology like Consul without breaking any code outside our cookbook.

But even if other cookbooks depending on our implementation isn’t a concern, implementing auxiliary logic in a central library instead of scattering it across recipe files makes it much easier to maintain. I’m going to talk about “Plain Old Ruby versus Chef DSL” in a separate post.

Managing multiple Git identities

I’ve always been struggling to use the right name and email address, separate between work and personal projects, for each of my Git repositories. Micah Henning solved that problem nicely by removing the global settings but making a repo-specific configuration mandatory. And a handy alias is the icing on the Git identity selection cake.

The Live Coders Conference

There won’t be many conferences in the next few weeks, at least not the sort that you need to attend in person. However, with COVID-19 handing out lemons, people start to discover the lemonade of online conferences!

I’m happy to be part of an initiative by The Live Coders, a community of people broadcasting their software development on Twitch. On 9th April, we’re going to broadcast more than 12 hours of presentations on a wide range of topics. With my talk about burnout prevention, I’m probably going to have a slot in the (European) afternoon.

For details, check out The Live Coders Conference. Andy, one of the organisers, also did a write-up on Setting up an online conference.

An easy and secure way to launch helper scripts in a project

On one of my recent live coding streams, a viewer asked what my abe script does. I showed that it simply launched a Ruby command in my project’s application container. Since I’m using Docker Compose to spin up most of my development environments, I have to run all development tasks within the application container. Typing abe rake test is much faster than typing docker-compose exec app bundle exec rake test, so I added this script to my project’s bin directory:

#!/bin/bash
docker-compose exec app bundle exec $@

It’s a nifty time-saver, but the smart part of this isn’t the script itself but how I make helper scripts in the bin directory of my projects easy to launch without having to prepend every command with ./bin/.

If you’re familiar with how a Unix shell finds the right program to execute, you ’ll probably suggest just adding ./bin to the environment variable PATH. But that’s a risky move because you don’t want to accidentally launch a malicious script after checking out a repository that happens to have an executable ls command in its bin directory.

Once again, it was the talented devs over at ThoughtBot who found a better solution. Instead of adding ./bin to PATH, they recommend adding .git/safe/../../bin. With this entry, the shell descends into .git, further down into safe, all the way back to the repository root and only then into bin. What makes this seemingly roundabout way to find your helper scripts secure is that it only works if you’ve first manually created the subdirectory safe within .git. The latter is, after all, git’s data directory which normally doesn’t contain a directory named safe.

There you have it — easy access to your project’s helper scripts is simple to achieve. And without any additional effort, it’s safe as well!

Telemetry != Observability

This post helps to understand the difference: Understanding Observability.

Tests are hygiene

Because I started programming a long, long time ago, I have to admit that I spent most of my career without writing tests to accompany my code. It was only in recent years that I learned how to use a test suite to my advantage. In hindsight, I should have started earlier because having tests completely changed my life as a software developer.

Tests create peace of mind. The amount of confidence with which I now release my code to production is worlds apart from the trepidation of past deployments when I was on edge for hours waiting for the other shoe to drop. Beginning of this year, I wrote about this in my article “Test-Driven”.

I remember how difficult it was for me to get into writing tests. Honestly, once in a while I still struggle with building clean and efficient tests. That’s why I can relate to developers who are turned off writing tests by the additional effort it takes.

Decades of me coding without a test suite prove that you can not have tests and still do a decent job. However, that doesn’t mean that it’s a good idea. Or that it’s good practice in 2019. I’d even go so far as to state that building tests for your code has become basic professional behaviour.

Martin Thompson draws an interesting parallel to the medical profession on the episode “Protocols and Sympathy” of the Arrested DevOps podcast (link jumps right to the related point in the conversation):

“A surgeon will not consider performing an operation without washing their hands.”

But did you know that it wasn’t until the efforts of people like Ignaz Semmelweis in the 1800s that antiseptic procedures were even considered par for the course by doctors? Setting higher standards for hygiene transformed medical practice.

I’ve come to the realisation that spending time on writing tests before coding business logic is like washing your hands ahead of performing surgery. In the long run, it saves time and money because it make sure that “done” means “done”. Better hygiene in medicine reduced the number of deaths and minimised the need for antibiotics and urgent follow-up operations. A robust test suite will reduce the number of outages and minimise the need for workarounds and urgent debugging sessions.

In both cases, there’s going to be far less suffering for everyone involved.

How I manage my dotfiles across hundreds of machines

Almost all of the command line tools I use on my Mac workstation, on my development server in the cloud, and on my company’s Linux server infrastructure store their configuration in so-called “dotfiles” — files (or even whole directories) in my user home directory that are hidden from plain sight by prefixing their name with a period, for example .vimrc. (Like this example, actually quite a few of these files do end in rc. That’s why they’re sometimes also called “rc files”.)

On dev.to, Jonathan Carter asked “How do you manage and synchronize your dotfiles across multiple machines?” Since “multiple” in my case means “more than 500 servers” (we operate a managed high-performance hosting platform for Drupal and WordPress), I thought I’d answer his question in a short blog post.

dotfile management

Most of the dotfiles I use come from our shared team dotfiles repository. Having a big overlap in configuration settings between our SRE allows us to easily share and take over a terminal session via tmate without having to struggle with individual tmux or vim keybindings. Doing pair work in a terminal session has the huge advantage that it takes up much less bandwidth than screensharing via Zoom or Slack.

Some tools still allow a certain degree of individuality. For example, the shell prompt details can vary without causing confusion. You can also have your own magic shell and git aliases. That’s why I layer my personal dotfiles repository on top of the team one.

Another complication is that the BSD subsystem on the Mac sometimes behaves a bit differently from Linux. That’s why, for a few configuration files, I’m maintaining alternative versions. They’re stored in subfolders starting with tag-. The dotfile deployment then installs the right version depending on the host OS.

dotfile deployment

So how do you maintain a set of dotfiles in your home directory on hundreds of machines if they come from two overlapping, even conflicting, git repositories? The trick is to clone both repositories and then symlink the configuration files to where they are expected by their application. The key to keeping this process simple and error-free is rcm. It can handle multiple dotfile directories, each with its own precendence. You can also provide it with tag names telling it from where to source certain dotfiles.

I install rcm manually when I set up a new development machine (which only happens once or twice a year). On our servers, it gets installed automatically via our configuration management software Chef.

Here’s the script that I use to deploy my dotfiles:

#!/bin/bash

if ! which rcup >/dev/null; then
  echo "Fatal: rcm is not installed on this machine."
  exit 1
fi

os_type=$(uname -s)

# Remove oh-my-zsh
rm -rf ~/.oh-my-zsh

if [[ -d ~/.dotfiles ]]; then
  cd ~/.dotfiles
  git pull
else
  git clone [github.com/geewiz/do...](https://github.com/geewiz/dotfiles.git) ~/.dotfiles
fi

if [[ -d ~/.freistil-dotfiles ]]; then
  cd ~/.freistil-dotfiles
  git pull
else
  git clone [github.com/freistil/...](https://github.com/freistil/dotfiles.git) ~/.freistil-dotfiles
fi

rcup -f -t $os_type -d ~/.dotfiles -d ~/.freistil-dotfiles

As you can see, it clones the dotfiles repositories (after a bit of cleanup) and finally calls rcup using an OS-specific tag (“linux” or “darwin”). Thanks to a a post-up hook, it even launches vim to update its plugins from Github.

You can find the current incarnation of the deployment script in my personal repository as bin/dotfiles. Since its directory gets symlinked as ~/.bin and added to my shell search path, I can execute the dotfiles command at any time to update my local configuration.

As for the initial deployment on a new machine, I simply curlbash the script:

curl -L [raw.githubusercontent.com/geewiz/do...](https://raw.githubusercontent.com/geewiz/dotfiles/master/bin/dotfiles) | bash

That’s how I manage and deploy my dotfiles to have a consistent setup across all my work machines and our hosting infrastructure.

If you’d like to watch me put these nifty dotfiles to good use, join me on Twitch for my next live coding session!