From 95d547d3e6c98f927eaad77346b84b2ccef95627 Mon Sep 17 00:00:00 2001 From: Michael Calmer Date: Thu, 19 Jan 2017 15:39:10 +0100 Subject: [PATCH] add options for dockerng * add buildargs option to dockerng.build * docker buildargs require minimal dockerpy 1.6.0 and docker version 1.9.0 * update version in the docs * support dryrun for dockerng.sls_build provide the possibility to put extra modules into the thin added unit test for dockerng.sls_build dryrun option --- salt/modules/dockerng.py | 51 ++++++++++++++++++----- tests/unit/modules/dockerng_test.py | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 11 deletions(-) diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py index 29af703ffa..c199fede1e 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/dockerng.py @@ -28,13 +28,13 @@ to replace references to ``dockerng`` with ``docker``. Installation Prerequisites -------------------------- -This execution module requires at least version 1.4.0 of both docker-py_ and -Docker_. docker-py can easily be installed using :py:func:`pip.install -`: +This execution module requires at least version 1.6.0 of docker-py_ and +version 1.9.0 of Docker_. docker-py can easily be installed using +:py:func:`pip.install `: .. code-block:: bash - salt myminion pip.install docker-py>=1.4.0 + salt myminion pip.install docker-py>=1.6.0 .. _docker-py: https://pypi.python.org/pypi/docker-py .. _Docker: https://www.docker.com/ @@ -332,8 +332,8 @@ __func_alias__ = { } # Minimum supported versions -MIN_DOCKER = (1, 6, 0) -MIN_DOCKER_PY = (1, 4, 0) +MIN_DOCKER = (1, 9, 0) +MIN_DOCKER_PY = (1, 6, 0) VERSION_RE = r'([\d.]+)' @@ -3557,7 +3557,8 @@ def build(path=None, rm=True, api_response=False, fileobj=None, - dockerfile=None): + dockerfile=None, + buildargs=None): ''' Builds a docker image from a Dockerfile or a URL @@ -3591,6 +3592,10 @@ def build(path=None, .. versionadded:: develop + buildargs + A dictionary of build arguments provided to the docker build process. + + **RETURN DATA** A dictionary containing one or more of the following keys: @@ -3637,7 +3642,8 @@ def build(path=None, fileobj=fileobj, rm=rm, nocache=not cache, - dockerfile=dockerfile) + dockerfile=dockerfile, + buildargs=buildargs) ret = {'Time_Elapsed': time.time() - time_started} _clear_context() @@ -5755,7 +5761,9 @@ def call(name, function, *args, **kwargs): raise CommandExecutionError('Missing function parameter') # move salt into the container - thin_path = salt.utils.thin.gen_thin(__opts__['cachedir']) + thin_path = salt.utils.thin.gen_thin(__opts__['cachedir'], + extra_mods=__salt__['config.option']("thin_extra_mods", ''), + so_mods=__salt__['config.option']("thin_so_mods", '')) with io.open(thin_path, 'rb') as file: _client_wrapper('put_archive', name, thin_dest_path, file) try: @@ -5865,7 +5873,7 @@ def sls(name, mods=None, saltenv='base', **kwargs): def sls_build(name, base='opensuse/python', mods=None, saltenv='base', - **kwargs): + dryrun=False, **kwargs): ''' Build a docker image using the specified sls modules and base image. @@ -5873,6 +5881,24 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', can build a docker image inside myminion that results of applying those states by doing: + base + the base image + + mods + the state modules to execute during build + + saltenv + the salt environment to use + + dryrun: False + when set to True the container will not be commited at the end of + the build. The dryrun succeed also when the state contains errors. + + **RETURN DATA** + + A dictionary with the ID of the new container. In case of a dryrun, + the state result is returned and the container gets removed. + CLI Example: .. code-block:: bash @@ -5905,9 +5931,12 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', # Now execute the state into the container ret = __salt__['dockerng.sls'](id_, mods, saltenv, **kwargs) # fail if the state was not successful - if not salt.utils.check_state_result(ret): + if not dryrun and not salt.utils.check_state_result(ret): raise CommandExecutionError(ret) finally: __salt__['dockerng.stop'](id_) + if dryrun: + __salt__['dockerng.rm'](id_) + return ret return __salt__['dockerng.commit'](id_, name) diff --git a/tests/unit/modules/dockerng_test.py b/tests/unit/modules/dockerng_test.py index ec4901e275..546635577a 100644 --- a/tests/unit/modules/dockerng_test.py +++ b/tests/unit/modules/dockerng_test.py @@ -680,6 +680,84 @@ class DockerngTestCase(TestCase): self.assertEqual( {'Id': 'ID2', 'Image': 'foo', 'Time_Elapsed': 42}, ret) + def test_sls_build_dryrun(self, *args): + ''' + test build sls image in dryrun mode. + ''' + docker_start_mock = MagicMock( + return_value={}) + docker_create_mock = MagicMock( + return_value={'Id': 'ID', 'Name': 'NAME'}) + docker_stop_mock = MagicMock( + return_value={'state': {'old': 'running', 'new': 'stopped'}, + 'result': True}) + docker_rm_mock = MagicMock( + return_value={}) + + docker_sls_mock = MagicMock( + return_value={ + "file_|-/etc/test.sh_|-/etc/test.sh_|-managed": { + "comment": "File /etc/test.sh is in the correct state", + "name": "/etc/test.sh", + "start_time": "07:04:26.834792", + "result": True, + "duration": 13.492, + "__run_num__": 0, + "changes": {} + }, + "test_|-always-passes_|-foo_|-succeed_without_changes": { + "comment": "Success!", + "name": "foo", + "start_time": "07:04:26.848915", + "result": True, + "duration": 0.363, + "__run_num__": 1, + "changes": {} + } + }) + + ret = None + with patch.dict(dockerng_mod.__salt__, { + 'dockerng.start': docker_start_mock, + 'dockerng.create': docker_create_mock, + 'dockerng.stop': docker_stop_mock, + 'dockerng.rm': docker_rm_mock, + 'dockerng.sls': docker_sls_mock}): + ret = dockerng_mod.sls_build( + 'foo', + mods='foo', + dryrun=True + ) + docker_create_mock.assert_called_once_with( + cmd='sleep infinity', + image='opensuse/python', interactive=True, name='foo', tty=True) + docker_start_mock.assert_called_once_with('ID') + docker_sls_mock.assert_called_once_with('ID', 'foo', 'base') + docker_stop_mock.assert_called_once_with('ID') + docker_rm_mock.assert_called_once_with('ID') + self.assertEqual( + { + "file_|-/etc/test.sh_|-/etc/test.sh_|-managed": { + "comment": "File /etc/test.sh is in the correct state", + "name": "/etc/test.sh", + "start_time": "07:04:26.834792", + "result": True, + "duration": 13.492, + "__run_num__": 0, + "changes": {} + }, + "test_|-always-passes_|-foo_|-succeed_without_changes": { + "comment": "Success!", + "name": "foo", + "start_time": "07:04:26.848915", + "result": True, + "duration": 0.363, + "__run_num__": 1, + "changes": {} + } + }, + ret) + def test_call_success(self): ''' test module calling inside containers @@ -694,6 +772,9 @@ class DockerngTestCase(TestCase): return_value={ 'retcode': 0 }) + docker_config_mock = MagicMock( + return_value='' + ) client = Mock() client.put_archive = Mock() @@ -704,6 +785,7 @@ class DockerngTestCase(TestCase): dockerng_mod.__salt__, { 'dockerng.run_all': docker_run_all_mock, 'dockerng.copy_to': docker_copy_to_mock, + 'config.option': docker_config_mock }), patch.dict( dockerng_mod.__context__, { -- 2.11.0