annotate sat/plugins/plugin_misc_app_manager.py @ 4009:48e8b3dba793

component AP gateway (http/outbox): return data when no RSM is available
author Goffi <goffi@goffi.org>
date Thu, 16 Mar 2023 15:56:11 +0100
parents 402d31527af4
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)