Add more functional tests

* Add positive tests for each container operation with basic
  scenario.
* Fix some container operations that don't return latest container
  information.

Partial-Implements: blueprint zun-functional-test
Change-Id: Idd79d2b1e5e0e06ae4a477bd0a5e4ed2d7cb5687
This commit is contained in:
Hongbin Lu 2016-11-23 18:21:47 -06:00
parent 4fcfa78852
commit 050dfaed64
6 changed files with 283 additions and 68 deletions

View File

@ -101,6 +101,17 @@ function install_docker {
sudo systemctl enable docker.service
sudo systemctl start docker || true
fi
# We put the stack user as owner of the socket so we do not need to
# run the Docker commands with sudo when developing.
local docker_socket_file=/var/run/docker.sock
echo -n "Waiting for Docker to create its socket file"
while [ ! -e "$docker_socket_file" ]; do
echo -n "."
sleep 1
done
echo ""
sudo chown "$STACK_USER":docker "$docker_socket_file"
}
# Test if any zun services are enabled

View File

@ -407,7 +407,7 @@ class ContainersController(rest.RestController):
LOG.debug('Calling compute.container_start with %s',
container.uuid)
context = pecan.request.context
pecan.request.rpcapi.container_start(context, container)
container = pecan.request.rpcapi.container_start(context, container)
return Container.convert_with_links(container)
@pecan.expose('json')
@ -418,7 +418,8 @@ class ContainersController(rest.RestController):
LOG.debug('Calling compute.container_stop with %s' %
container.uuid)
context = pecan.request.context
pecan.request.rpcapi.container_stop(context, container, timeout)
container = pecan.request.rpcapi.container_stop(context, container,
timeout)
return Container.convert_with_links(container)
@pecan.expose('json')
@ -429,7 +430,8 @@ class ContainersController(rest.RestController):
LOG.debug('Calling compute.container_reboot with %s' %
container.uuid)
context = pecan.request.context
pecan.request.rpcapi.container_reboot(context, container, timeout)
container = pecan.request.rpcapi.container_reboot(context, container,
timeout)
return Container.convert_with_links(container)
@pecan.expose('json')
@ -440,7 +442,7 @@ class ContainersController(rest.RestController):
LOG.debug('Calling compute.container_pause with %s' %
container.uuid)
context = pecan.request.context
pecan.request.rpcapi.container_pause(context, container)
container = pecan.request.rpcapi.container_pause(context, container)
return Container.convert_with_links(container)
@pecan.expose('json')
@ -451,7 +453,7 @@ class ContainersController(rest.RestController):
LOG.debug('Calling compute.container_unpause with %s' %
container.uuid)
context = pecan.request.context
pecan.request.rpcapi.container_unpause(context, container)
container = pecan.request.rpcapi.container_unpause(context, container)
return Container.convert_with_links(container)
@pecan.expose('json')
@ -483,6 +485,6 @@ class ContainersController(rest.RestController):
LOG.debug('Calling compute.container_kill with %s signal %s'
% (container.uuid, kw.get('signal', kw.get('signal', None))))
context = pecan.request.context
pecan.request.rpcapi.container_kill(context,
container, kw.get('signal', None))
container = pecan.request.rpcapi.container_kill(context, container,
kw.get('signal', None))
return Container.convert_with_links(container)

View File

@ -57,7 +57,7 @@ sudo chown -R jenkins:stack $BASE/new/tempest
# show tempest config
cat etc/tempest.conf
sudo -H -u jenkins tox -eall-plugin -- zun.tests.tempest.api --concurrency=1
sudo -E tox -eall-plugin -- zun.tests.tempest.api --concurrency=1
EXIT_CODE=$?

View File

@ -16,6 +16,7 @@ from tempest.lib.common import rest_client
from tempest.lib.services.compute import keypairs_client
from tempest import manager
from zun.container.docker import utils as docker_utils
from zun.tests.tempest.api.models import container_model
from zun.tests.tempest.api.models import service_model
from zun.tests.tempest import utils
@ -51,34 +52,46 @@ class ZunClient(rest_client.RestClient):
return resp, model_type.from_json(body)
@classmethod
def add_filters(cls, url, filters):
"""add_filters adds dict values (filters) to url as query parameters
:param url: base URL for the request
:param filters: dict with var:val pairs to add as parameters to URL
:returns: url string
"""
return url + "?" + parse(filters)
@classmethod
def containers_uri(cls, filters=None):
def containers_uri(cls, action=None):
url = "/containers/"
if filters:
url = cls.add_filters(url, filters)
if action:
url = "{0}/{1}".format(url, action)
return url
@classmethod
def container_uri(cls, container_id):
def container_uri(cls, container_id, action=None, params=None):
"""Construct container uri
"""
return "{0}/{1}".format(cls.containers_uri(), container_id)
url = None
if action is None:
url = "{0}/{1}".format(cls.containers_uri(), container_id)
else:
url = "{0}/{1}/{2}".format(cls.containers_uri(), container_id,
action)
if params:
url = cls.add_params(url, params)
return url
@classmethod
def services_uri(cls, filters=None):
def add_params(cls, url, params):
"""add_params adds dict values (params) to url as query parameters
:param url: base URL for the request
:param params: dict with var:val pairs to add as parameters to URL
:returns: url string
"""
url_parts = list(parse.urlparse(url))
query = dict(parse.parse_qsl(url_parts[4]))
query.update(params)
url_parts[4] = parse.urlencode(query)
return parse.urlunparse(url_parts)
@classmethod
def services_uri(cls):
url = "/services/"
if filters:
url = cls.add_filters(url, filters)
return url
def post_container(self, model, **kwargs):
@ -90,20 +103,66 @@ class ZunClient(rest_client.RestClient):
body=model.to_json(), **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def run_container(self, model, **kwargs):
resp, body = self.post(
self.containers_uri(action='run'),
body=model.to_json(), **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def get_container(self, container_id):
resp, body = self.get(self.container_uri(container_id))
return self.deserialize(resp, body, container_model.ContainerEntity)
def list_containers(self, filters=None, **kwargs):
resp, body = self.get(self.containers_uri(filters), **kwargs)
def list_containers(self, **kwargs):
resp, body = self.get(self.containers_uri(), **kwargs)
return self.deserialize(resp, body,
container_model.ContainerCollection)
def delete_container(self, container_id, **kwargs):
self.delete(self.container_uri(container_id), **kwargs)
def delete_container(self, container_id, params=None, **kwargs):
return self.delete(
self.container_uri(container_id, params=params), **kwargs)
def list_services(self, filters=None, **kwargs):
resp, body = self.get(self.services_uri(filters), **kwargs)
def start_container(self, container_id, **kwargs):
resp, body = self.post(
self.container_uri(container_id, action='start'), None, **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def stop_container(self, container_id, **kwargs):
resp, body = self.post(
self.container_uri(container_id, action='stop'), None, *kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def pause_container(self, container_id, **kwargs):
resp, body = self.post(
self.container_uri(container_id, action='pause'), None, **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def unpause_container(self, container_id, **kwargs):
resp, body = self.post(
self.container_uri(container_id, action='unpause'), None, **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def kill_container(self, container_id, **kwargs):
resp, body = self.post(
self.container_uri(container_id, action='kill'), None, **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def reboot_container(self, container_id, **kwargs):
resp, body = self.post(
self.container_uri(container_id, action='reboot'), None, **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def exec_container(self, container_id, command, **kwargs):
return self.post(
self.container_uri(container_id, action='execute'),
'{"command": "%s"}' % command, **kwargs)
def logs_container(self, container_id, **kwargs):
return self.get(
self.container_uri(container_id, action='logs'), None, **kwargs)
def list_services(self, **kwargs):
resp, body = self.get(self.services_uri(), **kwargs)
return self.deserialize(resp, body,
service_model.ServiceCollection)
@ -116,3 +175,13 @@ class ZunClient(rest_client.RestClient):
return True
utils.wait_for_condition(container_created)
class DockerClient(object):
def get_container(self, container_id):
with docker_utils.docker_client() as docker:
for info in docker.list_instances(inspect=True):
if container_id in info['Name']:
return info
return None

View File

@ -22,7 +22,6 @@ class TestContainer(base.BaseZunTest):
@classmethod
def get_client_manager(cls, credential_type=None, roles=None,
force_new=None):
manager = super(TestContainer, cls).get_client_manager(
credential_type=credential_type,
roles=roles,
@ -32,45 +31,155 @@ class TestContainer(base.BaseZunTest):
@classmethod
def setup_clients(cls):
super(TestContainer, cls).setup_clients()
cls.container_client = cls.os.container_client
cls.docker_client = clients.DockerClient()
@classmethod
def resource_setup(cls):
super(TestContainer, cls).resource_setup()
def tearDown(self):
_, model = self.container_client.list_containers()
for c in model.containers:
self.container_client.ensure_container_created(c['uuid'])
self.container_client.delete_container(c['uuid'])
self.container_client.delete_container(c['uuid'],
params={'force': True})
super(TestContainer, self).tearDown()
def _create_container(self, **kwargs):
model = datagen.container_data(**kwargs)
return self.container_client.post_container(model)
def _delete_container(self, container_id, **kwargs):
self.container_client.delete_container(container_id, **kwargs)
@decorators.idempotent_id('a04f61f2-15ae-4200-83b7-1f311b101f35')
def test_container_create_list_delete(self):
resp, container = self._create_container()
self.assertEqual(202, resp.status)
@decorators.idempotent_id('b8946b8c-57d5-4fdc-a09a-001d6b552725')
def test_create_container(self):
self._create_container()
@decorators.idempotent_id('b3e307d4-844b-4a57-8c60-8fb3f57aea7c')
def test_list_containers(self):
_, container = self._create_container()
resp, model = self.container_client.list_containers()
self.assertEqual(200, resp.status)
self.assertGreater(len(model.containers), 0)
# NOTE(mkrai): Check and wait for container creation to get over
self.container_client.ensure_container_created(container.uuid)
self._delete_container(container.uuid, headers={'force': True})
self.assertIn(
container.uuid,
list([c['uuid'] for c in model.containers]))
resp, model = self.container_client.list_containers()
@decorators.idempotent_id('0dd13c28-c5ff-4b9e-b73b-61185b410de4')
def test_get_container(self):
_, container = self._create_container()
resp, model = self.container_client.get_container(container.uuid)
self.assertEqual(200, resp.status)
self.assertEqual(len(model.containers), 0)
self.assertEqual(container.uuid, model.uuid)
@decorators.idempotent_id('cef53a56-22b7-4808-b01c-06b2b7126115')
def test_delete_container(self):
_, container = self._create_container()
self._delete_container(container.uuid)
@decorators.idempotent_id('ef69c9e7-0ce0-4e14-b7ec-c1dc581a3927')
def test_run_container(self):
self._run_container()
@decorators.idempotent_id('3fa024ef-aba1-48fe-9682-0d6b7854faa3')
def test_start_stop_container(self):
_, model = self._run_container()
resp, model = self.container_client.stop_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Stopped', model.status)
self.assertEqual('Stopped', self._get_container_state(model.uuid))
resp, model = self.container_client.start_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Running', model.status)
self.assertEqual('Running', self._get_container_state(model.uuid))
@decorators.idempotent_id('b5f39756-8898-4e0e-a48b-dda0a06b66b6')
def test_pause_unpause_container(self):
_, model = self._run_container()
resp, model = self.container_client.pause_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Paused', model.status)
self.assertEqual('Paused', self._get_container_state(model.uuid))
resp, model = self.container_client.unpause_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Running', model.status)
self.assertEqual('Running', self._get_container_state(model.uuid))
@decorators.idempotent_id('6179a588-3d48-4372-9599-f228411d1449')
def test_kill_container(self):
_, model = self._run_container()
resp, model = self.container_client.kill_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Stopped', model.status)
self.assertEqual('Stopped', self._get_container_state(model.uuid))
@decorators.idempotent_id('c2e54321-0a70-4331-ba62-9dcaa75ac250')
def test_reboot_container(self):
_, model = self._run_container()
container = self.docker_client.get_container(model.uuid)
pid = container.get('State').get('Pid')
resp, model = self.container_client.reboot_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Running', model.status)
self.assertEqual('Running', self._get_container_state(model.uuid))
# assert pid is changed
container = self.docker_client.get_container(model.uuid)
self.assertNotEqual(pid, container.get('State').get('Pid'))
@decorators.idempotent_id('8a591ff8-6793-427f-82a6-e3921d8b4f81')
def test_exec_container(self):
_, model = self._run_container()
resp, body = self.container_client.exec_container(model.uuid,
command='echo hello')
self.assertEqual(200, resp.status)
self.assertTrue('hello' in body)
@decorators.idempotent_id('a912ca23-14e7-442f-ab15-e05aaa315204')
def test_logs_container(self):
_, model = self._run_container(command='echo hello')
resp, body = self.container_client.logs_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertTrue('hello' in body)
def _create_container(self, **kwargs):
gen_model = datagen.container_data(**kwargs)
resp, model = self.container_client.post_container(gen_model)
self.assertEqual(202, resp.status)
# Wait for container to finish creation
self.container_client.ensure_container_created(model.uuid)
# Assert the container is created
resp, model = self.container_client.get_container(model.uuid)
self.assertEqual(200, resp.status)
self.assertEqual('Stopped', model.status)
self.assertEqual('Stopped', self._get_container_state(model.uuid))
return resp, model
def _run_container(self, **kwargs):
gen_model = datagen.container_data(**kwargs)
resp, model = self.container_client.run_container(gen_model)
self.assertEqual(200, resp.status)
self.assertTrue(model.status in ['Running', 'Stopped'])
self.assertTrue(self._get_container_state(model.uuid) in
['Running', 'Stopped'])
return resp, model
def _delete_container(self, container_id):
resp, _ = self.container_client.delete_container(container_id)
self.assertEqual(204, resp.status)
container = self.docker_client.get_container(container_id)
self.assertIsNone(container)
def _get_container_state(self, container_id):
container = self.docker_client.get_container(container_id)
status = container.get('State')
if status.get('Error') is True:
return 'Error'
elif status.get('Paused'):
return 'Paused'
elif status.get('Running'):
return 'Running'
else:
return 'Stopped'

View File

@ -401,21 +401,27 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_start')
def test_start_by_uuid(self, mock_container_start):
mock_container_start.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_start.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'start', 'uuid',
mock_container_start)
@patch('zun.compute.api.API.container_start')
def test_start_by_name(self, mock_container_start):
mock_container_start.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_start.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'start', 'name',
mock_container_start)
@patch('zun.compute.api.API.container_stop')
def test_stop_by_uuid(self, mock_container_stop):
mock_container_stop.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_stop.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'stop', 'uuid',
mock_container_stop,
@ -423,7 +429,9 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_stop')
def test_stop_by_name(self, mock_container_stop):
mock_container_stop.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_stop.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'stop', 'name',
mock_container_stop,
@ -431,35 +439,45 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_pause')
def test_pause_by_uuid(self, mock_container_pause):
mock_container_pause.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_pause.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'pause', 'uuid',
mock_container_pause)
@patch('zun.compute.api.API.container_pause')
def test_pause_by_name(self, mock_container_pause):
mock_container_pause.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_pause.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'pause', 'name',
mock_container_pause)
@patch('zun.compute.api.API.container_unpause')
def test_unpause_by_uuid(self, mock_container_unpause):
mock_container_unpause.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_unpause.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'unpause', 'uuid',
mock_container_unpause)
@patch('zun.compute.api.API.container_unpause')
def test_unpause_by_name(self, mock_container_unpause):
mock_container_unpause.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_unpause.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'unpause', 'name',
mock_container_unpause)
@patch('zun.compute.api.API.container_reboot')
def test_reboot_by_uuid(self, mock_container_reboot):
mock_container_reboot.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_reboot.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'reboot', 'uuid',
mock_container_reboot,
@ -467,7 +485,9 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_reboot')
def test_reboot_by_name(self, mock_container_reboot):
mock_container_reboot.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_reboot.return_value = test_container_obj
test_container = utils.get_test_container()
self._action_test(test_container, 'reboot', 'name',
mock_container_reboot,
@ -587,7 +607,9 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.objects.Container.get_by_uuid')
def test_kill_container_by_uuid(self,
mock_get_by_uuid, mock_container_kill):
mock_container_kill.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_kill.return_value = test_container_obj
test_container = utils.get_test_container()
test_container_obj = objects.Container(self.context, **test_container)
mock_get_by_uuid.return_value = test_container_obj
@ -604,7 +626,9 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.objects.Container.get_by_name')
def test_kill_container_by_name(self,
mock_get_by_name, mock_container_kill):
mock_container_kill.return_value = ""
test_container_obj = objects.Container(self.context,
**utils.get_test_container())
mock_container_kill.return_value = test_container_obj
test_container = utils.get_test_container()
test_container_obj = objects.Container(self.context, **test_container)
mock_get_by_name.return_value = test_container_obj