salt/tests/unit/states/dockerng_test.py

508 lines
21 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
'''
Unit tests for the dockerng state
'''
# Import Python Libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import (
MagicMock,
Mock,
NO_MOCK,
NO_MOCK_REASON,
patch
)
ensure_in_syspath('../../')
# Import Salt Libs
from salt.exceptions import CommandExecutionError
from salt.modules import dockerng as dockerng_mod
from salt.states import dockerng as dockerng_state
dockerng_mod.__context__ = {'docker.docker_version': ''}
dockerng_mod.__salt__ = {}
dockerng_state.__context__ = {}
dockerng_state.__opts__ = {'test': False}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class DockerngTestCase(TestCase):
'''
Validate dockerng state
'''
def test_running_with_no_predifined_volume(self):
'''
Test dockerng.running function with an image
that doens't have VOLUME defined.
The ``binds`` argument, should create a container
with respective volumes extracted from ``binds``.
'''
dockerng_create = Mock()
dockerng_start = Mock()
dockerng_history = MagicMock(return_value=[])
__salt__ = {'dockerng.list_containers': MagicMock(),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(),
'dockerng.inspect_image': MagicMock(),
'dockerng.create': dockerng_create,
'dockerng.start': dockerng_start,
'dockerng.history': dockerng_history,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
dockerng_state.running(
'cont',
image='image:latest',
binds=['/host-0:/container-0:ro'])
dockerng_create.assert_called_with(
'image:latest',
validate_input=False,
name='cont',
binds={'/host-0': {'bind': '/container-0', 'ro': True}},
volumes=['/container-0'],
validate_ip_addrs=False,
client_timeout=60)
dockerng_start.assert_called_with('cont')
def test_running_with_predifined_volume(self):
'''
Test dockerng.running function with an image
that already have VOLUME defined.
The ``binds`` argument, shouldn't have side effects on
container creation.
'''
dockerng_create = Mock()
dockerng_start = Mock()
dockerng_history = MagicMock(return_value=['VOLUME /container-0'])
__salt__ = {'dockerng.list_containers': MagicMock(),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(),
'dockerng.inspect_image': MagicMock(),
'dockerng.create': dockerng_create,
'dockerng.start': dockerng_start,
2015-05-20 18:54:20 +00:00
'dockerng.history': dockerng_history,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
dockerng_state.running(
'cont',
image='image:latest',
binds=['/host-0:/container-0:ro'])
dockerng_create.assert_called_with(
'image:latest',
validate_input=False,
binds={'/host-0': {'bind': '/container-0', 'ro': True}},
validate_ip_addrs=False,
name='cont',
client_timeout=60)
dockerng_start.assert_called_with('cont')
def test_running_with_no_predifined_ports(self):
'''
Test dockerng.running function with an image
that doens't have EXPOSE defined.
The ``port_bindings`` argument, should create a container
with respective ``ports`` extracted from ``port_bindings``.
'''
dockerng_create = Mock()
dockerng_start = Mock()
dockerng_history = MagicMock(return_value=[])
__salt__ = {'dockerng.list_containers': MagicMock(),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(),
'dockerng.inspect_image': MagicMock(),
'dockerng.create': dockerng_create,
'dockerng.start': dockerng_start,
'dockerng.history': dockerng_history,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
dockerng_state.running(
'cont',
image='image:latest',
port_bindings=['9090:9797/tcp'])
dockerng_create.assert_called_with(
'image:latest',
validate_input=False,
name='cont',
ports=[9797],
port_bindings={9797: [9090]},
validate_ip_addrs=False,
client_timeout=60)
dockerng_start.assert_called_with('cont')
def test_running_with_predifined_ports(self):
'''
Test dockerng.running function with an image
that contains EXPOSE statements.
The ``port_bindings`` argument, shouldn't have side effect on container
creation.
'''
dockerng_create = Mock()
dockerng_start = Mock()
dockerng_history = MagicMock(return_value=['EXPOSE 9797/tcp'])
__salt__ = {'dockerng.list_containers': MagicMock(),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(),
'dockerng.inspect_image': MagicMock(),
'dockerng.create': dockerng_create,
'dockerng.start': dockerng_start,
'dockerng.history': dockerng_history,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
dockerng_state.running(
'cont',
image='image:latest',
port_bindings=['9090:9797/tcp'])
dockerng_create.assert_called_with(
'image:latest',
validate_input=False,
name='cont',
port_bindings={9797: [9090]},
validate_ip_addrs=False,
client_timeout=60)
dockerng_start.assert_called_with('cont')
def test_running_compare_images_by_id(self):
'''
Make sure the container is running
against expected image.
Here the local image is named 'image:latest' and the container
is also running against an image called 'image:latest'.
Therefore the image ids are diverging because the tag 'image:latest'
moved to a fresher image.
Thus this test make sure the old container is droped and recreated.
'''
new_fake_image_id = 'abcdefgh'
old_fake_image_id = '123456789'
dockerng_inspect_image = Mock(return_value={'Id': new_fake_image_id})
dockerng_inspect_container = Mock(
return_value={'Image': old_fake_image_id,
'Config': {'Image': 'image:latest'}})
dockerng_list_containers = Mock(return_value=['cont'])
dockerng__state = Mock(return_value='running')
dockerng_stop = Mock(return_value={'result': True})
dockerng_rm = Mock(return_value=['container-id'])
__salt__ = {'dockerng.list_containers': dockerng_list_containers,
'dockerng.inspect_container': dockerng_inspect_container,
'dockerng.inspect_image': dockerng_inspect_image,
'dockerng.list_tags': MagicMock(),
'dockerng.state': dockerng__state,
'dockerng.pull': MagicMock(return_value=new_fake_image_id),
'dockerng.create': MagicMock(return_value='new_container'),
'dockerng.start': MagicMock(),
'dockerng.stop': dockerng_stop,
'dockerng.rm': dockerng_rm,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.running(
'cont',
image='image:latest',
)
dockerng_stop.assert_called_with('cont', timeout=10, unpause=True)
dockerng_rm.assert_called_with('cont')
self.assertEqual(ret, {'name': 'cont',
'comment': "Container 'cont' was replaced",
'result': True,
'changes': {'added': 'new_container',
'image': new_fake_image_id,
'removed': ['container-id']}
})
def test_image_present_already_local(self):
'''
According following sls,
.. code-block:: yaml
image:latest:
dockerng.image_present:
- force: true
if ``image:latest`` is already downloaded locally the state
should not report changes.
'''
dockerng_inspect_image = Mock(
return_value={'Id': 'abcdefghijk'})
dockerng_pull = Mock(
return_value={'Layers':
{'Already_Pulled': ['abcdefghijk'],
'Pulled': []},
'Status': 'Image is up to date for image:latest',
'Time_Elapsed': 1.1})
dockerng_list_tags = Mock(
return_value=['image:latest']
)
__salt__ = {'dockerng.list_tags': dockerng_list_tags,
'dockerng.pull': dockerng_pull,
'dockerng.inspect_image': dockerng_inspect_image,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.image_present('image:latest', force=True)
self.assertEqual(ret,
{'changes': {},
'result': True,
'comment': "Image 'image:latest' was pulled, "
"but there were no changes",
'name': 'image:latest',
})
def test_image_present_and_force(self):
'''
According following sls,
.. code-block:: yaml
image:latest:
dockerng.image_present:
- force: true
if ``image:latest`` is not downloaded and force is true
should pull a new image successfuly.
'''
dockerng_inspect_image = Mock(
side_effect=CommandExecutionError(
'Error 404: No such image/container: image:latest'))
dockerng_pull = Mock(
return_value={'Layers':
{'Already_Pulled': ['abcdefghijk'],
'Pulled': ['abcdefghijk']},
'Status': "Image 'image:latest' was pulled",
'Time_Elapsed': 1.1})
dockerng_list_tags = Mock(
side_effect=[[], ['image:latest']]
)
__salt__ = {'dockerng.list_tags': dockerng_list_tags,
'dockerng.pull': dockerng_pull,
'dockerng.inspect_image': dockerng_inspect_image,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.image_present('image:latest', force=True)
self.assertEqual(ret,
{'changes': {
'Layers': {'Already_Pulled': ['abcdefghijk'],
'Pulled': ['abcdefghijk']},
'Status': "Image 'image:latest' was pulled",
'Time_Elapsed': 1.1},
'result': True,
'comment': "Image 'image:latest' was pulled",
'name': 'image:latest',
})
def test_check_start_false(self):
2015-05-22 14:24:32 +00:00
'''
If start is False, then dockerng.running will not try
2015-05-22 14:24:32 +00:00
to start a container that is stopped.
'''
image_id = 'abcdefg'
dockerng_create = Mock()
dockerng_start = Mock()
dockerng_list_containers = Mock(return_value=['cont'])
dockerng_inspect_container = Mock(
return_value={'Config': {'Image': 'image:latest'},
'Image': image_id})
__salt__ = {'dockerng.list_containers': dockerng_list_containers,
'dockerng.inspect_container': dockerng_inspect_container,
'dockerng.inspect_image': MagicMock(
return_value={'Id': image_id}),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(side_effect=['stopped',
'running']),
'dockerng.create': dockerng_create,
'dockerng.start': dockerng_start,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.running(
'cont',
image='image:latest',
start=False,
2015-05-22 14:24:32 +00:00
)
self.assertEqual(ret, {'name': 'cont',
'comment': "Container 'cont' is already "
"configured as specified",
'changes': {},
'result': True,
})
def test_check_start_true(self):
2015-05-22 14:24:32 +00:00
'''
If start is True, then dockerng.running will try
2015-05-22 14:24:32 +00:00
to start a container that is stopped.
'''
image_id = 'abcdefg'
dockerng_create = Mock()
dockerng_start = Mock()
dockerng_list_containers = Mock(return_value=['cont'])
dockerng_inspect_container = Mock(
return_value={'Config': {'Image': 'image:latest'},
'Image': image_id})
__salt__ = {'dockerng.list_containers': dockerng_list_containers,
'dockerng.inspect_container': dockerng_inspect_container,
'dockerng.inspect_image': MagicMock(
return_value={'Id': image_id}),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(side_effect=['stopped',
'running']),
'dockerng.create': dockerng_create,
'dockerng.start': dockerng_start,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.running(
'cont',
image='image:latest',
start=True,
2015-05-22 14:24:32 +00:00
)
self.assertEqual(ret, {'name': 'cont',
'comment': "Container 'cont' changed state.",
'changes': {'state': {'new': 'running',
'old': 'stopped'}},
'result': True,
})
def test_running_discard_wrong_environemnt_values(self):
'''
environment values should be string.
It is easy to write wrong sls this way
.. code-block:: yaml
container:
dockerng.running:
- environment:
- KEY: 1
instead of:
.. code-block:: yaml
container:
dockerng.running:
- environment:
- KEY: "1"
'''
__salt__ = {'dockerng.list_containers': MagicMock(),
'dockerng.list_tags': MagicMock(),
'dockerng.inspect_image': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(),
'dockerng.create': MagicMock(),
'dockerng.start': MagicMock(),
'dockerng.history': MagicMock(),
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
for wrong_value in (1, .2, (), [], {}):
ret = dockerng_state.running(
'cont',
image='image:latest',
environment=[{'KEY': wrong_value}])
self.assertEqual(ret,
{'changes': {},
'comment': 'Environment values must'
' be strings KEY={0!r}'.format(wrong_value),
'name': 'cont',
'result': False})
def test_running_with_labels(self):
'''
Test dockerng.running with labels parameter.
'''
dockerng_create = Mock()
__salt__ = {'dockerng.list_containers': MagicMock(),
'dockerng.list_tags': MagicMock(),
'dockerng.pull': MagicMock(),
'dockerng.state': MagicMock(),
'dockerng.inspect_image': MagicMock(),
'dockerng.create': dockerng_create,
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
dockerng_state.running(
'cont',
image='image:latest',
labels=['LABEL1', 'LABEL2'],
)
dockerng_create.assert_called_with(
'image:latest',
validate_input=False,
validate_ip_addrs=False,
name='cont',
labels=['LABEL1', 'LABEL2'],
client_timeout=60)
2015-11-24 12:54:59 +00:00
def test_network_present(self):
'''
Test dockerng.network_present
'''
dockerng_create_network = Mock(return_value='created')
dockerng_connect_container_to_network = Mock(return_value='connected')
__salt__ = {'dockerng.create_network': dockerng_create_network,
'dockerng.connect_container_to_network': dockerng_connect_container_to_network,
'dockerng.networks': Mock(return_value=[]),
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.network_present(
'network_foo',
containers=['container'],
)
dockerng_create_network.assert_called_with('network_foo', driver=None)
dockerng_connect_container_to_network.assert_called_with('container',
'network_foo')
self.assertEqual(ret, {'name': 'network_foo',
'comment': '',
'changes': {'connected': 'connected',
'created': 'created'},
'result': True})
def test_network_absent(self):
'''
Test dockerng.network_absent
'''
dockerng_remove_network = Mock(return_value='removed')
dockerng_disconnect_container_from_network = Mock(return_value='disconnected')
__salt__ = {'dockerng.remove_network': dockerng_remove_network,
'dockerng.disconnect_container_from_network': dockerng_disconnect_container_from_network,
'dockerng.networks': Mock(return_value=[{'Containers': {'container': {}}}]),
}
with patch.dict(dockerng_state.__dict__,
{'__salt__': __salt__}):
ret = dockerng_state.network_absent(
'network_foo',
)
dockerng_disconnect_container_from_network.assert_called_with('container',
'network_foo')
dockerng_remove_network.assert_called_with('network_foo')
self.assertEqual(ret, {'name': 'network_foo',
'comment': '',
'changes': {'disconnected': 'disconnected',
'removed': 'removed'},
'result': True})
if __name__ == '__main__':
from integration import run_tests
run_tests(DockerngTestCase, needs_daemon=False)