Skip to content

Commit

Permalink
Load gunicorn defaults before overriding with app settings
Browse files Browse the repository at this point in the history
fixes: pulp#4917
  • Loading branch information
sdherr committed Jan 11, 2024
1 parent 78679eb commit 6cd4ab3
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGES/4917.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enable the gunicorn applications for pulp-api and pulp-content to load configs from a "gunicorn.conf.py" file.
64 changes: 55 additions & 9 deletions pulpcore/app/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
from logging import getLogger
import os
import socket
import sys

import click
import django
from django.conf import settings
from django.db import connection
from django.db.utils import InterfaceError, OperationalError
from gunicorn.workers.sync import SyncWorker
from gunicorn.app.base import BaseApplication
from gunicorn.app.base import Application

from pulpcore.app.apps import pulp_plugin_configs

Expand Down Expand Up @@ -80,19 +81,64 @@ def run(self):
self.api_app_status.delete()


class PulpcoreApiApplication(BaseApplication):
class PulpcoreApiApplication(Application):
def __init__(self, options):
self.options = options or {}
super().__init__()

def init(self, *args, **kwargs):
"""
A hook for setting application-specific configs, which we instead do below in load_config
where it's non-overridable.
"""
pass

def _set_option(self, key, value, enforced=False):
if value is None: # not specified by init script
return

def _is_default(key, value):
if value is None:
return True
defaults = {
"default_proc_name": "gunicorn",
"reload_extra_files": [],
"access_log_format": '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"',
"bind": ["127.0.0.1:8000"],
"threads": 1,
"worker_class": "<class 'gunicorn.workers.sync.SyncWorker'>",
}
return key in defaults and str(defaults[key]) == str(value)

current_value = getattr(self.cfg, key, None)
if not _is_default(key, current_value) and str(current_value) != str(value):
if enforced:
sys.stderr.write(
f"Error: {key} is set to {current_value} in gunicorn.conf.py but must not be "
"changed!\n"
)
exit(1)
else:
sys.stderr.write(
f"Warning: {key} is set to {current_value} in gunicorn.conf.py but is "
f"overridden to {value} by init script!\n"
)

self.cfg.set(key, value)

def load_config(self):
[
self.cfg.set(key.lower(), value)
for key, value in self.options.items()
if value is not None
]
self.cfg.set("default_proc_name", "pulpcore-api")
self.cfg.set("worker_class", PulpApiWorker.__module__ + "." + PulpApiWorker.__qualname__)
# Load default gunicorn configs, including reading from the default config file.
super().load_config()
# Override with settings that we've specified in the startup script.
for key, value in self.options.items():
self._set_option(key, value)
self._set_option("default_proc_name", "pulpcore-api", enforced=True)
self._set_option("threads", "1", enforced=True) # We don't use a threaded worker...
self._set_option(
"worker_class",
PulpApiWorker.__module__ + "." + PulpApiWorker.__qualname__,
enforced=True
)

def load(self):
using_pulp_api_worker.set(True)
Expand Down
61 changes: 52 additions & 9 deletions pulpcore/content/entrypoint.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,63 @@
import sys

import click
from gunicorn.app.base import BaseApplication
from gunicorn.app.base import Application


class PulpcoreContentApplication(BaseApplication):
class PulpcoreContentApplication(Application):
def __init__(self, options):
self.options = options or {}
super().__init__()

def init(self, *args, **kwargs):
"""
A hook for setting application-specific configs, which we instead do below in load_config
where it's non-overridable.
"""
pass

def _set_option(self, key, value, enforced=False):
if value is None: # not specified by init script
return

def _is_default(key, value):
if value is None:
return True
defaults = {
"default_proc_name": "gunicorn",
"reload_extra_files": [],
"access_log_format": '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"',
"bind": ["127.0.0.1:8000"],
"threads": 1,
"worker_class": "<class 'gunicorn.workers.sync.SyncWorker'>",
}
return key in defaults and str(defaults[key]) == str(value)

current_value = getattr(self.cfg, key, None)
if not _is_default(key, current_value) and str(current_value) != str(value):
if enforced:
sys.stderr.write(
f"Error: {key} is set to {current_value} in gunicorn.conf.py but must not be "
"changed!\n"
)
exit(1)
else:
sys.stderr.write(
f"Warning: {key} is set to {current_value} in gunicorn.conf.py but is "
f"overridden to {value} by init script!\n"
)

self.cfg.set(key, value)

def load_config(self):
[
self.cfg.set(key.lower(), value)
for key, value in self.options.items()
if value is not None
]
self.cfg.set("default_proc_name", "pulpcore-content")
self.cfg.set("worker_class", "aiohttp.GunicornWebWorker")
# Load default gunicorn configs, including reading from the default config file.
super().load_config()
# Override with settings that we've specified in the startup script.
for key, value in self.options.items():
self._set_option(key, value)
self._set_option("default_proc_name", "pulpcore-content", enforced=True)
self._set_option("threads", "1", enforced=True) # We don't use a threaded worker...
self._set_option("worker_class", "aiohttp.GunicornWebWorker", enforced=True)

def load(self):
import pulpcore.content
Expand Down

0 comments on commit 6cd4ab3

Please sign in to comment.