refactor: Improve code quality, error handling, and test coverage

- Add file_lock context manager to eliminate duplicate locking patterns
- Add ValidationError, ConfigurationError, CertificateError exceptions
- Improve rollback logic in haproxy_add_servers (track successful ops only)
- Decompose haproxy_add_domain into smaller helper functions
- Consolidate certificate constants (CERTS_DIR, ACME_HOME) to config.py
- Enhance docstrings for internal functions and magic numbers
- Add pytest framework with 48 new tests (269 -> 317 total)
- Increase test coverage from 76% to 86%
  - servers.py: 58% -> 82%
  - certificates.py: 67% -> 86%
  - configuration.py: 69% -> 94%

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kaffa
2026-02-03 12:50:00 +09:00
parent 18ce812920
commit 6bcfee519c
25 changed files with 6852 additions and 125 deletions

View File

@@ -13,6 +13,7 @@ from ..config import (
StateField,
StatField,
STATE_MIN_COLUMNS,
logger,
)
from ..exceptions import HaproxyError
from ..validation import validate_domain, validate_ip, validate_backend_name
@@ -252,6 +253,7 @@ def register_server_tools(mcp):
added = []
errors = []
failed_slots = []
successfully_added_slots = []
try:
for server_config in validated_servers:
@@ -260,19 +262,43 @@ def register_server_tools(mcp):
http_port = server_config["http_port"]
try:
configure_server_slot(backend, server_prefix, slot, ip, http_port)
successfully_added_slots.append(slot)
added.append(f"slot {slot}: {ip}:{http_port}")
except HaproxyError as e:
failed_slots.append(slot)
errors.append(f"slot {slot}: {e}")
except Exception as e:
# Rollback all saved configs on unexpected error
# Rollback only successfully added configs on unexpected error
for slot in successfully_added_slots:
try:
remove_server_from_config(domain, slot)
except Exception as rollback_error:
logger.error(
"Failed to rollback server config for %s slot %d: %s",
domain, slot, rollback_error
)
# Also rollback configs that weren't yet processed
for server_config in validated_servers:
remove_server_from_config(domain, server_config["slot"])
slot = server_config["slot"]
if slot not in successfully_added_slots:
try:
remove_server_from_config(domain, slot)
except Exception as rollback_error:
logger.error(
"Failed to rollback server config for %s slot %d: %s",
domain, slot, rollback_error
)
return f"Error: {e}"
# Rollback failed slots from config
for slot in failed_slots:
remove_server_from_config(domain, slot)
try:
remove_server_from_config(domain, slot)
except Exception as rollback_error:
logger.error(
"Failed to rollback server config for %s slot %d: %s",
domain, slot, rollback_error
)
# Build result message
result_parts = []