Skip to content

Instantly share code, notes, and snippets.

@codeskyblue
Created November 1, 2022 08:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codeskyblue/c28b85166835434a549279bc33d1756d to your computer and use it in GitHub Desktop.
Save codeskyblue/c28b85166835434a549279bc33d1756d to your computer and use it in GitHub Desktop.
Generate supervisor config file through yaml
#!/usr/bin/env python3
# coding: utf-8
#
"""
Refs:
- https://jinja.palletsprojects.com/en/3.1.x/templates/
- https://stackoverflow.com/questions/39288706/jinja2-load-template-from-string-typeerror-no-loader-for-this-environment-spec
改进型的yaml,支持符号~,增加默认配置redirect_stderr,stopasgroup,killasgroup,directory,user,stdout_logfile_backups=1,environment增加PATH
支持group增加统一前缀
Input
---
- group: test
programs:
- name: web
startsecs: 10
command: ~/miniconda3/envs/base/bin/python web.py
Output
---
[group:test]
programs=test-web
[program:test-web]
command=/Users/codeskyblue/miniconda3/envs/base/bin/python web.py
user=shengxiang
directory=/Users/codeskyblue/projects/test
stopasgroup=true
killasgroup=true
redirect_stderr=true
startsecs=10
environment=PYTHONUNBUFFERED="1",PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=1
"""
import os
import yaml
import pathlib
from pprint import pprint
import typing
from dataclasses import dataclass, field
from dataclasses_json import (CatchAll, DataClassJsonMixin, LetterCase,
Undefined)
from jinja2 import Environment, BaseLoader
class _BaseDataClassMixin(DataClassJsonMixin):
dataclass_json_config = dict()
def _asdict(self) -> dict:
""" required called by simplejson """
return self.to_dict()
@dataclass
class _BaseDataClass(_BaseDataClassMixin):
pass
@dataclass
class Program(_BaseDataClass):
name: str
command: str
numprocs: int = 1
user: str = os.environ["USER"]
directory: str = os.getcwd()
stopasgroup: bool = True
killasgroup: bool = True
redirect_stderr: bool = True
startsecs: int = 1
strip_ansi: bool = False
stdout_logfile_maxbytes: str = "10MB"
stdout_logfile_backups: int = 1
# stopsignal: str = "TERM"
# environment: typing.List[str] = ["PYTHONUNBUFFERED=1"]
@dataclass
class Group(_BaseDataClass):
group: str
programs: typing.List[Program]
path: str = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
# priority: int = 999
JINJA_TEMPLATE = """
{% for group in groups %}
[group:{{group.group}}]
programs={{ group.programs | join(",", attribute="name") }}
{% for program in group.programs %}
[program:{{program.name}}]
command={{program.command}}
user={{program.user}}
directory={{program.directory}}
stopasgroup={{program.stopasgroup | lower}}
killasgroup={{program.killasgroup | lower}}
redirect_stderr={{program.redirect_stderr | lower}}
startsecs={{program.startsecs}}
environment=PYTHONUNBUFFERED="1",PATH={{group.path | quote}}
stdout_logfile_maxbytes={{program.stdout_logfile_maxbytes}}
stdout_logfile_backups={{program.stdout_logfile_backups}}
{% if program.numprocs > 1 %}
numprocs={{program.numprocs}}
process_name=%(program_name)s_%(process_num)02d
{% endif %}
{% endfor %}
{% endfor %}
"""
def main():
valid_name = ("supervisor", "supervisor-conf")
valid_ext = (".yaml", ".yml")
valid_names = []
for name in valid_name:
for ext in valid_ext:
valid_names.append(name+ext)
p: pathlib.Path = None
for guess_path in valid_names:
_p = pathlib.Path(guess_path)
if _p.exists():
p = _p
break
if not p:
raise RuntimeError("No valid conf file found", valid_names)
with p.open("rb") as f:
data = yaml.load(f, yaml.SafeLoader)
groups: typing.List[Group] = Group.schema().load(data, many=True)
for g in groups:
for p in g.programs:
if not p.name.startswith(g.group):
p.name = g.group + "-" + p.name
p.command = os.path.expanduser(p.command)
environment = Environment(loader=BaseLoader)
environment.filters["quote"] = lambda v: '"' + str(v).replace('"', r'\"') + '"'
rtemplate = environment.from_string(JINJA_TEMPLATE)
data = rtemplate.render(groups=groups)
print(data.strip())
if __name__ == "__main__":
main()
jinja2
dataclasses-json
pyyaml
---
- group: test
programs:
- name: web
startsecs: 10
command: ~/miniconda3/envs/base/bin/python web.py
- name: proxy
command: ~/miniconda3/envs/base/bin/python run_proxy.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment