moonraker: backup the most recent working config
Attempt to take a backup of the configuration file if Moonraker loads successfully and has not seen a config change. If Moonraker fails to load due to a config error, attempt to fallback to the backup configuration. If that fails, exit the server. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
4ec781d66b
commit
5a966836b5
|
@ -122,9 +122,12 @@ class Server:
|
||||||
self.register_upload_handler = app.register_upload_handler
|
self.register_upload_handler = app.register_upload_handler
|
||||||
self.register_api_transport = app.register_api_transport
|
self.register_api_transport = app.register_api_transport
|
||||||
|
|
||||||
log_warn: Optional[str] = args.get('log_warning')
|
log_warn = args.get('log_warning', "")
|
||||||
if log_warn is not None:
|
if log_warn:
|
||||||
self.add_warning(log_warn)
|
self.add_warning(log_warn)
|
||||||
|
cfg_warn = args.get("config_warning", "")
|
||||||
|
if cfg_warn:
|
||||||
|
self.add_warning(cfg_warn)
|
||||||
|
|
||||||
self.register_endpoint(
|
self.register_endpoint(
|
||||||
"/server/info", ['GET'], self._handle_info_request)
|
"/server/info", ['GET'], self._handle_info_request)
|
||||||
|
@ -188,6 +191,10 @@ class Server:
|
||||||
if optional_comps:
|
if optional_comps:
|
||||||
await asyncio.gather(*optional_comps)
|
await asyncio.gather(*optional_comps)
|
||||||
|
|
||||||
|
if not self.warnings:
|
||||||
|
cfg_file = self.app_args['config_file']
|
||||||
|
await self.event_loop.run_in_thread(utils.backup_config, cfg_file)
|
||||||
|
|
||||||
# Start HTTP Server
|
# Start HTTP Server
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Starting Moonraker on ({self.host}, {self.port}), "
|
f"Starting Moonraker on ({self.host}, {self.port}), "
|
||||||
|
@ -859,7 +866,8 @@ def main() -> None:
|
||||||
os.path.expanduser(cmd_line_args.logfile))
|
os.path.expanduser(cmd_line_args.logfile))
|
||||||
app_args['software_version'] = version
|
app_args['software_version'] = version
|
||||||
ql, file_logger, warning = utils.setup_logging(app_args)
|
ql, file_logger, warning = utils.setup_logging(app_args)
|
||||||
app_args['log_warning'] = warning
|
if warning is not None:
|
||||||
|
app_args['log_warning'] = warning
|
||||||
|
|
||||||
if sys.version_info < (3, 7):
|
if sys.version_info < (3, 7):
|
||||||
msg = f"Moonraker requires Python 3.7 or above. " \
|
msg = f"Moonraker requires Python 3.7 or above. " \
|
||||||
|
@ -871,10 +879,27 @@ def main() -> None:
|
||||||
|
|
||||||
# Start asyncio event loop and server
|
# Start asyncio event loop and server
|
||||||
event_loop = EventLoop()
|
event_loop = EventLoop()
|
||||||
|
alt_config_loaded = False
|
||||||
estatus = 0
|
estatus = 0
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
server = Server(app_args, file_logger, event_loop)
|
server = Server(app_args, file_logger, event_loop)
|
||||||
|
except confighelper.ConfigError as e:
|
||||||
|
backup_cfg = utils.find_config_backup(app_args['config_file'])
|
||||||
|
if alt_config_loaded or backup_cfg is None:
|
||||||
|
logging.exception("Server Config Error")
|
||||||
|
estatus = 1
|
||||||
|
break
|
||||||
|
app_args['config_file'] = backup_cfg
|
||||||
|
app_args['config_warning'] = (
|
||||||
|
f"Server configuration error: {e}\n"
|
||||||
|
f"Loaded server from most recent working configuration:"
|
||||||
|
f" '{app_args['config_file']}'\n"
|
||||||
|
f"Please fix the issue in moonraker.conf and restart "
|
||||||
|
f"the server."
|
||||||
|
)
|
||||||
|
alt_config_loaded = True
|
||||||
|
continue
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Moonraker Error")
|
logging.exception("Moonraker Error")
|
||||||
estatus = 1
|
estatus = 1
|
||||||
|
@ -887,6 +912,12 @@ def main() -> None:
|
||||||
break
|
break
|
||||||
if server.exit_reason == "terminate":
|
if server.exit_reason == "terminate":
|
||||||
break
|
break
|
||||||
|
# Restore the original config and clear the warning
|
||||||
|
# before the server restarts
|
||||||
|
if alt_config_loaded:
|
||||||
|
app_args['config_file'] = cmd_line_args.configfile
|
||||||
|
app_args.pop('config_warning', None)
|
||||||
|
alt_config_loaded = False
|
||||||
event_loop.close()
|
event_loop.close()
|
||||||
# Since we are running outside of the the server
|
# Since we are running outside of the the server
|
||||||
# it is ok to use a blocking sleep here
|
# it is ok to use a blocking sleep here
|
||||||
|
|
|
@ -18,6 +18,8 @@ import hashlib
|
||||||
import json
|
import json
|
||||||
import shlex
|
import shlex
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
import filecmp
|
||||||
from queue import SimpleQueue as Queue
|
from queue import SimpleQueue as Queue
|
||||||
|
|
||||||
# Annotation imports
|
# Annotation imports
|
||||||
|
@ -230,3 +232,22 @@ def load_system_module(name: str) -> ModuleType:
|
||||||
else:
|
else:
|
||||||
raise ServerError(f"Unable to import module {name}")
|
raise ServerError(f"Unable to import module {name}")
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
def backup_config(cfg_path: str) -> None:
|
||||||
|
cfg = pathlib.Path(cfg_path).expanduser().resolve()
|
||||||
|
backup = cfg.parent.joinpath(f".{cfg.name}.bkp")
|
||||||
|
try:
|
||||||
|
if backup.exists() and filecmp.cmp(cfg, backup):
|
||||||
|
# Backup already exists and is current
|
||||||
|
return
|
||||||
|
shutil.copy2(cfg, backup)
|
||||||
|
logging.info(f"Backing up last working configuration to '{backup}'")
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Failed to create a backup")
|
||||||
|
|
||||||
|
def find_config_backup(cfg_path: str) -> Optional[str]:
|
||||||
|
cfg = pathlib.Path(cfg_path).expanduser().resolve()
|
||||||
|
backup = cfg.parent.joinpath(f".{cfg.name}.bkp")
|
||||||
|
if backup.is_file():
|
||||||
|
return str(backup)
|
||||||
|
return None
|
||||||
|
|
Loading…
Reference in New Issue