Merge "Implement container unique name space for etcd driver"

This commit is contained in:
Jenkins 2016-12-20 20:37:06 +00:00 committed by Gerrit Code Review
commit c7e96cab02
2 changed files with 94 additions and 4 deletions

View File

@ -31,11 +31,12 @@ from zun.db.etcd import models
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
CONF = zun.conf.CONF
def get_connection(): def get_connection():
connection = EtcdAPI(host=zun.conf.CONF.etcd.etcd_host, connection = EtcdAPI(host=CONF.etcd.etcd_host,
port=zun.conf.CONF.etcd.etcd_port) port=CONF.etcd.etcd_port)
return connection return connection
@ -159,13 +160,39 @@ class EtcdAPI(object):
return self._process_list_result(filtered_containers, return self._process_list_result(filtered_containers,
limit=limit, sort_key=sort_key) limit=limit, sort_key=sort_key)
def _validate_unique_container_name(self, context, name):
if not CONF.compute.unique_container_name_scope:
return
lowername = name.lower()
filters = {'name': name}
if CONF.compute.unique_container_name_scope == 'project':
filters['project_id'] = context.project_id
elif CONF.compute.unique_container_name_scope == 'global':
pass
else:
return
try:
containers = self.list_container(context, filters=filters)
except etcd.EtcdKeyNotFound:
return
except Exception as e:
LOG.error(_LE('Error occurred while retrieving container: %s'),
six.text_type(e))
raise
if len(containers) > 0:
raise exception.ContainerAlreadyExists(field='name',
value=lowername)
def create_container(self, context, container_data): def create_container(self, context, container_data):
# ensure defaults are present for new containers # ensure defaults are present for new containers
# TODO(pksingh): need to add validation for same container
# name validation in project and global scope
if not container_data.get('uuid'): if not container_data.get('uuid'):
container_data['uuid'] = uuidutils.generate_uuid() container_data['uuid'] = uuidutils.generate_uuid()
if container_data.get('name'):
self._validate_unique_container_name(context,
container_data['name'])
container = models.Container(container_data) container = models.Container(container_data)
try: try:
container.save() container.save()

View File

@ -23,6 +23,7 @@ import six
from zun.common import exception from zun.common import exception
import zun.conf import zun.conf
from zun.db import api as dbapi from zun.db import api as dbapi
from zun.db.etcd.api import EtcdAPI as etcd_api
from zun.tests.unit.db import base from zun.tests.unit.db import base
from zun.tests.unit.db import utils from zun.tests.unit.db import utils
@ -254,6 +255,9 @@ class EtcdDbContainerTestCase(base.DbTestCase):
@mock.patch.object(etcd_client, 'read') @mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write') @mock.patch.object(etcd_client, 'write')
def test_create_container_already_exists(self, mock_write, mock_read): def test_create_container_already_exists(self, mock_write, mock_read):
CONF.set_override("unique_container_name_scope", "",
group="compute",
enforce_type=True)
mock_read.side_effect = etcd.EtcdKeyNotFound mock_read.side_effect = etcd.EtcdKeyNotFound
utils.create_test_container(context=self.context) utils.create_test_container(context=self.context)
mock_read.side_effect = lambda *args: None mock_read.side_effect = lambda *args: None
@ -446,3 +450,62 @@ class EtcdDbContainerTestCase(base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue, self.assertRaises(exception.InvalidParameterValue,
dbapi.Connection.update_container, self.context, dbapi.Connection.update_container, self.context,
container.id, {'uuid': ''}) container.id, {'uuid': ''})
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
@mock.patch.object(etcd_api, 'list_container')
def test_create_container_already_exists_in_project_name_space(
self, mock_list_container, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
mock_list_container.return_value = []
CONF.set_override("unique_container_name_scope", "project",
group="compute",
enforce_type=True)
container1 = utils.create_test_container(
context=self.context, name='cont1')
mock_list_container.return_value = [container1]
with self.assertRaisesRegexp(exception.ContainerAlreadyExists,
'A container with name.*'):
utils.create_test_container(uuid=uuidutils.generate_uuid(),
context=self.context,
name='cont1')
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
@mock.patch.object(etcd_api, 'list_container')
def test_create_container_already_exists_in_global_name_space(
self, mock_list_container, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
mock_list_container.return_value = []
CONF.set_override("unique_container_name_scope", "global",
group="compute",
enforce_type=True)
container1 = utils.create_test_container(
context=self.context, name='cont1')
self.context.project_id = 'fake_project_1'
self.context.user_id = 'fake_user_1'
mock_list_container.return_value = [container1]
with self.assertRaisesRegexp(exception.ContainerAlreadyExists,
'A container with name.*'):
utils.create_test_container(uuid=uuidutils.generate_uuid(),
context=self.context,
name='cont1')
@mock.patch.object(etcd_client, 'read')
@mock.patch.object(etcd_client, 'write')
@mock.patch.object(etcd_api, 'list_container')
def test_create_container_already_exists_in_default_name_space(
self, mock_list_container, mock_write, mock_read):
mock_read.side_effect = etcd.EtcdKeyNotFound
mock_list_container.return_value = []
CONF.set_override("unique_container_name_scope", "",
group="compute",
enforce_type=True)
container1 = utils.create_test_container(
context=self.context, name='cont1',
uuid=uuidutils.generate_uuid())
mock_list_container.return_value = [container1]
self.context.project_id = 'fake_project_1'
self.context.user_id = 'fake_user_1'
utils.create_test_container(
context=self.context, name='cont1', uuid=uuidutils.generate_uuid())