Mercurial > libervia-backend
annotate libervia/backend/plugins/plugin_misc_app_manager.py @ 4140:13dd5660c28f
tests (unit/frontends): tests for webrtc implementation:
rel 426
| author | Goffi <goffi@goffi.org> |
|---|---|
| date | Wed, 01 Nov 2023 14:04:25 +0100 |
| parents | c93b02000ae4 |
| children |
| rev | line source |
|---|---|
| 3372 | 1 #!/usr/bin/env python3 |
| 2 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
3 # Libervia plugin to manage external applications |
| 3479 | 4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) |
| 3372 | 5 |
| 6 # This program is free software: you can redistribute it and/or modify | |
| 7 # it under the terms of the GNU Affero General Public License as published by | |
| 8 # the Free Software Foundation, either version 3 of the License, or | |
| 9 # (at your option) any later version. | |
| 10 | |
| 11 # This program is distributed in the hope that it will be useful, | |
| 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 # GNU Affero General Public License for more details. | |
| 15 | |
| 16 # You should have received a copy of the GNU Affero General Public License | |
| 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 18 | |
| 19 from pathlib import Path | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
20 from typing import Optional, List, Callable |
| 3372 | 21 from functools import partial, reduce |
| 22 import tempfile | |
| 23 import secrets | |
| 24 import string | |
| 25 import shortuuid | |
| 26 from twisted.internet import defer | |
| 27 from twisted.python.procutils import which | |
|
4071
4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents:
4037
diff
changeset
|
28 from libervia.backend.core.i18n import _ |
|
4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents:
4037
diff
changeset
|
29 from libervia.backend.core import exceptions |
|
4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents:
4037
diff
changeset
|
30 from libervia.backend.core.constants import Const as C |
|
4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents:
4037
diff
changeset
|
31 from libervia.backend.core.log import getLogger |
|
4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents:
4037
diff
changeset
|
32 from libervia.backend.tools.common import data_format |
|
4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
Goffi <goffi@goffi.org>
parents:
4037
diff
changeset
|
33 from libervia.backend.tools.common import async_process |
| 3372 | 34 |
| 35 log = getLogger(__name__) | |
| 36 | |
| 37 try: | |
| 38 import yaml | |
| 39 except ImportError: | |
| 40 raise exceptions.MissingModule( | |
| 41 'Missing module PyYAML, please download/install it. You can use ' | |
| 42 '"pip install pyyaml"' | |
| 43 ) | |
| 44 | |
| 45 try: | |
| 46 from yaml import CLoader as Loader, CDumper as Dumper | |
| 47 except ImportError: | |
| 48 log.warning( | |
| 49 "Can't use LibYAML binding (is libyaml installed?), pure Python version will be " | |
| 50 "used, but it is slower" | |
| 51 ) | |
| 52 from yaml import Loader, Dumper | |
| 53 | |
| 54 from yaml.constructor import ConstructorError | |
| 55 | |
| 56 | |
| 57 PLUGIN_INFO = { | |
| 58 C.PI_NAME: "Applications Manager", | |
| 59 C.PI_IMPORT_NAME: "APP_MANAGER", | |
| 60 C.PI_TYPE: C.PLUG_TYPE_MISC, | |
| 61 C.PI_MODES: C.PLUG_MODE_BOTH, | |
| 62 C.PI_MAIN: "AppManager", | |
| 63 C.PI_HANDLER: "no", | |
| 64 C.PI_DESCRIPTION: _( | |
| 65 """Applications Manager | |
| 66 | |
| 67 Manage external applications using packagers, OS virtualization/containers or other | |
| 68 software management tools. | |
| 69 """), | |
| 70 } | |
| 71 | |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
72 APP_FILE_PREFIX = "libervia_app_" |
| 3372 | 73 |
| 74 | |
| 75 class AppManager: | |
| 76 load = partial(yaml.load, Loader=Loader) | |
| 77 dump = partial(yaml.dump, Dumper=Dumper) | |
| 78 | |
| 79 def __init__(self, host): | |
| 80 log.info(_("plugin Applications Manager initialization")) | |
| 81 self.host = host | |
| 82 self._managers = {} | |
| 83 self._apps = {} | |
| 84 self._started = {} | |
| 85 # instance id to app data map | |
| 86 self._instances = {} | |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
87 host.bridge.add_method( |
|
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
88 "applications_list", |
| 3372 | 89 ".plugin", |
| 90 in_sign="as", | |
| 91 out_sign="as", | |
| 92 method=self.list_applications, | |
| 93 ) | |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
94 host.bridge.add_method( |
|
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
95 "application_start", |
| 3372 | 96 ".plugin", |
| 97 in_sign="ss", | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
98 out_sign="s", |
| 3372 | 99 method=self._start, |
| 100 async_=True, | |
| 101 ) | |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
102 host.bridge.add_method( |
|
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
103 "application_stop", |
| 3372 | 104 ".plugin", |
| 105 in_sign="sss", | |
| 106 out_sign="", | |
| 107 method=self._stop, | |
| 108 async_=True, | |
| 109 ) | |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
110 host.bridge.add_method( |
|
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
111 "application_exposed_get", |
| 3372 | 112 ".plugin", |
| 113 in_sign="sss", | |
| 114 out_sign="s", | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
115 method=self._get_exposed, |
| 3372 | 116 async_=True, |
| 117 ) | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
118 # application has been started succeesfully, |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
119 # args: name, instance_id, extra |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
120 host.bridge.add_signal( |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
121 "application_started", ".plugin", signature="sss" |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
122 ) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
123 # application went wrong with the application |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
124 # args: name, instance_id, extra |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
125 host.bridge.add_signal( |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
126 "application_error", ".plugin", signature="sss" |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
127 ) |
| 3372 | 128 yaml.add_constructor( |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
129 "!libervia_conf", self._libervia_conf_constr, Loader=Loader) |
| 3372 | 130 yaml.add_constructor( |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
131 "!libervia_generate_pwd", self._libervia_generate_pwd_constr, Loader=Loader) |
| 3372 | 132 yaml.add_constructor( |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
133 "!libervia_param", self._libervia_param_constr, Loader=Loader) |
| 3372 | 134 |
| 135 def unload(self): | |
| 136 log.debug("unloading applications manager") | |
| 137 for instances in self._started.values(): | |
| 138 for instance in instances: | |
| 139 data = instance['data'] | |
| 140 if not data['single_instance']: | |
| 141 log.debug( | |
| 142 f"cleaning temporary directory at {data['_instance_dir_path']}") | |
| 143 data['_instance_dir_obj'].cleanup() | |
| 144 | |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
145 def _libervia_conf_constr(self, loader, node): |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
146 """Get a value from Libervia configuration |
| 3372 | 147 |
| 148 A list is expected with either "name" of a config parameter, a one or more of | |
| 149 those parameters: | |
| 150 - section | |
| 151 - name | |
| 152 - default value | |
| 153 - filter | |
| 154 filter can be: | |
| 155 - "first": get the first item of the value | |
| 156 """ | |
| 157 config_data = loader.construct_sequence(node) | |
| 158 if len(config_data) == 1: | |
| 159 section, name, default, filter_ = "", config_data[0], None, None | |
| 160 if len(config_data) == 2: | |
| 161 (section, name), default, filter_ = config_data, None, None | |
| 162 elif len(config_data) == 3: | |
| 163 (section, name, default), filter_ = config_data, None | |
| 164 elif len(config_data) == 4: | |
| 165 section, name, default, filter_ = config_data | |
| 166 else: | |
| 167 raise ValueError( | |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
168 f"invalid !libervia_conf value ({config_data!r}), a list of 1 to 4 items is " |
| 3372 | 169 "expected" |
| 170 ) | |
| 171 | |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
172 value = self.host.memory.config_get(section, name, default) |
|
3564
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
173 # FIXME: "public_url" is used only here and doesn't take multi-sites into account |
|
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
174 if name == "public_url" and (not value or value.startswith('http')): |
| 3372 | 175 if not value: |
| 176 log.warning(_( | |
|
3564
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
177 'No value found for "public_url", using "example.org" for ' |
|
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
178 'now, please set the proper value in libervia.conf')) |
| 3372 | 179 else: |
| 180 log.warning(_( | |
|
3564
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
181 'invalid value for "public_url" ({value}), it musts not start with ' |
|
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
182 'schema ("http"), ignoring it and using "example.org" ' |
|
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
183 'instead') |
| 3372 | 184 .format(value=value)) |
|
3564
2c9e95796371
plugin app manager: "public_url" must NOT have a schema
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
185 value = "example.org" |
| 3372 | 186 |
| 187 if filter_ is None: | |
| 188 pass | |
| 189 elif filter_ == 'first': | |
| 190 value = value[0] | |
| 191 else: | |
| 192 raise ValueError(f"unmanaged filter: {filter_}") | |
| 193 | |
| 194 return value | |
| 195 | |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
196 def _libervia_generate_pwd_constr(self, loader, node): |
| 3372 | 197 alphabet = string.ascii_letters + string.digits |
| 198 return ''.join(secrets.choice(alphabet) for i in range(30)) | |
| 199 | |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
200 def _libervia_param_constr(self, loader, node): |
| 3372 | 201 """Get a parameter specified when starting the application |
| 202 | |
| 203 The value can be either the name of the parameter to get, or a list as | |
| 204 [name, default_value] | |
| 205 """ | |
| 206 try: | |
| 207 name, default = loader.construct_sequence(node) | |
| 208 except ConstructorError: | |
| 209 name, default = loader.construct_scalar(node), None | |
| 210 return self._params.get(name, default) | |
| 211 | |
| 212 def register(self, manager): | |
| 213 name = manager.name | |
| 214 if name in self._managers: | |
| 215 raise exceptions.ConflictError( | |
| 216 f"There is already a manager with the name {name}") | |
| 217 self._managers[manager.name] = manager | |
| 218 if hasattr(manager, "discover_path"): | |
| 219 self.discover(manager.discover_path, manager) | |
| 220 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
221 def get_manager(self, app_data: dict) -> object: |
| 3372 | 222 """Get manager instance needed for this app |
| 223 | |
| 224 @raise exceptions.DataError: something is wrong with the type | |
| 225 @raise exceptions.NotFound: manager is not registered | |
| 226 """ | |
| 227 try: | |
| 228 app_type = app_data["type"] | |
| 229 except KeyError: | |
| 230 raise exceptions.DataError( | |
| 231 "app file doesn't have the mandatory \"type\" key" | |
| 232 ) | |
| 233 if not isinstance(app_type, str): | |
| 234 raise exceptions.DataError( | |
| 235 f"invalid app data type: {app_type!r}" | |
| 236 ) | |
| 237 app_type = app_type.strip() | |
| 238 try: | |
| 239 return self._managers[app_type] | |
| 240 except KeyError: | |
| 241 raise exceptions.NotFound( | |
| 242 f"No manager found to manage app of type {app_type!r}") | |
| 243 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
244 def get_app_data( |
| 3372 | 245 self, |
| 246 id_type: Optional[str], | |
| 247 identifier: str | |
| 248 ) -> dict: | |
| 249 """Retrieve instance's app_data from identifier | |
| 250 | |
| 251 @param id_type: type of the identifier, can be: | |
| 252 - "name": identifier is a canonical application name | |
| 253 the first found instance of this application is returned | |
| 254 - "instance": identifier is an instance id | |
| 255 @param identifier: identifier according to id_type | |
| 256 @return: instance application data | |
| 257 @raise exceptions.NotFound: no instance with this id can be found | |
| 258 @raise ValueError: id_type is invalid | |
| 259 """ | |
| 260 if not id_type: | |
| 261 id_type = 'name' | |
| 262 if id_type == 'name': | |
| 263 identifier = identifier.lower().strip() | |
| 264 try: | |
| 265 return next(iter(self._started[identifier])) | |
| 266 except (KeyError, StopIteration): | |
| 267 raise exceptions.NotFound( | |
| 268 f"No instance of {identifier!r} is currently running" | |
| 269 ) | |
| 270 elif id_type == 'instance': | |
| 271 instance_id = identifier | |
| 272 try: | |
| 273 return self._instances[instance_id] | |
| 274 except KeyError: | |
| 275 raise exceptions.NotFound( | |
| 276 f"There is no application instance running with id {instance_id!r}" | |
| 277 ) | |
| 278 else: | |
| 279 raise ValueError(f"invalid id_type: {id_type!r}") | |
| 280 | |
| 281 def discover( | |
| 282 self, | |
| 283 dir_path: Path, | |
| 284 manager: Optional = None | |
| 285 ) -> None: | |
| 286 for file_path in dir_path.glob(f"{APP_FILE_PREFIX}*.yaml"): | |
| 287 if manager is None: | |
| 288 try: | |
| 289 app_data = self.parse(file_path) | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
290 manager = self.get_manager(app_data) |
| 3372 | 291 except (exceptions.DataError, exceptions.NotFound) as e: |
| 292 log.warning( | |
| 293 f"Can't parse {file_path}, skipping: {e}") | |
| 294 app_name = file_path.stem[len(APP_FILE_PREFIX):].strip().lower() | |
| 295 if not app_name: | |
| 296 log.warning( | |
| 297 f"invalid app file name at {file_path}") | |
| 298 continue | |
| 299 app_dict = self._apps.setdefault(app_name, {}) | |
| 300 manager_set = app_dict.setdefault(manager, set()) | |
| 301 manager_set.add(file_path) | |
| 302 log.debug( | |
| 303 f"{app_name!r} {manager.name} application found" | |
| 304 ) | |
| 305 | |
| 306 def parse(self, file_path: Path, params: Optional[dict] = None) -> dict: | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
307 """Parse Libervia application file |
| 3372 | 308 |
| 309 @param params: parameters for running this instance | |
| 310 @raise exceptions.DataError: something is wrong in the file | |
| 311 """ | |
| 312 if params is None: | |
| 313 params = {} | |
| 314 with file_path.open() as f: | |
| 315 # we set parameters to be used only with this instance | |
| 316 # no async method must used between this assignation and `load` | |
| 317 self._params = params | |
| 318 app_data = self.load(f) | |
| 319 self._params = None | |
| 320 if "name" not in app_data: | |
| 321 # note that we don't use lower() here as we want human readable name and | |
| 322 # uppercase may be set on purpose | |
| 323 app_data['name'] = file_path.stem[len(APP_FILE_PREFIX):].strip() | |
| 324 single_instance = app_data.setdefault("single_instance", True) | |
| 325 if not isinstance(single_instance, bool): | |
| 326 raise ValueError( | |
| 327 f'"single_instance" must be a boolean, but it is {type(single_instance)}' | |
| 328 ) | |
| 329 return app_data | |
| 330 | |
| 331 def list_applications(self, filters: Optional[List[str]]) -> List[str]: | |
| 332 """List available application | |
| 333 | |
| 334 @param filters: only show applications matching those filters. | |
| 335 using None will list all known applications | |
| 336 a filter can be: | |
| 337 - available: applications available locally | |
| 338 - running: only show launched applications | |
| 339 """ | |
| 340 if not filters: | |
| 341 return list(self.apps) | |
| 342 found = set() | |
| 343 for filter_ in filters: | |
| 344 if filter_ == "available": | |
| 345 found.update(self._apps) | |
| 346 elif filter_ == "running": | |
| 347 found.update(self._started) | |
| 348 else: | |
| 349 raise ValueError(f"Unknown filter: {filter_}") | |
| 350 return list(found) | |
| 351 | |
| 352 def _start(self, app_name, extra): | |
| 353 extra = data_format.deserialise(extra) | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
354 d = defer.ensureDeferred(self.start(str(app_name), extra)) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
355 d.addCallback(data_format.serialise) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
356 return d |
| 3372 | 357 |
| 358 async def start( | |
| 359 self, | |
| 360 app_name: str, | |
| 361 extra: Optional[dict] = None, | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
362 ) -> dict: |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
363 """Start an application |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
364 |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
365 @param app_name: name of the application to start |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
366 @param extra: extra parameters |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
367 @return: data with following keys: |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
368 - name (str): canonical application name |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
369 - instance (str): instance ID |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
370 - started (bool): True if the application is already started |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
371 if False, the "application_started" signal should be used to get notificed |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
372 when the application is actually started |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
373 - expose (dict): exposed data as given by [self.get_exposed] |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
374 exposed data which need to be computed are NOT returned, they will |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
375 available when the app will be fully started, throught the |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
376 [self.get_exposed] method. |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
377 """ |
| 3372 | 378 # FIXME: for now we use the first app manager available for the requested app_name |
| 379 # TODO: implement running multiple instance of the same app if some metadata | |
| 380 # to be defined in app_data allows explicitly it. | |
| 381 app_name = app_name.lower().strip() | |
| 382 try: | |
| 383 app_file_path = next(iter(next(iter(self._apps[app_name].values())))) | |
| 384 except KeyError: | |
| 385 raise exceptions.NotFound( | |
| 386 f"No application found with the name {app_name!r}" | |
| 387 ) | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
388 log.info(f"starting {app_name!r}") |
| 3372 | 389 started_data = self._started.setdefault(app_name, []) |
| 390 app_data = self.parse(app_file_path, extra) | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
391 app_data["_started"] = False |
| 3372 | 392 app_data['_file_path'] = app_file_path |
| 393 app_data['_name_canonical'] = app_name | |
| 394 single_instance = app_data['single_instance'] | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
395 ret_data = { |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
396 "name": app_name, |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
397 "started": False |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
398 } |
| 3372 | 399 if single_instance: |
| 400 if started_data: | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
401 instance_data = started_data[0] |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
402 instance_id = instance_data["_instance_id"] |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
403 ret_data["instance"] = instance_id |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
404 ret_data["started"] = instance_data["_started"] |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
405 ret_data["expose"] = await self.get_exposed( |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
406 instance_id, "instance", {"skip_compute": True} |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
407 ) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
408 log.info(f"{app_name!r} is already started or being started") |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
409 return ret_data |
| 3372 | 410 else: |
|
4037
524856bd7b19
massive refactoring to switch from camelCase to snake_case:
Goffi <goffi@goffi.org>
parents:
3998
diff
changeset
|
411 cache_path = self.host.memory.get_cache_path( |
| 3372 | 412 PLUGIN_INFO[C.PI_IMPORT_NAME], app_name |
| 413 ) | |
| 414 cache_path.mkdir(0o700, parents=True, exist_ok=True) | |
| 415 app_data['_instance_dir_path'] = cache_path | |
| 416 else: | |
|
4085
c93b02000ae4
plugin app manager: rename `sat` to `libervia`
Goffi <goffi@goffi.org>
parents:
4071
diff
changeset
|
417 dest_dir_obj = tempfile.TemporaryDirectory(prefix="libervia_app_") |
| 3372 | 418 app_data['_instance_dir_obj'] = dest_dir_obj |
| 419 app_data['_instance_dir_path'] = Path(dest_dir_obj.name) | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
420 instance_id = ret_data["instance"] = app_data['_instance_id'] = shortuuid.uuid() |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
421 manager = self.get_manager(app_data) |
| 3372 | 422 app_data['_manager'] = manager |
| 423 started_data.append(app_data) | |
| 424 self._instances[instance_id] = app_data | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
425 # we retrieve exposed data such as url_prefix which can be useful computed exposed |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
426 # data must wait for the app to be started, so we skip them for now |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
427 ret_data["expose"] = await self.get_exposed( |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
428 instance_id, "instance", {"skip_compute": True} |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
429 ) |
| 3372 | 430 |
| 431 try: | |
| 432 start = manager.start | |
| 433 except AttributeError: | |
| 434 raise exceptions.InternalError( | |
| 435 f"{manager.name} doesn't have the mandatory \"start\" method" | |
| 436 ) | |
| 437 else: | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
438 defer.ensureDeferred(self.start_app(start, app_data)) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
439 return ret_data |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
440 |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
441 async def start_app(self, start_cb: Callable, app_data: dict) -> None: |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
442 app_name = app_data["_name_canonical"] |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
443 instance_id = app_data["_instance_id"] |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
444 try: |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
445 await start_cb(app_data) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
446 except Exception as e: |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
447 log.exception(f"Can't start libervia app {app_name!r}") |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
448 self.host.bridge.application_error( |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
449 app_name, |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
450 instance_id, |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
451 data_format.serialise( |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
452 { |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
453 "class": str(type(e)), |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
454 "msg": str(e) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
455 } |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
456 )) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
457 else: |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
458 app_data["_started"] = True |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
459 self.host.bridge.application_started(app_name, instance_id, "") |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
460 log.info(f"{app_name!r} started") |
| 3372 | 461 |
| 462 def _stop(self, identifier, id_type, extra): | |
| 463 extra = data_format.deserialise(extra) | |
| 464 return defer.ensureDeferred( | |
| 465 self.stop(str(identifier), str(id_type) or None, extra)) | |
| 466 | |
| 467 async def stop( | |
| 468 self, | |
| 469 identifier: str, | |
| 470 id_type: Optional[str] = None, | |
| 471 extra: Optional[dict] = None, | |
| 472 ) -> None: | |
| 473 if extra is None: | |
| 474 extra = {} | |
| 475 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
476 app_data = self.get_app_data(id_type, identifier) |
| 3372 | 477 |
| 478 log.info(f"stopping {app_data['name']!r}") | |
| 479 | |
| 480 app_name = app_data['_name_canonical'] | |
| 481 instance_id = app_data['_instance_id'] | |
| 482 manager = app_data['_manager'] | |
| 483 | |
| 484 try: | |
| 485 stop = manager.stop | |
| 486 except AttributeError: | |
| 487 raise exceptions.InternalError( | |
| 488 f"{manager.name} doesn't have the mandatory \"stop\" method" | |
| 489 ) | |
| 490 else: | |
| 491 try: | |
| 492 await stop(app_data) | |
| 493 except Exception as e: | |
| 494 log.warning( | |
| 495 f"Instance {instance_id} of application {app_name} can't be stopped " | |
| 496 f"properly: {e}" | |
| 497 ) | |
| 498 return | |
| 499 | |
| 500 try: | |
| 501 del self._instances[instance_id] | |
| 502 except KeyError: | |
| 503 log.error( | |
| 504 f"INTERNAL ERROR: {instance_id!r} is not present in self._instances") | |
| 505 | |
| 506 try: | |
| 507 self._started[app_name].remove(app_data) | |
| 508 except ValueError: | |
| 509 log.error( | |
| 510 "INTERNAL ERROR: there is no app data in self._started with id " | |
| 511 f"{instance_id!r}" | |
| 512 ) | |
| 513 | |
| 514 log.info(f"{app_name!r} stopped") | |
| 515 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
516 def _get_exposed(self, identifier, id_type, extra): |
| 3372 | 517 extra = data_format.deserialise(extra) |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
518 d = defer.ensureDeferred(self.get_exposed(identifier, id_type, extra)) |
| 3372 | 519 d.addCallback(lambda d: data_format.serialise(d)) |
| 520 return d | |
| 521 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
522 async def get_exposed( |
| 3372 | 523 self, |
| 524 identifier: str, | |
| 525 id_type: Optional[str] = None, | |
| 526 extra: Optional[dict] = None, | |
| 527 ) -> dict: | |
| 528 """Get data exposed by the application | |
| 529 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
530 The manager's "compute_expose" method will be called if it exists. It can be used |
| 3372 | 531 to handle manager specific conventions. |
| 532 """ | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
533 app_data = self.get_app_data(id_type, identifier) |
| 3372 | 534 if app_data.get('_exposed_computed', False): |
| 535 return app_data['expose'] | |
| 536 if extra is None: | |
| 537 extra = {} | |
| 538 expose = app_data.setdefault("expose", {}) | |
| 539 if "passwords" in expose: | |
| 540 passwords = expose['passwords'] | |
| 541 for name, value in list(passwords.items()): | |
| 542 if isinstance(value, list): | |
| 543 # if we have a list, is the sequence of keys leading to the value | |
| 544 # to expose. We use "reduce" to retrieve the desired value | |
| 545 try: | |
| 546 passwords[name] = reduce(lambda l, k: l[k], value, app_data) | |
| 547 except Exception as e: | |
| 548 log.warning( | |
| 549 f"Can't retrieve exposed value for password {name!r}: {e}") | |
| 550 del passwords[name] | |
| 551 | |
| 552 url_prefix = expose.get("url_prefix") | |
| 553 if isinstance(url_prefix, list): | |
| 554 try: | |
| 555 expose["url_prefix"] = reduce(lambda l, k: l[k], url_prefix, app_data) | |
| 556 except Exception as e: | |
| 557 log.warning( | |
| 558 f"Can't retrieve exposed value for url_prefix: {e}") | |
| 559 del expose["url_prefix"] | |
| 560 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
561 if extra.get("skip_compute", False): |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
562 return expose |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
563 |
| 3372 | 564 try: |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
565 compute_expose = app_data['_manager'].compute_expose |
| 3372 | 566 except AttributeError: |
| 567 pass | |
| 568 else: | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
569 await compute_expose(app_data) |
| 3372 | 570 |
| 571 app_data['_exposed_computed'] = True | |
| 572 return expose | |
| 573 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
574 async def _do_prepare( |
| 3372 | 575 self, |
| 576 app_data: dict, | |
| 577 ) -> None: | |
| 578 name = app_data['name'] | |
| 579 dest_path = app_data['_instance_dir_path'] | |
| 580 if next(dest_path.iterdir(), None) != None: | |
| 581 log.debug(f"There is already a prepared dir at {dest_path}, nothing to do") | |
| 582 return | |
| 583 try: | |
| 584 prepare = app_data['prepare'].copy() | |
| 585 except KeyError: | |
| 586 prepare = {} | |
| 587 | |
| 588 if not prepare: | |
| 589 log.debug("Nothing to prepare for {name!r}") | |
| 590 return | |
| 591 | |
| 592 for action, value in list(prepare.items()): | |
| 593 log.debug(f"[{name}] [prepare] running {action!r} action") | |
| 594 if action == "git": | |
| 595 try: | |
| 596 git_path = which('git')[0] | |
| 597 except IndexError: | |
| 598 raise exceptions.NotFound( | |
| 599 "Can't find \"git\" executable, {name} can't be started without it" | |
| 600 ) | |
| 601 await async_process.run(git_path, "clone", value, str(dest_path)) | |
| 602 log.debug(f"{value!r} git repository cloned at {dest_path}") | |
| 603 else: | |
| 604 raise NotImplementedError( | |
| 605 f"{action!r} is not managed, can't start {name}" | |
| 606 ) | |
| 607 del prepare[action] | |
| 608 | |
| 609 if prepare: | |
| 610 raise exceptions.InternalError('"prepare" should be empty') | |
| 611 | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
612 async def _do_create_files( |
|
3565
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
613 self, |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
614 app_data: dict, |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
615 ) -> None: |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
616 dest_path = app_data['_instance_dir_path'] |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
617 files = app_data.get('files') |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
618 if not files: |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
619 return |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
620 if not isinstance(files, dict): |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
621 raise ValueError('"files" must be a dictionary') |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
622 for filename, data in files.items(): |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
623 path = dest_path / filename |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
624 if path.is_file(): |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
625 log.info(f"{path} already exists, skipping") |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
626 with path.open("w") as f: |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
627 f.write(data.get("content", "")) |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
628 log.debug(f"{path} created") |
|
d66a8453b02b
plugin app manager: add a way to create files:
Goffi <goffi@goffi.org>
parents:
3564
diff
changeset
|
629 |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
630 async def start_common(self, app_data: dict) -> None: |
| 3372 | 631 """Method running common action when starting a manager |
| 632 | |
| 633 It should be called by managers in "start" method. | |
| 634 """ | |
|
3998
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
635 await self._do_prepare(app_data) |
|
402d31527af4
plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents:
3565
diff
changeset
|
636 await self._do_create_files(app_data) |
