Some fixes and documentation
- Introduced ACME HTTP-01 challenge documentation. - Added Docker Compose example for HTTP-01 setup. - Updated `get_haproxy_command` logic with improved PID validation using `psutil`. - Enhanced logging with a new `SingleLineNonEmptyFilter`. - Expanded tests for daemonize functionalities. - Updated dependencies to include `psutil`.
This commit is contained in:
parent
b4b02916af
commit
ea9e4ee7c8
8 changed files with 125 additions and 24 deletions
|
|
@ -2,12 +2,12 @@ import os
|
|||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import psutil
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from multiprocessing import Process
|
||||
from typing import Final
|
||||
|
||||
import requests
|
||||
from OpenSSL import crypto
|
||||
|
||||
|
|
@ -120,6 +120,7 @@ class Functions:
|
|||
log_source_handler = logging.StreamHandler(sys.stdout)
|
||||
log_source_formatter = logging.Formatter('%(name)s [%(asctime)s] %(levelname)s - %(message)s')
|
||||
log_source_handler.setFormatter(log_source_formatter)
|
||||
log_source_handler.addFilter(SingleLineNonEmptyFilter())
|
||||
source.setLevel(selected_level)
|
||||
source.addHandler(log_source_handler)
|
||||
return selected_level
|
||||
|
|
@ -202,18 +203,26 @@ class DaemonizeHAProxy:
|
|||
if len(list(self.get_custom_config_files().keys())) != 0:
|
||||
custom_config_files = "-f %s" % self.custom_config_folder
|
||||
|
||||
if action == DaemonizeHAProxy.HAPROXY_START:
|
||||
if action == DaemonizeHAProxy.HAPROXY_START or not os.path.exists(pid_file):
|
||||
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -S /var/run/haproxy.sock" % (custom_config_files, pid_file)
|
||||
else:
|
||||
return_code, output = Functions().run_bash(loggerHaproxy, "cat %s" % pid_file, log_output=False)
|
||||
pid = "".join(output)
|
||||
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid_file, pid)
|
||||
pid = "".join(output).rstrip()
|
||||
if psutil.pid_exists(int(pid)):
|
||||
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid_file, pid)
|
||||
else:
|
||||
os.unlink(pid_file)
|
||||
loggerHaproxy.warning(
|
||||
"PID file %s does not exist. Restarting haproxy instead of reload." % pid_file
|
||||
)
|
||||
return self.get_haproxy_command(DaemonizeHAProxy.HAPROXY_START, pid_file)
|
||||
|
||||
def __prepare(self, command):
|
||||
if not isinstance(command, (list, tuple)):
|
||||
command = shlex.split(command)
|
||||
|
||||
try:
|
||||
loggerHaproxy.debug("HAPROXY command: %s" % command)
|
||||
self.process = subprocess.Popen(command,
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
|
|
@ -228,7 +237,7 @@ class DaemonizeHAProxy:
|
|||
try:
|
||||
with self.process.stdout:
|
||||
for line in iter(self.process.stdout.readline, b''):
|
||||
loggerHaproxy.info(line)
|
||||
loggerHaproxy.info(line.rstrip())
|
||||
|
||||
return_code = self.process.wait()
|
||||
loggerHaproxy.debug("Return code %s" % return_code)
|
||||
|
|
@ -354,6 +363,11 @@ class Certbot:
|
|||
if self.certbot_manual_auth_hook:
|
||||
certbot_certonly += ' --manual --manual-auth-hook \'{hook}\''.format(hook=self.certbot_manual_auth_hook)
|
||||
|
||||
if loggerCertbot.level == logging.DEBUG:
|
||||
certbot_certonly += ' -v'
|
||||
|
||||
loggerCertbot.debug("certbot_certonly: %s" % certbot_certonly)
|
||||
|
||||
ret_reload = False
|
||||
return_code_issue = 0
|
||||
return_code_renew = 0
|
||||
|
|
@ -424,6 +438,38 @@ class Certbot:
|
|||
self.freeze_issue[host] = self.retry_count
|
||||
loggerCertbot.debug("Freeze issuing ssl for %s due failure. The certificate is %s" % (host, cert_status))
|
||||
|
||||
|
||||
|
||||
class SingleLineNonEmptyFilter(logging.Filter):
|
||||
"""
|
||||
Logging filter that ensures messages are single-line and non-empty.
|
||||
- Collapses newlines into spaces and strips surrounding whitespace.
|
||||
- Drops the record if the resulting message is empty.
|
||||
"""
|
||||
def filter(self, record: logging.LogRecord) -> int:
|
||||
try:
|
||||
msg = record.getMessage()
|
||||
except Exception:
|
||||
# If formatting fails, drop the record
|
||||
return 0
|
||||
|
||||
# Convert any non-string to string representation
|
||||
if not isinstance(msg, str):
|
||||
msg = str(msg)
|
||||
|
||||
# Collapse multi-line to single line and trim
|
||||
sanitized = " ".join(msg.splitlines()).strip()
|
||||
|
||||
if sanitized == "":
|
||||
return 0
|
||||
|
||||
# If we changed the message, update the record and clear args
|
||||
if sanitized != record.getMessage():
|
||||
record.msg = sanitized
|
||||
record.args = ()
|
||||
return 1
|
||||
|
||||
|
||||
# ####################################################################################################################
|
||||
# Setup Global Log
|
||||
loggerInit = logging.getLogger(Functions.INIT_LOG)
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ pytest
|
|||
docker
|
||||
kubernetes
|
||||
deepdiff
|
||||
pyopenssl
|
||||
pyopenssl
|
||||
psutil
|
||||
|
|
@ -11,11 +11,14 @@
|
|||
acl is_rule_{{ host }}_2 hdr(host) -i {{ k }}:{{ o["port"] }}
|
||||
{% if certbot %}
|
||||
acl is_certbot_{{ host }} path_beg /.well-known/acme-challenge/
|
||||
use_backend certbot_backend if is_certbot_{{ host }} is_rule_{{ host }}_1 OR is_certbot_{{ host }} is_rule_{{ host }}_2
|
||||
{% endif %}
|
||||
{% if o["hosts"][k]["redirect_ssl"] %}
|
||||
http-request redirect scheme https code 301 if {% if certbot %}!is_certbot_{{ host }} {% endif %}is_rule_{{ host }}_1 OR {% if certbot %}!is_certbot_{{ host }} {% endif %}is_rule_{{ host }}_2
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{% if certbot %}
|
||||
use_backend certbot_backend if is_certbot_{{ host }} is_rule_{{ host }}_1 OR is_certbot_{{ host }} is_rule_{{ host }}_2
|
||||
{% endif %}
|
||||
{% if not o["hosts"][k]["redirect_ssl"] %}
|
||||
use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ frontend http_in_80
|
|||
acl is_rule_test_example_org_80_1 hdr(host) -i test.example.org
|
||||
acl is_rule_test_example_org_80_2 hdr(host) -i test.example.org:80
|
||||
acl is_certbot_test_example_org_80 path_beg /.well-known/acme-challenge/
|
||||
use_backend certbot_backend if is_certbot_test_example_org_80 is_rule_test_example_org_80_1 OR is_certbot_test_example_org_80 is_rule_test_example_org_80_2
|
||||
http-request redirect scheme https code 301 if !is_certbot_test_example_org_80 is_rule_test_example_org_80_1 OR !is_certbot_test_example_org_80 is_rule_test_example_org_80_2
|
||||
use_backend certbot_backend if is_certbot_test_example_org_80 is_rule_test_example_org_80_1 OR is_certbot_test_example_org_80 is_rule_test_example_org_80_2
|
||||
|
||||
acl is_rule_test2_example_org_80_1 hdr(host) -i test2.example.org
|
||||
acl is_rule_test2_example_org_80_2 hdr(host) -i test2.example.org:80
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import os
|
||||
|
||||
import psutil
|
||||
|
||||
from functions import DaemonizeHAProxy, Functions
|
||||
|
||||
|
||||
|
|
@ -17,10 +19,31 @@ def test_daemonize_haproxy_get_haproxy_command_start():
|
|||
command = daemon.get_haproxy_command(DaemonizeHAProxy.HAPROXY_START)
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock"
|
||||
|
||||
def test_daemonize_haproxy_get_haproxy_command_reload():
|
||||
def test_daemonize_haproxy_get_haproxy_command_reload_nopid():
|
||||
daemon = DaemonizeHAProxy()
|
||||
command = daemon.get_haproxy_command(DaemonizeHAProxy.HAPROXY_RELOAD)
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf "
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock"
|
||||
|
||||
def test_daemonize_haproxy_get_haproxy_command_reload_pidinvalid():
|
||||
daemon = DaemonizeHAProxy()
|
||||
try:
|
||||
with open("/tmp/temp.pid", 'w') as file:
|
||||
file.write("-1001")
|
||||
command = daemon.get_haproxy_command(DaemonizeHAProxy.HAPROXY_RELOAD, "/tmp/temp.pid")
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /tmp/temp.pid -S /var/run/haproxy.sock"
|
||||
finally:
|
||||
assert not os.path.exists("/tmp/temp.pid")
|
||||
|
||||
def test_daemonize_haproxy_get_haproxy_command_reload_existing_pin():
|
||||
daemon = DaemonizeHAProxy()
|
||||
try:
|
||||
with open("/tmp/temp.pid", 'w') as file:
|
||||
file.write("1")
|
||||
command = daemon.get_haproxy_command(DaemonizeHAProxy.HAPROXY_RELOAD, "/tmp/temp.pid")
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /tmp/temp.pid -x /var/run/haproxy.sock -sf 1"
|
||||
finally:
|
||||
assert os.path.exists("/tmp/temp.pid")
|
||||
os.unlink("/tmp/temp.pid")
|
||||
|
||||
def test_daemonize_haproxy2_check_config():
|
||||
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||
|
|
@ -34,15 +57,3 @@ def test_daemonize_haproxy2_get_haproxy_command_start():
|
|||
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||
command = daemon.get_haproxy_command(DaemonizeHAProxy.HAPROXY_START)
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures")
|
||||
|
||||
|
||||
def test_daemonize_haproxy2_get_haproxy_command_reload():
|
||||
tmp_pid_file = "/tmp/tmp_pid.txt"
|
||||
Functions.save(tmp_pid_file, "10")
|
||||
|
||||
try:
|
||||
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||
command = daemon.get_haproxy_command(DaemonizeHAProxy.HAPROXY_RELOAD, tmp_pid_file)
|
||||
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p %s -x /var/run/haproxy.sock -sf %s" % (os.path.dirname(__file__) + "/fixtures", tmp_pid_file, 10)
|
||||
finally:
|
||||
os.remove(tmp_pid_file)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue