Coverage for gws-app/gws/server/control.py: 22%
76 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +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.
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.
88 Returns:
89 The configured root object.
91 Raises:
92 ``gws.ConfigurationError``: If the configuration fails.
93 """
94 cr = gws.config.configure(
95 manifest_path=manifest_path,
96 config_path=config_path,
97 fallback_config=_FALLBACK_CONFIG,
98 )
99 gws.config.log_report(cr)
100 if not cr.root:
101 raise gws.ConfigurationError('configuration failed')
102 return gws.u.require(cr.root)
105def config_test(
106 manifest_path='',
107 config_path='',
108 dirs_to_watch='',
109 with_parse_only=False,
110 with_watch=False
111):
112 """Configure or parse the configuration and log the report.
114 In the watch mode, the test is repeated whenever a file in the watched directories changes,
115 and the function never returns.
117 Args:
118 manifest_path: Path to the application manifest.
119 config_path: Path to the configuration file.
120 dirs_to_watch: Directories to watch, ``/data`` by default.
121 with_parse_only: Only parse the configuration, do not configure the objects.
122 with_watch: Keep watching the directories and repeat the test on changes.
123 """
125 def _check(*args):
126 gws.log.info('=' * 80)
127 gws.log.info(f'TESTING CONFIGURATION...')
128 gws.log.info('=' * 80)
129 if with_parse_only:
130 cr = gws.config.parse(manifest_path, config_path)
131 else:
132 cr = gws.config.configure(manifest_path, config_path)
133 gws.config.log_report(cr)
135 _check()
137 if not with_watch:
138 return
140 w = gws.lib.watcher.new(_check)
141 dirs = gws.u.to_list(dirs_to_watch or '/data')
142 for d in dirs:
143 w.add_directory(d, recursive=True)
144 w.start()
146 while True:
147 gws.u.sleep(3)
151##
153def reload_all():
154 """Empty the transient directory and reload the spool and web servers and NGINX.
156 Returns:
157 Always True.
158 """
159 gws.lib.osx.run(['rm', '-fr', gws.c.TRANSIENT_DIR])
160 gws.u.ensure_system_dirs()
162 reload_app('spool')
163 reload_app('web')
165 reload_nginx()
166 return True
169def reload_app(srv):
170 """Reload a uWSGI backend, if it is running.
172 Args:
173 srv: Backend name, ``web`` or ``spool``.
174 """
175 if not app_is_running(srv):
176 gws.log.debug(f'reload: {srv=} not running')
177 return
178 gws.log.info(f'reloading {srv}...')
179 gws.lib.osx.run(['uwsgi', '--reload', _PID_PATHS[srv]])
182def reload_nginx():
183 """Reload the NGINX configuration."""
184 gws.log.info(f'reloading nginx...')
185 gws.lib.osx.run(['nginx', '-c', gws.c.SERVER_DIR + '/nginx.conf', '-s', 'reload'])
188def app_is_running(srv):
189 """Check whether a server is running, by its pid file.
191 Args:
192 srv: Server name, ``web``, ``spool`` or ``nginx``.
194 Returns:
195 True if the pid from the pid file belongs to a running process.
196 """
197 try:
198 with open(_PID_PATHS[srv]) as fp:
199 pid = int(fp.read())
200 except (FileNotFoundError, ValueError):
201 pid = 0
202 if pid == 0:
203 return False
204 gws.log.debug(f'found {pid=} for {srv=}')
205 return pid in gws.lib.osx.running_pids()
208##
211_FALLBACK_CONFIG = gws.Config(
212 server=gws.Config(
213 withMonitor=False,
214 withSpool=False,
215 withWeb=True,
216 log=gws.Config(level='INFO'),
217 qgis=gws.Config(host='qgis', port=80),
218 web=gws.Config(workers=1, timeout=60, maxRequestLength=10),
219 )
220)