feat(host): start docker addon

This commit is contained in:
Romain J 2021-02-11 23:18:12 +01:00
parent 7423b40337
commit c7ddba1bae
22 changed files with 394 additions and 12 deletions

7
.envs/.local/.postgres Normal file
View file

@ -0,0 +1,7 @@
# PostgreSQL
# ------------------------------------------------------------------------------
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=tuxbot_bot
POSTGRES_USER=debug
POSTGRES_PASSWORD=debug

4
.envs/.local/.tuxbot Normal file
View file

@ -0,0 +1,4 @@
# General
# ------------------------------------------------------------------------------
USE_DOCKER=yes
IPYTHONDIR=/app/.ipython

8
.gitignore vendored
View file

@ -36,4 +36,10 @@ venv
dist dist
build build
*.egg *.egg
*.egg-info *.egg-info
.ipython/
.env
.envs/*
!.envs/.local/

View file

@ -2,7 +2,7 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/tuxbot-bot.iml" filepath="$PROJECT_DIR$/.idea/tuxbot-bot.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/tuxbot_bot.iml" filepath="$PROJECT_DIR$/.idea/tuxbot_bot.iml" />
</modules> </modules>
</component> </component>
</project> </project>

View file

@ -6,7 +6,7 @@
<excludeFolder url="file://$MODULE_DIR$/dist" /> <excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/venv" /> <excludeFolder url="file://$MODULE_DIR$/venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.9 (tuxbot-bot)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.9 (venv)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="PyDocumentationSettings"> <component name="PyDocumentationSettings">

View file

@ -0,0 +1,37 @@
FROM python:3.8-slim-buster
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
# psycopg2 dependencies
&& apt-get install -y libpq-dev \
# Translations dependencies
&& apt-get install -y gettext \
# Git
&& apt-get install -y git \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
# Requirements are installed here to ensure they will be cached.
COPY ./dev.requirements.txt /app/dev.requirements.txt
COPY ./tuxbot /app/tuxbot
COPY ./setup.cfg /app/setup.cfg
COPY ./setup.py /app/setup.py
RUN pip install -r /app/dev.requirements.txt
RUN pip install ./app
COPY ./compose/production/tuxbot/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
RUN chmod +x /entrypoint
COPY ./compose/local/tuxbot/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start
WORKDIR /app
ENTRYPOINT ["/entrypoint"]

View file

@ -0,0 +1,6 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset

View file

@ -0,0 +1,6 @@
FROM postgres:12.3
COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance
RUN chmod +x /usr/local/bin/maintenance/*
RUN mv /usr/local/bin/maintenance/* /usr/local/bin \
&& rmdir /usr/local/bin/maintenance

View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
BACKUP_DIR_PATH='/backups'
BACKUP_FILE_PREFIX='backup'

View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
countdown() {
declare desc="A simple countdown. Source: https://superuser.com/a/611582"
local seconds="${1}"
local d=$(($(date +%s) + "${seconds}"))
while [ "$d" -ge `date +%s` ]; do
echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r";
sleep 0.1
done
}

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
message_newline() {
echo
}
message_debug()
{
echo -e "DEBUG: ${@}"
}
message_welcome()
{
echo -e "\e[1m${@}\e[0m"
}
message_warning()
{
echo -e "\e[33mWARNING\e[0m: ${@}"
}
message_error()
{
echo -e "\e[31mERROR\e[0m: ${@}"
}
message_info()
{
echo -e "\e[37mINFO\e[0m: ${@}"
}
message_suggestion()
{
echo -e "\e[33mSUGGESTION\e[0m: ${@}"
}
message_success()
{
echo -e "\e[32mSUCCESS\e[0m: ${@}"
}

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
yes_no() {
declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message."
local arg1="${1}"
local response=
read -r -p "${arg1} (y/[n])? " response
if [[ "${response}" =~ ^[Yy]$ ]]
then
exit 0
else
exit 1
fi
}

View file

@ -0,0 +1,38 @@
#!/usr/bin/env bash
### Create a database backup.
###
### Usage:
### $ docker-compose -f <environment>.yml (exec |run --rm) postgres backup
set -o errexit
set -o pipefail
set -o nounset
working_dir="$(dirname ${0})"
source "${working_dir}/_sourced/constants.sh"
source "${working_dir}/_sourced/messages.sh"
message_welcome "Backing up the '${POSTGRES_DB}' database..."
if [[ "${POSTGRES_USER}" == "postgres" ]]; then
message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
exit 1
fi
export PGHOST="${POSTGRES_HOST}"
export PGPORT="${POSTGRES_PORT}"
export PGUSER="${POSTGRES_USER}"
export PGPASSWORD="${POSTGRES_PASSWORD}"
export PGDATABASE="${POSTGRES_DB}"
backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz"
pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}"
message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'."

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
### View backups.
###
### Usage:
### $ docker-compose -f <environment>.yml (exec |run --rm) postgres backups
set -o errexit
set -o pipefail
set -o nounset
working_dir="$(dirname ${0})"
source "${working_dir}/_sourced/constants.sh"
source "${working_dir}/_sourced/messages.sh"
message_welcome "These are the backups you have got:"
ls -lht "${BACKUP_DIR_PATH}"

View file

@ -0,0 +1,55 @@
#!/usr/bin/env bash
### Restore database from a backup.
###
### Parameters:
### <1> filename of an existing backup.
###
### Usage:
### $ docker-compose -f <environment>.yml (exec |run --rm) postgres restore <1>
set -o errexit
set -o pipefail
set -o nounset
working_dir="$(dirname ${0})"
source "${working_dir}/_sourced/constants.sh"
source "${working_dir}/_sourced/messages.sh"
if [[ -z ${1+x} ]]; then
message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again."
exit 1
fi
backup_filename="${BACKUP_DIR_PATH}/${1}"
if [[ ! -f "${backup_filename}" ]]; then
message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again."
exit 1
fi
message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..."
if [[ "${POSTGRES_USER}" == "postgres" ]]; then
message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
exit 1
fi
export PGHOST="${POSTGRES_HOST}"
export PGPORT="${POSTGRES_PORT}"
export PGUSER="${POSTGRES_USER}"
export PGPASSWORD="${POSTGRES_PASSWORD}"
export PGDATABASE="${POSTGRES_DB}"
message_info "Dropping the database..."
dropdb "${PGDATABASE}"
message_info "Creating a new database..."
createdb --owner="${POSTGRES_USER}"
message_info "Applying the backup to the new database..."
gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}"
message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup."

View file

@ -0,0 +1,46 @@
FROM node:10-stretch-slim as client-builder
WORKDIR /app
COPY ./package.json /app
RUN npm install && npm cache clean --force
COPY . /app
RUN npm run build
# Python build stage
FROM python:3.8-slim-buster
ENV PYTHONUNBUFFERED 1
RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
# psycopg2 dependencies
&& apt-get install -y libpq-dev \
# Translations dependencies
&& apt-get install -y gettext \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
RUN addgroup --system tuxbot \
&& adduser --system --ingroup tuxbot tuxbot
# Requirements are installed here to ensure they will be cached.
RUN pip install --no-cache-dir psycopg2==2.8.6
COPY --chown=tuxbot:tuxbot ./compose/production/tuxbot/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
RUN chmod +x /entrypoint
COPY --chown=tuxbot:tuxbot ./compose/production/tuxbot/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start
COPY --from=client-builder --chown=tuxbot:tuxbot /app /app
USER tuxbot
WORKDIR /app
ENTRYPOINT ["/entrypoint"]

View file

@ -0,0 +1,42 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
if [ -z "${POSTGRES_USER}" ]; then
base_postgres_image_default_user='postgres'
export POSTGRES_USER="${base_postgres_image_default_user}"
fi
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
postgres_ready() {
python << END
import sys
import psycopg2
try:
psycopg2.connect(
dbname="${POSTGRES_DB}",
user="${POSTGRES_USER}",
password="${POSTGRES_PASSWORD}",
host="${POSTGRES_HOST}",
port="${POSTGRES_PORT}",
)
except psycopg2.OperationalError:
sys.exit(-1)
sys.exit(0)
END
}
until postgres_ready; do
>&2 echo 'Waiting for PostgreSQL to become available...'
sleep 1
done
>&2 echo 'PostgreSQL is available'
exec "$@"

View file

@ -0,0 +1,8 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
tuxbot dev

33
local.yml Normal file
View file

@ -0,0 +1,33 @@
version: '3'
volumes:
local_postgres_data: {}
local_postgres_data_backups: {}
services:
tuxbot:
build:
context: .
dockerfile: ./compose/local/tuxbot/Dockerfile
image: tuxbot_bot_local_tuxbot
container_name: tuxbot
depends_on:
- postgres
volumes:
- .:/app:z
env_file:
- ./.envs/.local/.tuxbot
- ./.envs/.local/.postgres
command: /start
postgres:
build:
context: .
dockerfile: ./compose/production/postgres/Dockerfile
image: tuxbot_bot_production_postgres
container_name: postgres
volumes:
- local_postgres_data:/var/lib/postgresql/data:Z
- local_postgres_data_backups:/backups:z
env_file:
- ./.envs/.local/.postgres

View file

@ -26,7 +26,7 @@ install_requires =
psutil==5.7.2 psutil==5.7.2
pydig==0.3.0 pydig==0.3.0
rich==9.10.0 rich==9.10.0
sentry_sdk==0.19.5 sentry_sdk==0.20.0
structured_config==4.12 structured_config==4.12
tortoise-orm==0.16.17 tortoise-orm==0.16.17

View file

@ -1,3 +1,4 @@
import sys
from tuxbot import ExitCodes from tuxbot import ExitCodes
from tuxbot.core.utils.console import console from tuxbot.core.utils.console import console
@ -9,10 +10,7 @@ def main() -> None:
run() run()
except SystemExit as exc: except SystemExit as exc:
if exc.code == ExitCodes.RESTART: if exc.code == ExitCodes.RESTART:
# reimport to load changes sys.exit(exc.code)
from .__run__ import run # pylint: disable=import-outside-toplevel
run()
else: else:
raise exc raise exc
except Exception: except Exception:

View file

@ -366,10 +366,10 @@ class Tux(commands.AutoShardedBot):
and reboot. and reboot.
""" """
if not restart:
self.shutdown_code = ExitCodes.SHUTDOWN self.shutdown_code = (
else: ExitCodes.RESTART if restart else ExitCodes.SHUTDOWN
self.shutdown_code = ExitCodes.RESTART )
await self.logout() await self.logout()