fix Dockerfile and create a docker management script
This commit is contained in:
parent
d322d41f07
commit
51984573da
7 changed files with 135 additions and 25 deletions
27
Dockerfile
27
Dockerfile
|
@ -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
|
||||||
|
|
17
README.md
17
README.md
|
@ -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
66
docker.sh
Executable 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
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.
|
Loading…
Reference in a new issue