-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmain.py
More file actions
144 lines (117 loc) · 4.63 KB
/
main.py
File metadata and controls
144 lines (117 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import contextlib
import logging
import os
import sys
import typing
from tempfile import mkdtemp
from django.core.management import (
execute_from_command_line as django_execute_from_command_line,
)
from environs import Env
from common.core.cli import healthcheck
from common.core.logging import setup_logging
from common.gunicorn.processors import make_gunicorn_access_processor
env = Env()
logger = logging.getLogger(__name__)
@contextlib.contextmanager
def ensure_cli_env() -> typing.Generator[None, None, None]:
"""
Set up the environment for the main entry point of the application
and clean up after it's done.
Add environment-related code that needs to happen before and after Django is involved
to here.
Use as a context manager, e.g.:
```python
with ensure_cli_env():
main()
```
"""
ctx = contextlib.ExitStack()
# Currently we don't install Flagsmith modules as a package, so we need to add
# $CWD to the Python path to be able to import them
sys.path.append(os.getcwd())
# TODO @khvn26 We should find a better way to pre-set the Django settings module
# without resorting to it being set outside of the application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.dev")
if "docgen" in sys.argv:
os.environ["DOCGEN_MODE"] = "true"
if "task-processor" in sys.argv:
# A hacky way to signal we're not running the API
os.environ["RUN_BY_PROCESSOR"] = "true"
# Set up OTel instrumentation (opt-in via OTEL_EXPORTER_OTLP_ENDPOINT).
# Must come after sys.path / DJANGO_SETTINGS_MODULE setup because
# DjangoInstrumentor accesses settings.MIDDLEWARE.
otel_processors = None
otel_endpoint = env.str("OTEL_EXPORTER_OTLP_ENDPOINT", None)
if otel_endpoint:
from common.core.otel import (
add_otel_trace_context,
build_otel_log_provider,
build_tracer_provider,
make_structlog_otel_processor,
setup_tracing,
)
service_name = env.str("OTEL_SERVICE_NAME", "flagsmith-api")
log_provider = build_otel_log_provider(
endpoint=f"{otel_endpoint}/v1/logs",
service_name=service_name,
)
otel_processors = [
add_otel_trace_context,
make_structlog_otel_processor(log_provider),
]
tracer_provider = build_tracer_provider(
endpoint=f"{otel_endpoint}/v1/traces",
service_name=service_name,
)
excluded_urls = env.str("OTEL_TRACING_EXCLUDED_URL_PATHS", None)
ctx.enter_context(setup_tracing(tracer_provider, excluded_urls=excluded_urls))
ctx.callback(log_provider.shutdown)
# Set up logging early, before Django settings are loaded.
setup_logging(
log_level=env.str("LOG_LEVEL", "INFO"),
log_format=env.str("LOG_FORMAT", "generic"),
logging_configuration_file=env.str("LOGGING_CONFIGURATION_FILE", None),
application_loggers=env.list("APPLICATION_LOGGERS", []) or None,
extra_foreign_processors=[
make_gunicorn_access_processor(
env.list("ACCESS_LOG_EXTRA_ITEMS", []) or None,
),
],
otel_processors=otel_processors,
)
# Prometheus multiproc support
if not os.environ.get("PROMETHEUS_MULTIPROC_DIR"):
os.environ["PROMETHEUS_MULTIPROC_DIR"] = mkdtemp(prefix="flagsmith-prometheus-")
with ctx:
yield
def execute_from_command_line(argv: list[str]) -> None:
try:
subcommand = argv[1]
subcommand_main = {
"healthcheck": healthcheck.main,
# Backwards compatibility for task-processor health checks
# See https://github.com/Flagsmith/flagsmith-task-processor/issues/24
"checktaskprocessorthreadhealth": healthcheck.main,
}[subcommand]
except (IndexError, KeyError):
logger.info("Invoking Django")
else:
return subcommand_main(
argv[2:],
prog=f"{os.path.basename(argv[0])} {subcommand}",
)
django_execute_from_command_line(argv)
def main(argv: list[str] = sys.argv) -> None:
"""
The main entry point to the Flagsmith application.
An equivalent to Django's `manage.py` script, this module is used to run management commands.
It's installed as the `flagsmith` command.
Everything that needs to be run before Django is started should be done here.
The end goal is to eventually replace Core API's `run-docker.sh` with this.
Usage:
`flagsmith <command> [options]`
"""
with ensure_cli_env():
# Run own commands and Django
execute_from_command_line(argv)