mirror of
https://github.com/taylanbakircioglu/haproxy-openmanager.git
synced 2026-09-20 01:25:12 +00:00
9e2ea04777
Bulk import / manual edit silently dropped `filter spoe engine ...` (Coraza WAF) and frontend `log-format` because the parser recognised only a fixed directive set. The regenerated config then missed the SPOE engine, so HAProxy failed with "unable to find SPOE engine 'coraza' used by the send-spoe-group". - parser: capture `filter` + `log-format`/`log-format-sd` into new ParsedFrontend fields - db: additive nullable `log_format` + `filters` TEXT columns on frontends (SCHEMA_VERSION 8->9) - generator: new `filter` bucket flushed before http-request rules so `filter` precedes `send-spoe-group`; `log-format` kept in prelude - bulk import: preview dict, change-detection, persist (create + merge-update); cluster-aware SPOE pre-flight advisories (missing-filter + host-prerequisite) surfaced in the UI - manual CRUD: full round-trip (get/create/update) incl. React form fields (no null-wipe) - reject/rollback: restore the new columns; restore path + wizard helper kept in parity - backend `option spop-check` recognised (suppresses spurious warning for coraza-spoa) - tests: test_spoe_filter_import.py; full suite green (1079 passed)
153 lines
5.6 KiB
Python
153 lines
5.6 KiB
Python
"""
|
|
frontend_service: extracted helper for INSERT-row creation of frontends.
|
|
|
|
Used by:
|
|
- routers/site_wizard.py (atomic transaction wizard)
|
|
- routers/letsencrypt.py _complete_certificate post_completion_actions
|
|
|
|
Design (Section 4.2 of v1.5.0 plan):
|
|
- M13: writes BOTH ssl_certificate_id (INT col) AND ssl_certificate_ids (JSONB col).
|
|
- mark_pending=True triggers post-INSERT UPDATE last_config_status='PENDING'
|
|
(matches existing frontend.py:526 pattern).
|
|
- Schema accuracy R38: bind_address / bind_port (NOT port); redirect_rules / acl_rules /
|
|
use_backend_rules are JSONB columns.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from typing import Any, Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def create_frontend_row(
|
|
conn,
|
|
payload: Any,
|
|
cluster_id: int,
|
|
*,
|
|
ssl_certificate_id: Optional[int] = None,
|
|
ssl_enabled: Optional[bool] = None,
|
|
bind_port_override: Optional[int] = None,
|
|
name_override: Optional[str] = None,
|
|
mark_pending: bool = True,
|
|
) -> int:
|
|
"""Insert a row into frontends; return new id.
|
|
|
|
Mirrors POST /api/frontends INSERT (frontend.py:481-499) field-for-field.
|
|
|
|
M13: writes ssl_certificate_id AND ssl_certificate_ids consistently. If
|
|
ssl_certificate_id resolved (param OR payload.ssl_certificate_id),
|
|
ssl_certificate_ids = json.dumps([id]); else json.dumps([]).
|
|
"""
|
|
cert_id = ssl_certificate_id if ssl_certificate_id is not None else getattr(payload, "ssl_certificate_id", None)
|
|
cert_ids_list = [cert_id] if cert_id else []
|
|
ssl_cert_ids_json = json.dumps(cert_ids_list)
|
|
|
|
fe_name = name_override if name_override is not None else payload.name
|
|
fe_ssl_enabled = ssl_enabled if ssl_enabled is not None else getattr(payload, "ssl_enabled", False)
|
|
fe_bind_port = bind_port_override if bind_port_override is not None else payload.bind_port
|
|
|
|
frontend_id = await conn.fetchval(
|
|
"""
|
|
INSERT INTO frontends (
|
|
name, bind_address, bind_port, default_backend, mode,
|
|
ssl_enabled, ssl_certificate_id, ssl_certificate_ids, ssl_port, ssl_cert_path, ssl_cert, ssl_verify,
|
|
ssl_alpn, ssl_npn, ssl_ciphers, ssl_ciphersuites, ssl_min_ver, ssl_max_ver, ssl_strict_sni,
|
|
acl_rules, redirect_rules, use_backend_rules,
|
|
request_headers, response_headers, options, tcp_request_rules, timeout_client, timeout_http_request,
|
|
rate_limit, compression, log_separate, monitor_uri,
|
|
cluster_id, maxconn, log_format, filters, updated_at
|
|
) VALUES (
|
|
$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, CURRENT_TIMESTAMP
|
|
)
|
|
RETURNING id
|
|
""",
|
|
fe_name,
|
|
getattr(payload, "bind_address", "*"),
|
|
fe_bind_port,
|
|
getattr(payload, "default_backend", None),
|
|
getattr(payload, "mode", "http"),
|
|
fe_ssl_enabled,
|
|
cert_id,
|
|
ssl_cert_ids_json,
|
|
getattr(payload, "ssl_port", None),
|
|
getattr(payload, "ssl_cert_path", None),
|
|
getattr(payload, "ssl_cert", None),
|
|
getattr(payload, "ssl_verify", None),
|
|
getattr(payload, "ssl_alpn", None),
|
|
getattr(payload, "ssl_npn", None),
|
|
getattr(payload, "ssl_ciphers", None),
|
|
getattr(payload, "ssl_ciphersuites", None),
|
|
getattr(payload, "ssl_min_ver", None),
|
|
getattr(payload, "ssl_max_ver", None),
|
|
getattr(payload, "ssl_strict_sni", None),
|
|
json.dumps(getattr(payload, "acl_rules", None) or []),
|
|
json.dumps(getattr(payload, "redirect_rules", None) or []),
|
|
json.dumps(getattr(payload, "use_backend_rules", None) or []),
|
|
getattr(payload, "request_headers", None),
|
|
getattr(payload, "response_headers", None),
|
|
getattr(payload, "options", None),
|
|
getattr(payload, "tcp_request_rules", None),
|
|
getattr(payload, "timeout_client", None),
|
|
getattr(payload, "timeout_http_request", None),
|
|
getattr(payload, "rate_limit", None),
|
|
getattr(payload, "compression", None),
|
|
getattr(payload, "log_separate", None),
|
|
getattr(payload, "monitor_uri", None),
|
|
cluster_id,
|
|
getattr(payload, "maxconn", None),
|
|
getattr(payload, "log_format", None), # Issue #38
|
|
getattr(payload, "filters", None), # Issue #38
|
|
)
|
|
|
|
if mark_pending:
|
|
await conn.execute(
|
|
"UPDATE frontends SET last_config_status='PENDING' WHERE id=$1",
|
|
frontend_id,
|
|
)
|
|
|
|
return frontend_id
|
|
|
|
|
|
async def check_bind_port_collision(
|
|
conn,
|
|
cluster_id: int,
|
|
bind_address: str,
|
|
bind_port: int,
|
|
*,
|
|
exclude_frontend_id: Optional[int] = None,
|
|
) -> Optional[int]:
|
|
"""Return id of an existing frontend that conflicts with bind_address+bind_port,
|
|
or None if no conflict.
|
|
|
|
Used by:
|
|
- wizard pre-create (Section 6.2 step before INSERT)
|
|
- _complete_certificate post-action HTTPS frontend create (M21/R35)
|
|
"""
|
|
if exclude_frontend_id is not None:
|
|
return await conn.fetchval(
|
|
"""
|
|
SELECT id FROM frontends
|
|
WHERE cluster_id=$1 AND bind_address=$2 AND bind_port=$3
|
|
AND is_active=TRUE AND id <> $4
|
|
""",
|
|
cluster_id,
|
|
bind_address,
|
|
bind_port,
|
|
exclude_frontend_id,
|
|
)
|
|
return await conn.fetchval(
|
|
"""
|
|
SELECT id FROM frontends
|
|
WHERE cluster_id=$1 AND bind_address=$2 AND bind_port=$3 AND is_active=TRUE
|
|
""",
|
|
cluster_id,
|
|
bind_address,
|
|
bind_port,
|
|
)
|