Merge pull request #23 from cmehay/setup_match

Add support for handling commands matching setup
envtobool v0.7.0
Christophe Mehay 4 years ago committed by GitHub
commit 5389f36478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,2 +1,9 @@
v0.7.0
- Add mapping of config commands and conf files to root commands
v0.6.0:
- Drop python 2 support
- Deprecation of `command` and `subcommands` settings for `commands`
v0.5.0: v0.5.0:
- add post_run_commands in entrypoint-config.yml - add post_run_commands in entrypoint-config.yml

@ -14,9 +14,12 @@ This tool avoids writing shell scripts to:
## Changelog ## Changelog
* V0.6.0 (2020-05-10) ###### v0.7.0 (2020-05-17)
* Drop python 2 support - Add command matching setup
* Deprecation of `command` and `subcommands` settings for `commands` (see bellow)
###### V0.6.0 (2020-05-10)
- Drop python 2 support
- Deprecation of `command` and `subcommands` settings for `commands` (see bellow)
## Usages ## Usages
@ -88,7 +91,7 @@ This is an example of `entrypoint-config.yml` file.
# This entry list commands handled by entrypoint. # This entry list commands handled by entrypoint.
# If you run the container with a command not in this list, # If you run the container with a command not in this list,
# pyentrypoint will run the command directly without any action # pyentrypoint will run the command directly without any action
# If this option and `command` are not set, all commands will be handled. # If this setting and `command` are not set, all commands will be handled.
# Support wildcard # Support wildcard
commands: commands:
- git - git
@ -189,6 +192,44 @@ debug: true
quiet: false quiet: false
``` ```
#### Handled command matching
All settings can be mapped to an handled command.
For instance:
```yaml
# This config will handle command `abc` and `xyz`
commands:
- abc
- xyz
# you can map commands to handled commands bellow
pre_conf_commands:
- abc:
- echo "will run for command abc"
- xyz:
- echo "will run for command xyz"
- echo "Can be multiple"
- echo "Will run for both commands"
user:
- abc: 1000
- xyz: 1001
# Mapping can also be a dictionnary
group:
abc: 1000
xyz: 1001
# Etc
```
Not supported for deprecated settings `command`, `subcommands` and `links`.
### Config templates ### Config templates
You can generate configuration for your service with jinja2 template. You can generate configuration for your service with jinja2 template.

@ -110,6 +110,19 @@ This is an example of ``entrypoint-config.yml`` file.
yaml references yaml references
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
commands
^^^^^^^^
This setup lists commands handled by entrypoint.
If you run the container with a command not in this list,
pyentrypoint will run the command directly without any action
If this setting and `command` are not set, all commands will be handled.
Support wildcard
.. code:: yaml
commands:
- git
- sl*
command command
^^^^^^^ ^^^^^^^
@ -118,6 +131,10 @@ command
If the container is not started with this commande, If the container is not started with this commande,
the configuration will not be applied. the configuration will not be applied.
.. pull-quote::
**DEPRECATED**: This setup is remplaced by ``commands``.
subcommands subcommands
^^^^^^^^^^^ ^^^^^^^^^^^
@ -135,7 +152,7 @@ Running container with a matching subcommand run it with setuped ``command``.
.. pull-quote:: .. pull-quote::
**Note**: Globbing pattern is enabled here. **DEPRECATED**: This setup will be dropped.
By default, all args started with hyphen are handled. By default, all args started with hyphen are handled.
@ -289,3 +306,40 @@ quiet
^^^^^ ^^^^^
Do not output anything except error Do not output anything except error
Handled command matching
========================
All settings can be mapped to an handled command.
For instance:
.. code:: yaml
# This config will handle command `abc` and `xyz`
commands:
- abc
- xyz
# you can map commands to handled commands bellow
pre_conf_commands:
- abc:
- echo "will run for command abc"
- xyz:
- echo "will run for command xyz"
- echo "Can be multiple"
- echo "Will run for both commands"
user:
- abc: 1000
- xyz: 1001
# Mapping can also be a dictionnary
group:
abc: 1000
xyz: 1001
# Etc
Not supported for deprecated settings `command`, `subcommands` and `links`.

@ -35,6 +35,64 @@ class ConfigMeta(object):
return self._config[item] return self._config[item]
return [] return []
def _match_command(self, match):
if self._args:
return bool(fnmatch(self._args[0], match))
return False
def _check_command_match_key(self, dic, item):
if len(dic) != 1:
raise Exception('{item} setup missformated.'.format(item=item))
def _get_by_command(self, item=None, content=None, value_types=[]):
"""Return settings for handled command"""
def _mapping_list(content):
for d in content:
if not isinstance(d, dict):
raise Exception(
'{item} setup missformated.'.format(item=item))
value = _mapping_dict(d)
if value:
return value
def _mapping_dict(content, check_value=False):
for key, value in content.items():
if check_value and not isinstance(value, list):
return content
if self._match_command(key):
return value
if not content:
if item not in self._config:
return [] if list in value_types else None
content = self._config[item]
if list not in value_types:
if isinstance(content, dict):
return _mapping_dict(content, dict in value_types)
if isinstance(content, list):
return _mapping_list(content)
return content
if not isinstance(content, list):
raise Exception('{item} setup missformated.'.format(item=item))
rtn = []
for line in content:
parsed = self._get_by_command(item=item,
content=line,
value_types=[
t for t in value_types
if t is dict
])
if parsed and isinstance(parsed, list):
rtn.extend(parsed)
continue
if parsed:
rtn.append(parsed)
return rtn
def get_templates(self): def get_templates(self):
"""Returns iterator of tuple (template, config_file)""" """Returns iterator of tuple (template, config_file)"""
config_files = self.config_files config_files = self.config_files
@ -180,9 +238,10 @@ class Config(ConfigMeta):
"Unix user or uid to run command." "Unix user or uid to run command."
self._get_from_env(env='ENTRYPOINT_USER', key='user') self._get_from_env(env='ENTRYPOINT_USER', key='user')
if 'user' in self._config: if 'user' in self._config:
if isinstance(self._config['user'], int): user = self._get_by_command(item='user', value_types=[int, str])
return self._config['user'] if isinstance(user, int):
return getpwnam(self._config['user']).pw_uid return user
return getpwnam(user).pw_uid
return os.getuid() return os.getuid()
@property @property
@ -190,20 +249,23 @@ class Config(ConfigMeta):
"Unix group or gid to run command." "Unix group or gid to run command."
self._get_from_env(env='ENTRYPOINT_GROUP', key='group') self._get_from_env(env='ENTRYPOINT_GROUP', key='group')
if 'group' in self._config: if 'group' in self._config:
if isinstance(self._config['group'], int): group = self._get_by_command(item='group', value_types=[int, str])
return self._config['group'] if isinstance(group, int):
return getgrnam(self._config['group']).gr_gid return group
return getgrnam(group).gr_gid
return os.getgid() return os.getgid()
@property @property
def config_files(self): def config_files(self):
"List of template config files." "List of template config files."
return self._return_item_lst('config_files') return self._get_by_command(item='config_files',
value_types=[list, dict])
@property @property
def secret_env(self): def secret_env(self):
"""Environment variables to delete before running command.""" """Environment variables to delete before running command."""
return self._return_item_lst('secret_env') return self._get_by_command(item='secret_env',
value_types=[list])
@property @property
def links(self): def links(self):
@ -221,17 +283,20 @@ class Config(ConfigMeta):
@property @property
def pre_conf_commands(self): def pre_conf_commands(self):
"""Return list of preconf commands""" """Return list of preconf commands"""
return self._return_item_lst('pre_conf_commands') return self._get_by_command(item='pre_conf_commands',
value_types=[list])
@property @property
def post_conf_commands(self): def post_conf_commands(self):
"""Return list of postconf commands""" """Return list of postconf commands"""
return self._return_item_lst('post_conf_commands') return self._get_by_command(item='post_conf_commands',
value_types=[list])
@property @property
def post_run_commands(self): def post_run_commands(self):
"""Return list of post run commands""" """Return list of post run commands"""
return self._return_item_lst('post_run_commands') return self._get_by_command(item='post_run_commands',
value_types=[list])
@property @property
def reload(self): def reload(self):
@ -249,14 +314,16 @@ class Config(ConfigMeta):
def clean_env(self): def clean_env(self):
"""Clean env from linked containers before running command""" """Clean env from linked containers before running command"""
if 'clean_env' in self._config: if 'clean_env' in self._config:
return bool(self._config['clean_env']) return bool(self._get_by_command(item='clean_env',
value_types=[bool]))
return True return True
@property @property
def remove_dockerenv(self): def remove_dockerenv(self):
"""Remove dockerenv and dockerinit files""" """Remove dockerenv and dockerinit files"""
if 'remove_dockerenv' in self._config: if 'remove_dockerenv' in self._config:
return bool(self._config['remove_dockerenv']) return bool(self._get_by_command(item='remove_dockerenv',
value_types=[bool]))
return True return True
@property @property
@ -265,7 +332,8 @@ class Config(ConfigMeta):
if envtobool('ENTRYPOINT_DEBUG', False): if envtobool('ENTRYPOINT_DEBUG', False):
return True return True
if 'debug' in self._config: if 'debug' in self._config:
return bool(self._config['debug']) return bool(self._get_by_command(item='debug',
value_types=[bool]))
return False return False
@property @property

@ -1,7 +1,7 @@
[tool] [tool]
[tool.poetry] [tool.poetry]
name = "pyentrypoint" name = "pyentrypoint"
version = "0.6.0" version = "0.7.0"
description = "pyentrypoint manages entrypoints in Docker containers." description = "pyentrypoint manages entrypoints in Docker containers."
license = "WTFPL" license = "WTFPL"
classifiers = ["Programming Language :: Python", "Development Status :: 1 - Planning", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6", "Topic :: System :: Installation/Setup"] classifiers = ["Programming Language :: Python", "Development Status :: 1 - Planning", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6", "Topic :: System :: Installation/Setup"]

@ -0,0 +1,62 @@
commands:
- bash
user:
- bash: 1000
- zsh: 1001
group:
bash: 1002
zsh: 1003
config_files:
- bash:
- file1.tpl
- file2: file3
- file4
- zsh:
- file5.tpl
- file6: file7
- file8
- file9
- file10: file11
secret_env:
- secret1
- bash:
- secret2
- zsh:
- secret3
pre_conf_commands:
- bash:
- cmd1
- zsh:
- cmd2
- cmd3
post_conf_commands:
- cmd4
- zsh:
- cmd5
- bash:
- cmd6
post_run_commands:
- bash:
- cmd7
- cmd8
- zsh:
- cmd9
debug:
- zsh: false
- '*sh': true
clean_env:
- bash: true
- '*sh': false
remove_dockerenv:
- bash: true
- zsh: true

@ -322,3 +322,74 @@ def test_commands_handling():
assert bash.is_handled assert bash.is_handled
assert not zsh.is_handled assert not zsh.is_handled
assert empty.is_handled assert empty.is_handled
def test_command_matching_setup():
bash = Entrypoint(conf='configs/matching_command.yml', args=['bash'])
zsh = Entrypoint(conf='configs/matching_command.yml', args=['zsh'])
assert bash.config.user == 1000
assert zsh.config.user == 1001
assert bash.config.group == 1002
assert zsh.config.group == 1003
assert bash.config.config_files == [
'file1.tpl',
{'file2': 'file3'},
'file4',
'file9',
{'file10': 'file11'},
]
assert zsh.config.config_files == [
'file5.tpl',
{'file6': 'file7'},
'file8',
'file9',
{'file10': 'file11'},
]
assert bash.config.secret_env == [
'secret1',
'secret2',
]
assert zsh.config.secret_env == [
'secret1',
'secret3',
]
assert bash.config.pre_conf_commands == [
'cmd1',
'cmd3',
]
assert zsh.config.pre_conf_commands == [
'cmd2',
'cmd3',
]
assert bash.config.post_conf_commands == [
'cmd4',
'cmd6',
]
assert zsh.config.post_conf_commands == [
'cmd4',
'cmd5',
]
assert bash.config.post_run_commands == [
'cmd7',
'cmd8',
]
assert zsh.config.post_run_commands == [
'cmd8',
'cmd9',
]
assert bash.config.debug
assert zsh.config.debug
assert bash.config.clean_env
assert not zsh.config.clean_env
assert bash.config.remove_dockerenv
assert zsh.config.remove_dockerenv

Loading…
Cancel
Save