chore: Fix save_config.py script with kubernetes + Update save_config Database function to return changed_plugins if no error happened

This commit is contained in:
Théophile Diot 2024-05-28 08:32:54 +01:00
parent 6f53e83c65
commit 6947b19b9a
No known key found for this signature in database
GPG key ID: 248FEA4BAE400D06
4 changed files with 85 additions and 47 deletions

View file

@ -140,6 +140,15 @@ class Config(ConfigCaller):
if err:
self.__logger.error(f"Failed to update instances: {err}")
# save config to database
changed_plugins = []
if "config" in changes:
err = self._db.save_config(self.__config, "autoconf", changed=False)
if isinstance(err, str):
success = False
self.__logger.error(f"Can't save config in database: {err}, config may not work as expected")
changed_plugins = err
# save custom configs to database
if "custom_configs" in changes:
err = self._db.save_custom_configs(custom_configs, "autoconf", changed=False)
@ -147,16 +156,9 @@ class Config(ConfigCaller):
success = False
self.__logger.error(f"Can't save autoconf custom configs in database: {err}, custom configs may not work as expected")
# save config to database
if "config" in changes:
err = self._db.save_config(self.__config, "autoconf")
if err:
success = False
self.__logger.error(f"Can't save config in database: {err}, config may not work as expected")
else:
# update changes in db
ret = self._db.checked_changes(changes, value=True)
if ret:
self.__logger.error(f"An error occurred when setting the changes to checked in the database : {ret}")
# update changes in db
ret = self._db.checked_changes(changes, plugins_changes=changed_plugins, value=True)
if ret:
self.__logger.error(f"An error occurred when setting the changes to checked in the database : {ret}")
return success

View file

@ -10,7 +10,7 @@ from os.path import join
from pathlib import Path
from re import compile as re_compile
from sys import argv, path as sys_path
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union
from time import sleep
from uuid import uuid4
from zipfile import ZIP_DEFLATED, ZipFile
@ -512,9 +512,12 @@ class Database:
except BaseException as e:
return str(e)
def checked_changes(self, changes: Optional[List[str]] = None, value: Optional[bool] = False) -> str:
def checked_changes(
self, changes: Optional[List[str]] = None, plugins_changes: Optional[Union[Set[str], List[str], Tuple[str]]] = None, value: Optional[bool] = False
) -> str:
"""Set changed bit for config, custom configs, instances and plugins"""
changes = changes or ["config", "custom_configs", "external_plugins", "pro_plugins", "instances"]
plugins_changes = plugins_changes or set()
with self.__db_session() as session:
if self.readonly:
return "The database is read-only, the changes will not be saved"
@ -536,25 +539,10 @@ class Database:
metadata.pro_plugins_changed = value
if "instances" in changes:
metadata.instances_changed = value
session.commit()
except BaseException as e:
return str(e)
return ""
if plugins_changes:
session.query(Plugins).filter(Plugins.id.in_(plugins_changes)).update({Plugins.config_changed: value})
def checked_plugins_changes(self, plugins: Optional[List[str]] = None, value: Optional[bool] = False) -> str:
"""Set changed bit for plugins"""
with self.__db_session() as session:
if self.readonly:
return "The database is read-only, the changes will not be saved"
plugins = plugins or []
try:
query = session.query(Plugins)
if plugins:
query = query.filter(Plugins.id.in_(plugins))
query.update({Plugins.config_changed: value})
session.commit()
except BaseException as e:
return str(e)
@ -1130,7 +1118,7 @@ class Database:
return True, ""
def save_config(self, config: Dict[str, Any], method: str, changed: Optional[bool] = True) -> str:
def save_config(self, config: Dict[str, Any], method: str, changed: Optional[bool] = True) -> Union[str, Set[str]]:
"""Save the config in the database"""
to_put = []
with self.__db_session() as session:
@ -1331,6 +1319,9 @@ class Database:
continue
query.update({Global_values.value: value})
if changed_services:
changed_plugins = set(plugin.id for plugin in session.query(Plugins).with_entities(Plugins.id).all())
if changed:
with suppress(ProgrammingError, OperationalError):
metadata = session.query(Metadata).get(1)
@ -1338,9 +1329,7 @@ class Database:
if not metadata.first_config_saved:
metadata.first_config_saved = True
if changed_services:
session.query(Plugins).update({Plugins.config_changed: True})
elif changed_plugins:
if changed_plugins:
session.query(Plugins).filter(Plugins.id.in_(changed_plugins)).update({Plugins.config_changed: True})
try:
@ -1349,7 +1338,7 @@ class Database:
except BaseException as e:
return str(e)
return ""
return changed_plugins
def save_custom_configs(
self,

View file

@ -15,6 +15,8 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
sys_path.append(deps_path)
from docker import DockerClient
from kubernetes import client as kube_client
from kubernetes import config as kube_config
from common_utils import get_integration # type: ignore
from logger import setup_logger # type: ignore
@ -142,6 +144,8 @@ if __name__ == "__main__":
logger.error(f"Missing RX rights on directory : {path}")
sys_exit(1)
tmp_config = {}
if args.variables:
variables_path = Path(args.variables)
logger.info(f"Variables : {variables_path}")
@ -178,7 +182,6 @@ if __name__ == "__main__":
api_http_port = None
api_server_name = None
tmp_config = {}
custom_confs = []
apis = []
@ -216,6 +219,49 @@ if __name__ == "__main__":
host=api_server_name or getenv("API_SERVER_NAME", "bwapi"),
)
)
else:
kube_config.load_incluster_config()
kubernetes_client = kube_client.CoreV1Api()
api_http_port = None
api_server_name = None
custom_confs = []
apis = []
for pod in kubernetes_client.list_pod_for_all_namespaces(watch=False).items:
if pod.metadata.annotations is not None and "bunkerweb.io/INSTANCE" in pod.metadata.annotations:
for env in pod.env:
if custom_confs_rx.match(env.name):
custom_conf = custom_confs_rx.search(env.name).groups()
custom_confs.append(
{
"value": f"# CREATED BY ENV\n{env.value}",
"exploded": (
custom_conf[0],
custom_conf[1],
custom_conf[2].replace(".conf", ""),
),
}
)
logger.info(
f"Found custom conf env var {'for service ' + custom_conf[0] if custom_conf[0] else 'without service'} with type {custom_conf[1]} and name {custom_conf[2]}"
)
else:
tmp_config[env.name] = env.value
if not db and env.name == "DATABASE_URI":
db = Database(logger, sqlalchemy_string=env.value)
elif env.name == "API_HTTP_PORT":
api_http_port = env.value
elif env.name == "API_SERVER_NAME":
api_server_name = env.value
apis.append(
API(
f"http://{pod.status.pod_ip or pod.metadata.name}:{api_http_port or getenv('API_HTTP_PORT', '5000')}",
host=api_server_name or getenv("API_SERVER_NAME", "bwapi"),
)
)
if not db:
db = Database(logger)
@ -278,11 +324,13 @@ if __name__ == "__main__":
sys_exit(0)
changes = []
changed_plugins = set()
err = db.save_config(config_files, args.method, changed=False)
if err:
if isinstance(err, str):
logger.warning(f"Couldn't save config to database : {err}, config may not work as expected")
else:
changed_plugins = err
changes.append("config")
logger.info("Config successfully saved to database")
@ -327,7 +375,7 @@ if __name__ == "__main__":
if not args.no_check_changes:
# update changes in db
ret = db.checked_changes(changes, value=True)
ret = db.checked_changes(changes, plugins_changes=changed_plugins, value=True)
if ret:
logger.error(f"An error occurred when setting the changes to checked in the database : {ret}")
except SystemExit as e:

View file

@ -7,7 +7,7 @@ from flask import flash
from json import loads as json_loads
from pathlib import Path
from re import error as RegexError, search as re_search
from typing import List, Literal, Optional, Tuple
from typing import List, Literal, Optional, Set, Tuple, Union
class Config:
@ -15,7 +15,9 @@ class Config:
self.__settings = json_loads(Path(sep, "usr", "share", "bunkerweb", "settings.json").read_text(encoding="utf-8"))
self.__db = db
def __gen_conf(self, global_conf: dict, services_conf: list[dict], *, check_changes: bool = True, changed_service: Optional[str] = None) -> None:
def __gen_conf(
self, global_conf: dict, services_conf: list[dict], *, check_changes: bool = True, changed_service: Optional[str] = None
) -> Union[str, Set[str]]:
"""Generates the nginx configuration file from the given configuration
Parameters
@ -136,9 +138,6 @@ class Config:
return error
def reload_config(self) -> Optional[str]:
return self.__gen_conf(self.get_config(methods=False), self.get_services(methods=False))
def new_service(self, variables: dict, is_draft: bool = False) -> Tuple[str, int]:
"""Creates a new service from the given variables
@ -165,7 +164,7 @@ class Config:
services.append(variables | {"IS_DRAFT": "yes" if is_draft else "no"})
ret = self.__gen_conf(self.get_config(methods=False), services, check_changes=not is_draft)
if ret:
if isinstance(ret, str):
return ret, 1
return f"Configuration for {variables['SERVER_NAME'].split(' ')[0]} has been generated.", 0
@ -205,7 +204,7 @@ class Config:
config.pop(k)
ret = self.__gen_conf(config, services, check_changes=check_changes, changed_service=server_name_splitted[0])
if ret:
if isinstance(ret, str):
return ret, 1
return f"Configuration for {old_server_name_splitted[0]} has been edited.", 0
@ -223,7 +222,7 @@ class Config:
the confirmation message
"""
ret = self.__gen_conf(self.get_config(methods=False) | variables, self.get_services(methods=False))
if ret:
if isinstance(ret, str):
return ret, 1
return "The global configuration has been edited.", 0
@ -273,6 +272,6 @@ class Config:
service.pop(k)
ret = self.__gen_conf(new_env, new_services, check_changes=check_changes)
if ret:
if isinstance(ret, str):
return ret, 1
return f"Configuration for {service_name} has been deleted.", 0