Coverage for gws-app/gws/server/manager.py: 0%
105 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""Configuration manager for embedded servers.
3This object creates configuration files for embedded servers and the server startup script.
5The configuration is template-based, there are following template subjects defined:
7- ``server.rsyslog_config`` - for the embedded ``rsyslogd`` daemon
8- ``server.uwsgi_config`` - for backend uWSGI servers (the ``uwsgi`` argument contains the specific backend name)
9- ``server.nginx_config`` - for the frontend NGINX proxy
11Each template receives a :obj:`TemplateArgs` object as arguments.
13By default, the Manager uses text-only templates from the ``templates`` directory.
14"""
16from typing import cast
18import grp
19import os
20import pwd
21import re
23import gws
24import gws.base.web
25import gws.config
26import gws.config.util
27import gws.gis.mpx.config
28import gws.lib.osx
30from . import core
33class TemplateArgs(gws.TemplateArgs):
34 """Arguments for configuration templates."""
36 root: gws.Root
37 """Root object."""
38 inContainer: bool
39 """True if we're running in a container."""
40 userName: str
41 """User name."""
42 groupName: str
43 """Group name."""
44 gwsEnv: dict
45 """A dict of GWS environment variables."""
46 mapproxyPid: str
47 """Mapproxy pid path."""
48 mapproxySocket: str
49 """Mapproxy socket path."""
50 nginxPid: str
51 """nginx pid path."""
52 serverDir: str
53 """Absolute path to app/server directory."""
54 spoolPid: str
55 """Spooler pid path."""
56 spoolSocket: str
57 """Spooler socket path."""
58 uwsgi: str
59 """uWSGI backend name."""
60 webPid: str
61 """Web server pid path."""
62 webSocket: str
63 """Web server socket path."""
66_DEFAULT_BASE_TIMEOUT = 60
67_DEFAULT_SPOOL_TIMEOUT = 300
69_SERVER_DIR = gws.u.dirname(__file__)
71_DEFAULT_TEMPLATES = [
72 gws.Config(type='text', subject='server.rsyslog_config', path=f'{_SERVER_DIR}/templates/rsyslog_config.cx.txt'),
73 gws.Config(type='text', subject='server.nginx_config', path=f'{_SERVER_DIR}/templates/nginx_config.cx.txt'),
74 gws.Config(type='text', subject='server.uwsgi_config', path=f'{_SERVER_DIR}/templates/uwsgi_config.cx.txt'),
75]
78class Object(gws.ServerManager):
79 def configure(self):
80 self.configure_environment()
81 self.configure_templates()
83 def configure_environment(self):
84 """Overwrite config values from the environment."""
86 p = gws.env.GWS_LOG_LEVEL
87 if p:
88 cast(core.Config, self.config).log.level = p
89 p = gws.env.GWS_WEB_WORKERS
90 if p:
91 cast(core.Config, self.config).web.workers = int(p)
92 p = gws.env.GWS_SPOOL_WORKERS
93 if p:
94 cast(core.Config, self.config).spool.workers = int(p)
96 def configure_templates(self):
97 gws.config.util.configure_templates_for(self, extra=_DEFAULT_TEMPLATES)
99 def create_server_configs(self, target_dir, script_path, pid_paths):
100 args = TemplateArgs(
101 root=self.root,
102 inContainer=gws.c.env.GWS_IN_CONTAINER,
103 userName=pwd.getpwuid(gws.c.UID).pw_name,
104 groupName=grp.getgrgid(gws.c.GID).gr_name,
105 gwsEnv={k: v for k, v in sorted(os.environ.items()) if k.startswith('GWS_')},
106 mapproxyPid=pid_paths['mapproxy'],
107 mapproxySocket=f'{gws.c.TMP_DIR}/mapproxy.uwsgi.sock',
108 nginxPid=pid_paths['nginx'],
109 serverDir=_SERVER_DIR,
110 spoolPid=pid_paths['spool'],
111 spoolSocket=f'{gws.c.TMP_DIR}/spool.uwsgi.sock',
112 webPid=pid_paths['web'],
113 webSocket=f'{gws.c.TMP_DIR}/web.uwsgi.sock',
114 )
116 commands = []
118 if args.inContainer:
119 path = self._create_config('server.rsyslog_config', f'{target_dir}/syslog.conf', args)
120 commands.append(f'rsyslogd -i {gws.c.PIDS_DIR}/rsyslogd.pid -f {path}')
122 if self.cfg('web.enabled'):
123 args.uwsgi = 'web'
124 path = self._create_config('server.uwsgi_config', f'{target_dir}/uwsgi_web.ini', args)
125 commands.append(f'uwsgi --ini {path}')
127 if self.cfg('mapproxy.enabled') and gws.u.is_file(gws.gis.mpx.config.CONFIG_PATH):
128 args.uwsgi = 'mapproxy'
129 path = self._create_config('server.uwsgi_config', f'{target_dir}/uwsgi_mapproxy.ini', args)
130 commands.append(f'uwsgi --ini {path}')
132 if self.cfg('spool.enabled'):
133 args.uwsgi = 'spool'
134 path = self._create_config('server.uwsgi_config', f'{target_dir}/uwsgi_spool.ini', args)
135 commands.append(f'uwsgi --ini {path}')
137 path = self._create_config('server.nginx_config', f'{target_dir}/nginx.conf', args, True)
138 commands.append(f'exec nginx -c {path}')
140 gws.u.write_file(script_path, '\n'.join(commands) + '\n')
142 def _create_config(self, subject: str, path: str, args: TemplateArgs, is_nginx=False) -> str:
143 tpl = self.root.app.templateMgr.find_template(subject, where=[self])
144 res = tpl.render(gws.TemplateRenderInput(args=args))
146 text = str(res.content)
147 if is_nginx:
148 text = re.sub(r'\s*{\s*', ' {\n', text)
149 text = re.sub(r'\s*}\s*', '\n}\n', text)
151 lines = []
152 indent = 0
154 for line in text.split('\n'):
155 line = re.sub(r'\s+', ' ', line.strip())
156 if not line:
157 continue
158 if is_nginx:
159 if line == '}':
160 indent -= 1
161 lines.append((' ' * (indent * 4)) + line)
162 if '{' in line:
163 indent += 1
164 else:
165 lines.append(line)
167 text = '\n'.join(lines) + '\n'
168 gws.u.write_file(path, text)
169 return path