Skip to content

Commit

Permalink
Support shell interpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
YuviGold authored and eranco74 committed Jul 28, 2021
1 parent f65801e commit d2e47ce
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,19 @@ env:
VAR: $$VAR_NOT_INTERPOLATED
````

### Shell Interpolation

Skipper supports evaluating shell commands inside its configuration file using `$(command)` notation.
e.g.

```yaml
env:
VAR: $(expr ${MY_NUMBER:-5} + 5)
volumes:
- $(which myprogram):/myprogram
```
### Volumes:
Skipper can bind-mount a host directory into the container.
you can add volumes in the configuration file:
Expand Down
14 changes: 11 additions & 3 deletions skipper/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from string import Template
from collections import defaultdict
import os
import yaml
from collections import defaultdict
from re import findall
from string import Template
from subprocess import check_output

import six
import yaml


def load_defaults():
Expand Down Expand Up @@ -32,4 +35,9 @@ def _normalize_config(config, normalized_config):


def _interpolate_env_vars(key):
for match in findall(r'\$\(.+\)', key):
output = check_output("echo " + match, shell=True).strip()
if not output:
raise ValueError(match)
key = key.replace(match, output)
return Template(key).substitute(defaultdict(lambda: "", os.environ))
58 changes: 58 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,33 @@
'container-context': SKIPPER_CONF_CONTAINER_CONTEXT
}

SKIPPER_CONF_WITH_SHELL_INTERPOLATION = {
'registry': REGISTRY,
'build-container-image': SKIPPER_CONF_BUILD_CONTAINER_IMAGE,
'build-container-tag': SKIPPER_CONF_BUILD_CONTAINER_TAG,
'make': {
'makefile': SKIPPER_CONF_MAKEFILE,
},
'volumes': [
'$(which cat):/cat',
],
'env': [
'KEY=$(expr ${MY_NUMBER:-5} + 5)'
]
}

SKIPPER_CONF_WITH_INVALID_SHELL_INTERPOLATION = {
'registry': REGISTRY,
'build-container-image': SKIPPER_CONF_BUILD_CONTAINER_IMAGE,
'build-container-tag': SKIPPER_CONF_BUILD_CONTAINER_TAG,
'make': {
'makefile': SKIPPER_CONF_MAKEFILE,
},
'volumes': [
'$(bla bla):/cat',
]
}


class TestCLI(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -1572,6 +1599,37 @@ def test_run_with_defaults_from_config_file_including_volumes(self, skipper_runn
volumes=['volume1', 'volume2'], workspace=None,
workdir=None, use_cache=False, env_file=())

@mock.patch('__builtin__.open', mock.MagicMock(create=True))
@mock.patch('os.path.exists', mock.MagicMock(autospec=True, return_value=True))
@mock.patch('yaml.safe_load', mock.MagicMock(autospec=True, return_value=SKIPPER_CONF_WITH_SHELL_INTERPOLATION))
@mock.patch('subprocess.check_output', mock.MagicMock(autospec=True, return_value='1234567\n'))
@mock.patch('skipper.runner.run', autospec=True)
def test_run_with_defaults_from_config_file_including_interpolated_volumes(self, skipper_runner_run_mock):
command = ['ls', '-l']
run_params = command
self._invoke_cli(
defaults=config.load_defaults(),
subcmd='run',
subcmd_params=run_params
)
expected_fqdn_image = 'skipper-conf-build-container-image:skipper-conf-build-container-tag'
skipper_runner_run_mock.assert_called_once_with(command, fqdn_image=expected_fqdn_image, environment=['KEY=10'],
interactive=False, name=None, net=None, publish=(),
volumes=['/bin/cat:/cat'], workspace=None,
workdir=None, use_cache=False, env_file=())

@mock.patch('yaml.safe_load', mock.MagicMock(autospec=True, return_value=SKIPPER_CONF_WITH_INVALID_SHELL_INTERPOLATION))
def test_run_with_defaults_from_config_file_including_invalid_interploated_volumes_interpolated(self):
command = ['ls', '-l']
run_params = command

with self.assertRaises(ValueError):
self._invoke_cli(
defaults=config.load_defaults(),
subcmd='run',
subcmd_params=run_params
)

@mock.patch('__builtin__.open', mock.MagicMock(create=True))
@mock.patch('os.path.exists', mock.MagicMock(autospec=True, return_value=True))
@mock.patch('yaml.safe_load', mock.MagicMock(autospec=True, return_value=SKIPPER_CONF_WITH_WORKDIR))
Expand Down

0 comments on commit d2e47ce

Please sign in to comment.