commit
64bc49b0be
7 changed files with 166 additions and 175 deletions
|
|
@ -1,14 +1,16 @@
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from OpenSSL import crypto
|
from OpenSSL import crypto
|
||||||
|
|
||||||
|
|
||||||
class ContainerEnv:
|
class ContainerEnv:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def read():
|
def read():
|
||||||
|
|
@ -82,7 +84,7 @@ class ContainerEnv:
|
||||||
env_vars["certbot"]["eab_hmac_key"] = os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = resp["eab_hmac_key"]
|
env_vars["certbot"]["eab_hmac_key"] = os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = resp["eab_hmac_key"]
|
||||||
else:
|
else:
|
||||||
del os.environ["EASYHAPROXY_CERTBOT_EMAIL"]
|
del os.environ["EASYHAPROXY_CERTBOT_EMAIL"]
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Could not obtain ZeroSSL credentials " + resp["error"]["type"])
|
loggerCertbot.error("Could not obtain ZeroSSL credentials " + resp["error"]["type"])
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_CERTBOT_SERVER'] = env_vars["certbot"]["server"]
|
os.environ['EASYHAPROXY_CERTBOT_SERVER'] = env_vars["certbot"]["server"]
|
||||||
|
|
||||||
|
|
@ -90,34 +92,37 @@ class ContainerEnv:
|
||||||
|
|
||||||
|
|
||||||
class Functions:
|
class Functions:
|
||||||
HAPROXY_LOG = "HAPROXY"
|
HAPROXY_LOG: Final[str] = "HAPROXY"
|
||||||
EASYHAPROXY_LOG = "EASYHAPROXY"
|
EASYHAPROXY_LOG: Final[str] = "EASYHAPROXY"
|
||||||
CERTBOT_LOG = "CERTBOT"
|
CERTBOT_LOG: Final[str] = "CERTBOT"
|
||||||
INIT_LOG = "INIT"
|
INIT_LOG: Final[str] = "INIT"
|
||||||
|
|
||||||
TRACE = "TRACE"
|
TRACE: Final[str] = "TRACE"
|
||||||
DEBUG = "DEBUG"
|
DEBUG: Final[str] = "DEBUG"
|
||||||
INFO = "INFO"
|
INFO: Final[str] = "INFO"
|
||||||
WARN = "WARN"
|
WARN: Final[str] = "WARN"
|
||||||
ERROR = "ERROR"
|
ERROR: Final[str] = "ERROR"
|
||||||
FATAL = "FATAL"
|
FATAL: Final[str] = "FATAL"
|
||||||
|
|
||||||
debug_log = None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def skip_log(source, log_level_str):
|
def setup_log(source):
|
||||||
level = os.getenv("%s_LOG_LEVEL" % (source.upper()), "").upper()
|
level = os.getenv("%s_LOG_LEVEL" % (source.name.upper()), "").upper()
|
||||||
level_importance = {
|
level_importance = {
|
||||||
Functions.TRACE: 0,
|
Functions.TRACE: logging.DEBUG,
|
||||||
Functions.DEBUG: 1,
|
Functions.DEBUG: logging.DEBUG,
|
||||||
Functions.INFO: 2,
|
Functions.INFO: logging.INFO,
|
||||||
Functions.WARN: 3,
|
Functions.WARN: logging.WARNING,
|
||||||
Functions.ERROR: 4,
|
Functions.ERROR: logging.ERROR,
|
||||||
Functions.FATAL: 5
|
Functions.FATAL: logging.FATAL
|
||||||
}
|
}
|
||||||
level_required = 1 if level not in level_importance else level_importance[level]
|
selected_level = level_importance[level] if level in level_importance else logging.INFO
|
||||||
level_asked = 1 if log_level_str.upper() not in level_importance else level_importance[log_level_str.upper()]
|
|
||||||
return level_asked < level_required
|
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)
|
||||||
|
source.setLevel(selected_level)
|
||||||
|
source.addHandler(log_source_handler)
|
||||||
|
return selected_level
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(filename):
|
def load(filename):
|
||||||
|
|
@ -130,24 +135,7 @@ class Functions:
|
||||||
file.write(contents)
|
file.write(contents)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log(source, level, message):
|
def run_bash(log_source, command, log_output=True, return_result=True):
|
||||||
if message is None or message == "":
|
|
||||||
return
|
|
||||||
|
|
||||||
if Functions.skip_log(source, level):
|
|
||||||
return
|
|
||||||
|
|
||||||
if not isinstance(message, (list, tuple)):
|
|
||||||
message = [message]
|
|
||||||
|
|
||||||
for line in message:
|
|
||||||
log = "[%s] %s [%s]: %s" % (source, datetime.now().strftime("%x %X"), level, line.rstrip())
|
|
||||||
print(log)
|
|
||||||
if Functions.debug_log is not None:
|
|
||||||
Functions.debug_log.append(log)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def run_bash(source, command, log_output=True, return_result=True):
|
|
||||||
if not isinstance(command, (list, tuple)):
|
if not isinstance(command, (list, tuple)):
|
||||||
command = shlex.split(command)
|
command = shlex.split(command)
|
||||||
|
|
||||||
|
|
@ -161,22 +149,24 @@ class Functions:
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
line = process.stdout.readline().rstrip()
|
line = process.stdout.readline().rstrip()
|
||||||
|
error_line = process.stderr.readline().rstrip()
|
||||||
output.append(line) if return_result else None
|
output.append(line) if return_result else None
|
||||||
Functions.log(source, Functions.INFO, line) if log_output else None
|
log_source.info(line) if log_output and len(line) > 0 else None
|
||||||
Functions.log(source, Functions.WARN, process.stderr.readline())
|
log_source.warning(error_line) if len(error_line) > 0 else None
|
||||||
return_code = process.poll()
|
return_code = process.poll()
|
||||||
if return_code is not None:
|
if return_code is not None:
|
||||||
lines = []
|
lines = []
|
||||||
|
error_line = process.stderr.readline().rstrip()
|
||||||
for line in process.stdout.readlines():
|
for line in process.stdout.readlines():
|
||||||
output.append(line.rstrip()) if return_result else None
|
output.append(line.rstrip()) if return_result else None
|
||||||
lines.append(line.rstrip())
|
lines.append(line.rstrip())
|
||||||
Functions.log(source, Functions.INFO, lines) if log_output else None
|
log_source.info(lines) if log_output and len(lines) > 0 else None
|
||||||
Functions.log(source, Functions.WARN, process.stderr.readlines())
|
log_source.warning(error_line) if len(error_line) > 0 else None
|
||||||
break
|
break
|
||||||
|
|
||||||
return [return_code, output]
|
return [return_code, output]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(source, Functions.ERROR, "%s" % e)
|
log_source.error("%s" % e)
|
||||||
return [-99, e]
|
return [-99, e]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -189,6 +179,9 @@ class Consts:
|
||||||
|
|
||||||
|
|
||||||
class DaemonizeHAProxy:
|
class DaemonizeHAProxy:
|
||||||
|
HAPROXY_START: Final[str] = "start"
|
||||||
|
HAPROXY_RELOAD: Final[str] = "reload"
|
||||||
|
|
||||||
def __init__(self, custom_config_folder = None):
|
def __init__(self, custom_config_folder = None):
|
||||||
self.process = None
|
self.process = None
|
||||||
self.thread = None
|
self.thread = None
|
||||||
|
|
@ -207,17 +200,16 @@ class DaemonizeHAProxy:
|
||||||
def get_haproxy_command(self, action, pid_file="/run/haproxy.pid"):
|
def get_haproxy_command(self, action, pid_file="/run/haproxy.pid"):
|
||||||
custom_config_files = ""
|
custom_config_files = ""
|
||||||
if len(list(self.get_custom_config_files().keys())) != 0:
|
if len(list(self.get_custom_config_files().keys())) != 0:
|
||||||
custom_config_files = "-f %s" % (self.custom_config_folder)
|
custom_config_files = "-f %s" % self.custom_config_folder
|
||||||
|
|
||||||
if action == "start":
|
if action == DaemonizeHAProxy.HAPROXY_START:
|
||||||
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -S /var/run/haproxy.sock" % (custom_config_files, 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:
|
else:
|
||||||
return_code, output = Functions().run_bash(Functions.HAPROXY_LOG, "cat %s" % pid_file, log_output=False)
|
return_code, output = Functions().run_bash(loggerHaproxy, "cat %s" % pid_file, log_output=False)
|
||||||
pid = "".join(output)
|
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)
|
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)
|
||||||
|
|
||||||
def __prepare(self, command):
|
def __prepare(self, command):
|
||||||
source = Functions.HAPROXY_LOG
|
|
||||||
if not isinstance(command, (list, tuple)):
|
if not isinstance(command, (list, tuple)):
|
||||||
command = shlex.split(command)
|
command = shlex.split(command)
|
||||||
|
|
||||||
|
|
@ -230,20 +222,19 @@ class DaemonizeHAProxy:
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(source, Functions.ERROR, "%s" % e)
|
loggerHaproxy.error("%s" % e)
|
||||||
|
|
||||||
def __start(self):
|
def __start(self):
|
||||||
source = Functions.HAPROXY_LOG
|
|
||||||
try:
|
try:
|
||||||
with self.process.stdout:
|
with self.process.stdout:
|
||||||
for line in iter(self.process.stdout.readline, b''):
|
for line in iter(self.process.stdout.readline, b''):
|
||||||
Functions.log(source, Functions.INFO, line)
|
loggerHaproxy.info(line)
|
||||||
|
|
||||||
return_code = self.process.wait()
|
return_code = self.process.wait()
|
||||||
Functions.log(source, Functions.DEBUG, "Return code %s" % return_code)
|
loggerHaproxy.debug("Return code %s" % return_code)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(source, Functions.ERROR, "%s" % e)
|
loggerHaproxy.error("%s" % e)
|
||||||
|
|
||||||
def is_alive(self):
|
def is_alive(self):
|
||||||
return self.thread.is_alive()
|
return self.thread.is_alive()
|
||||||
|
|
@ -330,14 +321,13 @@ class Certbot:
|
||||||
elif host in self.freeze_issue:
|
elif host in self.freeze_issue:
|
||||||
freeze_count = self.freeze_issue.pop(host, 0)
|
freeze_count = self.freeze_issue.pop(host, 0)
|
||||||
if freeze_count > 0:
|
if freeze_count > 0:
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG,
|
loggerCertbot.debug("Waiting freezing period (%d) for %s due previous errors" % (freeze_count, host))
|
||||||
"Waiting freezing period (%d) for %s due previous errors" % (freeze_count, host))
|
|
||||||
self.freeze_issue[host] = freeze_count-1
|
self.freeze_issue[host] = freeze_count-1
|
||||||
elif cert_status == "not_found" or cert_status == "expired":
|
elif cert_status == "not_found" or cert_status == "expired":
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "[%s] Request new certificate for %s" % (cert_status, host))
|
loggerCertbot.debug("[%s] Request new certificate for %s" % (cert_status, host))
|
||||||
request_certs.append(host_arg)
|
request_certs.append(host_arg)
|
||||||
elif cert_status == "expiring":
|
elif cert_status == "expiring":
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "[%s] Renew certificate for %s" % (cert_status, host))
|
loggerCertbot.debug("[%s] Renew certificate for %s" % (cert_status, host))
|
||||||
renew_certs.append(host_arg)
|
renew_certs.append(host_arg)
|
||||||
|
|
||||||
certbot_certonly = ('/usr/bin/certbot certonly {acme_server}'
|
certbot_certonly = ('/usr/bin/certbot certonly {acme_server}'
|
||||||
|
|
@ -368,11 +358,11 @@ class Certbot:
|
||||||
return_code_issue = 0
|
return_code_issue = 0
|
||||||
return_code_renew = 0
|
return_code_renew = 0
|
||||||
if len(request_certs) > 0:
|
if len(request_certs) > 0:
|
||||||
return_code_issue, output = Functions.run_bash(Functions.CERTBOT_LOG, certbot_certonly, return_result=False)
|
return_code_issue, output = Functions.run_bash(loggerCertbot, certbot_certonly, return_result=False)
|
||||||
ret_reload = True
|
ret_reload = True
|
||||||
|
|
||||||
if len(renew_certs) > 0:
|
if len(renew_certs) > 0:
|
||||||
return_code_renew, output = Functions.run_bash(Functions.CERTBOT_LOG, "/usr/bin/certbot renew", return_result=False)
|
return_code_renew, output = Functions.run_bash(loggerCertbot, "/usr/bin/certbot renew", return_result=False)
|
||||||
ret_reload = True
|
ret_reload = True
|
||||||
|
|
||||||
if ret_reload:
|
if ret_reload:
|
||||||
|
|
@ -385,7 +375,7 @@ class Certbot:
|
||||||
|
|
||||||
return ret_reload
|
return ret_reload
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "%s" % e)
|
loggerCertbot.error("%s" % e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -420,7 +410,7 @@ class Certbot:
|
||||||
elif (expiration_after - current_time) // (24 * 3600) <= 15:
|
elif (expiration_after - current_time) // (24 * 3600) <= 15:
|
||||||
return "expiring"
|
return "expiring"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Certificate %s error %s" % (host, e))
|
loggerCertbot.error("Certificate %s error %s" % (host, e))
|
||||||
return "error"
|
return "error"
|
||||||
|
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
@ -432,4 +422,15 @@ class Certbot:
|
||||||
cert_status = self.get_certificate_status(host)
|
cert_status = self.get_certificate_status(host)
|
||||||
if cert_status != "ok":
|
if cert_status != "ok":
|
||||||
self.freeze_issue[host] = self.retry_count
|
self.freeze_issue[host] = self.retry_count
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Freeze issuing ssl for %s due failure. The certificate is %s" % (host, cert_status))
|
loggerCertbot.debug("Freeze issuing ssl for %s due failure. The certificate is %s" % (host, cert_status))
|
||||||
|
|
||||||
|
# ####################################################################################################################
|
||||||
|
# Setup Global Log
|
||||||
|
loggerInit = logging.getLogger(Functions.INIT_LOG)
|
||||||
|
loggerHaproxy = logging.getLogger(Functions.HAPROXY_LOG)
|
||||||
|
loggerEasyHaproxy = logging.getLogger(Functions.EASYHAPROXY_LOG)
|
||||||
|
loggerCertbot = logging.getLogger(Functions.CERTBOT_LOG)
|
||||||
|
Functions.setup_log(loggerInit)
|
||||||
|
Functions.setup_log(loggerHaproxy)
|
||||||
|
Functions.setup_log(loggerEasyHaproxy)
|
||||||
|
Functions.setup_log(loggerCertbot)
|
||||||
|
|
|
||||||
43
src/main.py
43
src/main.py
|
|
@ -1,8 +1,10 @@
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from deepdiff import DeepDiff
|
from deepdiff import DeepDiff
|
||||||
|
|
||||||
from functions import Functions, DaemonizeHAProxy, Certbot, Consts
|
from functions import Functions, DaemonizeHAProxy, Certbot, Consts, loggerInit, loggerEasyHaproxy, loggerHaproxy, \
|
||||||
|
loggerCertbot
|
||||||
from processor import ProcessorInterface
|
from processor import ProcessorInterface
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -17,14 +19,13 @@ def start():
|
||||||
processor_obj.save_config(Consts.haproxy_config)
|
processor_obj.save_config(Consts.haproxy_config)
|
||||||
processor_obj.save_certs(Consts.certs_haproxy)
|
processor_obj.save_certs(Consts.certs_haproxy)
|
||||||
certbot_certs_found = processor_obj.get_certbot_hosts()
|
certbot_certs_found = processor_obj.get_certbot_hosts()
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG,
|
loggerEasyHaproxy.info('Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to run after save_config
|
||||||
'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to run after save_config
|
loggerEasyHaproxy.debug('Object Found: %s' % (processor_obj.get_parsed_object()))
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE, 'Object Found: %s' % (processor_obj.get_parsed_object()))
|
|
||||||
|
|
||||||
old_haproxy = None
|
old_haproxy = None
|
||||||
haproxy = DaemonizeHAProxy()
|
haproxy = DaemonizeHAProxy()
|
||||||
current_custom_config_files = haproxy.get_custom_config_files()
|
current_custom_config_files = haproxy.get_custom_config_files()
|
||||||
haproxy.haproxy("start")
|
haproxy.haproxy(DaemonizeHAProxy.HAPROXY_START)
|
||||||
haproxy.sleep()
|
haproxy.sleep()
|
||||||
|
|
||||||
certbot = Certbot(Consts.certs_certbot)
|
certbot = Certbot(Consts.certs_certbot)
|
||||||
|
|
@ -37,41 +38,39 @@ def start():
|
||||||
old_parsed = processor_obj.get_parsed_object()
|
old_parsed = processor_obj.get_parsed_object()
|
||||||
processor_obj.refresh()
|
processor_obj.refresh()
|
||||||
if certbot.check_certificates(certbot_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive() or DeepDiff(current_custom_config_files, haproxy.get_custom_config_files()) != {}:
|
if certbot.check_certificates(certbot_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive() or DeepDiff(current_custom_config_files, haproxy.get_custom_config_files()) != {}:
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'New configuration found. Reloading...')
|
loggerEasyHaproxy.info('New configuration found. Reloading...')
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE,
|
loggerEasyHaproxy.debug('Object Found: %s' % (processor_obj.get_parsed_object()))
|
||||||
'Object Found: %s' % (processor_obj.get_parsed_object()))
|
|
||||||
processor_obj.save_config(Consts.haproxy_config)
|
processor_obj.save_config(Consts.haproxy_config)
|
||||||
processor_obj.save_certs(Consts.certs_haproxy)
|
processor_obj.save_certs(Consts.certs_haproxy)
|
||||||
certbot_certs_found = processor_obj.get_certbot_hosts()
|
certbot_certs_found = processor_obj.get_certbot_hosts()
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG,
|
loggerEasyHaproxy.info('Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
||||||
'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
|
||||||
old_haproxy = haproxy
|
old_haproxy = haproxy
|
||||||
haproxy = DaemonizeHAProxy()
|
haproxy = DaemonizeHAProxy()
|
||||||
current_custom_config_files = haproxy.get_custom_config_files()
|
current_custom_config_files = haproxy.get_custom_config_files()
|
||||||
haproxy.haproxy("reload")
|
haproxy.haproxy(DaemonizeHAProxy.HAPROXY_RELOAD)
|
||||||
old_haproxy.terminate()
|
old_haproxy.terminate()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.FATAL, "Err: %s" % e)
|
loggerEasyHaproxy.fatal("Err: %s" % e)
|
||||||
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Heartbeat')
|
loggerEasyHaproxy.info('Heartbeat')
|
||||||
haproxy.sleep()
|
haproxy.sleep()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
Functions.run_bash(Functions.INIT_LOG, '/usr/sbin/haproxy -v')
|
Functions.run_bash(loggerInit, '/usr/sbin/haproxy -v')
|
||||||
|
|
||||||
Functions.log(Functions.INIT_LOG, Functions.INFO, " _ ")
|
loggerInit.info(" _ ")
|
||||||
Functions.log(Functions.INIT_LOG, Functions.INFO, " ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ ")
|
loggerInit.info(" ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ ")
|
||||||
Functions.log(Functions.INIT_LOG, Functions.INFO, "/ -_) _` (_-< || |___| ' \\/ _` | '_ \\ '_/ _ \\ \\ / || |")
|
loggerInit.info("/ -_) _` (_-< || |___| ' \\/ _` | '_ \\ '_/ _ \\ \\ / || |")
|
||||||
Functions.log(Functions.INIT_LOG, Functions.INFO, "\\___\\__,_/__/\\_, | |_||_\\__,_| .__/_| \\___/_\\_\\_, |")
|
loggerInit.info("\\___\\__,_/__/\\_, | |_||_\\__,_| .__/_| \\___/_\\_\\_, |")
|
||||||
Functions.log(Functions.INIT_LOG, Functions.INFO, " |__/ |_| |__/ ")
|
loggerInit.info(" |__/ |_| |__/ ")
|
||||||
|
|
||||||
Functions.log(Functions.INIT_LOG, Functions.INFO, "Release: %s" % (os.getenv("RELEASE_VERSION")))
|
loggerInit.info("Release: %s" % (os.getenv("RELEASE_VERSION")))
|
||||||
Functions.log(Functions.INIT_LOG, Functions.DEBUG, 'Environment:')
|
loggerInit.debug('Environment:')
|
||||||
for name, value in os.environ.items():
|
for name, value in os.environ.items():
|
||||||
if "HAPROXY" in name:
|
if "HAPROXY" in name:
|
||||||
Functions.log(Functions.INIT_LOG, Functions.DEBUG, "- {0}: {1}".format(name, value))
|
loggerInit.debug("- {0}: {1}".format(name, value))
|
||||||
|
|
||||||
start()
|
start()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import base64
|
import base64
|
||||||
import socket
|
import socket
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
import yaml
|
import yaml
|
||||||
|
|
@ -8,9 +9,15 @@ from kubernetes.client.rest import ApiException
|
||||||
|
|
||||||
from easymapping import HaproxyConfigGenerator
|
from easymapping import HaproxyConfigGenerator
|
||||||
from functions import Functions, Consts, ContainerEnv
|
from functions import Functions, Consts, ContainerEnv
|
||||||
|
from functions import loggerEasyHaproxy
|
||||||
|
|
||||||
|
|
||||||
class ProcessorInterface:
|
class ProcessorInterface:
|
||||||
|
STATIC: Final[str] = "static"
|
||||||
|
DOCKER: Final[str] = "docker"
|
||||||
|
SWARM: Final[str] = "swarm"
|
||||||
|
KUBERNETES: Final[str] = "kubernetes"
|
||||||
|
|
||||||
static_file = Consts.easyhaproxy_config
|
static_file = Consts.easyhaproxy_config
|
||||||
|
|
||||||
def __init__(self, filename=None):
|
def __init__(self, filename=None):
|
||||||
|
|
@ -27,17 +34,16 @@ class ProcessorInterface:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def factory(mode):
|
def factory(mode):
|
||||||
if mode == "static":
|
if mode == ProcessorInterface.STATIC:
|
||||||
return Static(ProcessorInterface.static_file)
|
return Static(ProcessorInterface.static_file)
|
||||||
elif mode == "docker":
|
elif mode == ProcessorInterface.DOCKER:
|
||||||
return Docker()
|
return Docker()
|
||||||
elif mode == "swarm":
|
elif mode == ProcessorInterface.SWARM:
|
||||||
return Swarm()
|
return Swarm()
|
||||||
elif mode == "kubernetes":
|
elif mode == ProcessorInterface.KUBERNETES:
|
||||||
return Kubernetes()
|
return Kubernetes()
|
||||||
else:
|
else:
|
||||||
Functions.log("EASYHAPROXY", Functions.FATAL,
|
loggerEasyHaproxy.fatal("Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % mode)
|
||||||
"Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % mode)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
|
|
@ -244,11 +250,9 @@ class Kubernetes(ProcessorInterface):
|
||||||
|
|
||||||
ssl_hosts.extend(tls.hosts)
|
ssl_hosts.extend(tls.hosts)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log("EASYHAPROXY", Functions.WARN,
|
loggerEasyHaproxy.warn("Ingress %s - Get secret failed: '%s'" % (ingress_name, e))
|
||||||
"Ingress %s - Get secret failed: '%s'" % (ingress_name, e))
|
|
||||||
|
|
||||||
Functions.log("EASYHAPROXY", Functions.TRACE,
|
loggerEasyHaproxy.debug("Ingress %s - SSL Hosts found '%s'" % (ingress_name, ssl_hosts))
|
||||||
"Ingress %s - SSL Hosts found '%s'" % (ingress_name, ssl_hosts))
|
|
||||||
|
|
||||||
for rule in ingress.spec.rules:
|
for rule in ingress.spec.rules:
|
||||||
rule_data = {}
|
rule_data = {}
|
||||||
|
|
@ -275,8 +279,7 @@ class Kubernetes(ProcessorInterface):
|
||||||
cluster_ip = api_response.spec.cluster_ip
|
cluster_ip = api_response.spec.cluster_ip
|
||||||
except ApiException as e:
|
except ApiException as e:
|
||||||
cluster_ip = None
|
cluster_ip = None
|
||||||
Functions.log("EASYHAPROXY", Functions.WARN,
|
loggerEasyHaproxy.warn("Ingress %s - Service %s - Failed: '%s'" % (ingress_name, service_name, e))
|
||||||
"Ingress %s - Service %s - Failed: '%s'" % (ingress_name, service_name, e))
|
|
||||||
|
|
||||||
if cluster_ip is not None:
|
if cluster_ip is not None:
|
||||||
if cluster_ip not in self.parsed_object.keys():
|
if cluster_ip not in self.parsed_object.keys():
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,15 @@ def test_daemonize_haproxy_check_config():
|
||||||
|
|
||||||
def test_daemonize_haproxy_get_haproxy_command_start():
|
def test_daemonize_haproxy_get_haproxy_command_start():
|
||||||
daemon = DaemonizeHAProxy()
|
daemon = DaemonizeHAProxy()
|
||||||
command = daemon.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"
|
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():
|
||||||
daemon = DaemonizeHAProxy()
|
daemon = DaemonizeHAProxy()
|
||||||
command = daemon.get_haproxy_command("reload")
|
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 -x /var/run/haproxy.sock -sf "
|
||||||
|
|
||||||
def test_daemonize_haproxy_check_config():
|
def test_daemonize_haproxy2_check_config():
|
||||||
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
filed = daemon.get_custom_config_files()
|
filed = daemon.get_custom_config_files()
|
||||||
assert filed == {
|
assert filed == {
|
||||||
|
|
@ -30,19 +30,19 @@ def test_daemonize_haproxy_check_config():
|
||||||
os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg")
|
os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg")
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_daemonize_haproxy_get_haproxy_command_start():
|
def test_daemonize_haproxy2_get_haproxy_command_start():
|
||||||
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
command = daemon.get_haproxy_command("start")
|
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")
|
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_haproxy_get_haproxy_command_reload():
|
def test_daemonize_haproxy2_get_haproxy_command_reload():
|
||||||
tmp_pid_file = "/tmp/tmp_pid.txt"
|
tmp_pid_file = "/tmp/tmp_pid.txt"
|
||||||
Functions.save(tmp_pid_file, "10")
|
Functions.save(tmp_pid_file, "10")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
command = daemon.get_haproxy_command("reload", tmp_pid_file)
|
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)
|
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:
|
finally:
|
||||||
os.remove(tmp_pid_file)
|
os.remove(tmp_pid_file)
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ def test_processor_docker():
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = 'docker@example.org'
|
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = 'docker@example.org'
|
||||||
|
|
||||||
static = ProcessorInterface.factory("docker")
|
static = ProcessorInterface.factory(ProcessorInterface.DOCKER)
|
||||||
assert static.get_certbot_hosts() is None
|
assert static.get_certbot_hosts() is None
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,37 @@
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
|
from logging import Logger
|
||||||
|
|
||||||
from functions import Functions
|
from functions import Functions, loggerEasyHaproxy, loggerCertbot, loggerHaproxy
|
||||||
|
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
log_stream = StringIO() # Create StringIO object
|
||||||
|
log_handler = logging.StreamHandler(log_stream)
|
||||||
|
log_formatter = logging.Formatter('%(levelname)s - %(message)s')
|
||||||
|
log_handler.setFormatter(log_formatter)
|
||||||
|
loggerDebug = logging.getLogger(__name__)
|
||||||
|
loggerDebug.setLevel(logging.DEBUG)
|
||||||
|
loggerDebug.addHandler(log_handler)
|
||||||
|
|
||||||
def test_functions_check_local_level():
|
def test_functions_check_local_level():
|
||||||
assert Functions.skip_log('CERTBOT', Functions.INFO) == False
|
assert Functions.setup_log(loggerCertbot) == logging.INFO
|
||||||
assert Functions.skip_log('HAPOROXY', Functions.INFO) == False
|
assert Functions.setup_log(loggerHaproxy) == logging.INFO
|
||||||
assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == False
|
assert Functions.setup_log(loggerEasyHaproxy) == logging.INFO
|
||||||
|
|
||||||
os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('CERTBOT', Functions.INFO) == True
|
assert Functions.setup_log(loggerCertbot) == logging.WARNING
|
||||||
del os.environ['CERTBOT_LOG_LEVEL']
|
del os.environ['CERTBOT_LOG_LEVEL']
|
||||||
|
|
||||||
os.environ['HAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['HAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('HAPROXY', Functions.INFO) == True
|
assert Functions.setup_log(loggerHaproxy) == logging.WARNING
|
||||||
del os.environ['HAPROXY_LOG_LEVEL']
|
del os.environ['HAPROXY_LOG_LEVEL']
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == True
|
assert Functions.setup_log(loggerEasyHaproxy) == logging.WARNING
|
||||||
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,122 +46,99 @@ def test_function_load_and_save():
|
||||||
finally:
|
finally:
|
||||||
os.unlink(filename)
|
os.unlink(filename)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_check_log_sanity():
|
|
||||||
print()
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 1")
|
|
||||||
assert Functions.debug_log is None
|
|
||||||
|
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 2")
|
|
||||||
assert len(Functions.debug_log) == 1
|
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Test 2", Functions.debug_log[0])
|
|
||||||
|
|
||||||
os.environ['CERTBOT_LOG_LEVEL'] = 'DEBUG'
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 3")
|
|
||||||
assert len(Functions.debug_log) == 2
|
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Test 3", Functions.debug_log[1])
|
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 4") # Should not log to debug
|
|
||||||
assert len(Functions.debug_log) == 2
|
|
||||||
|
|
||||||
finally:
|
|
||||||
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
|
||||||
Functions.debug_log = None
|
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_log_output():
|
def test_functions_run_bash_log_output():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 1'", log_output=True,
|
return_code, result = Functions.run_bash(loggerDebug, "echo 'test run 1'", log_output=True,
|
||||||
return_result=False)
|
return_result=False)
|
||||||
assert return_code == 0
|
assert return_code == 0
|
||||||
assert result == []
|
assert result == []
|
||||||
assert len(Functions.debug_log) == 1
|
log_value = log_stream.getvalue()
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: test run 1", Functions.debug_log[0])
|
assert len(log_value) > 0
|
||||||
|
assert log_value == "INFO - test run 1\n"
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_no_log_output():
|
def test_functions_run_bash_no_log_output():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 2'", log_output=False,
|
return_code, result = Functions.run_bash(loggerDebug, "echo 'test run 2'", log_output=False,
|
||||||
return_result=False)
|
return_result=False)
|
||||||
assert return_code == 0
|
assert return_code == 0
|
||||||
assert result == []
|
assert result == []
|
||||||
assert len(Functions.debug_log) == 0
|
assert len(log_stream.getvalue()) == 0
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_return():
|
def test_functions_run_bash_return():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 3'", log_output=False,
|
return_code, result = Functions.run_bash(loggerDebug, "echo 'test run 3'", log_output=False,
|
||||||
return_result=True)
|
return_result=True)
|
||||||
assert return_code == 0
|
assert return_code == 0
|
||||||
assert len(Functions.debug_log) == 0
|
assert len(log_stream.getvalue()) == 0
|
||||||
assert "".join(result) == 'test run 3'
|
assert "".join(result) == 'test run 3'
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_log_and_return_output():
|
def test_functions_run_bash_log_and_return_output():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 4'", log_output=True, return_result=True)
|
return_code, result = Functions.run_bash(loggerDebug, "echo 'test run 4'",
|
||||||
|
log_output=True,
|
||||||
|
return_result=True)
|
||||||
assert return_code == 0
|
assert return_code == 0
|
||||||
assert "".join(result) == 'test run 4'
|
assert "".join(result) == 'test run 4'
|
||||||
assert len(Functions.debug_log) == 1
|
log_value = log_stream.getvalue().strip("\x00")
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: test run 4", Functions.debug_log[0])
|
assert len(log_value) > 0
|
||||||
|
assert log_value == "INFO - test run 4\n"
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_ok():
|
def test_functions_run_bash_ok():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "%s/fixtures/run_bash.sh" % os.path.dirname(__file__), log_output=True,
|
return_code, result = Functions.run_bash(loggerDebug, "%s/fixtures/run_bash.sh" % os.path.dirname(__file__),
|
||||||
|
log_output=True,
|
||||||
return_result=False)
|
return_result=False)
|
||||||
assert return_code == 0
|
assert return_code == 0
|
||||||
assert result == []
|
assert result == []
|
||||||
assert len(Functions.debug_log) == 1
|
log_value = log_stream.getvalue().strip("\x00")
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Processing run_bash.sh", Functions.debug_log[0])
|
assert len(log_value) > 1
|
||||||
|
assert log_value == "INFO - Processing run_bash.sh\n"
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_fail():
|
def test_functions_run_bash_fail():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "%s/fixtures/run_bash.sh 15" % os.path.dirname(__file__), log_output=True,
|
return_code, result = Functions.run_bash(loggerDebug, "%s/fixtures/run_bash.sh 15" % os.path.dirname(__file__),
|
||||||
|
log_output=True,
|
||||||
return_result=False)
|
return_result=False)
|
||||||
assert return_code == 15
|
assert return_code == 15
|
||||||
assert result == []
|
assert result == []
|
||||||
assert len(Functions.debug_log) == 1
|
log_value = log_stream.getvalue().strip("\x00")
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Processing run_bash.sh", Functions.debug_log[0])
|
assert len(log_value) > 0
|
||||||
|
assert log_value == "INFO - Processing run_bash.sh\n"
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_command_not_found():
|
def test_functions_run_command_not_found():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
|
||||||
try:
|
try:
|
||||||
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "no_command_here", log_output=True,
|
return_code, result = Functions.run_bash(loggerDebug, "no_command_here",
|
||||||
|
log_output=True,
|
||||||
return_result=False)
|
return_result=False)
|
||||||
assert return_code == -99
|
assert return_code == -99
|
||||||
assert str(result) == "[Errno 2] No such file or directory: 'no_command_here'"
|
assert str(result) == "[Errno 2] No such file or directory: 'no_command_here'"
|
||||||
assert len(Functions.debug_log) == 1
|
log_value = log_stream.getvalue().strip("\x00")
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[ERROR\]: \[Errno 2\] No such file or directory: 'no_command_here'", Functions.debug_log[0])
|
assert len(log_value) > 0
|
||||||
|
assert log_value == "ERROR - [Errno 2] No such file or directory: 'no_command_here'\n"
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
log_stream.truncate(0)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from processor import ProcessorInterface
|
||||||
|
|
||||||
def test_processor_static():
|
def test_processor_static():
|
||||||
ProcessorInterface.static_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./fixtures/static.yml")
|
ProcessorInterface.static_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./fixtures/static.yml")
|
||||||
static = ProcessorInterface.factory("static")
|
static = ProcessorInterface.factory(ProcessorInterface.STATIC)
|
||||||
|
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue