diff --git a/docker/000-default.conf b/docker/000-default.conf index a94ae180..1eade42b 100644 --- a/docker/000-default.conf +++ b/docker/000-default.conf @@ -29,3 +29,10 @@ RewriteRule "^/robots.txt$" "/static/robots.txt" [L] RewriteRule "^/favicon.ico$" "/static/img/favicon.ico" [L] + +# Allow access to mod_status for monitoring + + SetHandler server-status + Require local + Require ip 10.123.242.171 + diff --git a/utils/lxr b/utils/lxr new file mode 100755 index 00000000..2f412af5 --- /dev/null +++ b/utils/lxr @@ -0,0 +1,123 @@ +#!/bin/sh + +checksum() +{ + # We don't checksum the full databases, that is slow. Instead, we checksum + # the list of database names and sizes. We never remove versions when + # indexing so the size will forever grow. + stat --printf="%n\t%s\n" $1/*/data/*.db | \ + sort | \ + sha256sum | \ + cut -d' ' -f1 +} + +update_index() +{ + flock -n /tmp/lxr-indexing-lock $0 update_index_nolock +} + +update_index_nolock() +{ + # We do an A/B scheme: there are two folders and one symlink pointing to either: + # - /srv/elixir-data-a + # - /srv/elixir-data-b + # - /srv/elixir-data is the symlink. + # + # "Hot" means the data pointed by the symlink, ie the one served on prod. + # "Cold" means the other repo. We only delete it at next indexing to simplify + # debugging & recovery. + + hot_data="$(readlink -f /srv/elixir-data)" + + if test "/srv/elixir-data-a" = "$hot_data"; then + cold_data="/srv/elixir-data-b" + elif test "/srv/elixir-data-b" = "$hot_data"; then + cold_data="/srv/elixir-data-a" + else + >&2 echo "error: unexpected data symlink: $hot_data" + exit 1 + fi + + test -z "$cold_data" && exit 1 # Last check before "rm -rf /*"! + rm -rf "$cold_data/*" + cp -r $hot_data/* "$cold_data/" + + # Not using `docker run --rm` to avoid losing logs. + docker rm lxr-indexing || true + + docker run -i --name lxr-indexing \ + -v "$cold_data":/srv/elixir-data \ + --entrypoint index elixir \ + /srv/elixir-data --all + + # Only update the symlink when something new got indexed. We want to + # avoid restarting the HTTP server container if nothing changed. + if test "$(checksum "$cold_data")" != "$(checksum "$hot_data")"; then + ln -snf "$cold_data" /srv/elixir-data + fi +} + +update_sources() +{ + src=/usr/local/elixir + + git -C $src fetch + git -C $src rebase + + version=$(git -C $src rev-parse --short HEAD) + + docker build -t elixir \ + --build-arg ELIXIR_VERSION=$version \ + -f $src/docker/Dockerfile $src +} + +restart_container() +{ + docker kill lxr || true + docker rm lxr || true + docker run -d --name lxr -p 8080:80 \ + -v /srv/elixir-data:/srv/elixir-data:ro \ + --network=anubis_default \ + elixir +} + +deploy() +{ + update_sources + restart_container +} + +index() +{ + hot_data="$(readlink -f /srv/elixir-data)" + + update_index + + new_hot_data="$(readlink -f /srv/elixir-data)" + if test "$hot_data" != "$new_hot_data"; then + restart_container + fi +} + +functions="deploy +index +restart_container +update_index +update_index_nolock +update_sources" + +if test "$#" -eq 0 || ! echo "$functions" | grep -q "^$1$"; then + >&2 echo "usage: $0 ..." + >&2 echo + >&2 echo "deploy: update_sources && restart_container" + >&2 echo "index: update_index && restart_container" + >&2 echo "restart_container: docker kill, docker run" + >&2 echo "update_index: copy data, run update container, swap symlink" + >&2 echo "update_sources: git pull, docker build container" + + exit 1 +fi + +set -ex + +$1