mirror of
https://github.com/cmehay/pyentrypoint
synced 2024-10-30 15:21:11 +00:00
Add post_run_commands
This commit is contained in:
parent
72e44abe8c
commit
c0dbfce4c2
12
.travis.yml
Normal file
12
.travis.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
sudo: required
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
language: python
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make test
|
2
CHANGELOG
Normal file
2
CHANGELOG
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
v0.5.0:
|
||||||
|
- add post_run_commands in entrypoint-config.yml
|
13
Makefile
13
Makefile
@ -3,8 +3,17 @@
|
|||||||
build:
|
build:
|
||||||
@docker-compose build
|
@docker-compose build
|
||||||
|
|
||||||
test: build
|
clean:
|
||||||
@docker-compose up --force-recreate testpython2 testpython3
|
docker-compose down --remove-orphans
|
||||||
|
|
||||||
|
test: build test-python2 test-python3 clean
|
||||||
|
|
||||||
|
test-python2:
|
||||||
|
@docker-compose run --rm testpython2
|
||||||
|
|
||||||
|
test-python3:
|
||||||
|
@docker-compose run --rm testpython3
|
||||||
|
|
||||||
|
|
||||||
test_debug: build
|
test_debug: build
|
||||||
@docker-compose up --force-recreate testpython2_debug testpython3_debug
|
@docker-compose up --force-recreate testpython2_debug testpython3_debug
|
||||||
|
@ -6,6 +6,7 @@ import os
|
|||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
|
|
||||||
from .logs import Logs
|
from .logs import Logs
|
||||||
|
from .runner import Runner
|
||||||
|
|
||||||
|
|
||||||
class Command(object):
|
class Command(object):
|
||||||
@ -21,6 +22,8 @@ class Command(object):
|
|||||||
self.log.debug('Arguments are: "{args}"'.format(
|
self.log.debug('Arguments are: "{args}"'.format(
|
||||||
args='" "'.join(self.args)
|
args='" "'.join(self.args)
|
||||||
))
|
))
|
||||||
|
self.runner = Runner(config=self.config,
|
||||||
|
cmds=self.config.post_run_commands)
|
||||||
|
|
||||||
def _rm_dockerenv(self):
|
def _rm_dockerenv(self):
|
||||||
"""Delete '/.dockerenv' and '/.dockerinit' files"""
|
"""Delete '/.dockerenv' and '/.dockerinit' files"""
|
||||||
@ -88,6 +91,11 @@ class Command(object):
|
|||||||
self.log.debug('Launching reloader process')
|
self.log.debug('Launching reloader process')
|
||||||
self.config.reload.run_in_process()
|
self.config.reload.run_in_process()
|
||||||
|
|
||||||
|
def _run_post_commands(self):
|
||||||
|
if self.config.post_run_commands:
|
||||||
|
self.log.debug('Running post run commands')
|
||||||
|
self.runner.run_in_process()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if not self.is_handled:
|
if not self.is_handled:
|
||||||
self._exec()
|
self._exec()
|
||||||
@ -107,4 +115,5 @@ class Command(object):
|
|||||||
[p for p in subcom if fnmatch(self.args[0], p)]:
|
[p for p in subcom if fnmatch(self.args[0], p)]:
|
||||||
self.args.insert(0, self.command)
|
self.args.insert(0, self.command)
|
||||||
self._run_reloader()
|
self._run_reloader()
|
||||||
|
self._run_post_commands()
|
||||||
self._exec()
|
self._exec()
|
||||||
|
@ -203,6 +203,11 @@ class Config(ConfigMeta):
|
|||||||
"""Return list of postconf commands"""
|
"""Return list of postconf commands"""
|
||||||
return self._return_item_lst('post_conf_commands')
|
return self._return_item_lst('post_conf_commands')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def post_run_commands(self):
|
||||||
|
"""Return list of post run commands"""
|
||||||
|
return self._return_item_lst('post_run_commands')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reload(self):
|
def reload(self):
|
||||||
"""Return Reloader object if reload is set"""
|
"""Return Reloader object if reload is set"""
|
||||||
@ -239,3 +244,20 @@ class Config(ConfigMeta):
|
|||||||
if 'ENTRYPOINT_QUIET' in os.environ:
|
if 'ENTRYPOINT_QUIET' in os.environ:
|
||||||
return True
|
return True
|
||||||
return bool(self._config.get('quiet', False))
|
return bool(self._config.get('quiet', False))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_disabled(self):
|
||||||
|
"""Return if service is disabled using environment"""
|
||||||
|
return 'ENTRYPOINT_DISABLE_SERVICE' in os.environ
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_config(self):
|
||||||
|
"""Check environment to tell if config should apply anyway"""
|
||||||
|
return 'ENTRYPOINT_FORCE' in os.environ
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw_output(self):
|
||||||
|
"""Check if command output should be displayed using logging or not"""
|
||||||
|
if 'ENTRYPOINT_RAW' in os.environ:
|
||||||
|
return True
|
||||||
|
return bool(self._config.get('raw_output', False))
|
||||||
|
@ -8,11 +8,8 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from subprocess import PIPE
|
|
||||||
from subprocess import Popen
|
|
||||||
from sys import argv
|
from sys import argv
|
||||||
from sys import exit
|
from sys import exit
|
||||||
from sys import stdout
|
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from jinja2 import Environment
|
from jinja2 import Environment
|
||||||
@ -22,6 +19,7 @@ from .config import Config
|
|||||||
from .constants import ENTRYPOINT_FILE
|
from .constants import ENTRYPOINT_FILE
|
||||||
from .docker_links import DockerLinks
|
from .docker_links import DockerLinks
|
||||||
from .logs import Logs
|
from .logs import Logs
|
||||||
|
from .runner import Runner
|
||||||
|
|
||||||
__all__ = ['Entrypoint', 'main']
|
__all__ = ['Entrypoint', 'main']
|
||||||
|
|
||||||
@ -49,6 +47,7 @@ class Entrypoint(object):
|
|||||||
if self.config.quiet:
|
if self.config.quiet:
|
||||||
Logs.set_critical()
|
Logs.set_critical()
|
||||||
self.args = args
|
self.args = args
|
||||||
|
self.runner = Runner(config=self.config)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_handled(self):
|
def is_handled(self):
|
||||||
@ -58,21 +57,21 @@ class Entrypoint(object):
|
|||||||
@property
|
@property
|
||||||
def is_disabled(self):
|
def is_disabled(self):
|
||||||
"""Return if service is disabled using environment"""
|
"""Return if service is disabled using environment"""
|
||||||
return 'ENTRYPOINT_DISABLE_SERVICE' in os.environ
|
return self.config.is_disabled
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_config(self):
|
def should_config(self):
|
||||||
"""Check environment to tell if config should apply anyway"""
|
"""Check environment to tell if config should apply anyway"""
|
||||||
return 'ENTRYPOINT_FORCE' in os.environ
|
return self.config.should_config
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def raw_output(self):
|
def raw_output(self):
|
||||||
"""Check if command output should be displayed using logging or not"""
|
"""Check if command output should be displayed using logging or not"""
|
||||||
return 'ENTRYPOINT_RAW' in os.environ
|
return self.config.raw_output
|
||||||
|
|
||||||
def exit_if_disabled(self):
|
def exit_if_disabled(self):
|
||||||
"""Exist 0 if service is disabled"""
|
"""Exist 0 if service is disabled"""
|
||||||
if not self.is_disabled:
|
if not self.config.is_disabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log.warning("Service is disabled by 'ENTRYPOINT_DISABLE_SERVICE' "
|
self.log.warning("Service is disabled by 'ENTRYPOINT_DISABLE_SERVICE' "
|
||||||
@ -94,41 +93,17 @@ class Entrypoint(object):
|
|||||||
yaml=yaml,
|
yaml=yaml,
|
||||||
containers=DockerLinks().to_containers()))
|
containers=DockerLinks().to_containers()))
|
||||||
|
|
||||||
def run_conf_cmd(self, cmd):
|
|
||||||
self.log.debug('run command: {}'.format(cmd))
|
|
||||||
proc = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
|
|
||||||
out, err = proc.communicate()
|
|
||||||
|
|
||||||
def dispout(output, cb):
|
|
||||||
enc = stdout.encoding or 'UTF-8'
|
|
||||||
output = output.decode(enc).split('\n')
|
|
||||||
l = len(output)
|
|
||||||
for c, line in enumerate(output):
|
|
||||||
if c + 1 == l and not len(line):
|
|
||||||
# Do not display last empty line
|
|
||||||
break
|
|
||||||
cb(line)
|
|
||||||
|
|
||||||
if out:
|
|
||||||
display_cb = self.log.info if not self.raw_output else print
|
|
||||||
dispout(out, display_cb)
|
|
||||||
if err:
|
|
||||||
display_cb = self.log.warning if not self.raw_output else print
|
|
||||||
dispout(err, display_cb)
|
|
||||||
if proc.returncode:
|
|
||||||
raise Exception('Command exit code: {}'.format(proc.returncode))
|
|
||||||
|
|
||||||
def run_pre_conf_cmds(self):
|
def run_pre_conf_cmds(self):
|
||||||
for cmd in self.config.pre_conf_commands:
|
for cmd in self.config.pre_conf_commands:
|
||||||
self.run_conf_cmd(cmd)
|
self.runner.run_cmd(cmd)
|
||||||
if 'ENTRYPOINT_PRECONF_COMMAND' in os.environ:
|
if 'ENTRYPOINT_PRECONF_COMMAND' in os.environ:
|
||||||
self.run_conf_cmd(os.environ['ENTRYPOINT_PRECONF_COMMAND'])
|
self.runner.run_cmd(os.environ['ENTRYPOINT_PRECONF_COMMAND'])
|
||||||
|
|
||||||
def run_post_conf_cmds(self):
|
def run_post_conf_cmds(self):
|
||||||
for cmd in self.config.post_conf_commands:
|
for cmd in self.config.post_conf_commands:
|
||||||
self.run_conf_cmd(cmd)
|
self.runner.run_cmd(cmd)
|
||||||
if 'ENTRYPOINT_POSTCONF_COMMAND' in os.environ:
|
if 'ENTRYPOINT_POSTCONF_COMMAND' in os.environ:
|
||||||
self.run_conf_cmd(os.environ['ENTRYPOINT_POSTCONF_COMMAND'])
|
self.runner.run_cmd(os.environ['ENTRYPOINT_POSTCONF_COMMAND'])
|
||||||
|
|
||||||
def launch(self):
|
def launch(self):
|
||||||
self.config.command.run()
|
self.config.command.run()
|
||||||
|
52
pyentrypoint/runner.py
Normal file
52
pyentrypoint/runner.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"Run commands"
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from multiprocessing import Process
|
||||||
|
from subprocess import PIPE
|
||||||
|
from subprocess import Popen
|
||||||
|
from sys import stdout
|
||||||
|
|
||||||
|
from .logs import Logs
|
||||||
|
|
||||||
|
|
||||||
|
class Runner(object):
|
||||||
|
'This object run commands'
|
||||||
|
|
||||||
|
def __init__(self, config, cmds=[]):
|
||||||
|
self.log = Logs().log
|
||||||
|
self.cmds = cmds
|
||||||
|
self.raw_output = config.raw_output
|
||||||
|
|
||||||
|
def run_cmd(self, cmd):
|
||||||
|
self.log.debug('run command: {}'.format(cmd))
|
||||||
|
proc = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
|
||||||
|
def dispout(output, cb):
|
||||||
|
enc = stdout.encoding or 'UTF-8'
|
||||||
|
output = output.decode(enc).split('\n')
|
||||||
|
l = len(output)
|
||||||
|
for c, line in enumerate(output):
|
||||||
|
if c + 1 == l and not len(line):
|
||||||
|
# Do not display last empty line
|
||||||
|
break
|
||||||
|
cb(line)
|
||||||
|
|
||||||
|
if out:
|
||||||
|
display_cb = self.log.info if not self.raw_output else print
|
||||||
|
dispout(out, display_cb)
|
||||||
|
if err:
|
||||||
|
display_cb = self.log.warning if not self.raw_output else print
|
||||||
|
dispout(err, display_cb)
|
||||||
|
if proc.returncode:
|
||||||
|
raise Exception('Command exit code: {}'.format(proc.returncode))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for cmd in self.cmds:
|
||||||
|
self.run_cmd(cmd)
|
||||||
|
|
||||||
|
def run_in_process(self):
|
||||||
|
self.proc = Process(target=self.run)
|
||||||
|
self.proc.start()
|
@ -1,23 +1 @@
|
|||||||
argparse==1.4.0
|
docker-compose==1.7.0
|
||||||
aspy.yaml==0.2.1
|
|
||||||
blessings==1.6
|
|
||||||
bpython==0.14.2
|
|
||||||
cached-property==1.3.0
|
|
||||||
colorlog==2.6.1
|
|
||||||
curtsies==0.1.23
|
|
||||||
docker-compose==1.5.2
|
|
||||||
docker-py==1.6.0
|
|
||||||
dockerpty==0.3.4
|
|
||||||
docopt==0.6.2
|
|
||||||
greenlet==0.4.9
|
|
||||||
jsonschema==2.5.1
|
|
||||||
nodeenv==0.13.6
|
|
||||||
ordereddict==1.1
|
|
||||||
pre-commit==0.7.5
|
|
||||||
Pygments==2.0.2
|
|
||||||
PyYAML==3.11
|
|
||||||
requests==2.7.0
|
|
||||||
six==1.10.0
|
|
||||||
texttable==0.8.4
|
|
||||||
virtualenv==13.1.2
|
|
||||||
websocket-client==0.35.0
|
|
||||||
|
2
setup.py
2
setup.py
@ -5,7 +5,7 @@ from setuptools import setup
|
|||||||
|
|
||||||
# Thanks Sam and Max
|
# Thanks Sam and Max
|
||||||
|
|
||||||
__version__ = '0.4.7'
|
__version__ = '0.5.0'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
setup(
|
setup(
|
||||||
|
5
tests/configs/runner.yml
Normal file
5
tests/configs/runner.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
post_run_commands:
|
||||||
|
- sleep 2
|
||||||
|
- echo 'OK' > /tmp/runner_test
|
||||||
|
|
||||||
|
command: sleep
|
25
tests/runner_test.py
Normal file
25
tests/runner_test.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"Tests for runner"
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
from multiprocessing import Process
|
||||||
|
|
||||||
|
from pyentrypoint import Entrypoint
|
||||||
|
|
||||||
|
|
||||||
|
def test_runner():
|
||||||
|
run = [
|
||||||
|
(Process(target=Entrypoint(
|
||||||
|
conf='configs/runner.yml',
|
||||||
|
args=['sleep', '5']).launch),
|
||||||
|
'/tmp/runner_test', 0, 0),
|
||||||
|
]
|
||||||
|
|
||||||
|
for proc, test, uid, gid in run:
|
||||||
|
proc.start()
|
||||||
|
proc.join()
|
||||||
|
with open(test, 'r') as f:
|
||||||
|
assert f.readline().startswith('OK')
|
||||||
|
assert os.stat(test).st_uid == uid
|
||||||
|
assert os.stat(test).st_gid == gid
|
Loading…
Reference in New Issue
Block a user