feat: add configuration testing option for Nginx reload endpoint

This commit is contained in:
Théophile Diot 2024-11-29 15:14:29 +01:00
parent 53d6114fc1
commit 818270384c
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
5 changed files with 46 additions and 13 deletions

View file

@ -92,19 +92,29 @@ api.global.GET["^/ping$"] = function(self)
return self:response(HTTP_OK, "success", "pong")
end
api.global.POST["^/reload$"] = function(self)
-- Check config
logger:log(NOTICE, "Checking Nginx configuration")
local status = execute("nginx -t")
if status ~= 0 then
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "config check failed")
api.global.POST["^/reload"] = function(self)
-- Get test argument
local args = ngx.req.get_uri_args()
local test_arg = args.test or "yes"
if test_arg ~= "no" then
-- Check Nginx configuration
logger:log(NOTICE, "Checking Nginx configuration")
local status = execute("nginx -t")
if status ~= 0 then
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "config check failed")
end
logger:log(NOTICE, "Nginx configuration is valid")
end
logger:log(NOTICE, "Nginx configuration is valid, reloading Nginx")
-- Reload Nginx
logger:log(NOTICE, "Reloading Nginx")
-- Send HUP signal to master process
local ok, err = kill(get_master_pid(), "HUP")
if not ok then
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
end
return self:response(HTTP_OK, "success", "reload successful")
end

View file

@ -60,7 +60,11 @@ try:
LOGGER.info(
f"Successfully sent API request to {api.endpoint}/lets-encrypt/certificates",
)
sent, err, status, resp = api.request("POST", "/reload", timeout=max(reload_min_timeout, 2 * len(services)))
sent, err, status, resp = api.request(
"POST",
f"/reload?test={'no' if getenv('DISABLE_CONFIGURATION_TESTING', 'no').lower() == 'yes' else 'yes'}",
timeout=max(reload_min_timeout, 2 * len(services)),
)
if not sent:
status = 1
LOGGER.error(f"Can't send API request to {api.endpoint}/reload : {err}")

View file

@ -134,7 +134,11 @@ class JobScheduler(ApiCaller):
self.__logger.error("RELOAD_MIN_TIMEOUT must be an integer, defaulting to 5")
reload_min_timeout = 5
reload_success = self.send_to_apis("POST", "/reload", timeout=max(int(reload_min_timeout), 2 * len(self.__env["SERVER_NAME"].split(" "))))[0]
reload_success = self.send_to_apis(
"POST",
f"/reload?test={'no' if self.__env.get('DISABLE_CONFIGURATION_TESTING', 'no').lower() == 'yes' else 'yes'}",
timeout=max(int(reload_min_timeout), 2 * len(self.__env["SERVER_NAME"].split(" "))),
)[0]
if reload_success:
self.__logger.info("Successfully reloaded nginx")
return True

View file

@ -101,6 +101,11 @@ if not RELOAD_MIN_TIMEOUT.isdigit():
RELOAD_MIN_TIMEOUT = int(RELOAD_MIN_TIMEOUT)
DISABLE_CONFIGURATION_TESTING = getenv("DISABLE_CONFIGURATION_TESTING", "no").lower() == "yes"
if DISABLE_CONFIGURATION_TESTING:
LOGGER.warning("Configuration testing is disabled, changes will be applied without testing (we hope you know what you're doing) ...")
def handle_stop(signum, frame):
current_time = datetime.now().astimezone()
@ -383,6 +388,8 @@ def healthcheck_job():
HEALTHCHECK_EVENT.set()
# env = SCHEDULER.db.get_config() # TODO: uncomment when the healthcheck endpoint is ready @fl0ppy-d1sk
for db_instance in SCHEDULER.db.get_instances():
bw_instance = API(f"http://{db_instance['hostname']}:{db_instance['port']}", db_instance["server_name"])
try:
@ -421,7 +428,7 @@ def healthcheck_job():
# api_caller.send_files(PRO_PLUGINS_PATH, "/pro_plugins")
# api_caller.send_files(join(sep, "etc", "nginx"), "/confs")
# api_caller.send_files(CACHE_PATH, "/cache")
# if not api_caller.send_to_apis("POST", "/reload")[0]:
# if not api_caller.send_to_apis("POST", f"/reload?test={'no' if DISABLE_CONFIGURATION_TESTING else 'yes'}", timeout=max(RELOAD_MIN_TIMEOUT, 2 * len(env["SERVER_NAME"].split(" "))),)[0]:
# HEALTHCHECK_LOGGER.error(f"Error while reloading instance {bw_instance.endpoint}")
# ret = SCHEDULER.db.update_instance(db_instance["hostname"], "loading")
# if ret:
@ -806,7 +813,10 @@ if __name__ == "__main__":
thread.join()
success, responses = SCHEDULER.send_to_apis(
"POST", "/reload", timeout=max(RELOAD_MIN_TIMEOUT, 2 * len(env["SERVER_NAME"].split(" "))), response=True
"POST",
f"/reload?test={'no' if DISABLE_CONFIGURATION_TESTING else 'yes'}",
timeout=max(RELOAD_MIN_TIMEOUT, 2 * len(env["SERVER_NAME"].split(" "))),
response=True,
)
if not success:
reachable = False
@ -872,7 +882,11 @@ if __name__ == "__main__":
for thread in tmp_threads:
thread.join()
if not SCHEDULER.send_to_apis("POST", "/reload", timeout=max(RELOAD_MIN_TIMEOUT, 2 * len(env["SERVER_NAME"].split(" "))))[0]:
if not SCHEDULER.send_to_apis(
"POST",
f"/reload?test={'no' if DISABLE_CONFIGURATION_TESTING else 'yes'}",
timeout=max(RELOAD_MIN_TIMEOUT, 2 * len(env["SERVER_NAME"].split(" "))),
)[0]:
LOGGER.error("Error while reloading bunkerweb with failover configuration, skipping ...")
elif not reachable:
LOGGER.warning("No BunkerWeb instance is reachable, skipping failover ...")

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python3
from datetime import datetime
from operator import itemgetter
from os import getenv
from typing import Any, List, Literal, Optional, Tuple, Union
from API import API # type: ignore
@ -67,7 +68,7 @@ class Instance:
def reload(self) -> str:
try:
result = self.apiCaller.send_to_apis("POST", "/reload")[0]
result = self.apiCaller.send_to_apis("POST", f"/reload?test={'no' if getenv('DISABLE_CONFIGURATION_TESTING', 'no').lower() == 'yes' else 'yes'}")[0]
except BaseException as e:
return f"Can't reload instance {self.hostname}: {e}"