Reformatted code base using black.
This commit is contained in:
parent
56319cbad3
commit
53fdaa9d09
2 changed files with 12604 additions and 8966 deletions
|
|
@ -45,7 +45,7 @@ try:
|
|||
import yaml
|
||||
except ImportError:
|
||||
raise exceptions.MissingModule(
|
||||
'Missing module PyYAML, please download/install it. You can use '
|
||||
"Missing module PyYAML, please download/install it. You can use "
|
||||
'"pip install pyyaml"'
|
||||
)
|
||||
|
||||
|
|
@ -59,8 +59,6 @@ except ImportError:
|
|||
from yaml import Loader, Dumper
|
||||
|
||||
|
||||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: "Applications Manager",
|
||||
C.PI_IMPORT_NAME: "APP_MANAGER",
|
||||
|
|
@ -73,7 +71,8 @@ PLUGIN_INFO = {
|
|||
|
||||
Manage external applications using packagers, OS virtualization/containers or other
|
||||
software management tools.
|
||||
"""),
|
||||
"""
|
||||
),
|
||||
}
|
||||
|
||||
APP_FILE_PREFIX = "libervia_app_"
|
||||
|
|
@ -92,7 +91,6 @@ class AppManager:
|
|||
# instance id to app data map
|
||||
self._instances = {}
|
||||
|
||||
|
||||
self.persistent_data = persistent.LazyPersistentBinaryDict("app_manager")
|
||||
|
||||
host.bridge.add_method(
|
||||
|
|
@ -128,30 +126,28 @@ class AppManager:
|
|||
)
|
||||
# application has been started succeesfully,
|
||||
# args: name, instance_id, extra
|
||||
host.bridge.add_signal(
|
||||
"application_started", ".plugin", signature="sss"
|
||||
)
|
||||
host.bridge.add_signal("application_started", ".plugin", signature="sss")
|
||||
# application went wrong with the application
|
||||
# args: name, instance_id, extra
|
||||
host.bridge.add_signal(
|
||||
"application_error", ".plugin", signature="sss"
|
||||
host.bridge.add_signal("application_error", ".plugin", signature="sss")
|
||||
yaml.add_constructor("!libervia_conf", self._libervia_conf_constr, Loader=Loader)
|
||||
yaml.add_constructor(
|
||||
"!libervia_generate_pwd", self._libervia_generate_pwd_constr, Loader=Loader
|
||||
)
|
||||
yaml.add_constructor(
|
||||
"!libervia_conf", self._libervia_conf_constr, Loader=Loader)
|
||||
yaml.add_constructor(
|
||||
"!libervia_generate_pwd", self._libervia_generate_pwd_constr, Loader=Loader)
|
||||
yaml.add_constructor(
|
||||
"!libervia_param", self._libervia_param_constr, Loader=Loader)
|
||||
"!libervia_param", self._libervia_param_constr, Loader=Loader
|
||||
)
|
||||
|
||||
def unload(self):
|
||||
log.debug("unloading applications manager")
|
||||
for instances in self._started.values():
|
||||
for instance in instances:
|
||||
data = instance['data']
|
||||
if not data['single_instance']:
|
||||
data = instance["data"]
|
||||
if not data["single_instance"]:
|
||||
log.debug(
|
||||
f"cleaning temporary directory at {data['_instance_dir_path']}")
|
||||
data['_instance_dir_obj'].cleanup()
|
||||
f"cleaning temporary directory at {data['_instance_dir_path']}"
|
||||
)
|
||||
data["_instance_dir_obj"].cleanup()
|
||||
|
||||
def _libervia_conf_constr(self, loader, node) -> str:
|
||||
"""Get a value from Libervia configuration
|
||||
|
|
@ -183,22 +179,27 @@ class AppManager:
|
|||
|
||||
value = self.host.memory.config_get(section, name, default)
|
||||
# FIXME: "public_url" is used only here and doesn't take multi-sites into account
|
||||
if name == "public_url" and (not value or value.startswith('http')):
|
||||
if name == "public_url" and (not value or value.startswith("http")):
|
||||
if not value:
|
||||
log.warning(_(
|
||||
'No value found for "public_url", using "example.org" for '
|
||||
'now, please set the proper value in libervia.conf'))
|
||||
log.warning(
|
||||
_(
|
||||
'No value found for "public_url", using "example.org" for '
|
||||
"now, please set the proper value in libervia.conf"
|
||||
)
|
||||
)
|
||||
else:
|
||||
log.warning(_(
|
||||
'invalid value for "public_url" ({value}), it musts not start with '
|
||||
'schema ("http"), ignoring it and using "example.org" '
|
||||
'instead')
|
||||
.format(value=value))
|
||||
log.warning(
|
||||
_(
|
||||
'invalid value for "public_url" ({value}), it musts not start with '
|
||||
'schema ("http"), ignoring it and using "example.org" '
|
||||
"instead"
|
||||
).format(value=value)
|
||||
)
|
||||
value = "example.org"
|
||||
|
||||
if filter_ is None:
|
||||
pass
|
||||
elif filter_ == 'first':
|
||||
elif filter_ == "first":
|
||||
value = value[0]
|
||||
elif filter_ == "not":
|
||||
value = C.bool(value)
|
||||
|
|
@ -232,7 +233,7 @@ class AppManager:
|
|||
except KeyError:
|
||||
alphabet = string.ascii_letters + string.digits
|
||||
key_size = int(kwargs.get("size", 30))
|
||||
key = ''.join(secrets.choice(alphabet) for __ in range(key_size))
|
||||
key = "".join(secrets.choice(alphabet) for __ in range(key_size))
|
||||
self._app_persistent_data[pwd_data_key] = key
|
||||
else:
|
||||
log.debug(f"Re-using existing key for {name!r} password.")
|
||||
|
|
@ -255,7 +256,8 @@ class AppManager:
|
|||
name = manager.name
|
||||
if name in self._managers:
|
||||
raise exceptions.ConflictError(
|
||||
f"There is already a manager with the name {name}")
|
||||
f"There is already a manager with the name {name}"
|
||||
)
|
||||
self._managers[manager.name] = manager
|
||||
if manager.discover_path is not None:
|
||||
self.discover(manager.discover_path, manager)
|
||||
|
|
@ -269,25 +271,18 @@ class AppManager:
|
|||
try:
|
||||
app_type = app_data["type"]
|
||||
except KeyError:
|
||||
raise exceptions.DataError(
|
||||
"app file doesn't have the mandatory \"type\" key"
|
||||
)
|
||||
raise exceptions.DataError('app file doesn\'t have the mandatory "type" key')
|
||||
if not isinstance(app_type, str):
|
||||
raise exceptions.DataError(
|
||||
f"invalid app data type: {app_type!r}"
|
||||
)
|
||||
raise exceptions.DataError(f"invalid app data type: {app_type!r}")
|
||||
app_type = app_type.strip()
|
||||
try:
|
||||
return self._managers[app_type]
|
||||
except KeyError:
|
||||
raise exceptions.NotFound(
|
||||
f"No manager found to manage app of type {app_type!r}")
|
||||
f"No manager found to manage app of type {app_type!r}"
|
||||
)
|
||||
|
||||
def get_app_data(
|
||||
self,
|
||||
id_type: Optional[str],
|
||||
identifier: str
|
||||
) -> dict:
|
||||
def get_app_data(self, id_type: Optional[str], identifier: str) -> dict:
|
||||
"""Retrieve instance's app_data from identifier
|
||||
|
||||
@param id_type: type of the identifier, can be:
|
||||
|
|
@ -300,8 +295,8 @@ class AppManager:
|
|||
@raise ValueError: id_type is invalid
|
||||
"""
|
||||
if not id_type:
|
||||
id_type = 'name'
|
||||
if id_type == 'name':
|
||||
id_type = "name"
|
||||
if id_type == "name":
|
||||
identifier = identifier.lower().strip()
|
||||
try:
|
||||
return next(iter(self._started[identifier]))
|
||||
|
|
@ -309,7 +304,7 @@ class AppManager:
|
|||
raise exceptions.NotFound(
|
||||
f"No instance of {identifier!r} is currently running"
|
||||
)
|
||||
elif id_type == 'instance':
|
||||
elif id_type == "instance":
|
||||
instance_id = identifier
|
||||
try:
|
||||
return self._instances[instance_id]
|
||||
|
|
@ -320,11 +315,7 @@ class AppManager:
|
|||
else:
|
||||
raise ValueError(f"invalid id_type: {id_type!r}")
|
||||
|
||||
def discover(
|
||||
self,
|
||||
dir_path: Path,
|
||||
manager: AppManagerBackend|None = None
|
||||
) -> None:
|
||||
def discover(self, dir_path: Path, manager: AppManagerBackend | None = None) -> None:
|
||||
"""Search for app configuration file.
|
||||
|
||||
App configuration files must start with [APP_FILE_PREFIX] and have a ``.yaml``
|
||||
|
|
@ -336,19 +327,16 @@ class AppManager:
|
|||
app_data = self.parse(file_path)
|
||||
manager = self.get_manager(app_data)
|
||||
except (exceptions.DataError, exceptions.NotFound) as e:
|
||||
log.warning(
|
||||
f"Can't parse {file_path}, skipping: {e}")
|
||||
log.warning(f"Can't parse {file_path}, skipping: {e}")
|
||||
continue
|
||||
app_name = file_path.stem[len(APP_FILE_PREFIX):].strip().lower()
|
||||
app_name = file_path.stem[len(APP_FILE_PREFIX) :].strip().lower()
|
||||
if not app_name:
|
||||
log.warning(f"invalid app file name at {file_path}")
|
||||
continue
|
||||
app_dict = self._apps.setdefault(app_name, {})
|
||||
manager_set = app_dict.setdefault(manager, set())
|
||||
manager_set.add(file_path)
|
||||
log.debug(
|
||||
f"{app_name!r} {manager.name} application found"
|
||||
)
|
||||
log.debug(f"{app_name!r} {manager.name} application found")
|
||||
|
||||
def parse(self, file_path: Path, params: Optional[dict] = None) -> dict:
|
||||
"""Parse Libervia application file
|
||||
|
|
@ -367,7 +355,7 @@ class AppManager:
|
|||
if "name" not in app_data:
|
||||
# note that we don't use lower() here as we want human readable name and
|
||||
# uppercase may be set on purpose
|
||||
app_data['name'] = file_path.stem[len(APP_FILE_PREFIX):].strip()
|
||||
app_data["name"] = file_path.stem[len(APP_FILE_PREFIX) :].strip()
|
||||
single_instance = app_data.setdefault("single_instance", True)
|
||||
if not isinstance(single_instance, bool):
|
||||
raise ValueError(
|
||||
|
|
@ -429,9 +417,7 @@ class AppManager:
|
|||
try:
|
||||
app_file_path = next(iter(next(iter(self._apps[app_name].values()))))
|
||||
except KeyError:
|
||||
raise exceptions.NotFound(
|
||||
f"No application found with the name {app_name!r}"
|
||||
)
|
||||
raise exceptions.NotFound(f"No application found with the name {app_name!r}")
|
||||
log.info(f"starting {app_name!r}")
|
||||
self._app_persistent_data = await self.persistent_data.get(app_name) or {}
|
||||
self._app_persistent_data["last_started"] = time.time()
|
||||
|
|
@ -439,13 +425,10 @@ class AppManager:
|
|||
app_data = self.parse(app_file_path, extra)
|
||||
await self.persistent_data.aset(app_name, self._app_persistent_data)
|
||||
app_data["_started"] = False
|
||||
app_data['_file_path'] = app_file_path
|
||||
app_data['_name_canonical'] = app_name
|
||||
single_instance = app_data['single_instance']
|
||||
ret_data = {
|
||||
"name": app_name,
|
||||
"started": False
|
||||
}
|
||||
app_data["_file_path"] = app_file_path
|
||||
app_data["_name_canonical"] = app_name
|
||||
single_instance = app_data["single_instance"]
|
||||
ret_data = {"name": app_name, "started": False}
|
||||
if single_instance:
|
||||
if started_data:
|
||||
instance_data = started_data[0]
|
||||
|
|
@ -462,14 +445,14 @@ class AppManager:
|
|||
PLUGIN_INFO[C.PI_IMPORT_NAME], app_name
|
||||
)
|
||||
cache_path.mkdir(0o700, parents=True, exist_ok=True)
|
||||
app_data['_instance_dir_path'] = cache_path
|
||||
app_data["_instance_dir_path"] = cache_path
|
||||
else:
|
||||
dest_dir_obj = tempfile.TemporaryDirectory(prefix="libervia_app_")
|
||||
app_data['_instance_dir_obj'] = dest_dir_obj
|
||||
app_data['_instance_dir_path'] = Path(dest_dir_obj.name)
|
||||
instance_id = ret_data["instance"] = app_data['_instance_id'] = shortuuid.uuid()
|
||||
app_data["_instance_dir_obj"] = dest_dir_obj
|
||||
app_data["_instance_dir_path"] = Path(dest_dir_obj.name)
|
||||
instance_id = ret_data["instance"] = app_data["_instance_id"] = shortuuid.uuid()
|
||||
manager = self.get_manager(app_data)
|
||||
app_data['_manager'] = manager
|
||||
app_data["_manager"] = manager
|
||||
started_data.append(app_data)
|
||||
self._instances[instance_id] = app_data
|
||||
# we retrieve exposed data such as url_prefix which can be useful computed exposed
|
||||
|
|
@ -482,7 +465,7 @@ class AppManager:
|
|||
start = manager.start
|
||||
except AttributeError:
|
||||
raise exceptions.InternalError(
|
||||
f"{manager.name} doesn't have the mandatory \"start\" method"
|
||||
f'{manager.name} doesn\'t have the mandatory "start" method'
|
||||
)
|
||||
else:
|
||||
defer.ensureDeferred(self.start_app(start, app_data))
|
||||
|
|
@ -498,12 +481,8 @@ class AppManager:
|
|||
self.host.bridge.application_error(
|
||||
app_name,
|
||||
instance_id,
|
||||
data_format.serialise(
|
||||
{
|
||||
"class": str(type(e)),
|
||||
"msg": str(e)
|
||||
}
|
||||
))
|
||||
data_format.serialise({"class": str(type(e)), "msg": str(e)}),
|
||||
)
|
||||
else:
|
||||
app_data["_started"] = True
|
||||
self.host.bridge.application_started(app_name, instance_id, "")
|
||||
|
|
@ -512,7 +491,8 @@ class AppManager:
|
|||
def _stop(self, identifier, id_type, extra):
|
||||
extra = data_format.deserialise(extra)
|
||||
return defer.ensureDeferred(
|
||||
self.stop(str(identifier), str(id_type) or None, extra))
|
||||
self.stop(str(identifier), str(id_type) or None, extra)
|
||||
)
|
||||
|
||||
async def stop(
|
||||
self,
|
||||
|
|
@ -527,15 +507,15 @@ class AppManager:
|
|||
|
||||
log.info(f"stopping {app_data['name']!r}")
|
||||
|
||||
app_name = app_data['_name_canonical']
|
||||
instance_id = app_data['_instance_id']
|
||||
manager = app_data['_manager']
|
||||
app_name = app_data["_name_canonical"]
|
||||
instance_id = app_data["_instance_id"]
|
||||
manager = app_data["_manager"]
|
||||
|
||||
try:
|
||||
stop = manager.stop
|
||||
except AttributeError:
|
||||
raise exceptions.InternalError(
|
||||
f"{manager.name} doesn't have the mandatory \"stop\" method"
|
||||
f'{manager.name} doesn\'t have the mandatory "stop" method'
|
||||
)
|
||||
else:
|
||||
try:
|
||||
|
|
@ -551,7 +531,8 @@ class AppManager:
|
|||
del self._instances[instance_id]
|
||||
except KeyError:
|
||||
log.error(
|
||||
f"INTERNAL ERROR: {instance_id!r} is not present in self._instances")
|
||||
f"INTERNAL ERROR: {instance_id!r} is not present in self._instances"
|
||||
)
|
||||
|
||||
try:
|
||||
self._started[app_name].remove(app_data)
|
||||
|
|
@ -590,13 +571,13 @@ class AppManager:
|
|||
to handle manager specific conventions.
|
||||
"""
|
||||
app_data = self.get_app_data(id_type, identifier)
|
||||
if app_data.get('_exposed_computed', False):
|
||||
return app_data['expose']
|
||||
if app_data.get("_exposed_computed", False):
|
||||
return app_data["expose"]
|
||||
if extra is None:
|
||||
extra = {}
|
||||
expose = app_data.setdefault("expose", {})
|
||||
if "passwords" in expose:
|
||||
passwords = expose['passwords']
|
||||
passwords = expose["passwords"]
|
||||
for name, value in list(passwords.items()):
|
||||
if isinstance(value, list):
|
||||
# if we have a list, is the sequence of keys leading to the value
|
||||
|
|
@ -605,7 +586,8 @@ class AppManager:
|
|||
passwords[name] = self.get_app_data_value(value, app_data)
|
||||
except KeyError:
|
||||
log.warning(
|
||||
f"Can't retrieve exposed value for password {name!r}: {e}")
|
||||
f"Can't retrieve exposed value for password {name!r}: {e}"
|
||||
)
|
||||
del passwords[name]
|
||||
|
||||
for key in ("url_prefix", "front_url"):
|
||||
|
|
@ -614,9 +596,7 @@ class AppManager:
|
|||
try:
|
||||
expose[key] = self.get_app_data_value(value, app_data)
|
||||
except KeyError:
|
||||
log.warning(
|
||||
f"Can't retrieve exposed value for {key!r} at {value}"
|
||||
)
|
||||
log.warning(f"Can't retrieve exposed value for {key!r} at {value}")
|
||||
del expose[key]
|
||||
|
||||
front_url = expose.get("front_url")
|
||||
|
|
@ -628,35 +608,35 @@ class AppManager:
|
|||
path_elt = parsed_url.path.split("/", 1)
|
||||
parsed_url = parsed_url._replace(
|
||||
netloc=path_elt[0],
|
||||
path=f"/{path_elt[1]}" if len(path_elt) > 1 else ""
|
||||
path=f"/{path_elt[1]}" if len(path_elt) > 1 else "",
|
||||
)
|
||||
parsed_url = parsed_url._replace(scheme='https')
|
||||
parsed_url = parsed_url._replace(scheme="https")
|
||||
expose["front_url"] = urlunparse(parsed_url)
|
||||
|
||||
if extra.get("skip_compute", False):
|
||||
return expose
|
||||
|
||||
try:
|
||||
compute_expose = app_data['_manager'].compute_expose
|
||||
compute_expose = app_data["_manager"].compute_expose
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
await compute_expose(app_data)
|
||||
|
||||
app_data['_exposed_computed'] = True
|
||||
app_data["_exposed_computed"] = True
|
||||
return expose
|
||||
|
||||
async def _do_prepare(
|
||||
self,
|
||||
app_data: dict,
|
||||
) -> None:
|
||||
name = app_data['name']
|
||||
dest_path = app_data['_instance_dir_path']
|
||||
name = app_data["name"]
|
||||
dest_path = app_data["_instance_dir_path"]
|
||||
if next(dest_path.iterdir(), None) != None:
|
||||
log.debug(f"There is already a prepared dir at {dest_path}, nothing to do")
|
||||
return
|
||||
try:
|
||||
prepare = app_data['prepare'].copy()
|
||||
prepare = app_data["prepare"].copy()
|
||||
except KeyError:
|
||||
prepare = {}
|
||||
|
||||
|
|
@ -668,7 +648,7 @@ class AppManager:
|
|||
log.debug(f"[{name}] [prepare] running {action!r} action")
|
||||
if action == "git":
|
||||
try:
|
||||
git_path = which('git')[0]
|
||||
git_path = which("git")[0]
|
||||
except IndexError:
|
||||
raise exceptions.NotFound(
|
||||
"Can't find \"git\" executable, {name} can't be started without it"
|
||||
|
|
@ -688,8 +668,8 @@ class AppManager:
|
|||
self,
|
||||
app_data: dict,
|
||||
) -> None:
|
||||
dest_path = app_data['_instance_dir_path']
|
||||
files = app_data.get('files')
|
||||
dest_path = app_data["_instance_dir_path"]
|
||||
files = app_data.get("files")
|
||||
if not files:
|
||||
return
|
||||
if not isinstance(files, dict):
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class AppManagerBackend(abc.ABC):
|
|||
"""Abstract class for App Manager."""
|
||||
|
||||
name: str
|
||||
discover_path: Path|None = None
|
||||
discover_path: Path | None = None
|
||||
|
||||
def __init__(self, host) -> None:
|
||||
"""Initialize the App Manager.
|
||||
|
|
|
|||
Loading…
Reference in a new issue