Skip to content

Commit

Permalink
Merge pull request #316 from freezingsaddles/fix-docker-compose-setup
Browse files Browse the repository at this point in the history
Fix freezing-web docker-compose setup
  • Loading branch information
obscurerichard authored Dec 31, 2024
2 parents ab98c2b + 9f0ee79 commit 6d91d89
Show file tree
Hide file tree
Showing 19 changed files with 312 additions and 258 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*.pyc
*.tmp
.env*
.gitkeep
.venv*
/*.iml
/build
Expand Down
18 changes: 18 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black
- repo: https://github.com/jackdewinter/pymarkdown
rev: v0.9.26
hooks:
- id: pymarkdown
args:
- "-d MD013 scan ."
4 changes: 2 additions & 2 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ members of the project's leadership.
## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html)

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq)
196 changes: 111 additions & 85 deletions README.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion bin/fmt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ echo "*** isort ***"
isort freezing
echo "*** djlint ***"
djlint --reformat freezing/web/templates

1 change: 0 additions & 1 deletion bin/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ flake8 freezing
echo "*** mypy ***"
echo "*** djlint ***"
djlint --check freezing/web/templates

File renamed without changes.
67 changes: 35 additions & 32 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,55 +1,58 @@
---
version: '3'

volumes:
freezing-data:
external: true

services:

freezing-db:
image: mysql:5.7
container_name: freezing-db
image: mysql:8.0
container_name: freezing-db-dev
ports:
- "3306:3306"
- "${MYSQL_PORT:-3306}:3306"
volumes:
- freezing-data:/var/lib/mysql
- ./docker/db/sql-scripts:/sql
- ./data/mysql/data:/var/lib/mysql
- ./data/mysql/sql:/sql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-fr33z3}
MYSQL_DATABASE: ${MYSQL_DATABASE:-freezing}
MYSQL_USER: ${MYSQL_USER:-freezing}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-zeer0}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-fr33z3}
MYSQL_ROOT_USER: ${MYSQL_ROOT_USER:-root}
MYSQL_USER: ${MYSQL_USER:-freezing}

freezing-web:
build: .
container_name: freezing-web
command: bafs-server
build: ./
container_name: freezing-web-dev
depends_on:
- freezing-db
ports:
- "5000:8000"
- "${FREEZING_WEB_PORT:-8000}:8000"
volumes:
- ./data/cache:/data/cache
- ./leaderboards:/data/leaderboards
- ./data/sessions:/data/sessions
- ./resources/docker/settings.cfg:/config/settings.cfg
links:
- freezing-db:freezing-db.container
environment:
BEANSTALKD_HOST: ${BEANSTALKD_HOST:-beanstalkd.invalid}
BEANSTALKD_PORT: ${BEANSTALKD_PORT:-11300}
BIND_INTERFACE: "0.0.0.0"
VIRTUAL_HOST: ${FREEZING_WEB_FQDN}
LETSENCRYPT_HOST: ${FREEZING_WEB_FQDN}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
SECRET_KEY: ${SECRET_KEY}
DEBUG: ${DEBUG}
SQLALCHEMY_URL: ${SQLALCHEMY_URL:-mysql+pymysql://freezing:[email protected]/freezing?charset=utf8mb4&binary_prefix=true}
BEANSTALKD_HOST: beanstalkd.container
BEANSTALKD_PORT: 11300
STRAVA_CLIENT_ID: ${STRAVA_CLIENT_ID}
STRAVA_CLIENT_SECRET: ${STRAVA_CLIENT_SECRET}
TEAMS: ${TEAMS}
OBSERVER_TEAMS: ${OBSERVER_TEAMS}
MAIN_TEAM: ${MAIN_TEAM}
START_DATE: ${START_DATE}
END_DATE: ${END_DATE}
TIMEZONE: ${TIMEZONE:-America/New_York}
COMPETITION_TITLE: ${COMPETITION_TITLE:-Freezing Saddles Local Dev}
DEBUG: ${DEBUG:-True}
END_DATE: ${END_DATE:-2118-03-20T00:01:00-04:00}
ENVIRONMENT: ${ENVIRONMENT:-localdev}
FAULTHANDLER: ${FAULT_HANDLER:-True}
FORUM_SITE: https://www.bikearlingtonforum.com/forums/forum/freezing-saddles-winter-riding-competition/ # yamllint disable-line
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL:[email protected]}
LETSENCRYPT_HOST: ${FREEZING_WEB_FQDN:-localhost.localdomain}
LOG_LEVEL: ${LOG_LEVEL:-DEBUG}
MAIN_TEAM: ${MAIN_TEAM:-324147}
OBSERVER_TEAMS: ${OBSERVER_TEAMS:-5678,9013}
REGISTRATION_SITE: ${REGISTRATION_SITE:-https://freezingsaddles.info/}
SECRET_KEY: ${SECRET_KEY:-e6c07402-0307-11e8-b087-000000000000} # yamllint disable-line
SQLALCHEMY_URL: "mysql+pymysql://${MYSQL_USER:-freezing}:${MYSQL_PASSWORD:-zeer0}@freezing-db-dev/${MYSQL_DATABASE:-freezing}?charset=utf8mb4&binary_prefix=true" # yamllint disable-line
SQLALCHEMY_URL_ROOT: "mysql+pymysql://${MYSQL_ROOT_USER:-freezing}:${MYSQL_ROOT_PASSWORD:-zeer0}@freezing-db-dev/mysql?charset=utf8mb4&binary_prefix=true" # yamllint disable-line
START_DATE: ${START_DATE:-2018-01-01T00:00:00-05:00}
STRAVA_CLIENT_ID: ${STRAVA_CLIENT_ID:-?}
STRAVA_CLIENT_SECRET: ${STRAVA_CLIENT_SECRET:-?}
TEAMS: ${TEAMS:-1234,1235}
TIMEZONE: ${TIMEZONE:-America/New_York}
VIRTUAL_HOST: ${FREEZING_WEB_FQDN:-localhost.localdomain}
41 changes: 27 additions & 14 deletions example.cfg
Original file line number Diff line number Diff line change
@@ -1,44 +1,57 @@
# You can use a file containing environment vars like this:
# APP_SETTINGS=/path/to/envfile.cfg freezing-server
#
# See freezing/webapp/config.py for more information on these settings.

# Suitable same-machine testing. Set this to 0.0.0.0 to allow connections from other machines.
BIND_INTERFACE=127.0.0.1

DEBUG=true

# By default this uses the localdev environment for local development, which enables
# several features useful for developers, such as user impersonation.
# See
#ENVIRONMENT=localdev

# The SECRET_KEY is used by Flask to sign sessions. Set this to something else.
SECRET_KEY=e6c07402-0307-11e8-b087-000000000000

# The URL to the database. Note that the pymysql driver must be explicitly specified.
SQLALCHEMY_URL=mysql+pymysql://freezing:[email protected]/freezing?charset=utf8mb4&binary_prefix=true
#
# It is important to use the utf8mb4 charset to get full support for Unicode characters,
# including emoji.
#
# This URL is suitable for use with the database running in the freezing-compose setup in this repository.
SQLALCHEMY_URL="mysql+pymysql://freezing:zeer0mfreezing-db-dev/freezing?charset=utf8mb4&binary_prefix=true"
# Use this one if you are running this on your host vs. the database in the
# https://github.com/freezingsaddles/freezing-compose project.
# NOTE: If you are using a MySQL docker via freezing-compose use 127.0.0.1 as the host, NOT localhost
#SQLALCHEMY_URL="mysql+pymysql://freezing:[email protected]/freezing?charset=utf8mb4&binary_prefix=true"
# If you keep your MySQL database somewhere else, fix this up to match.
#SQLALCHEMY_URL="mysql+pymysql://freezing:[email protected]/freezing?charset=utf8mb4&binary_prefix=true""

# Configuration for the Strava client. These settings come from your App setup.
# Setting this is only required if you are testing parts of this application that exercise the Strava API,
# such as user registration. That is an advanced topic and not required to make most changes to
# the web site. Most of the action with the Strava API happens in freezing-sync, not here.
STRAVA_CLIENT_ID=?
STRAVA_CLIENT_SECRET=?
STRAVA_ACTIVITY_CACHE_DIR=/path/to/cache/activities

WUNDERGROUND_API_KEY=?
WUNDERGROUND_CACHE_DIR=/path/to/cache/wunderground

# Main team ID that everyone should join
MAIN_TEAM=324147
# Comma-separated list of Strava Clubs that are the participating teams.
TEAMS=1234,1235

# Comma-separated list of teams that should be included for overall stats but are not "playing the game"
OBSERVER_TEAMS=5678,9013

# Main team ID that everyone should join
MAIN_TEAM=324147

# The competition title
COMPETITION_TITLE='BikeArlington Freezing Saddles 2019'
COMPETITION_TITLE='BikeArlington Freezing Saddles 2018 - localdev'

# The start date of the competition -- WITH TIME ZONE
START_DATE=2018-01-01T00:00:00-05:00

# The end date of the competition -- WITH TIME ZONE. The sync will stop fetching rides after this date (plus grace
# period)
# The end date of the competition -- WITH TIME ZONE.
END_DATE=2018-03-20T00:01:00-04:00

# Related web sites
REGISTRATION_SITE=https://freezingsaddles.info/
FORUM_SITE=https://www.bikearlingtonforum.com/forums/forum/freezing-saddles-winter-riding-competition/
REGISTRATION_SITE=https://freezingsaddles.info/
137 changes: 98 additions & 39 deletions freezing/web/__init__.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,74 @@
"""
This is the main entry point for the freezing-web application.
It sets up the Flask app and initializes the database and logging.
It also sets up a fault handler with a signal early to ensure stack traces happen.
"""

import freezing.web._faulthandler # noqa isort: skip

from socket import gethostbyname
from time import sleep
from urllib.parse import urlparse

from flask import Flask, g, session
from freezing.model import init_model, meta

from .config import config

# Thanks https://stackoverflow.com/a/17073583
app = Flask(
__name__,
static_folder="static",
static_url_path="/",
instance_path=config.INSTANCE_PATH,
)
app.config.from_object(config)

init_model(config.SQLALCHEMY_URL)

# This needs to be after the app is created, unfortunately.
from freezing.web.views import ( # noqa
alt_scoring,
api,
chartdata,
general,
leaderboard,
people,
photos,
pointless,
tribes,
user,
)

# Register our blueprints

app.register_blueprint(general.blueprint)
app.register_blueprint(leaderboard.blueprint, url_prefix="/leaderboard")
app.register_blueprint(alt_scoring.blueprint, url_prefix="/alt_scoring")
app.register_blueprint(chartdata.blueprint, url_prefix="/chartdata")
app.register_blueprint(people.blueprint, url_prefix="/people")
app.register_blueprint(pointless.blueprint, url_prefix="/pointless")
app.register_blueprint(photos.blueprint, url_prefix="/photos")
app.register_blueprint(user.blueprint, url_prefix="/my")
app.register_blueprint(api.blueprint, url_prefix="/api")
app.register_blueprint(tribes.blueprint, url_prefix="/tribes")
from freezing.web.autolog import log

from .config import config, init_logging

init_logging(color=config.DEBUG is False)


_BANNER = "*************************"


def _get_app():
log.info(
f"{_BANNER} Configuring Flask app with instance_path={config.INSTANCE_PATH} {_BANNER}"
)
# Thanks https://stackoverflow.com/a/17073583
app = Flask(
__name__,
static_folder="static",
static_url_path="/",
instance_path=config.INSTANCE_PATH,
)
app.config.from_object(config)
return app


def _register_blueprints(app):
# This needs to be after the app is created, unfortunately.
from freezing.web.views import (
alt_scoring,
api,
chartdata,
general,
leaderboard,
people,
photos,
pointless,
tribes,
user,
)

app.register_blueprint(general.blueprint)
app.register_blueprint(leaderboard.blueprint, url_prefix="/leaderboard")
app.register_blueprint(alt_scoring.blueprint, url_prefix="/alt_scoring")
app.register_blueprint(chartdata.blueprint, url_prefix="/chartdata")
app.register_blueprint(people.blueprint, url_prefix="/people")
app.register_blueprint(pointless.blueprint, url_prefix="/pointless")
app.register_blueprint(photos.blueprint, url_prefix="/photos")
app.register_blueprint(user.blueprint, url_prefix="/my")
app.register_blueprint(api.blueprint, url_prefix="/api")
app.register_blueprint(tribes.blueprint, url_prefix="/tribes")


# This has to be done before we define the functions with @app decorators
app = _get_app()
_register_blueprints(app)


@app.before_request
Expand Down Expand Up @@ -69,3 +98,33 @@ def inject_config():
"forum_site": config.FORUM_SITE,
"version_string": config.VERSION_STRING,
}


def init_db():
"""
Initialize the database. If the database is not available, keep trying for a bit.
"""
TRIES = 6
delay = 2
for x in range(1, TRIES + 1):
try:
# Use urllib to extract the host and test whether it can be resolved
url = urlparse(config.SQLALCHEMY_URL)
host = gethostbyname(url.hostname)
log.debug(f"gethostbyname({url.hostname})=={host}")
log.info(f"{_BANNER} Connecting to database on {url.hostname} {_BANNER}")
init_model(config.SQLALCHEMY_URL)
break
except Exception as ex:
if x == TRIES:
raise ex from None
log.warning(
f"Failed to connect to database, retrying in {delay}s ({x}) - error was {str(ex)}"
)
delay = delay * 2
sleep(delay)


init_db()

log.info(f"{_BANNER} freezing-web initialized in {config.ENVIRONMENT} mode {_BANNER}")
6 changes: 6 additions & 0 deletions freezing/web/_faulthandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import faulthandler
import signal

# Register this super early so we can get a stack trace if
# there is a problem in initializing the config module
faulthandler.register(signal.SIGUSR1)
Loading

0 comments on commit 6d91d89

Please sign in to comment.