From 35a37aab8a68c43aca22886aece7a1efcaf3a9c1 Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Sat, 26 Nov 2022 18:13:56 -0700
Subject: [PATCH] Rework so that dockerized deployments can self-configure

---
 projects/activitypub_relay/Dockerfile         | 13 ++--
 projects/activitypub_relay/docker_relay.sh    | 11 ++++
 .../src/python/relay/__main__.py              | 60 +++++++++----------
 .../src/python/relay/config.py                | 13 ++--
 4 files changed, 51 insertions(+), 46 deletions(-)
 create mode 100644 projects/activitypub_relay/docker_relay.sh

diff --git a/projects/activitypub_relay/Dockerfile b/projects/activitypub_relay/Dockerfile
index ceeb650..07b8a6c 100644
--- a/projects/activitypub_relay/Dockerfile
+++ b/projects/activitypub_relay/Dockerfile
@@ -8,15 +8,18 @@ RUN mkdir -p /app
 RUN chown -R app:app /app
 USER app
 WORKDIR /app
-ENV PATH="/app/.local/bin:${PATH}"
-ENV PYTHONPATH="/app:${PYTHONPATH}"
-# Trivialize detecting dockerization
+VOLUME /data
 ENV DOCKER_RUNNING=true
 
+ENV PYTHONPATH="/app:${PYTHONPATH}"
+ENV PATH="/app/.local/bin:${PATH}"
+
 ### App specific crap
 # Deps vary least so do them first
 RUN pip3 install --user install aiohttp aiohttp_basicauth async_lru cachetools click pycryptodome pyyaml retry
 
-COPY --chown=app:app src/python relay.yaml relay.jsonld /app/
+COPY --chown=app:app docker_relay.sh /app/relay.sh
+COPY --chown=app:app src/python /app/
 
-CMD ["python3", "relay/__main__.py", "-c", "relay.yaml", "run"]
+EXPOSE 8080
+ENTRYPOINT ["/bin/sh", "/app/relay.sh"]
diff --git a/projects/activitypub_relay/docker_relay.sh b/projects/activitypub_relay/docker_relay.sh
new file mode 100644
index 0000000..5a08fdf
--- /dev/null
+++ b/projects/activitypub_relay/docker_relay.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env sh
+
+# A launcher script for the dockerized relay
+
+# First do config init if needed
+if [ ! -f "/data/config.yml" ]; then
+    python3 -m "relay" setup
+fi
+
+# Then run the blame thing
+exec python3 -m "relay" "${@:-run}"
diff --git a/projects/activitypub_relay/src/python/relay/__main__.py b/projects/activitypub_relay/src/python/relay/__main__.py
index a37c30a..87a3fe0 100644
--- a/projects/activitypub_relay/src/python/relay/__main__.py
+++ b/projects/activitypub_relay/src/python/relay/__main__.py
@@ -9,7 +9,6 @@ from relay.application import (
     Application,
     request_id_middleware,
 )
-from relay.config import relay_software_names
 
 
 @click.group(
@@ -238,13 +237,6 @@ def cli_software_list(obj: Application):
 def cli_software_ban(obj: Application, name, fetch_nodeinfo):
     "Ban software. Use RELAYS for NAME to ban relays"
 
-    if name == "RELAYS":
-        for name in relay_software_names:
-            obj.config.ban_software(name)
-
-        obj.config.save()
-        return click.echo("Banned all relay software")
-
     if fetch_nodeinfo:
         software = asyncio.run(misc.fetch_nodeinfo(name))
 
@@ -273,13 +265,6 @@ def cli_software_ban(obj: Application, name, fetch_nodeinfo):
 def cli_software_unban(obj: Application, name, fetch_nodeinfo):
     "Ban software. Use RELAYS for NAME to unban relays"
 
-    if name == "RELAYS":
-        for name in relay_software_names:
-            obj.config.unban_software(name)
-
-        config.save()
-        return click.echo("Unbanned all relay software")
-
     if fetch_nodeinfo:
         software = asyncio.run(misc.fetch_nodeinfo(name))
 
@@ -345,31 +330,40 @@ def cli_whitelist_remove(obj: Application, instance):
 def relay_setup(obj: Application):
     "Generate a new config"
 
-    while True:
-        obj.config.host = click.prompt(
-            "What domain will the relay be hosted on?", default=obj.config.host
+    if not obj.config.is_docker:
+        while True:
+            obj.config.host = os.getenv("RELAY_HOSTNAME") or click.prompt(
+                "What domain will the relay be hosted on?", default=obj.config.host
+            )
+
+            if not obj.config.host.endswith("example.com"):
+                break
+
+            click.echo("The domain must not be example.com")
+
+        obj.config.listen = os.getenv("LISTEN_ADDRESS") or click.prompt(
+            "Which address should the relay listen on?", default=obj.config.listen
         )
 
-        if not obj.config.host.endswith("example.com"):
+        while True:
+            obj.config.port = click.prompt(
+                "What TCP port should the relay listen on?",
+                default=obj.config.port,
+                type=int,
+            )
             break
 
-        click.echo("The domain must not be example.com")
-
-    obj.config.listen = click.prompt(
-        "Which address should the relay listen on?", default=obj.config.listen
-    )
-
-    while True:
-        obj.config.port = click.prompt(
-            "What TCP port should the relay listen on?",
-            default=obj.config.port,
-            type=int,
-        )
-        break
+    else:
+        obj.config.listen = os.getenv("LISTEN_ADDRESS", obj.config.listen)
+        obj.config.port = int(os.getenv("LISTEN_PORT", obj.config.port))
+        obj.config.host = os.getenv("RELAY_HOSTNAME")
+        if not obj.config.host:
+            click.echo("Error: No relay host configured! Set $RELAY_HOSTNAME")
+            exit(1)
 
     obj.config.save()
 
-    if not obj["is_docker"] and click.confirm(
+    if not obj.config.is_docker and click.confirm(
         "Relay all setup! Would you like to run it now?"
     ):
         relay_run.callback()
diff --git a/projects/activitypub_relay/src/python/relay/config.py b/projects/activitypub_relay/src/python/relay/config.py
index e53785c..431a674 100644
--- a/projects/activitypub_relay/src/python/relay/config.py
+++ b/projects/activitypub_relay/src/python/relay/config.py
@@ -1,3 +1,4 @@
+import os
 from pathlib import Path
 from urllib.parse import urlparse
 
@@ -5,14 +6,6 @@ from relay.misc import DotDict
 import yaml
 
 
-relay_software_names = [
-    "activityrelay",
-    "aoderelay",
-    "social.seattle.wa.us-relay",
-    "unciarelay",
-]
-
-
 class RelayConfig(DotDict):
     apkeys = {
         "host",
@@ -44,6 +37,10 @@ class RelayConfig(DotDict):
 
         super().__setitem__(key, value)
 
+    @property
+    def is_docker(self):
+        return bool(os.getenv("DOCKER_RUNNING"))
+
     @property
     def db(self):
         return Path(self["db"]).expanduser().resolve()