Skip to content
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ target-version = "py311"
select = ["E", "F", "W", "I", "N", "UP"]
ignore = ["E501"]

[tool.ruff.lint.pep8-naming]
# @classproperty (src/functions/consts.py) passes the class: check for `cls` like @classmethod
classmethod-decorators = ["classproperty"]
# Decorator classes are lowercase by convention, like the builtin property/classmethod
extend-ignore-names = ["classproperty"]

[tool.coverage.run]
source = ["src"]
omit = ["*/tests/*", "*/test_*.py"]
Expand Down
2 changes: 1 addition & 1 deletion src/easyhaproxy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,4 @@ def main():


if __name__ == '__main__':
main()
main()
2 changes: 1 addition & 1 deletion src/easymapping/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .config_generator import HaproxyConfigGenerator
from .label_handler import DockerLabelHandler

__all__ = ["DockerLabelHandler", "HaproxyConfigGenerator"]
__all__ = ["DockerLabelHandler", "HaproxyConfigGenerator"]
4 changes: 2 additions & 2 deletions src/easymapping/config_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from jinja2 import Environment, FileSystemLoader

from functions import Functions, logger_easyhaproxy, Consts
from functions import Consts, logger_easyhaproxy

from .label_handler import DockerLabelHandler

Expand Down Expand Up @@ -313,4 +313,4 @@ def parse(self, container_metadata):
if self.label.get_bool(self.label.create([definition, "ssl"])):
easymapping[port]["ssl"] = True if not clone_to_ssl else False

return easymapping.values()
return easymapping.values()
2 changes: 1 addition & 1 deletion src/easymapping/label_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ def set_data(self, data):
def has_label(self, label):
if label in self.__data:
return True
return False
return False
2 changes: 1 addition & 1 deletion src/functions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"logger_easyhaproxy",
"logger_haproxy",
"logger_init",
]
]
2 changes: 1 addition & 1 deletion src/functions/certbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,4 @@ def find_missing_certificates(self, hosts):
cert_status = self.get_certificate_status(host)
if cert_status != "ok":
self.freeze_issue[host] = self.retry_count
logger_certbot.debug(f"Freeze issuing ssl for {host} due failure. The certificate is {cert_status}")
logger_certbot.debug(f"Freeze issuing ssl for {host} due failure. The certificate is {cert_status}")
2 changes: 1 addition & 1 deletion src/functions/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ def www_path(cls):
"""Path to the web assets directory (dashboard, static files)."""
return f"{cls.base_path}/www"

DASHBOARD_SERVER_PORT = 9190
DASHBOARD_SERVER_PORT = 9190
2 changes: 1 addition & 1 deletion src/functions/container_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,4 @@ def _yaml_to_env(yaml_config):
else:
env_value = str(config_value)

os.environ[env_key] = env_value
os.environ[env_key] = env_value
2 changes: 1 addition & 1 deletion src/functions/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ def filter(self, record: logging.LogRecord) -> int:
if sanitized != record.getMessage():
record.msg = sanitized
record.args = ()
return 1
return 1
2 changes: 1 addition & 1 deletion src/functions/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@ def run_bash(log_source, command, log_output=True, return_result=True):
return [return_code, output]
except Exception as e:
log_source.error(f"{e}")
return [-99, e]
return [-99, e]
4 changes: 2 additions & 2 deletions src/functions/haproxy.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import multiprocessing
import os
import shlex
import shutil
import subprocess
import sys
import time
import multiprocessing
from typing import Final

import psutil
Expand Down Expand Up @@ -149,4 +149,4 @@ def get_custom_config_files(self):
for file in os.listdir(self.custom_config_folder):
if file.endswith(".cfg"):
files[os.path.join(self.custom_config_folder, file)] = os.path.getmtime(os.path.join(self.custom_config_folder, file))
return dict(sorted(files.items(), key=lambda t: t[0]))
return dict(sorted(files.items(), key=lambda t: t[0]))
2 changes: 1 addition & 1 deletion src/functions/loggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
Functions.setup_log(logger_init)
Functions.setup_log(logger_haproxy)
Functions.setup_log(logger_easyhaproxy)
Functions.setup_log(logger_certbot)
Functions.setup_log(logger_certbot)
2 changes: 1 addition & 1 deletion src/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
"PluginResult",
"PluginType",
"ResourceRequest",
]
]
2 changes: 1 addition & 1 deletion src/plugins/builtin/cloudflare.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from functions import logger_easyhaproxy, Consts
from functions import Consts, logger_easyhaproxy
from plugins import InitializationResult, PluginContext, PluginInterface, PluginResult, PluginType, ResourceRequest


Expand Down
2 changes: 1 addition & 1 deletion src/plugins/builtin/jwt_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from functions import Functions, logger_easyhaproxy, Consts
from functions import Consts, Functions, logger_easyhaproxy
from plugins import InitializationResult, PluginContext, PluginInterface, PluginResult, PluginType, ResourceRequest


Expand Down
2 changes: 1 addition & 1 deletion src/plugins/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ def initialize(self) -> InitializationResult:
Returns:
InitializationResult with resource requests
"""
return InitializationResult()
return InitializationResult()
2 changes: 1 addition & 1 deletion src/plugins/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,4 @@ def _handle_error(self, message: str) -> None:
self.logger.error(message)
raise RuntimeError(message)
else:
self.logger.warning(message)
self.logger.warning(message)
2 changes: 1 addition & 1 deletion src/plugins/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ class PluginResult:
modified_easymapping: list | None = None # Modified easymapping structure
metadata: dict[str, Any] = field(default_factory=dict) # Plugin metadata for logging
global_configs: list[str] = field(default_factory=list) # HAProxy global-level configs
defaults_configs: list[str] = field(default_factory=list) # HAProxy defaults-level configs
defaults_configs: list[str] = field(default_factory=list) # HAProxy defaults-level configs
2 changes: 1 addition & 1 deletion src/processor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
from .static import Static
from .swarm import Swarm

__all__ = ["ProcessorInterface", "Static", "Docker", "Swarm", "Kubernetes"]
__all__ = ["ProcessorInterface", "Static", "Docker", "Swarm", "Kubernetes"]
2 changes: 1 addition & 1 deletion src/processor/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ def inspect_network(self):
container = self.client.containers.get(container.name) # refresh object

ip_address = container.attrs["NetworkSettings"]["Networks"][ha_proxy_network_name]["IPAddress"]
self.parsed_object[ip_address] = container.labels
self.parsed_object[ip_address] = container.labels
6 changes: 3 additions & 3 deletions src/processor/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ def __init__(self, filename=None):

@staticmethod
def factory(mode):
from .static import Static
from .docker import Docker
from .swarm import Swarm
from .kubernetes import Kubernetes
from .static import Static
from .swarm import Swarm

if mode == ProcessorInterface.STATIC:
return Static(ProcessorInterface.static_file)
Expand Down Expand Up @@ -84,4 +84,4 @@ def save_config(self, filename):

def save_certs(self, path):
for cert in self.get_certs():
Functions.save(f"{path}/{cert}", self.get_certs(cert))
Functions.save(f"{path}/{cert}", self.get_certs(cert))
2 changes: 1 addition & 1 deletion src/processor/kubernetes.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,4 +462,4 @@ def inspect_network(self):

# Update ingress status if enabled
if env_config['update_ingress_status'] and ingress_addresses:
self._update_ingress_status(ingress, ingress_addresses)
self._update_ingress_status(ingress, ingress_addresses)
2 changes: 1 addition & 1 deletion src/processor/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,4 @@ def _convert_yaml_to_labels(self):

def parse(self):
"""Create HaproxyConfigGenerator with YAML config merged into env vars"""
self.cfg = HaproxyConfigGenerator(ContainerEnv.read(self.static_content))
self.cfg = HaproxyConfigGenerator(ContainerEnv.read(self.static_content))
2 changes: 1 addition & 1 deletion src/processor/swarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ def inspect_network(self):
service.update(networks=network_list)
continue # skip to the next service to give time to update the network

self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"]
self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"]
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import os
import shutil
import pytest

import pytest

# Fixed temporary directory for all tests — predictable so expected fixtures can reference it
_test_session_dir = "/tmp/easyhaproxy_test"
Expand Down Expand Up @@ -65,4 +65,4 @@ def pytest_sessionfinish(session, exitstatus):
shutil.rmtree(_test_session_dir)
except Exception:
# Ignore cleanup errors
pass
pass
14 changes: 6 additions & 8 deletions tests/test_certbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
import os
import sys
import tempfile
import time
from datetime import datetime, timedelta
from unittest.mock import MagicMock, Mock, mock_open, patch
from unittest.mock import Mock, patch

from OpenSSL import crypto

# Add src to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from functions import Certbot, ContainerEnv, Functions
from functions import Certbot, Functions


class TestCertbotStaticMethods:
Expand Down Expand Up @@ -215,7 +213,7 @@ def test_certbot_init_basic(self):
assert certbot.freeze_issue == {}
assert certbot.retry_count == 60 # default
assert certbot.certbot_preferred_challenges == "http" # default
assert certbot.certbot_manual_auth_hook == False # default
assert certbot.certbot_manual_auth_hook is False # default

def test_certbot_init_with_eab(self):
"""Test Certbot initialization with EAB credentials"""
Expand Down Expand Up @@ -374,7 +372,7 @@ def test_merge_certificate(self):
try:
Certbot.merge_certificate(cert, key, filename)

with open(filename, 'r') as f:
with open(filename) as f:
content = f.read()

assert content == cert + key
Expand Down Expand Up @@ -648,7 +646,7 @@ def test_find_live_certificates_with_certs(self):
# Verify merged certificate was created
merged_file = os.path.join(output_dir, "example.com.pem")
if os.path.exists(merged_file):
with open(merged_file, 'r') as f:
with open(merged_file) as f:
content = f.read()
assert content == cert_content + key_content

Expand Down Expand Up @@ -885,4 +883,4 @@ def test_webhook_with_multiple_providers(self, mock_run_bash):
assert '--eab-kid "zerossl-kid"' in command
assert '--eab-hmac-key "zerossl-hmac"' in command
assert '--preferred-challenges dns' in command
assert "--manual-auth-hook '/hooks/route53-dns.py'" in command
assert "--manual-auth-hook '/hooks/route53-dns.py'" in command
4 changes: 2 additions & 2 deletions tests/test_certbot_haproxy_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def test_certbot_backend_format(self):
# Should only have mode and server lines
assert 'mode http' in certbot_backend_lines
assert 'server certbot 127.0.0.1:2080' in certbot_backend_lines
assert len([l for l in certbot_backend_lines if l]) == 2 # Only 2 non-empty lines
assert len([line for line in certbot_backend_lines if line]) == 2 # Only 2 non-empty lines

def test_certbot_acl_order_before_use_backend(self):
"""Test that ACL definitions come before use_backend rules"""
Expand All @@ -411,4 +411,4 @@ def test_certbot_acl_order_before_use_backend(self):

assert acl_pos > 0, "ACL not found"
assert use_backend_pos > 0, "use_backend not found"
assert acl_pos < use_backend_pos, "ACL must be defined before use_backend"
assert acl_pos < use_backend_pos, "ACL must be defined before use_backend"
2 changes: 1 addition & 1 deletion tests/test_containerenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ def test_yaml_to_env_combined():
try:
result = ContainerEnv.read(yaml_config)
# Check the result
assert result["customerrors"] == True
assert result["customerrors"] is True
assert result["ssl_mode"] == "strict"
assert result["logLevel"]["easyhaproxy"] == Functions.WARN
assert result["logLevel"]["haproxy"] == Functions.ERROR
Expand Down
3 changes: 1 addition & 2 deletions tests/test_daemonize.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
from functions import Consts

from functions import DaemonizeHAProxy
from functions import Consts, DaemonizeHAProxy

BIN = DaemonizeHAProxy.get_haproxy_bin()

Expand Down
3 changes: 1 addition & 2 deletions tests/test_kubernetes_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import os
import sys
from unittest.mock import MagicMock, Mock
from types import SimpleNamespace

# Add src to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Expand Down Expand Up @@ -416,4 +415,4 @@ def test_k8s_secret_api_key_variations(self):
assert len(parsed) == 1
ingress_data = list(parsed.values())[0]

assert "easyhaproxy.test-example-com_8080.plugin.api_plugin.api_key" in ingress_data
assert "easyhaproxy.test-example-com_8080.plugin.api_plugin.api_key" in ingress_data
6 changes: 3 additions & 3 deletions tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def test_cloudflare_plugin_with_base64_ip_list(self):

# Verify file was written with our IPs
assert os.path.exists("/tmp/test_cloudflare_ips.lst")
with open("/tmp/test_cloudflare_ips.lst", 'r') as f:
with open("/tmp/test_cloudflare_ips.lst") as f:
content = f.read()

assert "10.0.0.0/8" in content
Expand Down Expand Up @@ -275,7 +275,7 @@ def test_cloudflare_plugin_ip_list_precedence(self):
result = plugin.process(context)

# Verify file contains ONLY our IP, not built-in IPs
with open("/tmp/test_precedence.lst", 'r') as f:
with open("/tmp/test_precedence.lst") as f:
content = f.read()

assert "127.0.0.1" in content
Expand Down Expand Up @@ -317,7 +317,7 @@ def test_cloudflare_plugin_invalid_base64(self):

# Verify fallback to built-in IPs
assert os.path.exists("/tmp/test_invalid.lst")
with open("/tmp/test_invalid.lst", 'r') as f:
with open("/tmp/test_invalid.lst") as f:
content = f.read()

assert "173.245.48.0/20" in content # Built-in IP
Expand Down
Loading
Loading