fix(discovery): prevent WORDPRESS_ADVANCED_ prefix collision with WORDPRESS_
ProjectManager and SiteManager now skip env vars that belong to a more specific plugin type (e.g. WORDPRESS_ADVANCED_*) when discovering sites for a shorter prefix (WORDPRESS_). This prevents ghost projects, ERROR logs, and false health alerts on startup. Also downgrades Docker socket errors in WP-CLI to warnings — if Docker socket is not mounted, WP-CLI features are silently unavailable instead of logging ERROR and triggering health alerts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -61,11 +61,24 @@ class ProjectManager:
|
|||||||
"""
|
"""
|
||||||
prefix = plugin_type.upper() + "_"
|
prefix = plugin_type.upper() + "_"
|
||||||
|
|
||||||
|
# Build list of longer prefixes from other plugin types to avoid collisions.
|
||||||
|
# e.g. WORDPRESS_ must not match WORDPRESS_ADVANCED_ env vars.
|
||||||
|
plugin_types = registry.get_registered_types()
|
||||||
|
longer_prefixes = [
|
||||||
|
pt.upper() + "_"
|
||||||
|
for pt in plugin_types
|
||||||
|
if pt != plugin_type and pt.upper().startswith(plugin_type.upper() + "_")
|
||||||
|
]
|
||||||
|
|
||||||
# Find all project IDs for this plugin type
|
# Find all project IDs for this plugin type
|
||||||
project_ids = set()
|
project_ids = set()
|
||||||
env_pattern = re.compile(f"^{prefix}([A-Z0-9_]+?)_(.+)$")
|
env_pattern = re.compile(f"^{prefix}([A-Z0-9_]+?)_(.+)$")
|
||||||
|
|
||||||
for env_key in os.environ.keys():
|
for env_key in os.environ.keys():
|
||||||
|
# Skip env vars that belong to a more specific plugin type
|
||||||
|
if any(env_key.startswith(lp) for lp in longer_prefixes):
|
||||||
|
continue
|
||||||
|
|
||||||
match = env_pattern.match(env_key)
|
match = env_pattern.match(env_key)
|
||||||
if match:
|
if match:
|
||||||
project_id = match.group(1).lower()
|
project_id = match.group(1).lower()
|
||||||
|
|||||||
@@ -211,12 +211,27 @@ class SiteManager:
|
|||||||
"""
|
"""
|
||||||
prefix = plugin_type.upper() + "_"
|
prefix = plugin_type.upper() + "_"
|
||||||
|
|
||||||
|
# Build list of longer prefixes from other plugin types to avoid collisions.
|
||||||
|
# e.g. WORDPRESS_ must not match WORDPRESS_ADVANCED_ env vars.
|
||||||
|
from plugins import registry as plugin_registry
|
||||||
|
|
||||||
|
all_plugin_types = plugin_registry.get_registered_types()
|
||||||
|
longer_prefixes = [
|
||||||
|
pt.upper() + "_"
|
||||||
|
for pt in all_plugin_types
|
||||||
|
if pt != plugin_type and pt.upper().startswith(plugin_type.upper() + "_")
|
||||||
|
]
|
||||||
|
|
||||||
# Pattern to match: WORDPRESS_SITE1_URL, WORDPRESS_SITE2_USERNAME, etc.
|
# Pattern to match: WORDPRESS_SITE1_URL, WORDPRESS_SITE2_USERNAME, etc.
|
||||||
env_pattern = re.compile(f"^{prefix}([A-Z0-9_]+?)_(.+)$")
|
env_pattern = re.compile(f"^{prefix}([A-Z0-9_]+?)_(.+)$")
|
||||||
|
|
||||||
# Find all unique site IDs
|
# Find all unique site IDs
|
||||||
site_ids = set()
|
site_ids = set()
|
||||||
for env_key in os.environ.keys():
|
for env_key in os.environ.keys():
|
||||||
|
# Skip env vars that belong to a more specific plugin type
|
||||||
|
if any(env_key.startswith(lp) for lp in longer_prefixes):
|
||||||
|
continue
|
||||||
|
|
||||||
match = env_pattern.match(env_key)
|
match = env_pattern.match(env_key)
|
||||||
if match:
|
if match:
|
||||||
site_id = match.group(1).lower()
|
site_id = match.group(1).lower()
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ Security:
|
|||||||
- WP-CLI installation check
|
- WP-CLI installation check
|
||||||
- Timeout protection (30s default)
|
- Timeout protection (30s default)
|
||||||
- Graceful error handling
|
- Graceful error handling
|
||||||
|
|
||||||
Phase 5.1: Cache Management (4 tools)
|
|
||||||
Phase 5.2: Database & Plugin/Theme Info (7 tools)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -75,10 +72,7 @@ class WPCLIManager:
|
|||||||
Check if the Docker container exists and is running.
|
Check if the Docker container exists and is running.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if container exists and is running
|
bool: True if container exists and is running, False otherwise
|
||||||
|
|
||||||
Raises:
|
|
||||||
Exception: If docker command fails or container not found
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# First, test if we have Docker socket access
|
# First, test if we have Docker socket access
|
||||||
@@ -93,15 +87,12 @@ class WPCLIManager:
|
|||||||
|
|
||||||
if test_process.returncode != 0:
|
if test_process.returncode != 0:
|
||||||
error_msg = test_stderr.decode().strip()
|
error_msg = test_stderr.decode().strip()
|
||||||
self.logger.error(f"Cannot access Docker daemon: {error_msg}")
|
self.logger.warning(
|
||||||
raise Exception(
|
f"Docker daemon not accessible for container '{self.container_name}': "
|
||||||
f"Cannot access Docker daemon. This is likely a permissions issue. "
|
f"{error_msg}. WP-CLI features will be unavailable. "
|
||||||
f"Error: {error_msg}. "
|
f"Mount /var/run/docker.sock to enable WP-CLI."
|
||||||
f"Please ensure:\n"
|
|
||||||
f"1. Docker socket is mounted: /var/run/docker.sock\n"
|
|
||||||
f"2. User has permission to access Docker (member of docker group)\n"
|
|
||||||
f"3. Docker daemon is running on the host"
|
|
||||||
)
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
docker_version = test_stdout.decode().strip()
|
docker_version = test_stdout.decode().strip()
|
||||||
self.logger.debug(f"Docker access OK - Server version: {docker_version}")
|
self.logger.debug(f"Docker access OK - Server version: {docker_version}")
|
||||||
@@ -151,15 +142,14 @@ class WPCLIManager:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
self.logger.error("Docker command timed out")
|
self.logger.warning(
|
||||||
raise Exception("Docker command timed out after 5 seconds")
|
f"Docker command timed out for container '{self.container_name}'. "
|
||||||
|
f"WP-CLI features will be unavailable."
|
||||||
|
)
|
||||||
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Re-raise exceptions with full context
|
self.logger.warning(f"Docker check failed for '{self.container_name}': {e}")
|
||||||
if "Cannot access Docker" in str(e) or "not found" in str(e):
|
return False
|
||||||
raise
|
|
||||||
else:
|
|
||||||
self.logger.error(f"Error checking container: {e}")
|
|
||||||
raise Exception(f"Failed to check container existence: {str(e)}")
|
|
||||||
|
|
||||||
async def _check_wp_cli_available(self) -> bool:
|
async def _check_wp_cli_available(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user