fix Dockerfile and create a docker management script

This commit is contained in:
Izalia Mae 2022-04-12 04:13:14 -04:00
parent d322d41f07
commit 51984573da
7 changed files with 135 additions and 25 deletions

View file

@ -1,11 +1,28 @@
FROM python:3-alpine FROM python:3-alpine
WORKDIR /workdir
# install build deps for pycryptodome and other c-based python modules
RUN apk add alpine-sdk autoconf automake libtool gcc RUN apk add alpine-sdk autoconf automake libtool gcc
ADD requirements.txt /workdir/ # add env var to let the relay know it's in a container
RUN pip3 install -r requirements.txt ENV DOCKER_RUNNING=true
ADD . /workdir/ # setup various container properties
VOLUME ["/data"]
CMD ["python", "-m", "relay"] CMD ["python", "-m", "relay"]
EXPOSE 8080/tcp
WORKDIR /opt/activityrelay
VOLUME ["/workdir/data"] # install and update important python modules
RUN pip3 install -U setuptools wheel pip
# only copy necessary files
COPY relay ./relay
COPY LICENSE .
COPY README.md .
COPY requirements.txt .
COPY setup.cfg .
COPY setup.py .
COPY .git ./.git
# install relay deps
RUN pip3 install -r requirements.txt

View file

@ -15,9 +15,9 @@ in this package as the `LICENSE` file.
You need at least Python 3.6 (latest version of 3.x recommended) to make use of this software. You need at least Python 3.6 (latest version of 3.x recommended) to make use of this software.
It simply will not run on older Python versions. It simply will not run on older Python versions.
Download the project and install with pip (`pip3 install .`). Download the project and install with pip (`pip3 install -r requirements.txt`).
Run `activityrelay setup` and answer the prompts or copy `relay.yaml.example` to `relay.yaml` Run `python3 -m relay setup` and answer the prompts or copy `relay.yaml.example` to `relay.yaml`
and edit it as appropriate: and edit it as appropriate:
$ cp relay.yaml.example relay.yaml $ cp relay.yaml.example relay.yaml
@ -25,7 +25,7 @@ and edit it as appropriate:
Finally, you can launch the relay: Finally, you can launch the relay:
$ activityrelay run $ python3 -m relay run
It is suggested to run this under some sort of supervisor, such as runit, daemontools, It is suggested to run this under some sort of supervisor, such as runit, daemontools,
s6 or systemd. Configuration of the supervisor is not covered here, as it is different s6 or systemd. Configuration of the supervisor is not covered here, as it is different
@ -60,17 +60,12 @@ You can perform a few management tasks such as peering or depeering other relays
This will show the available management tasks: This will show the available management tasks:
$ activityrelay --help $ python3 -m relay --help
When following remote relays, you should use the `/actor` endpoint as you would in When following remote relays, you should use the `/actor` endpoint as you would in
Pleroma and other LitePub-compliant software. Pleroma and other LitePub-compliant software.
## Docker ## Docker
You can run ActivityRelay with docker. Edit `relay.yaml` so that the database You can run ActivityRelay with docker via the docker management script: `docker.sh`.
location is set to `./data/relay.jsonld` and then build and run the docker Run it without arguments to see the list of commands.
image :
$ docker volume create activityrelay-data
$ docker build -t activityrelay .
$ docker run -d -p 8080:8080 -v activityrelay-data:/workdir/data activityrelay

66
docker.sh Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/env bash
case $1 in
install)
docker build -f Dockerfile -t activityrelay . && \
docker volume create activityrelay-data && \
docker run -it -p 8080:8080 -v activityrelay-data:/data --name activityrelay activityrelay
;;
uninstall)
docker stop activityrelay && \
docker container rm activityrelay && \
docker volume rm activityrelay-data && \
docker image rm activityrelay
;;
start)
docker start activityrelay
;;
stop)
docker stop activityrelay
;;
manage)
shift
docker exec -it activityrelay python3 -m relay "$@"
;;
shell)
docker exec -it activityrelay bash
;;
rescue)
docker run -it --rm --entrypoint bash -v activityrelay-data:/data activityrelay
;;
edit)
if [ -z ${EDITOR} ]; then
echo "EDITOR environmental variable not set"
exit
fi
CONFIG="/tmp/relay-$(date +"%T").yaml"
docker cp activityrelay:/data/relay.yaml $CONFIG && \
$EDITOR $CONFIG && \
docker cp $CONFIG activityrelay:/data/relay.yaml && \
rm $CONFIG
;;
*)
COLS="%-22s %s\n"
echo "Valid commands:"
printf "$COLS" "- start" "Run the relay in the background"
printf "$COLS" "- stop" "Stop the relay"
printf "$COLS" "- manage <cmd> [args]" "Run a relay management command"
printf "$COLS" "- edit" "Edit the relay's config in \$EDITOR"
printf "$COLS" "- shell" "Drop into a bash shell on the running container"
printf "$COLS" "- rescue" "Drop into a bash shell on a temp container with the data volume mounted"
printf "$COLS" "- install" "Build the image, create a new container and volume, and run relay setup"
printf "$COLS" "- uninstall" "Delete the relay image, container, and volume"
;;
esac

View file

@ -60,11 +60,15 @@ class RelayConfig(DotDict):
} }
def __init__(self, path): def __init__(self, path, is_docker):
self._path = Path(path).expanduser().resolve() if is_docker:
path = '/data/relay.yaml'
self._isdocker = is_docker
self._path = Path(path).expanduser()
super().__init__({ super().__init__({
'db': f'{self._path.stem}.jsonld', 'db': str(self._path.parent.joinpath(f'{self._path.stem}.jsonld')),
'listen': '0.0.0.0', 'listen': '0.0.0.0',
'port': 8080, 'port': 8080,
'note': 'Make a note about your instance here.', 'note': 'Make a note about your instance here.',
@ -81,6 +85,9 @@ class RelayConfig(DotDict):
def __setitem__(self, key, value): def __setitem__(self, key, value):
if self._isdocker and key in ['db', 'listen', 'port']:
return
if key in ['blocked_instances', 'blocked_software', 'whitelist']: if key in ['blocked_instances', 'blocked_software', 'whitelist']:
assert isinstance(value, (list, set, tuple)) assert isinstance(value, (list, set, tuple))
@ -234,7 +241,7 @@ class RelayConfig(DotDict):
self[key] = value self[key] = value
if self.host == 'example.com': if self.host.endswith('example.com'):
return False return False
return True return True

View file

@ -3,6 +3,7 @@ import asyncio
import click import click
import json import json
import logging import logging
import os
import platform import platform
from aiohttp.web import AppRunner, TCPSite from aiohttp.web import AppRunner, TCPSite
@ -11,14 +12,15 @@ from cachetools import LRUCache
from . import app, misc, views from . import app, misc, views
from .config import DotDict, RelayConfig from .config import DotDict, RelayConfig
from .database import RelayDatabase from .database import RelayDatabase
from .misc import follow_remote_actor, unfollow_remote_actor from .misc import check_open_port, follow_remote_actor, unfollow_remote_actor
@click.group('cli', context_settings={'show_default': True}, invoke_without_command=True) @click.group('cli', context_settings={'show_default': True}, invoke_without_command=True)
@click.option('--config', '-c', default='relay.yaml', help='path to the relay\'s config') @click.option('--config', '-c', default='relay.yaml', help='path to the relay\'s config')
@click.pass_context @click.pass_context
def cli(ctx, config): def cli(ctx, config):
app['config'] = RelayConfig(config) app['is_docker'] = bool(os.environ.get('DOCKER_RUNNING'))
app['config'] = RelayConfig(config, app['is_docker'])
if not app['config'].load(): if not app['config'].load():
app['config'].save() app['config'].save()
@ -33,6 +35,10 @@ def cli(ctx, config):
app['cache'][key] = LRUCache(app['config'][key]) app['cache'][key] = LRUCache(app['config'][key])
if not ctx.invoked_subcommand: if not ctx.invoked_subcommand:
if app['config'].host.endswith('example.com'):
relay_setup.callback()
else:
relay_run.callback() relay_run.callback()
@ -239,7 +245,7 @@ def relay_setup():
config.save() config.save()
if click.confirm('Relay all setup! Would you like to run it now?'): if not app['is_docker'] and click.confirm('Relay all setup! Would you like to run it now?'):
relay_run.callback() relay_run.callback()
@ -247,7 +253,9 @@ def relay_setup():
def relay_run(): def relay_run():
'Run the relay' 'Run the relay'
if app['config'].host.endswith('example.com'): config = app['config']
if config.host.endswith('example.com'):
return click.echo('Relay is not set up. Please edit your relay config or run "activityrelay setup".') return click.echo('Relay is not set up. Please edit your relay config or run "activityrelay setup".')
vers_split = platform.python_version().split('.') vers_split = platform.python_version().split('.')
@ -262,6 +270,9 @@ def relay_run():
click.echo('Warning: PyCrypto is old and should be replaced with pycryptodome') click.echo('Warning: PyCrypto is old and should be replaced with pycryptodome')
return click.echo(pip_command) return click.echo(pip_command)
if not check_open_port(config.listen, config.port):
return click.echo(f'Error: A server is already running on port {config.port}')
# web pages # web pages
app.router.add_get('/', views.home) app.router.add_get('/', views.home)

View file

@ -2,6 +2,7 @@ import asyncio
import base64 import base64
import json import json
import logging import logging
import socket
import traceback import traceback
from Crypto.Hash import SHA, SHA256, SHA512 from Crypto.Hash import SHA, SHA256, SHA512
@ -28,6 +29,18 @@ def build_signing_string(headers, used_headers):
return '\n'.join(map(lambda x: ': '.join([x.lower(), headers[x]]), used_headers)) return '\n'.join(map(lambda x: ': '.join([x.lower(), headers[x]]), used_headers))
def check_open_port(host, port):
if host == '0.0.0.0':
host = '127.0.0.1'
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
return s.connect_ex((host , port)) != 0
except socket.error as e:
return False
def create_signature_header(headers): def create_signature_header(headers):
headers = {k.lower(): v for k, v in headers.items()} headers = {k.lower(): v for k, v in headers.items()}
used_headers = headers.keys() used_headers = headers.keys()

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
.