#+TITLE: Making docker-compose easier with wdocker
#+DATE: 2016-02-21
#+COLESLAW_TAGS: wdocker docker docker-compose
#+OPTIONS: num:nil

* Introduction

  [[https://github.com/babab/wdocker][wdocker]] is a little utility written by a [[https://benjamin.althu.es][friend]] and former colleague
  of mine. It allows you to define commands for it in a
  ~Dockerfile~. He wrote it because he used a lot of composite
  commands when writing docker images like:

  #+BEGIN_SRC sh
    docker stop CONTAINER && docker rm CONTAINER && docker rmi IMAGE && \
        docker build -t IMAGE && docker run --name CONTAINER IMAGE
  #+END_SRC

  By using wdocker to define a command he can greatly simplify his own
  workflow. Let's call it rebuild:

  #+BEGIN_SRC dockerfile
    #wd# container = CONTAINER
    #wd# image = IMAGE
    #wd# stop = docker stop {container}
    #wd# rm = docker rm {container}
    #wd# rmi = docker rmi {container}
    #wd# build = docker build -t {image}
    #wd# run = docker run --name {container} {image}

    #wd# rebuild: {stop} && {rm} && {rmi} && {build} && {run}

    FROM ubuntu

    # ...
  #+END_SRC

  Now he can use the following command instead of the list presented
  before:

  : wdocker rebuild

* Syntax

  wdocker has very simple syntax. You can define variables and
  commands:

  : #wd# variable = value
  : #wd# command: program

  Variables can be used by putting them in braces, including in other
  variables, as you've seen in the first example.

  : #wd# variable = -l
  : #wd# list: ls {variable}

  This would run =ls -l= when the command =wdocker list= is called.

  As you can see you're not limited to using docker in your wdocker
  commands. This property is what allows me to use wdocker in my
  workflow.

* Combining with docker-compose

  I started using docker not too long ago at work to develop our
  projects in. This is nice because it allows me to completely isolate
  my development environments. Since we have a few processes running
  together a single docker image isn't a great option, so I use
  docker-compose to define and combine the containers I need.

  As a side-effect this requires me to write long commands to do
  something like run rspec tests:

  : docker-compose run --rm -e RACK_ENV=test -e RAILS_ENV=test \
  :     container bundle exec rspec

  The alternative is defining a specialized test container with a
  bogus entry command (such as ~true~) and use that, which would still
  make the command:

  : docker-compose run --rm test-container bundle exec rspec

  Instead I can define a wdocker command in the ~Dockerfile~ used to
  build the containers used:

  #+BEGIN_SRC dockerfile
    #wd# rspec: docker-compose run --rm -e RACK_ENV=test -e RAILS_ENV=test container bundle exec rspec

    FROM ruby

    #...
  #+END_SRC

  Now I can run the following, much shorter, command to run the rspec
  tests:

  : wdocker rspec

  We also use cucumber for some other tests, which is even longer to
  type in, adding the ~cucumber~ command is easy:

  #+BEGIN_SRC dockerfile
    #wd# rspec: docker-compose run --rm -e RACK_ENV=test -e RAILS_ENV=test container bundle exec rspec
    #wd# cucumber: docker-compose run --rm -e RACK_ENV=test -e RAILS_ENV=test container bundle exec cucumber

    FROM ruby

    # ...
  #+END_SRC

  Now I can run =wdocker cucumber= as well.

  The latest git version of wdocker passes any arguments after the
  command name directly to the command to be executed. So if I need to
  run tests in a single spec file I can just do:

  : wdocker rspec spec/models/mymodel_spec.rb

  We have two commands defined now that are 90% the same. I always use
  the ~--rm~ switch to remove the started container after it's done, I
  don't want a lot of containers piling up. I also always have to use
  ~bundle exec~ to run commands, since the containers don't use rvm or
  add the script directories to ~$PATH~. We can extract them to some
  variables:

  #+BEGIN_SRC dockerfile
    #wd# run = docker-compose run --rm
    #wd# exec = bundle exec
    #wd# test = -e RACK_ENV=test -e RAILS_ENV=test

    #wd# rspec: {run} {test} container {exec} rspec
    #wd# cucumber: {run} {test} container {exec} cucumber

    FROM ruby

    # ...
  #+END_SRC

  Right now these commands always use the ~container~ service defined
  in ~docker-compose.yml~. I could add it to the ~run~ command, but I
  might need to run some commands on another container, but I can
  define another variable:

  #+BEGIN_SRC dockerfile
    #wd# run = docker-compose run --rm
    #wd# test = -e RACK_ENV=test -e RAILS_ENV=test
    #wd# run-test-container = {run} {test} container
    #wd# exec = bundle exec

    #wd# rspec: {run-test-container} {exec} rspec
    #wd# cucumber: {run-test-container} {exec} cucumber

    FROM ruby

    # ...
  #+END_SRC

  Now you also see that variables can be nested in other variables.

  If you ever forget what you defined or if the mix of commands and
  variables becomes too much for you, you can call the wdocker command
  without arguments to see the commands you defined and the shell
  commands they'll run.
