In this post, we are going to take a look at how we can run docker commands from Common Lisp. Lately I have been exploring Common Lisp development in Docker. So I have to run docker commands from a terminal frequently.
I was basically looking for a way to run docker commands within SLIME itself. Then I came across the uiop:run-program function from the Common Lisp Cookbook
uiop:run-program fully portably runs a program as a synchronous external subprocess.
So if you want to list all the Docker containers from SLIME or any Lisp REPL, you have to call the
run-program function like this:
(uiop:run-program "docker ps -a" :output t)
We need to provide the
:output t argument to print the results of the command to the standard output.
The result will be something like:
CL-USER> (uiop:run-program "docker ps -a" :output t) CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 673b20976166 docker101tutorial "/docker-entrypoint.…" 19 months ago Exited (0) 4 days ago docker-tutorial NIL NIL 0
Running docker commands everytime using
uiop:run-program and passing all the options to print the output to the standard output felt like a time-consuming and non-Lispy way of doing stuff for me.
I wanted to run docker commands in a more natural way using Lisp, so I planned to write a package for the same. The original inspiration for this idea came from another Common Lisp package called cl-virtualbox by Fernando Borretti.
cl-virtualbox is a library that allows you to control VirtualBox from Common Lisp, by calling the
So our new package called cl-docker will allow us to control Docker from Common Lisp by calling the
So now, to list out all the Docker containers in your machine, you can just call the below function in your REPL
;; List all docker containers (docker:ps :a)
"CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 673b20976166 docker101tutorial \"/docker-entrypoint.…\" 19 months ago Exited (0) 16 hours ago docker-tutorial " NIL 0
Following is some of the docker commands and their corresponding
|docker ps -a||(docker:ps :a)|
|docker build DIRECTORY||(docker:build "myimage")|
|docker rm CONTAINER||(docker:rm "web")|
|docker start CONTAINER||(docker:start "web")|
|docker stop CONTAINER||(docker:stop "web")|
The more complete list of commands can be viewed from the project README
Moving away from uiop:run-program
uiop:run-program to run and manage docker from Common Lisp seems like an easy way to get things done. But it's not a clean and scalable way of doing things, basically we are interfacing with docker using the operating system command line capabilities. There are still issues in capturing the proper output, error information and unnecessary output like
0, which are actually the function return values and the return codes for the commands you run with
So how do we get around these things? The best way to properly manage docker from Common Lisp is to use the Docker Engine SDKs.
Docker provides an API for interacting with the Docker daemon (called the Docker Engine API), as well as SDKs for Go and Python. At present, there is no pre-built SDK available for Common Lisp.
The SDKs allow you to build and scale Docker apps and solutions quickly and easily. So for Common Lisp, we have to use the Docker Engine API directly.
The Docker Engine API is a RESTful API accessed by an HTTP client such as wget or curl, or the HTTP library which is part of most modern programming languages.
Using the SDK API to get a list of containers, just like using
docker ps, we need to send a curl request like this:
curl --unix-socket /var/run/docker.sock http://localhost/v1.41/containers/json
All we need to figure out is how to replicate this curl request in Common Lisp using an HTTP Client library using unix sockets. That's what I am trying to figure out now. If you have any ideas, please let me know in the comments.
cl-docker is still in early stages of development. It may not have covered all the docker commands with all the options that can be provided. So please give a try to the package and let me know for any queries/feedback in the comments section. You can also raise bug fixes, enhancements and feature requests through the Github issues
Oldest comments (2)
For sending http or https client requests you can use drakma or zaserve (available thru quicklisp).
(drakma:http-request ...) or (net.aserve.client:do-http-request ...)
And if their API is working through JSON format, which it likely is, then you can try cl-json or yason for the json encoding/decoding.