Add support for handling commands matching setup

It's now possible to map setups to specific commands.
It's easier to handle many commands in one container.
pull/23/head
Christophe Mehay 4 years ago
parent f5aec4c0d5
commit 5b0e0c8657

@ -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:
- add post_run_commands in entrypoint-config.yml

@ -14,9 +14,12 @@ This tool avoids writing shell scripts to:
## Changelog
* V0.6.0 (2020-05-10)
* Drop python 2 support
* Deprecation of `command` and `subcommands` settings for `commands` (see bellow)
###### v0.7.0 (2020-05-17)
- Add command matching setup
###### V0.6.0 (2020-05-10)
- Drop python 2 support
- Deprecation of `command` and `subcommands` settings for `commands` (see bellow)
## Usages
@ -88,7 +91,7 @@ This is an example of `entrypoint-config.yml` file.
# This entry list 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 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
commands:
- git
@ -189,6 +192,44 @@ debug: true
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
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
~~~~~~~~~~~~~~~
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
^^^^^^^
@ -118,6 +131,10 @@ command
If the container is not started with this commande,
the configuration will not be applied.
.. pull-quote::
**DEPRECATED**: This setup is remplaced by ``commands``.
subcommands
^^^^^^^^^^^
@ -135,7 +152,7 @@ Running container with a matching subcommand run it with setuped ``command``.
.. pull-quote::
**Note**: Globbing pattern is enabled here.
**DEPRECATED**: This setup will be dropped.
By default, all args started with hyphen are handled.
@ -289,3 +306,40 @@ quiet
^^^^^
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 []
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):
"""Returns iterator of tuple (template, config_file)"""
config_files = self.config_files
@ -180,9 +238,10 @@ class Config(ConfigMeta):
"Unix user or uid to run command."
self._get_from_env(env='ENTRYPOINT_USER', key='user')
if 'user' in self._config:
if isinstance(self._config['user'], int):
return self._config['user']
return getpwnam(self._config['user']).pw_uid
user = self._get_by_command(item='user', value_types=[int, str])
if isinstance(user, int):
return user
return getpwnam(user).pw_uid
return os.getuid()
@property
@ -190,20 +249,23 @@ class Config(ConfigMeta):
"Unix group or gid to run command."
self._get_from_env(env='ENTRYPOINT_GROUP', key='group')
if 'group' in self._config:
if isinstance(self._config['group'], int):
return self._config['group']
return getgrnam(self._config['group']).gr_gid
group = self._get_by_command(item='group', value_types=[int, str])
if isinstance(group, int):
return group
return getgrnam(group).gr_gid
return os.getgid()
@property
def config_files(self):
"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
def secret_env(self):
"""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
def links(self):
@ -221,17 +283,20 @@ class Config(ConfigMeta):
@property
def pre_conf_commands(self):
"""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
def post_conf_commands(self):
"""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
def post_run_commands(self):
"""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
def reload(self):
@ -249,14 +314,16 @@ class Config(ConfigMeta):
def clean_env(self):
"""Clean env from linked containers before running command"""
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
@property
def remove_dockerenv(self):
"""Remove dockerenv and dockerinit files"""
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
@property
@ -265,7 +332,8 @@ class Config(ConfigMeta):
if envtobool('ENTRYPOINT_DEBUG', False):
return True
if 'debug' in self._config:
return bool(self._config['debug'])
return bool(self._get_by_command(item='debug',
value_types=[bool]))
return False
@property

@ -1,7 +1,7 @@
[tool]
[tool.poetry]
name = "pyentrypoint"
version = "0.6.0"
version = "0.7.0"
description = "pyentrypoint manages entrypoints in Docker containers."
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"]

@ -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 not zsh.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