Kamyab's Blog

~/ github mail

Docker Find File

I regularly need to view/edit/manipulate files in a Docker container that are not mounted from the outside. It sure is nice to do that from the comfort of Emacs.

My desired behavior was a this:

  1. invoke a function
  2. see a list of running containers and select one
  3. find file or directory from that container

docker.el can be used for interacting with the Docker API. For the second step, I use ivy/counsel as my completion framework but anything works, really. For the final and most important step, there's the almighty TRAMP.

I have a function for listing containers, which is just a wrapper around docker.el functions.

(defun kz/docker-container-entries (&rest filters)
  (if (and filters (car filters))
      (aio-wait-for
       (docker-container-entries
        (mapconcat (lambda (f) (format "-f %s" f)) filters " "))))
  (aio-wait-for (docker-container-entries "--all")))

This will synchronously call docker-container-entries (from docker.el), with provided filters.

What I didn't know and learned while trying to do this, was the completing-read function. I used it all the time, but not in my own functions. Here's the documentation. ivy (and alternatives) make this "minibuffer completions" smoother.

(completing-read "Select container: " (kz/docker-container-entries "status=running"))

If I need to open, for example, /etc/nginx/nginx.conf from nginx-container, using TRAMP, I could write:

(find-file "/docker:root@nginx-container:/etc/nginx/nginx.conf")

For my purpose, since I want a more interactive experience, I will use counsel-find-file and pass it the prefix /docker:root@container:/ to make my life easier. This is the final product:

(defun kz/docker-find-file ()
  (interactive)
  (let ((container (completing-read "Select container: " (kz/docker-container-entries "status=running"))))
    (counsel-find-file (concat "/docker:root@" container ":/"))))