Coverage for gws-app/gws/server/control.py: 20%
84 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Functions to start, reconfigure and reload the servers."""
3import gws
4import gws.config
5import gws.lib.datetimex
6import gws.lib.osx
7import gws.lib.watcher
9# see bin/gws
10_SERVER_START_SCRIPT = f'{gws.c.VAR_DIR}/server.sh'
12_PID_PATHS = {
13 'web': f'{gws.c.PIDS_DIR}/web.uwsgi.pid',
14 'spool': f'{gws.c.PIDS_DIR}/spool.uwsgi.pid',
15 'nginx': f'{gws.c.PIDS_DIR}/nginx.pid',
16}
19def start(manifest_path='', config_path=''):
20 """Configure the application and write the server configuration files and the start script.
22 Called once on the container start. The start script itself is executed by ``bin/gws``.
23 Exits the process with code 1 if the web server is already running.
25 Args:
26 manifest_path: Path to the application manifest.
27 config_path: Path to the configuration file.
29 Raises:
30 ``gws.ConfigurationError``: If the configuration fails.
31 """
32 if app_is_running('web'):
33 gws.log.error(f'server already running')
34 gws.u.exit(1)
35 root = configure_and_store(manifest_path, config_path, is_starting=True)
36 root.app.serverMgr.create_server_configs(gws.c.SERVER_DIR, _SERVER_START_SCRIPT, _PID_PATHS)
39def reconfigure(manifest_path='', config_path=''):
40 """Configure the application, rewrite the server configuration files and reload all servers.
42 Exits the process with code 1 if the web server is not running.
44 Args:
45 manifest_path: Path to the application manifest.
46 config_path: Path to the configuration file.
48 Raises:
49 ``gws.ConfigurationError``: If the configuration fails.
50 """
51 if not app_is_running('web'):
52 gws.log.error(f'server not running')
53 gws.u.exit(1)
54 root = configure_and_store(manifest_path, config_path, is_starting=False)
55 root.app.serverMgr.create_server_configs(gws.c.SERVER_DIR, _SERVER_START_SCRIPT, _PID_PATHS)
56 reload_all()
59def configure_and_store(manifest_path='', config_path='', is_starting=False):
60 """Configure the application and store the configuration, so that the servers can load it.
62 Args:
63 manifest_path: Path to the application manifest.
64 config_path: Path to the configuration file.
65 is_starting: True on the server start, runs the ``server.autoRun`` command before initialization.
67 Returns:
68 The configured root object.
70 Raises:
71 ``gws.ConfigurationError``: If the configuration fails.
72 """
73 root = configure(manifest_path, config_path, is_starting)
74 gws.config.save(root)
75 return root
78def configure(manifest_path='', config_path='', is_starting=False):
79 """Configure the application and log the configuration report.
81 If the configuration fails and the manifest enables ``withFallbackConfig``, a minimal fallback configuration is used.
83 Args:
84 manifest_path: Path to the application manifest.
85 config_path: Path to the configuration file.
86 is_starting: True on the server start, runs the ``server.autoRun`` command before initialization.
88 Returns:
89 The configured root object.
91 Raises:
92 ``gws.ConfigurationError``: If the configuration fails.
93 """
94 def _pre_init(ld: gws.config.loader.Object):
95 autorun = gws.u.get(ld.config, 'server.autoRun')
96 if autorun:
97 gws.log.info(f'AUTORUN: {autorun!r}')
98 gws.lib.osx.run(autorun, echo=True)
100 hooks = []
101 if is_starting:
102 hooks.append(['preInitialize', _pre_init])
104 cr = gws.config.configure(
105 manifest_path=manifest_path,
106 config_path=config_path,
107 fallback_config=_FALLBACK_CONFIG,
108 hooks=hooks,
109 )
110 gws.config.log_report(cr)
111 if not cr.root:
112 raise gws.ConfigurationError('configuration failed')
113 return gws.u.require(cr.root)
116def config_test(
117 manifest_path='',
118 config_path='',
119 dirs_to_watch='',
120 with_parse_only=False,
121 with_watch=False
122):
123 """Configure or parse the configuration and log the report.
125 In the watch mode, the test is repeated whenever a file in the watched directories changes,
126 and the function never returns.
128 Args:
129 manifest_path: Path to the application manifest.
130 config_path: Path to the configuration file.
131 dirs_to_watch: Directories to watch, ``/data`` by default.
132 with_parse_only: Only parse the configuration, do not configure the objects.
133 with_watch: Keep watching the directories and repeat the test on changes.
134 """
136 def _check(*args):
137 gws.log.info('=' * 80)
138 gws.log.info(f'TESTING CONFIGURATION...')
139 gws.log.info('=' * 80)
140 if with_parse_only:
141 cr = gws.config.parse(manifest_path, config_path)
142 else:
143 cr = gws.config.configure(manifest_path, config_path)
144 gws.config.log_report(cr)
146 _check()
148 if not with_watch:
149 return
151 w = gws.lib.watcher.new(_check)
152 dirs = gws.u.to_list(dirs_to_watch or '/data')
153 for d in dirs:
154 w.add_directory(d, recursive=True)
155 w.start()
157 while True:
158 gws.u.sleep(3)
162##
164def reload_all():
165 """Empty the transient directory and reload the spool and web servers and NGINX.
167 Returns:
168 Always True.
169 """
170 gws.lib.osx.run(['rm', '-fr', gws.c.TRANSIENT_DIR])
171 gws.u.ensure_system_dirs()
173 reload_app('spool')
174 reload_app('web')
176 reload_nginx()
177 return True
180def reload_app(srv):
181 """Reload a uWSGI backend, if it is running.
183 Args:
184 srv: Backend name, ``web`` or ``spool``.
185 """
186 if not app_is_running(srv):
187 gws.log.debug(f'reload: {srv=} not running')
188 return
189 gws.log.info(f'reloading {srv}...')
190 gws.lib.osx.run(['uwsgi', '--reload', _PID_PATHS[srv]])
193def reload_nginx():
194 """Reload the NGINX configuration."""
195 gws.log.info(f'reloading nginx...')
196 gws.lib.osx.run(['nginx', '-c', gws.c.SERVER_DIR + '/nginx.conf', '-s', 'reload'])
199def app_is_running(srv):
200 """Check whether a server is running, by its pid file.
202 Args:
203 srv: Server name, ``web``, ``spool`` or ``nginx``.
205 Returns:
206 True if the pid from the pid file belongs to a running process.
207 """
208 try:
209 with open(_PID_PATHS[srv]) as fp:
210 pid = int(fp.read())
211 except (FileNotFoundError, ValueError):
212 pid = 0
213 if pid == 0:
214 return False
215 gws.log.debug(f'found {pid=} for {srv=}')
216 return pid in gws.lib.osx.running_pids()
219##
222_FALLBACK_CONFIG = gws.Config(
223 server=gws.Config(
224 timeZone="Europe/Berlin",
225 monitor=gws.Config(disabled=True),
226 log=gws.Config(level='INFO'),
227 qgis=gws.Config(host='qgis', port=80),
228 spool=gws.Config(disabled=True),
229 web=gws.Config(disabled=False, workers=1, timeout=60, maxRequestLength=10),
230 )
231)