annotate sat/plugins/plugin_misc_app_manager.py @ 3998:402d31527af4

plugin app manager: `start` doesn't wait anymore for actual app start: Application may be long to start (e.g. a Docker app may have to download images first, and even without the downloading, the starting could be long), which may lead to UI blocking or bridge time out. To prevent that, `start` is now returning immediately, and 2 new signals are used to indicate when the application is started, of if something wrong happened. `start` now returns initial app data, including exposed data without the computed exposed data. The computed data must be retrieved after the app has been started.
author Goffi <goffi@goffi.org>
date Sat, 04 Mar 2023 18:30:47 +0100
parents d66a8453b02b
children 524856bd7b19
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
3372
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/env python3
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
be6d91572633 date update
Goffi <goffi@goffi.org>
parents: 3417
diff changeset
4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
3372
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
5
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 # This program is free software: you can redistribute it and/or modify
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # it under the terms of the GNU Affero General Public License as published by
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # the Free Software Foundation, either version 3 of the License, or
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # (at your option) any later version.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 # This program is distributed in the hope that it will be useful,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # GNU Affero General Public License for more details.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
15
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 # You should have received a copy of the GNU Affero General Public License
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
18
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 from functools import partial, reduce
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
22 import tempfile
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
23 import secrets
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 import string
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 import shortuuid
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
26 from twisted.internet import defer
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
27 from twisted.python.procutils import which
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
28 from sat.core.i18n import _
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
29 from sat.core import exceptions
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
30 from sat.core.constants import Const as C
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
31 from sat.core.log import getLogger
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
32 from sat.tools.common import data_format
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
33 from sat.tools.common import async_process
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
34
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
35 log = getLogger(__name__)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
36
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
37 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
38 import yaml
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
39 except ImportError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
40 raise exceptions.MissingModule(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 'Missing module PyYAML, please download/install it. You can use '
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
42 '"pip install pyyaml"'
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
43 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
44
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
46 from yaml import CLoader as Loader, CDumper as Dumper
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
47 except ImportError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
48 log.warning(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
49 "Can't use LibYAML binding (is libyaml installed?), pure Python version will be "
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
50 "used, but it is slower"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 from yaml import Loader, Dumper
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
53
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 from yaml.constructor import ConstructorError
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
55
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
56
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 PLUGIN_INFO = {
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 C.PI_NAME: "Applications Manager",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 C.PI_IMPORT_NAME: "APP_MANAGER",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 C.PI_TYPE: C.PLUG_TYPE_MISC,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 C.PI_MODES: C.PLUG_MODE_BOTH,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 C.PI_MAIN: "AppManager",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 C.PI_HANDLER: "no",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 C.PI_DESCRIPTION: _(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
65 """Applications Manager
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
66
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 Manage external applications using packagers, OS virtualization/containers or other
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
68 software management tools.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 """),
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
70 }
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
71
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
72 APP_FILE_PREFIX = "sat_app_"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
73
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
74
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
75 class AppManager:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
76 load = partial(yaml.load, Loader=Loader)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
77 dump = partial(yaml.dump, Dumper=Dumper)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
78
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
79 def __init__(self, host):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
80 log.info(_("plugin Applications Manager initialization"))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
81 self.host = host
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
82 self._managers = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
83 self._apps = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
84 self._started = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 # instance id to app data map
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
86 self._instances = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 host.bridge.addMethod(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
88 "applicationsList",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
89 ".plugin",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
90 in_sign="as",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
91 out_sign="as",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
92 method=self.list_applications,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 host.bridge.addMethod(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
95 "applicationStart",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
96 ".plugin",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
99 method=self._start,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
100 async_=True,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
101 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
102 host.bridge.addMethod(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 "applicationStop",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
104 ".plugin",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
105 in_sign="sss",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
106 out_sign="",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
107 method=self._stop,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
108 async_=True,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
109 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
110 host.bridge.addMethod(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
111 "applicationExposedGet",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
112 ".plugin",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
113 in_sign="sss",
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
116 async_=True,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
402d31527af4 plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents: 3565
diff changeset
120 host.bridge.addSignal(
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
402d31527af4 plugin app manager: `start` doesn't wait anymore for actual app start:
Goffi <goffi@goffi.org>
parents: 3565
diff changeset
125 host.bridge.addSignal(
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
128 yaml.add_constructor(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
129 "!sat_conf", self._sat_conf_constr, Loader=Loader)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
130 yaml.add_constructor(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
131 "!sat_generate_pwd", self._sat_generate_pwd_constr, Loader=Loader)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
132 yaml.add_constructor(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
133 "!sat_param", self._sat_param_constr, Loader=Loader)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
134
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
135 def unload(self):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
136 log.debug("unloading applications manager")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
137 for instances in self._started.values():
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
138 for instance in instances:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
139 data = instance['data']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
140 if not data['single_instance']:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
141 log.debug(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
142 f"cleaning temporary directory at {data['_instance_dir_path']}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
143 data['_instance_dir_obj'].cleanup()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
144
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
145 def _sat_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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
147
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
148 A list is expected with either "name" of a config parameter, a one or more of
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
149 those parameters:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
150 - section
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
151 - name
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
152 - default value
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
153 - filter
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
154 filter can be:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
155 - "first": get the first item of the value
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
156 """
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
157 config_data = loader.construct_sequence(node)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
158 if len(config_data) == 1:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
159 section, name, default, filter_ = "", config_data[0], None, None
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
160 if len(config_data) == 2:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
161 (section, name), default, filter_ = config_data, None, None
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
162 elif len(config_data) == 3:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
163 (section, name, default), filter_ = config_data, None
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
164 elif len(config_data) == 4:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
165 section, name, default, filter_ = config_data
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
166 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
167 raise ValueError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
168 f"invalid !sat_conf value ({config_data!r}), a list of 1 to 4 items is "
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
169 "expected"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
170 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
171
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
172 value = self.host.memory.getConfig(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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
175 if not value:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
179 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
186
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
187 if filter_ is None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
188 pass
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
189 elif filter_ == 'first':
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
190 value = value[0]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
191 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
192 raise ValueError(f"unmanaged filter: {filter_}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
193
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
194 return value
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
195
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
196 def _sat_generate_pwd_constr(self, loader, node):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
197 alphabet = string.ascii_letters + string.digits
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
198 return ''.join(secrets.choice(alphabet) for i in range(30))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
199
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
200 def _sat_param_constr(self, loader, node):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
201 """Get a parameter specified when starting the application
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
202
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
203 The value can be either the name of the parameter to get, or a list as
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
204 [name, default_value]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
205 """
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
206 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
207 name, default = loader.construct_sequence(node)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
208 except ConstructorError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
209 name, default = loader.construct_scalar(node), None
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
210 return self._params.get(name, default)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
211
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
212 def register(self, manager):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
213 name = manager.name
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
214 if name in self._managers:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
215 raise exceptions.ConflictError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
216 f"There is already a manager with the name {name}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
217 self._managers[manager.name] = manager
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
218 if hasattr(manager, "discover_path"):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
219 self.discover(manager.discover_path, manager)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
222 """Get manager instance needed for this app
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
223
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
224 @raise exceptions.DataError: something is wrong with the type
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
225 @raise exceptions.NotFound: manager is not registered
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
226 """
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
227 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
228 app_type = app_data["type"]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
229 except KeyError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
230 raise exceptions.DataError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
231 "app file doesn't have the mandatory \"type\" key"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
232 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
233 if not isinstance(app_type, str):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
234 raise exceptions.DataError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
235 f"invalid app data type: {app_type!r}"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
236 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
237 app_type = app_type.strip()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
238 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
239 return self._managers[app_type]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
240 except KeyError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
241 raise exceptions.NotFound(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
242 f"No manager found to manage app of type {app_type!r}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
245 self,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
246 id_type: Optional[str],
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
247 identifier: str
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
248 ) -> dict:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
249 """Retrieve instance's app_data from identifier
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
250
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
251 @param id_type: type of the identifier, can be:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
252 - "name": identifier is a canonical application name
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
253 the first found instance of this application is returned
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
254 - "instance": identifier is an instance id
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
255 @param identifier: identifier according to id_type
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
256 @return: instance application data
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
257 @raise exceptions.NotFound: no instance with this id can be found
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
258 @raise ValueError: id_type is invalid
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
259 """
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
260 if not id_type:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
261 id_type = 'name'
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
262 if id_type == 'name':
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
263 identifier = identifier.lower().strip()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
264 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
265 return next(iter(self._started[identifier]))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
266 except (KeyError, StopIteration):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
267 raise exceptions.NotFound(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
268 f"No instance of {identifier!r} is currently running"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
269 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
270 elif id_type == 'instance':
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
271 instance_id = identifier
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
272 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
273 return self._instances[instance_id]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
274 except KeyError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
275 raise exceptions.NotFound(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
276 f"There is no application instance running with id {instance_id!r}"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
277 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
278 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
279 raise ValueError(f"invalid id_type: {id_type!r}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
280
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
281 def discover(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
282 self,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
283 dir_path: Path,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
284 manager: Optional = None
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
285 ) -> None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
286 for file_path in dir_path.glob(f"{APP_FILE_PREFIX}*.yaml"):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
287 if manager is None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
288 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
291 except (exceptions.DataError, exceptions.NotFound) as e:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
292 log.warning(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
293 f"Can't parse {file_path}, skipping: {e}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
294 app_name = file_path.stem[len(APP_FILE_PREFIX):].strip().lower()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
295 if not app_name:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
296 log.warning(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
297 f"invalid app file name at {file_path}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
298 continue
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
299 app_dict = self._apps.setdefault(app_name, {})
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
300 manager_set = app_dict.setdefault(manager, set())
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
301 manager_set.add(file_path)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
302 log.debug(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
303 f"{app_name!r} {manager.name} application found"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
304 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
305
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
308
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
309 @param params: parameters for running this instance
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
310 @raise exceptions.DataError: something is wrong in the file
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
311 """
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
312 if params is None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
313 params = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
314 with file_path.open() as f:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
315 # we set parameters to be used only with this instance
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
316 # no async method must used between this assignation and `load`
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
317 self._params = params
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
318 app_data = self.load(f)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
319 self._params = None
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
320 if "name" not in app_data:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
321 # note that we don't use lower() here as we want human readable name and
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
322 # uppercase may be set on purpose
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
323 app_data['name'] = file_path.stem[len(APP_FILE_PREFIX):].strip()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
324 single_instance = app_data.setdefault("single_instance", True)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
325 if not isinstance(single_instance, bool):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
326 raise ValueError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
327 f'"single_instance" must be a boolean, but it is {type(single_instance)}'
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
328 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
329 return app_data
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
330
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
331 def list_applications(self, filters: Optional[List[str]]) -> List[str]:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
332 """List available application
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
333
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
334 @param filters: only show applications matching those filters.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
335 using None will list all known applications
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
336 a filter can be:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
337 - available: applications available locally
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
338 - running: only show launched applications
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
339 """
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
340 if not filters:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
341 return list(self.apps)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
342 found = set()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
343 for filter_ in filters:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
344 if filter_ == "available":
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
345 found.update(self._apps)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
346 elif filter_ == "running":
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
347 found.update(self._started)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
348 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
349 raise ValueError(f"Unknown filter: {filter_}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
350 return list(found)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
351
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
352 def _start(self, app_name, extra):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
357
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
358 async def start(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
359 self,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
360 app_name: str,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
378 # FIXME: for now we use the first app manager available for the requested app_name
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
379 # TODO: implement running multiple instance of the same app if some metadata
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
380 # to be defined in app_data allows explicitly it.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
381 app_name = app_name.lower().strip()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
382 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
383 app_file_path = next(iter(next(iter(self._apps[app_name].values()))))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
384 except KeyError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
385 raise exceptions.NotFound(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
386 f"No application found with the name {app_name!r}"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
389 started_data = self._started.setdefault(app_name, [])
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
392 app_data['_file_path'] = app_file_path
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
393 app_data['_name_canonical'] = app_name
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
399 if single_instance:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
410 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
411 cache_path = self.host.memory.getCachePath(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
412 PLUGIN_INFO[C.PI_IMPORT_NAME], app_name
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
413 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
414 cache_path.mkdir(0o700, parents=True, exist_ok=True)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
415 app_data['_instance_dir_path'] = cache_path
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
416 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
417 dest_dir_obj = tempfile.TemporaryDirectory(prefix="sat_app_")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
418 app_data['_instance_dir_obj'] = dest_dir_obj
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
422 app_data['_manager'] = manager
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
423 started_data.append(app_data)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
430
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
431 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
432 start = manager.start
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
433 except AttributeError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
434 raise exceptions.InternalError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
435 f"{manager.name} doesn't have the mandatory \"start\" method"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
436 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
461
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
462 def _stop(self, identifier, id_type, extra):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
463 extra = data_format.deserialise(extra)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
464 return defer.ensureDeferred(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
465 self.stop(str(identifier), str(id_type) or None, extra))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
466
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
467 async def stop(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
468 self,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
469 identifier: str,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
470 id_type: Optional[str] = None,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
471 extra: Optional[dict] = None,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
472 ) -> None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
473 if extra is None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
474 extra = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
477
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
478 log.info(f"stopping {app_data['name']!r}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
479
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
480 app_name = app_data['_name_canonical']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
481 instance_id = app_data['_instance_id']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
482 manager = app_data['_manager']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
483
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
484 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
485 stop = manager.stop
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
486 except AttributeError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
487 raise exceptions.InternalError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
488 f"{manager.name} doesn't have the mandatory \"stop\" method"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
489 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
490 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
491 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
492 await stop(app_data)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
493 except Exception as e:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
494 log.warning(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
495 f"Instance {instance_id} of application {app_name} can't be stopped "
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
496 f"properly: {e}"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
497 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
498 return
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
499
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
500 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
501 del self._instances[instance_id]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
502 except KeyError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
503 log.error(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
504 f"INTERNAL ERROR: {instance_id!r} is not present in self._instances")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
505
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
506 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
507 self._started[app_name].remove(app_data)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
508 except ValueError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
509 log.error(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
510 "INTERNAL ERROR: there is no app data in self._started with id "
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
511 f"{instance_id!r}"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
512 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
513
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
514 log.info(f"{app_name!r} stopped")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
519 d.addCallback(lambda d: data_format.serialise(d))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
520 return d
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
523 self,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
524 identifier: str,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
525 id_type: Optional[str] = None,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
526 extra: Optional[dict] = None,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
527 ) -> dict:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
528 """Get data exposed by the application
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
531 to handle manager specific conventions.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
534 if app_data.get('_exposed_computed', False):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
535 return app_data['expose']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
536 if extra is None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
537 extra = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
538 expose = app_data.setdefault("expose", {})
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
539 if "passwords" in expose:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
540 passwords = expose['passwords']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
541 for name, value in list(passwords.items()):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
542 if isinstance(value, list):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
543 # if we have a list, is the sequence of keys leading to the value
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
544 # to expose. We use "reduce" to retrieve the desired value
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
545 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
546 passwords[name] = reduce(lambda l, k: l[k], value, app_data)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
547 except Exception as e:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
548 log.warning(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
549 f"Can't retrieve exposed value for password {name!r}: {e}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
550 del passwords[name]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
551
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
552 url_prefix = expose.get("url_prefix")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
553 if isinstance(url_prefix, list):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
554 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
555 expose["url_prefix"] = reduce(lambda l, k: l[k], url_prefix, app_data)
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
556 except Exception as e:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
557 log.warning(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
558 f"Can't retrieve exposed value for url_prefix: {e}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
559 del expose["url_prefix"]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
566 except AttributeError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
567 pass
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
570
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
571 app_data['_exposed_computed'] = True
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
572 return expose
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
575 self,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
576 app_data: dict,
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
577 ) -> None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
578 name = app_data['name']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
579 dest_path = app_data['_instance_dir_path']
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
580 if next(dest_path.iterdir(), None) != None:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
581 log.debug(f"There is already a prepared dir at {dest_path}, nothing to do")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
582 return
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
583 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
584 prepare = app_data['prepare'].copy()
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
585 except KeyError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
586 prepare = {}
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
587
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
588 if not prepare:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
589 log.debug("Nothing to prepare for {name!r}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
590 return
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
591
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
592 for action, value in list(prepare.items()):
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
593 log.debug(f"[{name}] [prepare] running {action!r} action")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
594 if action == "git":
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
595 try:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
596 git_path = which('git')[0]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
597 except IndexError:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
598 raise exceptions.NotFound(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
599 "Can't find \"git\" executable, {name} can't be started without it"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
600 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
601 await async_process.run(git_path, "clone", value, str(dest_path))
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
602 log.debug(f"{value!r} git repository cloned at {dest_path}")
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
603 else:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
604 raise NotImplementedError(
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
605 f"{action!r} is not managed, can't start {name}"
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
606 )
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
607 del prepare[action]
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
608
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
609 if prepare:
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
610 raise exceptions.InternalError('"prepare" should be empty')
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
631 """Method running common action when starting a manager
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
632
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
633 It should be called by managers in "start" method.
5d926c7b0d99 plugin app manager: first draft:
Goffi <goffi@goffi.org>
parents:
diff changeset
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)