build: add Docker functionality and documentation

This commit is contained in:
Shyam Sunder
2018-07-11 23:30:13 -04:00
committed by rr-
parent 9730aa5c05
commit 6a6c4dc822
13 changed files with 590 additions and 212 deletions

8
server/.dockerignore Normal file
View File

@ -0,0 +1,8 @@
szurubooru/tests/*
setup.cfg
.pylintrc
mypi.ini
Dockerfile
.dockerignore
**/.gitignore

46
server/Dockerfile Normal file
View File

@ -0,0 +1,46 @@
FROM scratch as approot
WORKDIR /opt/app
COPY alembic.ini wait-for-es generate-thumb ./
COPY szurubooru/ ./szurubooru/
COPY config.yaml.dist ../
FROM python:3.6-slim
WORKDIR /opt/app
ARG PUID=1000
ARG PGID=1000
ARG PORT=6666
RUN \
# Set users
mkdir -p /opt/app /data && \
groupadd -g ${PGID} app && \
useradd -d /opt/app -M -c '' -g app -r -u ${PUID} app && \
chown -R app:app /opt/app /data && \
# Create init file
echo "#!/bin/sh" >> /init && \
echo "set -e" >> /init && \
echo "cd /opt/app" >> /init && \
echo "./wait-for-es" >> /init && \
echo "alembic upgrade head" >> /init && \
echo "exec waitress-serve --port ${PORT} szurubooru.facade:app" \
>> /init && \
chmod a+x /init && \
# Install ffmpeg
apt-get -yqq update && \
apt-get -yq install --no-install-recommends ffmpeg && \
rm -rf /var/lib/apt/lists/* && \
# Install waitress
pip3 install --no-cache-dir waitress
COPY --chown=app:app requirements.txt ./requirements.txt
RUN pip3 install --no-cache-dir -r ./requirements.txt
# done to minimize number of layers in final image
COPY --chown=app:app --from=approot / /
VOLUME ["/data/"]
EXPOSE ${PORT}
USER app
CMD ["/init"]

144
server/config.yaml.dist Normal file
View File

@ -0,0 +1,144 @@
# rather than editing this file, it is strongly suggested to create config.yaml
# and override only what you need.
# shown in the website title and on the front page
name: szurubooru
# user agent name used to download files from the web on behalf of the api users
user_agent:
# used to salt the users' password hashes
secret: change
# required for running the test suite
test_database: 'sqlite:///:memory:'
# Delete thumbnails and source files on post delete
# Original functionality is no, to mitigate the impacts of admins going
# on unchecked post purges.
delete_source_files: no
thumbnails:
avatar_width: 300
avatar_height: 300
post_width: 300
post_height: 300
convert:
gif:
to_webm: false
to_mp4: false
# used to send password reset e-mails
smtp:
host: # example: localhost
port: # example: 25
user: # example: bot
pass: # example: groovy123
# host can be left empty, in which case it is recommended to fill contactEmail.
contact_email: # example: bob@example.com. Meant for manual password reset procedures
enable_safety: yes
tag_name_regex: ^\S+$
tag_category_name_regex: ^[^\s%+#/]+$
# don't make these more restrictive unless you want to annoy people; if you do
# customize them, make sure to update the instructions in the registration form
# template as well.
password_regex: '^.{5,}$'
user_name_regex: '^[a-zA-Z0-9_-]{1,32}$'
default_rank: regular
privileges:
'users:create:self': anonymous # Registration permission
'users:create:any': administrator
'users:list': regular
'users:view': regular
'users:edit:any:name': moderator
'users:edit:any:pass': moderator
'users:edit:any:email': moderator
'users:edit:any:avatar': moderator
'users:edit:any:rank': moderator
'users:edit:self:name': regular
'users:edit:self:pass': regular
'users:edit:self:email': regular
'users:edit:self:avatar': regular
'users:edit:self:rank': moderator # one can't promote themselves or anyone to upper rank than their own.
'users:delete:any': administrator
'users:delete:self': regular
'user_tokens:list:any': administrator
'user_tokens:list:self': regular
'user_tokens:create:any': administrator
'user_tokens:create:self': regular
'user_tokens:edit:any': administrator
'user_tokens:edit:self': regular
'user_tokens:delete:any': administrator
'user_tokens:delete:self': regular
'posts:create:anonymous': regular
'posts:create:identified': regular
'posts:list': anonymous
'posts:reverse_search': regular
'posts:view': anonymous
'posts:view:featured': anonymous
'posts:edit:content': power
'posts:edit:flags': regular
'posts:edit:notes': regular
'posts:edit:relations': regular
'posts:edit:safety': power
'posts:edit:source': regular
'posts:edit:tags': regular
'posts:edit:thumbnail': power
'posts:feature': moderator
'posts:delete': moderator
'posts:score': regular
'posts:merge': moderator
'posts:favorite': regular
'posts:bulk-edit:tags': power
'posts:bulk-edit:safety': power
'tags:create': regular
'tags:edit:names': power
'tags:edit:category': power
'tags:edit:description': power
'tags:edit:implications': power
'tags:edit:suggestions': power
'tags:list': regular
'tags:view': anonymous
'tags:merge': moderator
'tags:delete': moderator
'tag_categories:create': moderator
'tag_categories:edit:name': moderator
'tag_categories:edit:color': moderator
'tag_categories:list': anonymous
'tag_categories:view': anonymous
'tag_categories:delete': moderator
'tag_categories:set_default': moderator
'comments:create': regular
'comments:delete:any': moderator
'comments:delete:own': regular
'comments:edit:any': moderator
'comments:edit:own': regular
'comments:list': regular
'comments:view': regular
'comments:score': regular
'snapshots:list': power
'uploads:create': regular
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
#debug: 0 # generate server logs?
#show_sql: 0 # show sql in server logs?
#data_url: /data/
#data_dir: /var/www/data
## usage: schema://user:password@host:port/database_name
## example: postgres://szuru:dog@localhost:5432/szuru_test
#database:
#elasticsearch: # used for reverse image search
# host: localhost
# port: 9200
# index: szurubooru

View File

@ -1,6 +1,7 @@
from typing import Dict
import os
import yaml
from szurubooru import errors
def merge(left: Dict, right: Dict) -> Dict:
@ -15,12 +16,43 @@ def merge(left: Dict, right: Dict) -> Dict:
return left
def docker_config() -> Dict:
for key in [
'POSTGRES_USER',
'POSTGRES_PASSWORD',
'POSTGRES_HOST',
'ESEARCH_HOST'
]:
if not os.getenv(key, False):
raise errors.ConfigError(f'Environment variable "{key}" not set')
return {
'debug': True,
'show_sql': int(os.getenv('LOG_SQL', 0)),
'data_url': os.getenv('DATA_URL', '/data/'),
'data_dir': '/data/',
'database': 'postgres://%(user)s:%(pass)s@%(host)s:%(port)d/%(db)s' % {
'user': os.getenv('POSTGRES_USER'),
'pass': os.getenv('POSTGRES_PASSWORD'),
'host': os.getenv('POSTGRES_HOST'),
'port': int(os.getenv('POSTGRES_PORT', 5432)),
'db': os.getenv('POSTGRES_DB', os.getenv('POSTGRES_USER'))
},
'elasticsearch': {
'host': os.getenv('ESEARCH_HOST'),
'port': int(os.getenv('ESEARCH_PORT', 9200)),
'index': os.getenv('ESEARCH_INDEX', 'szurubooru')
}
}
def read_config() -> Dict:
with open('../config.yaml.dist') as handle:
ret = yaml.load(handle.read())
if os.path.exists('../config.yaml'):
with open('../config.yaml') as handle:
ret = merge(ret, yaml.load(handle.read()))
if os.path.exists('/.dockerenv'):
ret = merge(ret, docker_config())
return ret

33
server/wait-for-es Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
'''
Docker helper script. Blocks until the ElasticSearch service is ready.
'''
import logging
import time
import elasticsearch
from szurubooru import config, errors
def main():
print('Looking for ElasticSearch connection...')
logging.basicConfig(level=logging.ERROR)
es = elasticsearch.Elasticsearch([{
'host': config.config['elasticsearch']['host'],
'port': config.config['elasticsearch']['port'],
}])
TIMEOUT = 30
DELAY = 0.1
for _ in range(int(TIMEOUT / DELAY)):
try:
es.cluster.health(wait_for_status='yellow')
print('Connected to ElasticSearch!')
return
except Exception:
time.sleep(DELAY)
pass
raise errors.ThirdPartyError('Error connecting to ElasticSearch')
if __name__ == '__main__':
main()