Continuous Delivery for Elixir (Part 2 — Our Project)

Jeff Weiss
4 min readMay 28, 2016

--

Picking Our Exemplar Project

In order to exercise our Continuous Delivery pipeline, we need a project, specifically something which will need a different configuration between development, staging, and production. Since chatbots are all the rage, we’re going to create one, using the Hedwig framework from Sonny Scroggin.

Hedwig bot in action

This bot isn’t just all cat gifs and internet points. We need it to do something real. We’ll have it connect to our (fake) sales and customer database to provide an easier interface those on the front lines about customers running a specific version of a our product, or how much we’ve brought in in deals this quarter, or how much is from deal in flight, but projected to land this quarter.

We need a name for this bot. The canonical example in all of the Hedwig documentation is Alfred, thus Alfred it will be.

Brief aside — Why not a Phoenix web application?

I know what you’re thinking, Phoenix is such a huge part of the Elixir community, why not make a Phoenix app as our project?

Chris gets a delivery

I love Phoenix, I do. In fact, I get a text message from Sticker Mule every time Chris McCord orders new Phoenix stickers. However, I didn’t want to confuse the issue of deployment, especially for non-web applications, with “Why don’t you just use Heroku?” The first Elixir application we deployed to production, which we used to prove out this Continuous Delivery pipeline a little over a year ago, is a Phoenix application, but hosted internally primarily because it requires resources only available within our VPN.

Alfred Ahead

Hedwig currently supports several connection adapters

  • Slack
  • XMPP
  • IRC
  • HipChat (via XMPP)

This particular example will use the HipChat adapter. Not necessarily an endorsement of HipChat, I simply happen to already be a part of 8 Slacks, 2 IRC servers, and 1 HipChat server. Adding a second HipChat is merely evening things out a bit. ¯\_(ツ)_/¯

The instructions work quite similarly if you choose any of the other adapters. If you, like me, are spread across multiple technologies, Hedwig also allows you to create a single bot that can connect to several services and respond appropriately. Quite handy if you’re looking to build core business functionality into your chatbot, but need continuity or failover in the event of an upstream chat provider outage. This extended multi-adapter functionality is beyond the scope of this series.

Let’s go begin creating the Alfred project. You can also investigate my version. If you’re using an adapter other than HipChat, follow the instructions of that adapter.

$ mix new alfred --sup
...
$ cd alfred

We’ll need to add hedwig_hipchat as a dependency in mix.exs. At press time, we had an incompatibility between hedwig_hipchat and romeo 0.5, so we’ll also pin romeo to the 0.4 series

defp deps do
[
{:exml, github: "paulgray/exml", override: true},
{:hedwig_hipchat, "~> 0.9"},
{:romeo, "~> 0.4.0", override: true},
]
end

As directed by hedwig_hipchat’s README, we’ll also add it to our list of applications to start before us.

def application do
[applications: [:logger, :hedwig_hipchat],
mod: {Alfred, []}]
end

Now we’ll fetch and compile our dependencies and run the bot generator task.

$ mix deps.get
$ mix deps.compile
$ mix hedwig.gen.robot

Welcome to the Hedwig Robot Generator!

Let's get started.

What would you like to name your bot?: alfred

Available adapters

1. Hedwig.Adapters.HipChat
2. Hedwig.Adapters.Console
3. Hedwig.Adapters.Test

Please select an adapter: 1

* creating lib/alfred
* creating lib/alfred/robot.ex
* updating config/config.exs

Don't forget to add your new robot to your supervision tree
(typically in lib/alfred.ex):

worker(Alfred.Robot, [])

Add Alfred to your supervision tree as directed.

Hard coded configuration

In order to initially test Alfred, add the appropriate configuration settings to config/config.exs. Here’s mine for reference:

config :alfred, Alfred.Robot,
adapter: Hedwig.Adapters.HipChat,
name: "Alfred",
aka: "!",
jid: "587458_3985539@chat.hipchat.com",
password: "not_my_real_password",
rooms: [
{"587458_testing@conf.hipchat.com", []},
{"587458_elixircontinuousdelivery@conf.hipchat.com", []},
],
responders: [
{Hedwig.Responders.Help, []},
{Hedwig.Responders.GreatSuccess, []},
{Hedwig.Responders.ShipIt, []}
]

Please don’t commit your password.

Later in the series, we’ll use a combination of puppet and conform to extract the relevant configuration items to a template and then have the configuration automatically read at startup. This dynamic configuration will enable Ops to manage and rotate credentials without our knowledge or involvement.

Great Success

What’s next?

In the next section, we’ll use conform to identify our dynamic configuration with the goal of at startup, instead of compile time, looking at configuration for

  • Username
  • Password
  • Rooms

Rooms is the trickier one because of how Hedwig expects the configuration: a list of tuples. No one wants to either explain to Ops about the exacting format for a list of tuples or write formatters for their configuration management system to output a list of tuples. So we’ll take a shortcut. We’ll specify the rooms in the .conf file as a comma-separated list of strings, thereby reducing the cognitive burden on the 3am Ops lizard brain.

alfred.Elixir.Alfred.Robot.rooms = "587458_testing@conf.hipchat.com", "587458_elixircontinuousdelivery@conf.hipchat.com"

We then use a conform transform to convert that Ops-friendly and integration-friendly value into the Erlang Term Format of sys.config.

{rooms, [{<<"587458_testing@conf.hipchat.com">>, []},
{<<"587458_elixircontinuousdelivery@conf.hipchat.com">>, []}]}

Once we have the releases and packages created, we’ll return to the content of Alfred and enable connectivity to our fake sales database so that we have have Alfred connect to different databases, with different credentials, in each of development, staging, and production.

--

--

Jeff Weiss

A man of infinite resource and sagacity. Filled with ‘satiable curiosity. A howler himself. All places are alike to me. 18A8 5300 B15A 36D1