Refactor nginx to path-based domain model with config migration
Replace the legacy top-level management key with a unified paths-based model. Each domain now contains a paths map where each entry defines its own backend, auth, headers, and flags (is_management, is_websocket). - Add _migrate_config() to auto-migrate legacy formats on first load - Remove set_management_proxy() and POST_NGINX_MANAGEMENT endpoint - Update server_block.conf template to iterate paths with per-location auth - Update daemon handler, API blueprint, state collector, and install script - Add server config generation tests for paths, WebSocket, auth inheritance - Update frontend proxy page to display per-path rows with flags
This commit is contained in:
+139
-6
@@ -63,7 +63,10 @@ class TestSaveConfig:
|
||||
}
|
||||
nginx.save_config(cfg)
|
||||
loaded = nginx.get_config()
|
||||
assert loaded["domains"]["example.com"]["backend"]["host"] == "localhost"
|
||||
assert (
|
||||
loaded["domains"]["example.com"]["paths"]["/"]["backend"]["host"]
|
||||
== "localhost"
|
||||
)
|
||||
|
||||
|
||||
class TestGetDomains:
|
||||
@@ -97,8 +100,10 @@ class TestAddDomain:
|
||||
nginx.add_domain("example.com", "10.0.0.5", 8080)
|
||||
cfg = nginx.get_config()
|
||||
assert "example.com" in cfg["domains"]
|
||||
assert cfg["domains"]["example.com"]["backend"]["host"] == "10.0.0.5"
|
||||
assert cfg["domains"]["example.com"]["backend"]["port"] == 8080
|
||||
assert (
|
||||
cfg["domains"]["example.com"]["paths"]["/"]["backend"]["host"] == "10.0.0.5"
|
||||
)
|
||||
assert cfg["domains"]["example.com"]["paths"]["/"]["backend"]["port"] == 8080
|
||||
|
||||
@patch("lib.nginx.get_config")
|
||||
def test_duplicate_domain_raises(self, mock_get, temp_data_dir):
|
||||
@@ -163,21 +168,149 @@ class TestWriteSite:
|
||||
assert "server { listen 443; }" in content
|
||||
|
||||
|
||||
class TestGenerateServerConf:
|
||||
def test_simple_root_path(self, temp_data_dir):
|
||||
cfg = {
|
||||
"domain": "example.com",
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {"host": "10.0.0.1", "port": 80, "proto": "http"},
|
||||
"headers": {"X-Custom": "value"},
|
||||
}
|
||||
},
|
||||
"force_ssl": True,
|
||||
"cert": "acme",
|
||||
}
|
||||
out = nginx.generate_server_conf(cfg)
|
||||
assert "location /" in out
|
||||
assert "proxy_pass http://10.0.0.1:80;" in out
|
||||
assert "proxy_set_header X-Custom value;" in out
|
||||
assert "add_header X-Content-Type-Options" in out
|
||||
|
||||
def test_multiple_paths(self, temp_data_dir):
|
||||
cfg = {
|
||||
"domain": "app.example.com",
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {"host": "10.0.0.1", "port": 80, "proto": "http"},
|
||||
"headers": {},
|
||||
},
|
||||
"/api": {
|
||||
"backend": {"host": "10.0.0.2", "port": 8080, "proto": "http"},
|
||||
},
|
||||
},
|
||||
"force_ssl": True,
|
||||
"cert": "acme",
|
||||
}
|
||||
out = nginx.generate_server_conf(cfg)
|
||||
assert "proxy_pass http://10.0.0.1:80;" in out
|
||||
assert "proxy_pass http://10.0.0.2:8080;" in out
|
||||
assert "location /api" in out
|
||||
|
||||
def test_management_path(self, temp_data_dir):
|
||||
cfg = {
|
||||
"domain": "mgmt.example.com",
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {"host": "127.0.0.1", "port": 9090, "proto": "http"},
|
||||
"is_management": True,
|
||||
}
|
||||
},
|
||||
"force_ssl": True,
|
||||
"cert": "acme",
|
||||
"auth": {"user": "admin", "htpasswd": "/path/.htpasswd"},
|
||||
}
|
||||
out = nginx.generate_server_conf(cfg)
|
||||
assert "proxy_pass http://127.0.0.1:9090;" in out
|
||||
assert "add_header X-Content-Type-Options" not in out
|
||||
assert "wall_mgmt_access.log" in out
|
||||
|
||||
def test_websocket_path(self, temp_data_dir):
|
||||
cfg = {
|
||||
"domain": "mgmt.example.com",
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {"host": "127.0.0.1", "port": 9090, "proto": "http"},
|
||||
},
|
||||
"/ws": {
|
||||
"backend": {"host": "127.0.0.1", "port": 9091, "proto": "http"},
|
||||
"is_websocket": True,
|
||||
},
|
||||
},
|
||||
"force_ssl": True,
|
||||
"cert": "acme",
|
||||
}
|
||||
out = nginx.generate_server_conf(cfg)
|
||||
assert "proxy_pass http://127.0.0.1:9091;" in out
|
||||
assert "proxy_set_header Upgrade" in out
|
||||
assert "proxy_read_timeout 86400s;" in out
|
||||
|
||||
def test_auth_inheritance(self, temp_data_dir):
|
||||
cfg = {
|
||||
"domain": "app.example.com",
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {"host": "10.0.0.1", "port": 80, "proto": "http"},
|
||||
"headers": {},
|
||||
},
|
||||
"/api": {
|
||||
"backend": {"host": "10.0.0.2", "port": 8080, "proto": "http"},
|
||||
"auth": None,
|
||||
},
|
||||
"/admin": {
|
||||
"backend": {"host": "10.0.0.3", "port": 9000, "proto": "http"},
|
||||
"auth": {"user": "admin", "htpasswd": "/other/.htpasswd"},
|
||||
},
|
||||
},
|
||||
"force_ssl": True,
|
||||
"cert": "acme",
|
||||
"auth": {"user": "admin", "htpasswd": "/path/.htpasswd"},
|
||||
}
|
||||
out = nginx.generate_server_conf(cfg)
|
||||
assert "auth_basic_user_file /path/.htpasswd;" in out
|
||||
lines = out.split("\n")
|
||||
api_idx = next(i for i, line in enumerate(lines) if "location /api" in line)
|
||||
admin_idx = next(i for i, line in enumerate(lines) if "location /admin" in line)
|
||||
# /api should have auth_basic off
|
||||
assert "auth_basic off;" in "\n".join(lines[api_idx : api_idx + 5])
|
||||
# /admin should have path-level auth override
|
||||
assert "auth_basic_user_file /other/.htpasswd;" in "\n".join(
|
||||
lines[admin_idx : admin_idx + 5]
|
||||
)
|
||||
|
||||
|
||||
class TestWriteAllSites:
|
||||
@patch("lib.nginx.get_config")
|
||||
def test_writes_all_domains(self, mock_get, temp_data_dir):
|
||||
mock_get.return_value = {
|
||||
"domains": {
|
||||
"a.com": {
|
||||
"backend": {"host": "10.0.0.1", "port": 80, "proto": "http"},
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {
|
||||
"host": "10.0.0.1",
|
||||
"port": 80,
|
||||
"proto": "http",
|
||||
},
|
||||
"headers": {},
|
||||
}
|
||||
},
|
||||
"force_ssl": True,
|
||||
},
|
||||
"b.com": {
|
||||
"backend": {"host": "10.0.0.2", "port": 80, "proto": "http"},
|
||||
"paths": {
|
||||
"/": {
|
||||
"backend": {
|
||||
"host": "10.0.0.2",
|
||||
"port": 80,
|
||||
"proto": "http",
|
||||
},
|
||||
"headers": {},
|
||||
}
|
||||
},
|
||||
"force_ssl": True,
|
||||
},
|
||||
},
|
||||
"management": None,
|
||||
"ssl": {"protocols": "TLSv1.2 TLSv1.3"},
|
||||
}
|
||||
nginx.write_all_sites()
|
||||
|
||||
Reference in New Issue
Block a user