From f1882dab3c6cdc8a9367a56bb72fe84ecc040400 Mon Sep 17 00:00:00 2001 From: Christophe Mehay Date: Sat, 30 May 2020 18:49:13 +0200 Subject: [PATCH] Add set_environment in settings ```yaml set_environment: - ENV_1: echo 'The environment variable ENV_1 will be added with this phrase' - ENV_2: head /dev/urandom | base64 - ENV_3: echo ${ENV_1} and ${ENV_2} are now available here - ENV_4: exit 1 || true # Set like this if you need to ignore error ``` --- CHANGELOG | 3 +++ README.md | 12 ++++++++++++ docs/config.rst | 11 ++++++++++- pyentrypoint/config.py | 6 ++++++ pyentrypoint/entrypoint.py | 16 +++++++++++++--- pyentrypoint/runner.py | 14 ++++++++------ tests/configs/base.yml | 6 ++++++ tests/configs/matching_command.yml | 6 ++++++ tests/pyentrypoint_test.py | 10 ++++++++++ tests/reloader_test.py | 4 ++-- 10 files changed, 76 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3459394..be77590 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +v0.7.2 + - add set_environment in settings + v0.7.1 - add envtobool function in configuration template diff --git a/README.md b/README.md index 6352f75..3cb910c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ This tool avoids writing shell scripts to: ## Changelog +###### v0.7.2 (2020-05-30) + - add set_environment in settings + ###### v0.7.1 (2020-05-24) - add envtobool function in configuration template @@ -161,6 +164,15 @@ links: # true by default required: true +# Set custom environment variable by running commands +# Will run before pre_conf_commands and capture stdout only +# Stop init if crash +set_environment: + - ENV_1: echo 'The environment variable ENV_1 will be added with this phrase' + - ENV_2: head /dev/urandom | base64 + - ENV_3: echo ${ENV_1} and ${ENV_2} are now available here + - ENV_4: exit 1 || true # Set like this if you need to ignore error + # Commands to run before applying configuration pre_conf_commands: - echo something > to_this_file diff --git a/docs/config.rst b/docs/config.rst index 05e4082..a806afd 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -74,7 +74,16 @@ This is an example of ``entrypoint-config.yml`` file. single: true # Set to false to get optional link # true by default - required: true + required: true + + # Set custom environment variable by running commands + # Will run before pre_conf_commands and capture stdout only + # Stop init if crash + set_environment: + - ENV_1: echo 'The environment variable ENV_1 will be added with this phrase' + - ENV_2: head /dev/urandom | base64 + - ENV_3: echo ${ENV_1} and ${ENV_2} are now available here + - ENV_4: exit 1 || true # Add this if you need to ignore error # Commands to run before applying configuration pre_conf_commands: diff --git a/pyentrypoint/config.py b/pyentrypoint/config.py index 53494c0..8c90263 100755 --- a/pyentrypoint/config.py +++ b/pyentrypoint/config.py @@ -233,6 +233,12 @@ class Config(ConfigMeta): else: return ['-*'] + @property + def set_environment(self): + """Set environment variables at runtime""" + return self._get_by_command(item='set_environment', + value_types=[list, dict]) + @property def user(self): "Unix user or uid to run command." diff --git a/pyentrypoint/entrypoint.py b/pyentrypoint/entrypoint.py index 2e07b6a..a14626d 100644 --- a/pyentrypoint/entrypoint.py +++ b/pyentrypoint/entrypoint.py @@ -30,7 +30,7 @@ class Entrypoint(object): def __init__(self, conf=ENTRYPOINT_FILE, args=[]): self._set_logguer() - if 'ENTRYPOINT_CONFIG' in os.environ: + if os.environ.get('ENTRYPOINT_CONFIG'): conf = os.environ['ENTRYPOINT_CONFIG'] try: self.config = Config(conf=conf, args=args) @@ -91,16 +91,25 @@ class Entrypoint(object): envtobool=envtobool, containers=DockerLinks().to_containers())) + def run_set_enviroment(self): + for set_env in self.config.set_environment: + if not isinstance(set_env, dict) and not len(set_env) == 1: + raise Exception("set_environment is miss configured, " + "please check syntax in entrypoint config") + env, cmd = next(((e, c) for e, c in set_env.items())) + self.log.debug(f'Set environment variable {env}') + os.environ[env] = self.runner.run_cmd(cmd, stdout=True) + def run_pre_conf_cmds(self): for cmd in self.config.pre_conf_commands: self.runner.run_cmd(cmd) - if 'ENTRYPOINT_PRECONF_COMMAND' in os.environ: + if os.environ.get('ENTRYPOINT_PRECONF_COMMAND'): self.runner.run_cmd(os.environ['ENTRYPOINT_PRECONF_COMMAND']) def run_post_conf_cmds(self): for cmd in self.config.post_conf_commands: self.runner.run_cmd(cmd) - if 'ENTRYPOINT_POSTCONF_COMMAND' in os.environ: + if os.environ.get('ENTRYPOINT_POSTCONF_COMMAND'): self.runner.run_cmd(os.environ['ENTRYPOINT_POSTCONF_COMMAND']) def launch(self): @@ -117,6 +126,7 @@ def main(argv): entry.launch() entry.config.set_to_env() entry.log.debug("Starting config") + entry.run_set_enviroment() entry.run_pre_conf_cmds() entry.apply_conf() entry.run_post_conf_cmds() diff --git a/pyentrypoint/runner.py b/pyentrypoint/runner.py index 89908b2..2b8e233 100644 --- a/pyentrypoint/runner.py +++ b/pyentrypoint/runner.py @@ -2,7 +2,7 @@ from multiprocessing import Process from subprocess import PIPE from subprocess import Popen -from sys import stdout +from sys import stdout as stdoutput from .logs import Logs @@ -15,13 +15,13 @@ class Runner(object): self.cmds = cmds self.raw_output = config.raw_output - def run_cmd(self, cmd): + def run_cmd(self, cmd, stdout=False): 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' + enc = stdoutput.encoding or 'UTF-8' output = output.decode(enc).split('\n') lenght = len(output) for c, line in enumerate(output): @@ -30,12 +30,14 @@ class Runner(object): 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 out: + display_cb = self.log.info if not self.raw_output else print + if stdout: + return out.decode() + dispout(out, display_cb) if proc.returncode: raise Exception('Command exit code: {}'.format(proc.returncode)) diff --git a/tests/configs/base.yml b/tests/configs/base.yml index 0e153a1..5d05e1e 100644 --- a/tests/configs/base.yml +++ b/tests/configs/base.yml @@ -27,6 +27,7 @@ pre_conf_commands: - echo TEST > /tmp/OK - echo "INFO IS DISPLAYED" - echo "WARNING IS DISPLAYED\nON TWO LINES" 1>&2 + - echo ${ENV_1} > /tmp_env_1 post_conf_commands: - echo TEST2 > /tmp/OKOK @@ -36,5 +37,10 @@ post_conf_commands: - echo ${ENTRYPOINT_DEBUG} > /tmp/debug - echo "INFO IS DISPLAYED\nON TWO LINES" - echo "WARNING IS DISPLAYED" 1>&2 + - echo ${ENV_2} > /tmp_env_2 + +set_environment: + - ENV_1: echo ENV_1 set + - ENV_2: echo ENV_2 set debug: true diff --git a/tests/configs/matching_command.yml b/tests/configs/matching_command.yml index d76fbab..7f9a7cf 100644 --- a/tests/configs/matching_command.yml +++ b/tests/configs/matching_command.yml @@ -49,6 +49,12 @@ post_run_commands: - zsh: - cmd9 +set_environment: + - bash: + - ENV_1: echo set ENV_1 + - zsh: + - ENV_2: echo set ENV_2 + debug: - zsh: false - '*sh': true diff --git a/tests/pyentrypoint_test.py b/tests/pyentrypoint_test.py index 646caa1..5dad6df 100644 --- a/tests/pyentrypoint_test.py +++ b/tests/pyentrypoint_test.py @@ -171,12 +171,15 @@ def test_conf_commands(): ('/tmp/user', '1000'), ('/tmp/group', '1000'), ('/tmp/debug', 'true'), + ('/tmp_env_1', 'ENV_1 set'), + ('/tmp_env_2', 'ENV_2 set') ] os.environ['ENTRYPOINT_PRECONF_COMMAND'] = 'echo TEST4 > /tmp/OKOKOKOK' os.environ['ENTRYPOINT_POSTCONF_COMMAND'] = 'echo TEST5 > /tmp/OKOKOKOKOK' entry.config.set_to_env() + entry.run_set_enviroment() entry.run_pre_conf_cmds() entry.run_post_conf_cmds() @@ -380,6 +383,13 @@ def test_command_matching_setup(): 'cmd5', ] + assert bash.config.set_environment == [ + {'ENV_1': 'echo set ENV_1'} + ] + assert zsh.config.set_environment == [ + {'ENV_2': 'echo set ENV_2'} + ] + assert bash.config.post_run_commands == [ 'cmd7', 'cmd8', diff --git a/tests/reloader_test.py b/tests/reloader_test.py index 1b4902f..97a98ac 100644 --- a/tests/reloader_test.py +++ b/tests/reloader_test.py @@ -36,7 +36,7 @@ def _reloader_check(conf, command): def test_reloader(): - if 'ENTRYPOINT_DISABLE_RELOAD' in os.environ: + if os.environ.get('ENTRYPOINT_DISABLE_RELOAD'): os.environ.pop('ENTRYPOINT_DISABLE_RELOAD') with mock.patch('os.kill') as os_kill: @@ -58,7 +58,7 @@ def test_disabled_reloader(): def test_reloader_custom(): - if 'ENTRYPOINT_DISABLE_RELOAD' in os.environ: + if os.environ.get('ENTRYPOINT_DISABLE_RELOAD'): os.environ.pop('ENTRYPOINT_DISABLE_RELOAD') subprocess.check_call(['mkdir', '-p', '/tmp/1', '/tmp/2'])