root: move database calls from ready() to dedicated startup signal (#9081)

* root: move database calls from ready() to dedicated startup signal

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* optimise gunicorn startup to only do DB code in one worker

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* always use 2 workers in compose

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* send startup signals for test runner

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove k8s import that isn't really needed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ci: bump nested actions

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix @reconcile_app not triggering reconcile due to changed functions

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* connect startup with uid

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* adjust some log levels

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove internal healthcheck

we didn't really use it to do anything, and we shouldn't have to since the live/ready probes are handled by django anyways and so the container runtime will restart the server if needed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add setproctitle for gunicorn and celery process titles

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* configure structlog early to use it

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Revert "configure structlog early to use it"

This reverts commit 16778fdbbca0f5c474d376c2f85c6f8032c06044.

* Revert "adjust some log levels"

This reverts commit a129f7ab6aecf27f1206aea1ad8384ce897b74ad.

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

# Conflicts:
#	authentik/root/settings.py

* optimize startup to not spawn a bunch of one-off processes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* idk why this shows up

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L
2024-04-02 14:19:32 +02:00
committed by GitHub
parent 4d8ee983ef
commit 7ea721c487
21 changed files with 290 additions and 82 deletions
+3 -3
View File
@@ -16,18 +16,18 @@ runs:
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext
- name: Setup python and restore poetry - name: Setup python and restore poetry
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version-file: "pyproject.toml" python-version-file: "pyproject.toml"
cache: "poetry" cache: "poetry"
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version-file: web/package.json node-version-file: web/package.json
cache: "npm" cache: "npm"
cache-dependency-path: web/package-lock.json cache-dependency-path: web/package-lock.json
- name: Setup go - name: Setup go
uses: actions/setup-go@v4 uses: actions/setup-go@v5
with: with:
go-version-file: "go.mod" go-version-file: "go.mod"
- name: Setup dependencies - name: Setup dependencies
+6 -1
View File
@@ -8,6 +8,8 @@ from django.apps import AppConfig
from django.db import DatabaseError, InternalError, ProgrammingError from django.db import DatabaseError, InternalError, ProgrammingError
from structlog.stdlib import BoundLogger, get_logger from structlog.stdlib import BoundLogger, get_logger
from authentik.root.signals import startup
class ManagedAppConfig(AppConfig): class ManagedAppConfig(AppConfig):
"""Basic reconciliation logic for apps""" """Basic reconciliation logic for apps"""
@@ -23,9 +25,12 @@ class ManagedAppConfig(AppConfig):
def ready(self) -> None: def ready(self) -> None:
self.import_related() self.import_related()
startup.connect(self._on_startup_callback, dispatch_uid=self.label)
return super().ready()
def _on_startup_callback(self, sender, **_):
self._reconcile_global() self._reconcile_global()
self._reconcile_tenant() self._reconcile_tenant()
return super().ready()
def import_related(self): def import_related(self):
"""Automatically import related modules which rely on just being imported """Automatically import related modules which rely on just being imported
+1 -1
View File
@@ -39,7 +39,7 @@ def reconcile_app(app_name: str):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
config = apps.get_app_config(app_name) config = apps.get_app_config(app_name)
if isinstance(config, ManagedAppConfig): if isinstance(config, ManagedAppConfig):
config.ready() config._on_startup_callback(None)
return func(*args, **kwargs) return func(*args, **kwargs)
return wrapper return wrapper
@@ -1,10 +1,34 @@
"""custom runserver command""" """custom runserver command"""
from typing import TextIO
from daphne.management.commands.runserver import Command as RunServer from daphne.management.commands.runserver import Command as RunServer
from daphne.server import Server
from authentik.root.signals import post_startup, pre_startup, startup
class SignalServer(Server):
"""Server which signals back to authentik when it finished starting up"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def ready_callable():
pre_startup.send(sender=self)
startup.send(sender=self)
post_startup.send(sender=self)
self.ready_callable = ready_callable
class Command(RunServer): class Command(RunServer):
"""custom runserver command, which doesn't show the misleading django startup message""" """custom runserver command, which doesn't show the misleading django startup message"""
def on_bind(self, server_port): server_cls = SignalServer
pass
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Redirect standard stdout banner from Daphne into the void
# as there are a couple more steps that happen before startup is fully done
self.stdout = TextIO()
+4 -2
View File
@@ -3,12 +3,14 @@
import os import os
from importlib import import_module from importlib import import_module
from pathlib import Path from pathlib import Path
from tempfile import gettempdir
from django.conf import settings from django.conf import settings
from kubernetes.config.incluster_config import SERVICE_HOST_ENV_NAME
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST"
def all_subclasses(cls, sort=True): def all_subclasses(cls, sort=True):
"""Recursively return all subclassess of cls""" """Recursively return all subclassess of cls"""
@@ -55,7 +57,7 @@ def get_env() -> str:
return "dev" return "dev"
if SERVICE_HOST_ENV_NAME in os.environ: if SERVICE_HOST_ENV_NAME in os.environ:
return "kubernetes" return "kubernetes"
if Path("/tmp/authentik-mode").exists(): # nosec if (Path(gettempdir()) / "authentik-mode").exists():
return "compose" return "compose"
if "AK_APPLIANCE" in os.environ: if "AK_APPLIANCE" in os.environ:
return os.environ["AK_APPLIANCE"] return os.environ["AK_APPLIANCE"]
+2 -2
View File
@@ -45,14 +45,14 @@ class AuthentikOutpostConfig(ManagedAppConfig):
outpost.managed = MANAGED_OUTPOST outpost.managed = MANAGED_OUTPOST
outpost.save() outpost.save()
return return
outpost, updated = Outpost.objects.update_or_create( outpost, created = Outpost.objects.update_or_create(
defaults={ defaults={
"type": OutpostType.PROXY, "type": OutpostType.PROXY,
"name": MANAGED_OUTPOST_NAME, "name": MANAGED_OUTPOST_NAME,
}, },
managed=MANAGED_OUTPOST, managed=MANAGED_OUTPOST,
) )
if updated: if created:
if KubernetesServiceConnection.objects.exists(): if KubernetesServiceConnection.objects.exists():
outpost.service_connection = KubernetesServiceConnection.objects.first() outpost.service_connection = KubernetesServiceConnection.objects.first()
elif DockerServiceConnection.objects.exists(): elif DockerServiceConnection.objects.exists():
+12 -1
View File
@@ -60,7 +60,18 @@ class RouteNotFoundMiddleware:
raise exc raise exc
application = SentryAsgiMiddleware( class AuthentikAsgi(SentryAsgiMiddleware):
"""Root ASGI App wrapper"""
def call_startup(self):
from authentik.root.signals import post_startup, pre_startup, startup
pre_startup.send(sender=self)
startup.send(sender=self)
post_startup.send(sender=self)
application = AuthentikAsgi(
ProtocolTypeRouter( ProtocolTypeRouter(
{ {
"http": get_asgi_application(), "http": get_asgi_application(),
+26
View File
@@ -0,0 +1,26 @@
from datetime import timedelta
from django.core.signals import Signal
from django.dispatch import receiver
from django.utils.timezone import now
from structlog.stdlib import get_logger
# Signal dispatched before actual startup trigger
pre_startup = Signal()
# Signal dispatched which should trigger all startup logic
startup = Signal()
# Signal dispatched after the startup logic
post_startup = Signal()
LOGGER = get_logger()
@receiver(pre_startup)
def pre_startup_log(sender, **_):
sender._start_time = now()
@receiver(post_startup)
def post_startup_log(sender, **_):
took: timedelta = now() - sender._start_time
LOGGER.info("authentik Core Worker finished starting", took_s=took.total_seconds())
+5
View File
@@ -9,6 +9,7 @@ from django.test.runner import DiscoverRunner
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.sentry import sentry_init from authentik.lib.sentry import sentry_init
from authentik.root.signals import post_startup, pre_startup, startup
from tests.e2e.utils import get_docker_tag from tests.e2e.utils import get_docker_tag
# globally set maxDiff to none to show full assert error # globally set maxDiff to none to show full assert error
@@ -46,6 +47,10 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
CONFIG.set("error_reporting.send_pii", True) CONFIG.set("error_reporting.send_pii", True)
sentry_init() sentry_init()
pre_startup.send(sender=self, mode="test")
startup.send(sender=self, mode="test")
post_startup.send(sender=self, mode="test")
@classmethod @classmethod
def add_arguments(cls, parser: ArgumentParser): def add_arguments(cls, parser: ArgumentParser):
"""Add more pytest-specific arguments""" """Add more pytest-specific arguments"""
+2 -5
View File
@@ -96,18 +96,15 @@ func (g *GoUnicorn) healthcheck() {
g.log.Debug("starting healthcheck") g.log.Debug("starting healthcheck")
// Default healthcheck is every 1 second on startup // Default healthcheck is every 1 second on startup
// once we've been healthy once, increase to 30 seconds // once we've been healthy once, increase to 30 seconds
for range time.Tick(time.Second) { for range time.NewTicker(time.Second).C {
if g.Healthcheck() { if g.Healthcheck() {
g.alive = true g.alive = true
g.log.Info("backend is alive, backing off with healthchecks") g.log.Debug("backend is alive, backing off with healthchecks")
g.HealthyCallback() g.HealthyCallback()
break break
} }
g.log.Debug("backend not alive yet") g.log.Debug("backend not alive yet")
} }
for range time.Tick(30 * time.Second) {
g.Healthcheck()
}
} }
func (g *GoUnicorn) Reload() { func (g *GoUnicorn) Reload() {
-3
View File
@@ -32,9 +32,6 @@ func (ws *WebServer) configureProxy() {
} }
rp.ErrorHandler = ws.proxyErrorHandler rp.ErrorHandler = ws.proxyErrorHandler
rp.ModifyResponse = ws.proxyModifyResponse rp.ModifyResponse = ws.proxyModifyResponse
ws.m.Path("/-/health/live/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(204)
}))
ws.m.PathPrefix("/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) { ws.m.PathPrefix("/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) {
if !ws.g.IsRunning() { if !ws.g.IsRunning() {
ws.proxyErrorHandler(rw, r, errors.New("authentik starting")) ws.proxyErrorHandler(rw, r, errors.New("authentik starting"))
-3
View File
@@ -7,7 +7,6 @@ function log {
function wait_for_db { function wait_for_db {
python -m lifecycle.wait_for_db python -m lifecycle.wait_for_db
python -m lifecycle.migrate
log "Bootstrap completed" log "Bootstrap completed"
} }
@@ -65,7 +64,6 @@ if [[ "${AUTHENTIK_REMOTE_DEBUG}" == "true" ]]; then
fi fi
if [[ "$1" == "server" ]]; then if [[ "$1" == "server" ]]; then
wait_for_db
set_mode "server" set_mode "server"
# If we have bootstrap credentials set, run bootstrap tasks outside of main server # If we have bootstrap credentials set, run bootstrap tasks outside of main server
# sync, so that we can sure the first start actually has working bootstrap # sync, so that we can sure the first start actually has working bootstrap
@@ -75,7 +73,6 @@ if [[ "$1" == "server" ]]; then
fi fi
run_authentik run_authentik
elif [[ "$1" == "worker" ]]; then elif [[ "$1" == "worker" ]]; then
wait_for_db
set_mode "worker" set_mode "worker"
check_if_root "python -m manage worker" check_if_root "python -m manage worker"
elif [[ "$1" == "worker-status" ]]; then elif [[ "$1" == "worker-status" ]]; then
+24 -8
View File
@@ -2,13 +2,11 @@
import os import os
from hashlib import sha512 from hashlib import sha512
from multiprocessing import cpu_count
from os import makedirs from os import makedirs
from pathlib import Path from pathlib import Path
from tempfile import gettempdir from tempfile import gettempdir
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from kubernetes.config.incluster_config import SERVICE_HOST_ENV_NAME
from prometheus_client.values import MultiProcessValue from prometheus_client.values import MultiProcessValue
from authentik import get_full_version from authentik import get_full_version
@@ -17,11 +15,18 @@ from authentik.lib.logging import get_logger_config
from authentik.lib.utils.http import get_http_session from authentik.lib.utils.http import get_http_session
from authentik.lib.utils.reflection import get_env from authentik.lib.utils.reflection import get_env
from authentik.root.install_id import get_install_id_raw from authentik.root.install_id import get_install_id_raw
from lifecycle.migrate import run_migrations
from lifecycle.wait_for_db import wait_for_db
from lifecycle.worker import DjangoUvicornWorker from lifecycle.worker import DjangoUvicornWorker
if TYPE_CHECKING: if TYPE_CHECKING:
from gunicorn.app.wsgiapp import WSGIApplication
from gunicorn.arbiter import Arbiter from gunicorn.arbiter import Arbiter
from authentik.root.asgi import AuthentikAsgi
wait_for_db()
_tmp = Path(gettempdir()) _tmp = Path(gettempdir())
worker_class = "lifecycle.worker.DjangoUvicornWorker" worker_class = "lifecycle.worker.DjangoUvicornWorker"
worker_tmp_dir = str(_tmp.joinpath("authentik_worker_tmp")) worker_tmp_dir = str(_tmp.joinpath("authentik_worker_tmp"))
@@ -35,17 +40,14 @@ bind = f"unix://{str(_tmp.joinpath('authentik-core.sock'))}"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", prometheus_tmp_dir) os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", prometheus_tmp_dir)
preload = True
max_requests = 1000 max_requests = 1000
max_requests_jitter = 50 max_requests_jitter = 50
logconfig_dict = get_logger_config() logconfig_dict = get_logger_config()
# if we're running in kubernetes, use fixed workers because we can scale with more pods default_workers = 2
# otherwise (assume docker-compose), use as much as we can
if SERVICE_HOST_ENV_NAME in os.environ:
default_workers = 2
else:
default_workers = max(cpu_count() * 0.25, 1) + 1 # Minimum of 2 workers
workers = CONFIG.get_int("web.workers", default_workers) workers = CONFIG.get_int("web.workers", default_workers)
threads = CONFIG.get_int("web.threads", 4) threads = CONFIG.get_int("web.threads", 4)
@@ -100,6 +102,18 @@ def pre_fork(server: "Arbiter", worker: DjangoUvicornWorker):
worker._worker_id = _next_worker_id(server) worker._worker_id = _next_worker_id(server)
def post_worker_init(worker: DjangoUvicornWorker):
"""Notify ASGI app that its started up"""
# Only trigger startup DB logic on first worker
# Startup code that imports code or is otherwise needed in every worker
# does not use this signal, so we can skip this safely
if worker._worker_id != 1:
return
app: "WSGIApplication" = worker.app
root_app: "AuthentikAsgi" = app.callable
root_app.call_startup()
if not CONFIG.get_bool("disable_startup_analytics", False): if not CONFIG.get_bool("disable_startup_analytics", False):
env = get_env() env = get_env()
should_send = env not in ["dev", "ci"] should_send = env not in ["dev", "ci"]
@@ -129,3 +143,5 @@ if CONFIG.get_bool("remote_debug"):
import debugpy import debugpy
debugpy.listen(("0.0.0.0", 6800)) # nosec debugpy.listen(("0.0.0.0", 6800)) # nosec
run_migrations()
+5 -1
View File
@@ -68,7 +68,7 @@ def release_lock(cursor: Cursor):
cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,)) cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))
if __name__ == "__main__": def run_migrations():
conn = connect( conn = connect(
dbname=CONFIG.get("postgresql.name"), dbname=CONFIG.get("postgresql.name"),
user=CONFIG.get("postgresql.user"), user=CONFIG.get("postgresql.user"),
@@ -117,3 +117,7 @@ if __name__ == "__main__":
) )
finally: finally:
release_lock(curr) release_lock(curr)
if __name__ == "__main__":
run_migrations()
+53 -44
View File
@@ -11,52 +11,61 @@ from redis.exceptions import RedisError
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
CONFIG.log("info", "Starting authentik bootstrap")
# Sanity check, ensure SECRET_KEY is set before we even check for database connectivity def check_postgres():
if CONFIG.get("secret_key") is None or len(CONFIG.get("secret_key")) == 0: while True:
CONFIG.log("info", "----------------------------------------------------------------------") try:
CONFIG.log("info", "Secret key missing, check https://goauthentik.io/docs/installation/.") conn = connect(
CONFIG.log("info", "----------------------------------------------------------------------") dbname=CONFIG.get("postgresql.name"),
sysexit(1) user=CONFIG.get("postgresql.user"),
password=CONFIG.get("postgresql.password"),
host=CONFIG.get("postgresql.host"),
port=CONFIG.get_int("postgresql.port"),
sslmode=CONFIG.get("postgresql.sslmode"),
sslrootcert=CONFIG.get("postgresql.sslrootcert"),
sslcert=CONFIG.get("postgresql.sslcert"),
sslkey=CONFIG.get("postgresql.sslkey"),
)
conn.cursor()
break
except OperationalError as exc:
sleep(1)
CONFIG.log("info", f"PostgreSQL connection failed, retrying... ({exc})")
CONFIG.log("info", "PostgreSQL connection successful")
while True: def check_redis():
try: REDIS_PROTOCOL_PREFIX = "redis://"
conn = connect( if CONFIG.get_bool("redis.tls", False):
dbname=CONFIG.get("postgresql.name"), REDIS_PROTOCOL_PREFIX = "rediss://"
user=CONFIG.get("postgresql.user"), REDIS_URL = (
password=CONFIG.get("postgresql.password"), f"{REDIS_PROTOCOL_PREFIX}:"
host=CONFIG.get("postgresql.host"), f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
port=CONFIG.get_int("postgresql.port"), f"{CONFIG.get_int('redis.port')}/{CONFIG.get('redis.db')}"
sslmode=CONFIG.get("postgresql.sslmode"), )
sslrootcert=CONFIG.get("postgresql.sslrootcert"), while True:
sslcert=CONFIG.get("postgresql.sslcert"), try:
sslkey=CONFIG.get("postgresql.sslkey"), redis = Redis.from_url(REDIS_URL)
) redis.ping()
conn.cursor() break
break except RedisError as exc:
except OperationalError as exc: sleep(1)
sleep(1) CONFIG.log("info", f"Redis Connection failed, retrying... ({exc})", redis_url=REDIS_URL)
CONFIG.log("info", f"PostgreSQL connection failed, retrying... ({exc})") CONFIG.log("info", "Redis Connection successful")
CONFIG.log("info", "PostgreSQL connection successful")
REDIS_PROTOCOL_PREFIX = "redis://"
if CONFIG.get_bool("redis.tls", False):
REDIS_PROTOCOL_PREFIX = "rediss://"
REDIS_URL = (
f"{REDIS_PROTOCOL_PREFIX}:"
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
f"{CONFIG.get_int('redis.port')}/{CONFIG.get('redis.db')}"
)
while True:
try:
redis = Redis.from_url(REDIS_URL)
redis.ping()
break
except RedisError as exc:
sleep(1)
CONFIG.log("info", f"Redis Connection failed, retrying... ({exc})", redis_url=REDIS_URL)
CONFIG.log("info", "Redis Connection successful")
CONFIG.log("info", "Finished authentik bootstrap") def wait_for_db():
CONFIG.log("info", "Starting authentik bootstrap")
# Sanity check, ensure SECRET_KEY is set before we even check for database connectivity
if CONFIG.get("secret_key") is None or len(CONFIG.get("secret_key")) == 0:
CONFIG.log("info", "----------------------------------------------------------------------")
CONFIG.log("info", "Secret key missing, check https://goauthentik.io/docs/installation/.")
CONFIG.log("info", "----------------------------------------------------------------------")
sysexit(1)
check_postgres()
check_redis()
CONFIG.log("info", "Finished authentik bootstrap")
if __name__ == "__main__":
wait_for_db()
+2
View File
@@ -12,3 +12,5 @@ class DjangoUvicornWorker(UvicornWorker):
"lifespan": "off", "lifespan": "off",
"ws": "wsproto", "ws": "wsproto",
} }
_worker_id: int
+15
View File
@@ -5,6 +5,10 @@ import sys
import warnings import warnings
from defusedxml import defuse_stdlib from defusedxml import defuse_stdlib
from django.utils.autoreload import DJANGO_AUTORELOAD_ENV
from lifecycle.migrate import run_migrations
from lifecycle.wait_for_db import wait_for_db
warnings.filterwarnings("ignore", "SelectableGroups dict interface") warnings.filterwarnings("ignore", "SelectableGroups dict interface")
warnings.filterwarnings( warnings.filterwarnings(
@@ -20,6 +24,17 @@ defuse_stdlib()
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
wait_for_db()
if (
len(sys.argv) > 1
# Explicitly only run migrate for server and worker
# `bootstrap_tasks` is a special case as that command might be triggered by the `ak`
# script to pre-run certain tasks for an automated install
and sys.argv[1] in ["dev_server", "worker", "bootstrap_tasks"]
# and don't run if this is the child process of a dev_server
and os.environ.get(DJANGO_AUTORELOAD_ENV, None) is None
):
run_migrations()
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
Generated
+101 -1
View File
@@ -3637,6 +3637,106 @@ idna = ["idna"]
mypy = ["idna", "mypy", "types-pyopenssl"] mypy = ["idna", "mypy", "types-pyopenssl"]
tests = ["coverage[toml] (>=5.0.2)", "pytest"] tests = ["coverage[toml] (>=5.0.2)", "pytest"]
[[package]]
name = "setproctitle"
version = "1.3.3"
description = "A Python module to customize the process title"
optional = false
python-versions = ">=3.7"
files = [
{file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"},
{file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"},
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"},
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"},
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"},
{file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"},
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"},
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"},
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"},
{file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"},
{file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"},
{file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"},
{file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"},
{file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"},
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"},
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"},
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"},
{file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"},
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"},
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"},
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"},
{file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"},
{file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"},
{file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"},
{file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"},
{file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"},
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"},
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"},
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"},
{file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"},
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"},
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"},
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"},
{file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"},
{file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"},
{file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"},
{file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"},
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"},
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"},
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"},
{file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"},
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"},
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"},
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"},
{file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"},
{file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"},
{file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"},
{file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"},
{file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"},
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"},
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"},
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"},
{file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"},
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"},
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"},
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"},
{file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"},
{file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"},
{file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"},
{file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"},
{file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"},
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"},
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"},
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"},
{file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"},
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"},
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"},
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"},
{file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"},
{file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"},
{file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"},
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"},
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"},
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"},
{file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"},
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"},
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"},
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"},
{file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"},
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"},
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"},
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"},
{file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"},
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"},
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"},
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"},
{file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"},
{file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"},
]
[package.extras]
test = ["pytest"]
[[package]] [[package]]
name = "setuptools" name = "setuptools"
version = "69.0.3" version = "69.0.3"
@@ -4536,4 +4636,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "~3.12" python-versions = "~3.12"
content-hash = "04ef13e2692c158e5eda1e89876a215746c56a891a161d8434d808dc12c0fc7a" content-hash = "c5a36b528980277b07f80200da251a2bea31cc2b7d5438250706f23a825f3628"
+1
View File
@@ -133,6 +133,7 @@ pyyaml = "*"
requests-oauthlib = "*" requests-oauthlib = "*"
sentry-sdk = "*" sentry-sdk = "*"
service_identity = "*" service_identity = "*"
setproctitle = "*"
structlog = "*" structlog = "*"
swagger-spec-validator = "*" swagger-spec-validator = "*"
tenant-schemas-celery = "*" tenant-schemas-celery = "*"
@@ -112,9 +112,7 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
item.permissions.filter((uperm) => uperm.codename === perm.codename).length > 0; item.permissions.filter((uperm) => uperm.codename === perm.codename).length > 0;
baseRow.push( baseRow.push(
html`${granted html`${granted
? html`<pf-tooltip ? html`<pf-tooltip position="top" content=${msg("Directly assigned")}
position="top"
content=${msg("Directly assigned")}
>✓</pf-tooltip >✓</pf-tooltip
>` >`
: html`X`} `, : html`X`} `,
+1 -2
View File
@@ -328,8 +328,7 @@ Requires authentik 2022.9
Configure how many gunicorn worker processes should be started (see https://docs.gunicorn.org/en/stable/design.html). Configure how many gunicorn worker processes should be started (see https://docs.gunicorn.org/en/stable/design.html).
If running in Kubernetes, the default value is set to 2 and should in most cases not be changed, as scaling can be done with multiple pods running the web server. Defaults to 2. A value below 2 workers is not recommended. In environments where scaling with multiple replicas of the authentik server is not possible, this number can be increased to handle higher loads.
Otherwise, authentik will use 1 worker for each 4 CPU cores + 1. A value below 2 workers is not recommended.
### `AUTHENTIK_WEB__THREADS` ### `AUTHENTIK_WEB__THREADS`