mirror of
https://github.com/frappe/frappe_docker.git
synced 2026-06-20 23:05:09 +00:00
143 lines
3.8 KiB
Python
143 lines
3.8 KiB
Python
from collections import defaultdict
|
|
from collections import namedtuple
|
|
import datetime
|
|
import errno
|
|
import shlex
|
|
import os
|
|
import re
|
|
import signal
|
|
|
|
from . import compat
|
|
|
|
if compat.ON_WINDOWS:
|
|
import ctypes
|
|
|
|
|
|
PROCFILE_LINE = re.compile(r'^([A-Za-z0-9_]+):\s*(.+)$')
|
|
|
|
|
|
class Env(object):
|
|
|
|
def now(self):
|
|
return datetime.datetime.now()
|
|
|
|
if compat.ON_WINDOWS:
|
|
def terminate(self, pid):
|
|
# The first argument to OpenProcess represents the desired access
|
|
# to the process. 1 represents the PROCESS_TERMINATE access right.
|
|
handle = ctypes.windll.kernel32.OpenProcess(1, False, pid)
|
|
ctypes.windll.kernel32.TerminateProcess(handle, -1)
|
|
ctypes.windll.kernel32.CloseHandle(handle)
|
|
else:
|
|
def terminate(self, pid):
|
|
try:
|
|
os.killpg(pid, signal.SIGTERM)
|
|
except OSError as e:
|
|
if e.errno not in [errno.EPERM, errno.ESRCH]:
|
|
raise
|
|
|
|
if compat.ON_WINDOWS:
|
|
def kill(self, pid):
|
|
# There's no SIGKILL on Win32...
|
|
self.terminate(pid)
|
|
else:
|
|
def kill(self, pid):
|
|
try:
|
|
os.killpg(pid, signal.SIGKILL)
|
|
except OSError as e:
|
|
if e.errno not in [errno.EPERM, errno.ESRCH]:
|
|
raise
|
|
|
|
|
|
class Procfile(object):
|
|
"""A data structure representing a Procfile"""
|
|
|
|
def __init__(self):
|
|
self.processes = compat.OrderedDict()
|
|
|
|
def add_process(self, name, command):
|
|
assert name not in self.processes, \
|
|
"process names must be unique within a Procfile"
|
|
self.processes[name] = command
|
|
|
|
|
|
def parse_procfile(contents):
|
|
p = Procfile()
|
|
for line in contents.splitlines():
|
|
m = PROCFILE_LINE.match(line)
|
|
if m:
|
|
p.add_process(m.group(1), m.group(2))
|
|
return p
|
|
|
|
|
|
def parse(content):
|
|
"""
|
|
Parse the content of a .env file (a line-delimited KEY=value format) into a
|
|
dictionary mapping keys to values.
|
|
"""
|
|
values = {}
|
|
for line in content.splitlines():
|
|
lexer = shlex.shlex(line, posix=True)
|
|
tokens = list(lexer)
|
|
|
|
# parses the assignment statement
|
|
if len(tokens) < 3:
|
|
continue
|
|
|
|
name, op = tokens[:2]
|
|
value = ''.join(tokens[2:])
|
|
|
|
if op != '=':
|
|
continue
|
|
if not re.match(r'[A-Za-z_][A-Za-z_0-9]*', name):
|
|
continue
|
|
|
|
value = value.replace(r'\n', '\n')
|
|
value = value.replace(r'\t', '\t')
|
|
values[name] = value
|
|
|
|
return values
|
|
|
|
|
|
ProcessParams = namedtuple("ProcessParams", "name cmd quiet env")
|
|
|
|
|
|
def expand_processes(processes, concurrency=None, env=None, quiet=None, port=None):
|
|
"""
|
|
Get a list of the processes that need to be started given the specified
|
|
list of process types, concurrency, environment, quietness, and base port
|
|
number.
|
|
|
|
Returns a list of ProcessParams objects, which have `name`, `cmd`, `env`,
|
|
and `quiet` attributes, corresponding to the parameters to the constructor
|
|
of `honcho.process.Process`.
|
|
"""
|
|
if env is not None and env.get("PORT") is not None:
|
|
port = int(env.get("PORT"))
|
|
|
|
if quiet is None:
|
|
quiet = []
|
|
|
|
con = defaultdict(lambda: 1)
|
|
if concurrency is not None:
|
|
con.update(concurrency)
|
|
|
|
out = []
|
|
|
|
for name, cmd in compat.iteritems(processes):
|
|
for i in range(con[name]):
|
|
n = "{0}.{1}".format(name, i + 1)
|
|
c = cmd
|
|
q = name in quiet
|
|
e = {'HONCHO_PROCESS_NAME': n}
|
|
if env is not None:
|
|
e.update(env)
|
|
if port is not None:
|
|
e['PORT'] = str(port + i)
|
|
|
|
params = ProcessParams(n, c, q, e)
|
|
out.append(params)
|
|
if port is not None:
|
|
port += 100
|
|
|
|
return out
|