fix(ops): nginx pointait sur le port du serveur de developpement
STD-06, de l'audit anterieur : "ports et adresses d'ecoute incoherents entre cinq fichiers". ARCH-007 a unifie les points d'entree Python en vague E/F et OPS-002 a fait de HOST et PORT des variables en vague H, mais personne n'est retourne dans app/nginx.conf. proxy_pass pointait sur 127.0.0.1:5000, qui est le defaut de run.py, le serveur de developpement. wsgi.py -- ce qui sert reellement la production -- ecoute PORT avec un defaut de 10000. Installer le fichier tel qu'il est livre donne donc 502 sur chaque page, depuis une configuration qui se lit comme parfaitement raisonnable et un serveur qui tourne tres bien. Rien dans le depot ne reliait les deux nombres, donc rien ne pouvait remarquer qu'ils avaient diverge. Le test est cette relation, ecrite quelque part qui s'execute. Il couvre aussi les blocs commentes : celui qu'on decommente dans un an porte le port avec lequel il a ete ecrit. Le second test garde l'avertissement colle au chemin alias du bloc /static/, livre avec une valeur devinee et toujours a regler sur le noeud. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+12
-3
@@ -119,9 +119,18 @@ http {
|
||||
# ---------------------------------------------------------------------
|
||||
# Proxy to Waitress (Flask)
|
||||
# ---------------------------------------------------------------------
|
||||
#
|
||||
# 10000 is `PORT`'s default in wsgi.py, which is what serves this
|
||||
# application in production. This line said 5000 — run.py's default,
|
||||
# the development server — so anyone installing this file as shipped
|
||||
# got 502 Bad Gateway on every page, from a configuration that looks
|
||||
# entirely reasonable (STD-06).
|
||||
#
|
||||
# If PORT is set in the server's .env, this must match it.
|
||||
# tests/test_nginx_config.py fails if this drifts from wsgi.py again.
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
|
||||
proxy_pass http://127.0.0.1:10000;
|
||||
|
||||
# Proxy headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -188,7 +197,7 @@ http {
|
||||
|
||||
# location /auth/login {
|
||||
# limit_req zone=login burst=5 nodelay;
|
||||
# proxy_pass http://127.0.0.1:5000;
|
||||
# proxy_pass http://127.0.0.1:10000;
|
||||
# }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
"""The shipped nginx template, checked against the code it fronts.
|
||||
|
||||
STD-06. `app/nginx.conf` proxied to `127.0.0.1:5000` — run.py's development
|
||||
default — while `wsgi.py`, which is what actually serves production, binds
|
||||
`PORT` with a default of 10000. Installing the file as shipped therefore
|
||||
produced 502 Bad Gateway on every page, from a configuration that reads as
|
||||
entirely reasonable and a server that is running perfectly well.
|
||||
|
||||
Nothing in the repository related the two numbers, so nothing could notice
|
||||
they had diverged. That is what this file is: the relation, written down
|
||||
somewhere that runs.
|
||||
|
||||
It deliberately checks only what can be wrong in a way that breaks the site
|
||||
silently. The rest of the nginx configuration is the operator's.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
NGINX_CONF = ROOT / 'app' / 'nginx.conf'
|
||||
WSGI = ROOT / 'wsgi.py'
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def nginx_text():
|
||||
return NGINX_CONF.read_text(encoding='utf-8')
|
||||
|
||||
|
||||
def _wsgi_default_port():
|
||||
"""The port wsgi.py binds when PORT is unset."""
|
||||
match = re.search(r"os\.getenv\(\s*'PORT'\s*,\s*(\d+)\s*\)", WSGI.read_text(encoding='utf-8'))
|
||||
assert match, 'wsgi.py no longer reads PORT the way this test expects'
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class TestTheUpstreamPort:
|
||||
def test_the_wsgi_default_is_findable(self):
|
||||
"""Premise: if the regex stopped matching, every test below would be
|
||||
comparing against nothing."""
|
||||
assert _wsgi_default_port().isdigit()
|
||||
|
||||
def test_every_proxy_pass_targets_it(self, nginx_text):
|
||||
"""Including the commented-out ones. A block someone uncomments a
|
||||
year from now carries whatever port it was written with."""
|
||||
expected = _wsgi_default_port()
|
||||
ports = re.findall(r'proxy_pass\s+http://127\.0\.0\.1:(\d+)', nginx_text)
|
||||
|
||||
assert ports, 'no proxy_pass found; this test is no longer checking anything'
|
||||
assert set(ports) == {expected}, (
|
||||
f'nginx proxies to {sorted(set(ports))} but wsgi.py binds {expected}. '
|
||||
f'Installing this file would give 502 on every page.'
|
||||
)
|
||||
|
||||
|
||||
class TestTheStaticAliasIsStillFlagged:
|
||||
"""The /static/ alias is shipped with a guessed path, and the HANDOFF
|
||||
lists it as a node setting. This test does not know the right value —
|
||||
it makes sure the warning stays next to it."""
|
||||
|
||||
def test_the_alias_says_it_must_be_checked(self, nginx_text):
|
||||
lines = nginx_text.splitlines()
|
||||
alias_lines = [i for i, line in enumerate(lines) if line.strip().startswith('alias ')]
|
||||
if not alias_lines:
|
||||
pytest.skip('no alias directive in this configuration')
|
||||
|
||||
for index in alias_lines:
|
||||
preceding = '\n'.join(lines[max(0, index - 20) : index]).lower()
|
||||
assert any(word in preceding for word in ('adjust', 'check', 'guess', 'must match')), (
|
||||
f'line {index + 1}: the alias path is a guess about the deployment '
|
||||
f'layout; the note saying so must stay with it'
|
||||
)
|
||||
Reference in New Issue
Block a user