Open Source Consultant, Software Developer and System Administrator

If you are interrested in hiring a consultant for the effective use of open source software on an enterprise grade, take a look around in the About section to see, what I have to offer.

Blog and snippets

Various snippets or code parts I found useful, so I keep them here for reference.

Keep var with clj-reload

When writing ring servers it's common pass a var down as handler while developing to make the code reload-able, e.g. #'handler. But when using clj-reload the whole name-space is "replaced" (as in: a new name-space is created, and the old one will GC eventually).

So the handler var the server holds is still fine, but it is the old version. While clj-reload suggests some ways to handle this situation, I wondered how to re-resolve a var, but found no build-in way. So the following function makes sure to resolve the current "version" of the given var:

(defn re-ressolving-var
  [v]
  {:pre [(var? v)]}
  (let [{vns :ns vname :name} (meta v)]
    (ns-resolve (create-ns (.getName vns)) vname)))

And with reitit:

(let [dev-mode? (get opts :dev-mode false)
      rh (if dev-mode?
           (reitit.ring/reloading-ring-handler #(re-ressolving-var handler))
           (handler))]
  (jetty/run-jetty rh opts))))

WREPL now uses Clojure CLI

WREPL no longer is a standalone program, but since version 0.2 now is integrated into the Clojure CLI. This makes it easier to use on a regular Clojurist machine. See the example-Folder on how to migrate.

Back when WREPL started there were no official way to quickly spin up a Clojure REPL any more. Spec was released, but was not part of the core. So before that you could just run a Clojure REPL with the JAR file in your ~/.m2/repository/org/clojure/ directory. But with the additional dependencies into the Spec libraries, this was no longer so easy.

So WREPL was again a single JAR to run your (standalone) REPL and also allowed configuration of all the "letters" of REPL and some additional stuff (e.g. loading dependencies, run some file on startup, ...). Nowadays, the Clojure CLI is installed on basically every Clojure developer machine and brings back the entry point to quickly spin up a REPL. It brings the facilities to run your own code and pull the dependencies for it.

Yet the part to configure a REPL to your liking is still a usable feature. Integrating "REPL-ish" tools with each other is not convenient. So WREPL remains useful still to e.g. use a different prompt to denote some other state than the current namespace or configure a pretty-printer to your liking in your rebel-readline REPL.

Proxy ports into a running Docker(-Compose) container

Publishing ports in Docker is done only once at the beginning on the creation of the container. Yet sometimes there is the need to talk to the service inside a container, but the container itself does not have the proper tools to do what you want with it built-in. Yet re-creating the container just to publish the ports, maybe manifesting it temporarily in some docker-compose.yml, and then forgetting about it to open up some security problem is not great.

So the following script allows to temporally proxy to a port of a running container, whether for manually ran containers or the ones in a network from Docker-Compose. It tries to find the network and attaches to it, unless it's bridge, and uses a socat to run the proxy. The arguments are the container name or ID, the port to proxy to, and an optional port where to publish (if not given, Docker will pick one).

#!/bin/sh

set -eu

if test ! "$#" -ge 2; then
    echo "$0 <container> <target_port> (<local_port>)"
    exit 1
fi

container="$1"
port="$2"

network=$(docker inspect --format '{{range $k,$v:= .NetworkSettings.Networks}}{{println $k}}{{end}}' "$container" | head -n1)

inner_port=8080
publish="$inner_port"
if test "$#" -ge 3; then
    publish="$3:$publish"
fi

docker run \
    --rm \
    --publish="$publish" \
    --link="$container:target" \
    $(if test "$network" != "bridge"; then echo "--network=$network"; fi) \
    alpine/socat \
        "tcp-listen:$inner_port,fork,reuseaddr" \
        "tcp-connect:target:$port"