1
0
Fork 0

Refactor E2E tests and configuration format

- Replaced `easymapping` configuration with `containers` for better maintainability and clarity.
- Introduced `DockerComposeFixture` class in `utils.py` to manage Docker Compose lifecycle and smart build strategy.
- Enhanced YAML-to-environment variable conversion in `ContainerEnv` for dynamic configuration support.
- Updated HAProxy configurations and test fixtures to reflect the new format.
- Improved test coverage for YAML parsing, environment variable handling, and HAProxy config generation.
This commit is contained in:
Joao Gilberto Magalhaes 2026-02-13 11:44:26 -05:00
parent c125985150
commit 97845b8a52
22 changed files with 1042 additions and 442 deletions

View file

@ -98,48 +98,132 @@ class Static(ProcessorInterface):
super().__init__(filename)
def inspect_network(self):
self.parsed_object = {}
self.static_content = None
def get_parsed_object(self):
return self.static_content["easymapping"] if "easymapping" in self.static_content else []
def get_hosts(self):
hosts = []
for obj in self.get_parsed_object():
if "hosts" not in obj:
continue
for host in obj["hosts"].keys():
hosts.append(f"{host}:{obj['port']}")
return hosts
def parse(self):
"""Load YAML and convert containers to Docker-style container metadata"""
# Load YAML
self.static_content = yaml.load(Functions.load(self.filename), Loader=yaml.FullLoader)
# Merge plugin config from YAML with env vars
if "plugins" in self.static_content:
# Get env var config
container_env = ContainerEnv.read()
# Convert containers to label format
self.parsed_object = self._convert_yaml_to_labels()
# Merge YAML plugins config with env config
# YAML config takes precedence over env vars
if "plugins" not in self.static_content:
self.static_content["plugins"] = container_env.get("plugins", {})
def _convert_yaml_to_labels(self):
"""
Convert static YAML containers to Docker label format.
Returns: {IP: {labels}} structure that parse() can process
"""
import json
container_metadata = {}
# Get global plugin configuration
global_plugins = self.static_content.get("plugins", {})
global_enabled = global_plugins.get("enabled", [])
global_plugin_config = global_plugins.get("config", {})
for host_port, config in self.static_content.get("containers", {}).items():
# Parse hostname:port from key
if ":" in host_port:
hostname, port = host_port.rsplit(":", 1)
else:
# Merge configs - YAML overrides env vars
yaml_plugins = self.static_content["plugins"]
env_plugins = container_env.get("plugins", {})
hostname = host_port
port = "80"
# Merge individual plugin configs
for plugin_name, plugin_config in env_plugins.get("config", {}).items():
if plugin_name not in yaml_plugins:
yaml_plugins[plugin_name] = {}
# Env vars fill in missing keys, YAML takes precedence
for key, value in plugin_config.items():
if key not in yaml_plugins[plugin_name]:
yaml_plugins[plugin_name][key] = value
# Create definition: hostname_port (e.g., host1_com_br_80)
definition = hostname.replace(".", "_") + f"_{port}"
self.cfg = HaproxyConfigGenerator(self.static_content)
# Handle redirect-only entries (no backend)
if "redirect" in config and "ip" not in config:
# Create metadata with redirect but mark as redirect-only to skip backend creation
fake_ip = f"redirect-{hostname}-{port}"
if fake_ip not in container_metadata:
container_metadata[fake_ip] = {}
container_metadata[fake_ip].update({
f"easyhaproxy.{definition}.host": hostname,
f"easyhaproxy.{definition}.port": port,
f"easyhaproxy.{definition}.redirect": json.dumps({hostname: config["redirect"]}),
f"easyhaproxy.{definition}.redirect_only": "true", # Marker to skip backend
})
continue
# Get IPs/containers
ip_list = config.get("ip", [hostname])
# Process each container/IP
for container_spec in ip_list:
# Parse container:localport
if ":" in container_spec:
container_addr, localport = container_spec.rsplit(":", 1)
else:
container_addr = container_spec
localport = "80"
# Use container address as IP (could be IP, DNS, or container name)
ip = container_addr
# Build labels dict
labels = {
f"easyhaproxy.{definition}.host": hostname,
f"easyhaproxy.{definition}.port": port,
f"easyhaproxy.{definition}.localport": localport,
}
# Add optional settings
for key in ["mode", "certbot", "redirect_ssl", "ssl", "balance", "proto", "ssl-check", "clone_to_ssl"]:
if key in config:
value = config[key]
# Convert boolean to string
if isinstance(value, bool):
value = "true" if value else "false"
labels[f"easyhaproxy.{definition}.{key}"] = str(value)
# Handle plugins
host_plugins = config.get("plugins", global_enabled)
if host_plugins:
# Convert list to comma-separated string if needed
if isinstance(host_plugins, list):
plugins_str = ",".join(host_plugins)
else:
plugins_str = host_plugins
labels[f"easyhaproxy.{definition}.plugins"] = plugins_str
# Process plugin configurations
host_plugin_config = config.get("plugin", {})
# Parse plugins list
plugins_list = host_plugins if isinstance(host_plugins, list) else [p.strip() for p in host_plugins.split(",")]
for plugin_name in plugins_list:
# Merge global and host-specific config
merged_config = {}
if plugin_name in global_plugin_config:
merged_config.update(global_plugin_config[plugin_name])
if plugin_name in host_plugin_config:
merged_config.update(host_plugin_config[plugin_name])
# Convert plugin config to labels
for config_key, config_value in merged_config.items():
label_key = f"easyhaproxy.{definition}.plugin.{plugin_name}.{config_key}"
# Convert list values to comma-separated strings
if isinstance(config_value, list):
label_value = ",".join(str(v) for v in config_value)
else:
label_value = str(config_value)
labels[label_key] = label_value
# Initialize container entry if it doesn't exist
if ip not in container_metadata:
container_metadata[ip] = {}
# Merge labels instead of overwriting
container_metadata[ip].update(labels)
return container_metadata
def parse(self):
"""Create HaproxyConfigGenerator with YAML config merged into env vars"""
self.cfg = HaproxyConfigGenerator(ContainerEnv.read(self.static_content))
class Docker(ProcessorInterface):