Merge "Implement container unique name space for etcd driver"
This commit is contained in:
commit
c7e96cab02
@ -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()
|
||||||
|
@ -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())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user