Fix overwriting of existing tags while creating new tags
It was observed that md-tag-create-multiple (/v2/metadefs/namespaces/{namespace_name}/tags) API overwrites existing tags for specified namespace rather than creating new one in addition to the existing tags. This patch resolves the issue by introducing a header 'X-Openstack-Append' which on being True will append the new tags to existing ones and if False will continue to overwrite the tags. Implements: blueprint append-tags Closes-Bug: #1939169 Change-Id: I29448746b14c542e5fbf0283011968ae1516642e
This commit is contained in:
parent
a42fda92dc
commit
2a9a4c8e0e
@ -191,6 +191,7 @@ Request
|
|||||||
|
|
||||||
.. rest_parameters:: metadefs-parameters.yaml
|
.. rest_parameters:: metadefs-parameters.yaml
|
||||||
|
|
||||||
|
- X-Openstack-Append: append
|
||||||
- namespace_name: namespace_name
|
- namespace_name: namespace_name
|
||||||
- tags: tags
|
- tags: tags
|
||||||
|
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
# variables in header
|
# variables in header
|
||||||
|
append:
|
||||||
|
description: |
|
||||||
|
If present and set to True, new metadefs tags are appended to the existing ones.
|
||||||
|
Otherwise, existing tags are overwritten.
|
||||||
|
in: header
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
Content-Type-json:
|
Content-Type-json:
|
||||||
description: |
|
description: |
|
||||||
The media type descriptor for the request body. Use
|
The media type descriptor for the request body. Use
|
||||||
|
@ -18,6 +18,7 @@ import http.client as http
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
|
from oslo_utils import strutils
|
||||||
import webob.exc
|
import webob.exc
|
||||||
from wsme.rest import json
|
from wsme.rest import json
|
||||||
|
|
||||||
@ -120,11 +121,14 @@ class TagsController(object):
|
|||||||
md_resource=namespace_obj,
|
md_resource=namespace_obj,
|
||||||
enforcer=self.policy).add_metadef_tags()
|
enforcer=self.policy).add_metadef_tags()
|
||||||
|
|
||||||
|
can_append = strutils.bool_from_string(req.headers.get(
|
||||||
|
'X-Openstack-Append'))
|
||||||
|
|
||||||
tag_list = []
|
tag_list = []
|
||||||
for metadata_tag in metadata_tags.tags:
|
for metadata_tag in metadata_tags.tags:
|
||||||
tag_list.append(tag_factory.new_tag(
|
tag_list.append(tag_factory.new_tag(
|
||||||
namespace=namespace, **metadata_tag.to_dict()))
|
namespace=namespace, **metadata_tag.to_dict()))
|
||||||
tag_repo.add_tags(tag_list)
|
tag_repo.add_tags(tag_list, can_append)
|
||||||
tag_list_out = [MetadefTag(**{'name': db_metatag.name})
|
tag_list_out = [MetadefTag(**{'name': db_metatag.name})
|
||||||
for db_metatag in tag_list]
|
for db_metatag in tag_list]
|
||||||
metadef_tags = MetadefTags()
|
metadef_tags = MetadefTags()
|
||||||
|
@ -846,7 +846,7 @@ class MetadefTagRepo(object):
|
|||||||
self._format_metadef_tag_to_db(metadata_tag)
|
self._format_metadef_tag_to_db(metadata_tag)
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_tags(self, metadata_tags):
|
def add_tags(self, metadata_tags, can_append=False):
|
||||||
tag_list = []
|
tag_list = []
|
||||||
namespace = None
|
namespace = None
|
||||||
for metadata_tag in metadata_tags:
|
for metadata_tag in metadata_tags:
|
||||||
@ -855,7 +855,7 @@ class MetadefTagRepo(object):
|
|||||||
namespace = metadata_tag.namespace
|
namespace = metadata_tag.namespace
|
||||||
|
|
||||||
self.db_api.metadef_tag_create_tags(
|
self.db_api.metadef_tag_create_tags(
|
||||||
self.context, namespace, tag_list)
|
self.context, namespace, tag_list, can_append)
|
||||||
|
|
||||||
def get(self, namespace, name):
|
def get(self, namespace, name):
|
||||||
try:
|
try:
|
||||||
|
@ -1910,7 +1910,8 @@ def metadef_tag_create(context, namespace_name, values):
|
|||||||
|
|
||||||
|
|
||||||
@log_call
|
@log_call
|
||||||
def metadef_tag_create_tags(context, namespace_name, tag_list):
|
def metadef_tag_create_tags(context, namespace_name, tag_list,
|
||||||
|
can_append=False):
|
||||||
"""Create a metadef tag"""
|
"""Create a metadef tag"""
|
||||||
global DATA
|
global DATA
|
||||||
|
|
||||||
@ -1921,6 +1922,12 @@ def metadef_tag_create_tags(context, namespace_name, tag_list):
|
|||||||
allowed_attributes = ['name']
|
allowed_attributes = ['name']
|
||||||
data_tag_list = []
|
data_tag_list = []
|
||||||
tag_name_list = []
|
tag_name_list = []
|
||||||
|
if can_append:
|
||||||
|
# NOTE(mrjoshi): We need to fetch existing tags here for duplicate
|
||||||
|
# check while adding new one
|
||||||
|
tag_name_list = [tag['name']
|
||||||
|
for tag in metadef_tag_get_all(context,
|
||||||
|
namespace_name)]
|
||||||
for tag_value in tag_list:
|
for tag_value in tag_list:
|
||||||
tag_values = copy.deepcopy(tag_value)
|
tag_values = copy.deepcopy(tag_value)
|
||||||
tag_name = tag_values['name']
|
tag_name = tag_values['name']
|
||||||
@ -1945,8 +1952,8 @@ def metadef_tag_create_tags(context, namespace_name, tag_list):
|
|||||||
|
|
||||||
tag_values['namespace_id'] = namespace['id']
|
tag_values['namespace_id'] = namespace['id']
|
||||||
data_tag_list.append(_format_tag(tag_values))
|
data_tag_list.append(_format_tag(tag_values))
|
||||||
|
if not can_append:
|
||||||
DATA['metadef_tags'] = []
|
DATA['metadef_tags'] = []
|
||||||
for tag in data_tag_list:
|
for tag in data_tag_list:
|
||||||
DATA['metadef_tags'].append(tag)
|
DATA['metadef_tags'].append(tag)
|
||||||
|
|
||||||
|
@ -2180,11 +2180,11 @@ def metadef_tag_create(context, namespace_name, tag_dict,
|
|||||||
|
|
||||||
|
|
||||||
def metadef_tag_create_tags(context, namespace_name, tag_list,
|
def metadef_tag_create_tags(context, namespace_name, tag_list,
|
||||||
session=None):
|
can_append=False, session=None):
|
||||||
"""Create a metadata-schema tag or raise if it already exists."""
|
"""Create a metadata-schema tag or raise if it already exists."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
return metadef_tag_api.create_tags(
|
return metadef_tag_api.create_tags(
|
||||||
context, namespace_name, tag_list, session)
|
context, namespace_name, tag_list, can_append, session)
|
||||||
|
|
||||||
|
|
||||||
@utils.no_4byte_params
|
@utils.no_4byte_params
|
||||||
|
@ -111,7 +111,7 @@ def create(context, namespace_name, values, session):
|
|||||||
return metadef_tag.to_dict()
|
return metadef_tag.to_dict()
|
||||||
|
|
||||||
|
|
||||||
def create_tags(context, namespace_name, tag_list, session):
|
def create_tags(context, namespace_name, tag_list, can_append, session):
|
||||||
|
|
||||||
metadef_tags_list = []
|
metadef_tags_list = []
|
||||||
if tag_list:
|
if tag_list:
|
||||||
@ -119,10 +119,10 @@ def create_tags(context, namespace_name, tag_list, session):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with session.begin():
|
with session.begin():
|
||||||
query = (session.query(models.MetadefTag).filter_by(
|
if not can_append:
|
||||||
namespace_id=namespace['id']))
|
query = (session.query(models.MetadefTag).filter_by(
|
||||||
query.delete(synchronize_session='fetch')
|
namespace_id=namespace['id']))
|
||||||
|
query.delete(synchronize_session='fetch')
|
||||||
for value in tag_list:
|
for value in tag_list:
|
||||||
value.update({'namespace_id': namespace['id']})
|
value.update({'namespace_id': namespace['id']})
|
||||||
metadef_utils.drop_protected_attrs(
|
metadef_utils.drop_protected_attrs(
|
||||||
|
@ -547,11 +547,11 @@ class MetadefTagRepo(object):
|
|||||||
def add(self, meta_tag):
|
def add(self, meta_tag):
|
||||||
self.base.add(self.tag_proxy_helper.unproxy(meta_tag))
|
self.base.add(self.tag_proxy_helper.unproxy(meta_tag))
|
||||||
|
|
||||||
def add_tags(self, meta_tags):
|
def add_tags(self, meta_tags, can_append=False):
|
||||||
tags_list = []
|
tags_list = []
|
||||||
for meta_tag in meta_tags:
|
for meta_tag in meta_tags:
|
||||||
tags_list.append(self.tag_proxy_helper.unproxy(meta_tag))
|
tags_list.append(self.tag_proxy_helper.unproxy(meta_tag))
|
||||||
self.base.add_tags(tags_list)
|
self.base.add_tags(tags_list, can_append)
|
||||||
|
|
||||||
def list(self, *args, **kwargs):
|
def list(self, *args, **kwargs):
|
||||||
tags = self.base.list(*args, **kwargs)
|
tags = self.base.list(*args, **kwargs)
|
||||||
|
@ -913,8 +913,9 @@ class MetadefTagRepoProxy(NotificationRepoProxy, domain_proxy.MetadefTagRepo):
|
|||||||
self.send_notification('metadef_tag.create', metadef_tag)
|
self.send_notification('metadef_tag.create', metadef_tag)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def add_tags(self, metadef_tags):
|
def add_tags(self, metadef_tags, can_append=False):
|
||||||
result = super(MetadefTagRepoProxy, self).add_tags(metadef_tags)
|
result = super(MetadefTagRepoProxy, self).add_tags(metadef_tags,
|
||||||
|
can_append)
|
||||||
for metadef_tag in metadef_tags:
|
for metadef_tag in metadef_tags:
|
||||||
self.send_notification('metadef_tag.create', metadef_tag)
|
self.send_notification('metadef_tag.create', metadef_tag)
|
||||||
|
|
||||||
|
@ -590,6 +590,34 @@ class MetadefTagTests(object):
|
|||||||
expected = set(['Tag1', 'Tag2', 'Tag3'])
|
expected = set(['Tag1', 'Tag2', 'Tag3'])
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_tag_create_tags_with_append(self):
|
||||||
|
fixture = build_namespace_fixture()
|
||||||
|
created_ns = self.db_api.metadef_namespace_create(self.context,
|
||||||
|
fixture)
|
||||||
|
self.assertIsNotNone(created_ns)
|
||||||
|
self._assert_saved_fields(fixture, created_ns)
|
||||||
|
|
||||||
|
tags = build_tags_fixture(['Tag1', 'Tag2', 'Tag3'])
|
||||||
|
created_tags = self.db_api.metadef_tag_create_tags(
|
||||||
|
self.context, created_ns['namespace'], tags)
|
||||||
|
actual = set([tag['name'] for tag in created_tags])
|
||||||
|
expected = set(['Tag1', 'Tag2', 'Tag3'])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
new_tags = build_tags_fixture(['Tag4', 'Tag5', 'Tag6'])
|
||||||
|
new_created_tags = self.db_api.metadef_tag_create_tags(
|
||||||
|
self.context, created_ns['namespace'], new_tags, can_append=True)
|
||||||
|
actual = set([tag['name'] for tag in new_created_tags])
|
||||||
|
expected = set(['Tag4', 'Tag5', 'Tag6'])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
tags = self.db_api.metadef_tag_get_all(self.context,
|
||||||
|
created_ns['namespace'],
|
||||||
|
sort_key='created_at')
|
||||||
|
actual = set([tag['name'] for tag in tags])
|
||||||
|
expected = set(['Tag1', 'Tag2', 'Tag3', 'Tag4', 'Tag5', 'Tag6'])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_tag_create_duplicate_tags_1(self):
|
def test_tag_create_duplicate_tags_1(self):
|
||||||
fixture = build_namespace_fixture()
|
fixture = build_namespace_fixture()
|
||||||
created_ns = self.db_api.metadef_namespace_create(self.context,
|
created_ns = self.db_api.metadef_namespace_create(self.context,
|
||||||
@ -619,6 +647,22 @@ class MetadefTagTests(object):
|
|||||||
self.db_api.metadef_tag_create,
|
self.db_api.metadef_tag_create,
|
||||||
self.context, created_ns['namespace'], dup_tag)
|
self.context, created_ns['namespace'], dup_tag)
|
||||||
|
|
||||||
|
def test_tag_create_duplicate_tags_3(self):
|
||||||
|
fixture = build_namespace_fixture()
|
||||||
|
created_ns = self.db_api.metadef_namespace_create(self.context,
|
||||||
|
fixture)
|
||||||
|
self.assertIsNotNone(created_ns)
|
||||||
|
self._assert_saved_fields(fixture, created_ns)
|
||||||
|
|
||||||
|
tags = build_tags_fixture(['Tag1', 'Tag2', 'Tag3'])
|
||||||
|
self.db_api.metadef_tag_create_tags(self.context,
|
||||||
|
created_ns['namespace'], tags)
|
||||||
|
dup_tags = build_tags_fixture(['Tag3', 'Tag4', 'Tag5'])
|
||||||
|
self.assertRaises(exception.Duplicate,
|
||||||
|
self.db_api.metadef_tag_create_tags,
|
||||||
|
self.context, created_ns['namespace'],
|
||||||
|
dup_tags, can_append=True)
|
||||||
|
|
||||||
def test_tag_get(self):
|
def test_tag_get(self):
|
||||||
fixture_ns = build_namespace_fixture()
|
fixture_ns = build_namespace_fixture()
|
||||||
created_ns = self.db_api.metadef_namespace_create(self.context,
|
created_ns = self.db_api.metadef_namespace_create(self.context,
|
||||||
|
@ -160,6 +160,36 @@ class TestMetadefTags(metadef_base.MetadefFunctionalTestBase):
|
|||||||
tags = jsonutils.loads(response.text)['tags']
|
tags = jsonutils.loads(response.text)['tags']
|
||||||
self.assertEqual(3, len(tags))
|
self.assertEqual(3, len(tags))
|
||||||
|
|
||||||
|
# Create new tags and append to existing tags.
|
||||||
|
path = self._url('/v2/metadefs/namespaces/%s/tags' %
|
||||||
|
(namespace_name))
|
||||||
|
headers = self._headers({'content-type': 'application/json',
|
||||||
|
'X-Openstack-Append': 'True'})
|
||||||
|
data = jsonutils.dumps(
|
||||||
|
{"tags": [{"name": "tag4"}, {"name": "tag5"}, {"name": "tag6"}]}
|
||||||
|
)
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# List out all six tags.
|
||||||
|
response = requests.get(path, headers=self._headers())
|
||||||
|
self.assertEqual(http.OK, response.status_code)
|
||||||
|
tags = jsonutils.loads(response.text)['tags']
|
||||||
|
self.assertEqual(6, len(tags))
|
||||||
|
|
||||||
|
# Attempt to create duplicate existing tag6
|
||||||
|
data = jsonutils.dumps(
|
||||||
|
{"tags": [{"name": "tag6"}, {"name": "tag7"}, {"name": "tag8"}]}
|
||||||
|
)
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CONFLICT, response.status_code)
|
||||||
|
|
||||||
|
# Verify the previous 6 still exist
|
||||||
|
response = requests.get(path, headers=self._headers())
|
||||||
|
self.assertEqual(http.OK, response.status_code)
|
||||||
|
tags = jsonutils.loads(response.text)['tags']
|
||||||
|
self.assertEqual(6, len(tags))
|
||||||
|
|
||||||
def _create_tags(self, namespaces):
|
def _create_tags(self, namespaces):
|
||||||
tags = []
|
tags = []
|
||||||
for namespace in namespaces:
|
for namespace in namespaces:
|
||||||
|
@ -515,6 +515,18 @@ class TestMetadefRepo(test_utils.BaseTestCase):
|
|||||||
tag_names = set([t.name for t in tags])
|
tag_names = set([t.name for t in tags])
|
||||||
self.assertEqual(set([TAG3, TAG4, TAG5]), tag_names)
|
self.assertEqual(set([TAG3, TAG4, TAG5]), tag_names)
|
||||||
|
|
||||||
|
def test_add_tags_with_append_true(self):
|
||||||
|
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||||
|
tag_names = set([t.name for t in tags])
|
||||||
|
self.assertEqual(set([TAG1, TAG2, TAG3]), tag_names)
|
||||||
|
|
||||||
|
tags = _db_tags_fixture([TAG4, TAG5])
|
||||||
|
self.db.metadef_tag_create_tags(self.context, NAMESPACE1, tags,
|
||||||
|
can_append=True)
|
||||||
|
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||||
|
tag_names = set([t.name for t in tags])
|
||||||
|
self.assertEqual(set([TAG1, TAG2, TAG3, TAG4, TAG5]), tag_names)
|
||||||
|
|
||||||
def test_add_duplicate_tags_with_pre_existing_tags(self):
|
def test_add_duplicate_tags_with_pre_existing_tags(self):
|
||||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||||
tag_names = set([t.name for t in tags])
|
tag_names = set([t.name for t in tags])
|
||||||
|
@ -239,7 +239,7 @@ class MdTagRepoStub(object):
|
|||||||
def add(self, tag):
|
def add(self, tag):
|
||||||
return 'mdtag_add'
|
return 'mdtag_add'
|
||||||
|
|
||||||
def add_tags(self, tags):
|
def add_tags(self, tags, can_append=False):
|
||||||
return ['mdtag_add_tags']
|
return ['mdtag_add_tags']
|
||||||
|
|
||||||
def get(self, ns, tag_name):
|
def get(self, ns, tag_name):
|
||||||
|
@ -64,7 +64,7 @@ def sort_url_by_qs_keys(url):
|
|||||||
|
|
||||||
|
|
||||||
def get_fake_request(path='', method='POST', is_admin=False, user=USER1,
|
def get_fake_request(path='', method='POST', is_admin=False, user=USER1,
|
||||||
roles=None, tenant=TENANT1):
|
roles=None, headers=None, tenant=TENANT1):
|
||||||
if roles is None:
|
if roles is None:
|
||||||
roles = ['member', 'reader']
|
roles = ['member', 'reader']
|
||||||
|
|
||||||
@ -72,6 +72,9 @@ def get_fake_request(path='', method='POST', is_admin=False, user=USER1,
|
|||||||
req.method = method
|
req.method = method
|
||||||
req.headers = {'x-openstack-request-id': 'my-req'}
|
req.headers = {'x-openstack-request-id': 'my-req'}
|
||||||
|
|
||||||
|
if headers is not None:
|
||||||
|
req.headers.update(headers)
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'user': user,
|
'user': user,
|
||||||
'tenant': tenant,
|
'tenant': tenant,
|
||||||
|
@ -2002,6 +2002,94 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_tag_create_tags_with_append_true(self):
|
||||||
|
request = unit_test_utils.get_fake_request(
|
||||||
|
headers={'X-Openstack-Append': 'True'}, roles=['admin'])
|
||||||
|
|
||||||
|
metadef_tags = tags.MetadefTags()
|
||||||
|
# As TAG1 is already created in setup, just creating other two tags.
|
||||||
|
metadef_tags.tags = _db_tags_fixture([TAG2, TAG3])
|
||||||
|
output = self.tag_controller.create_tags(
|
||||||
|
request, metadef_tags, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(2, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG2, TAG3])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
self.assertNotificationLog(
|
||||||
|
"metadef_tag.create", [
|
||||||
|
{'name': TAG2, 'namespace': NAMESPACE1},
|
||||||
|
{'name': TAG3, 'namespace': NAMESPACE1},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
metadef_tags = tags.MetadefTags()
|
||||||
|
metadef_tags.tags = _db_tags_fixture([TAG4, TAG5])
|
||||||
|
output = self.tag_controller.create_tags(
|
||||||
|
request, metadef_tags, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(2, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG4, TAG5])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
self.assertNotificationLog(
|
||||||
|
"metadef_tag.create", [
|
||||||
|
{'name': TAG4, 'namespace': NAMESPACE1},
|
||||||
|
{'name': TAG5, 'namespace': NAMESPACE1},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
output = self.tag_controller.index(request, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(5, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG1, TAG2, TAG3, TAG4, TAG5])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_tag_create_tags_with_append_false(self):
|
||||||
|
request = unit_test_utils.get_fake_request(
|
||||||
|
headers={'X-Openstack-Append': 'False'}, roles=['admin'])
|
||||||
|
|
||||||
|
metadef_tags = tags.MetadefTags()
|
||||||
|
# As TAG1 is already created in setup, just creating other two tags.
|
||||||
|
metadef_tags.tags = _db_tags_fixture([TAG2, TAG3])
|
||||||
|
output = self.tag_controller.create_tags(
|
||||||
|
request, metadef_tags, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(2, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG2, TAG3])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
self.assertNotificationLog(
|
||||||
|
"metadef_tag.create", [
|
||||||
|
{'name': TAG2, 'namespace': NAMESPACE1},
|
||||||
|
{'name': TAG3, 'namespace': NAMESPACE1},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
metadef_tags = tags.MetadefTags()
|
||||||
|
metadef_tags.tags = _db_tags_fixture([TAG4, TAG5])
|
||||||
|
output = self.tag_controller.create_tags(
|
||||||
|
request, metadef_tags, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(2, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG4, TAG5])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
self.assertNotificationLog(
|
||||||
|
"metadef_tag.create", [
|
||||||
|
{'name': TAG4, 'namespace': NAMESPACE1},
|
||||||
|
{'name': TAG5, 'namespace': NAMESPACE1},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
output = self.tag_controller.index(request, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(2, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG4, TAG5])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_tag_create_duplicate_tags(self):
|
def test_tag_create_duplicate_tags(self):
|
||||||
request = unit_test_utils.get_fake_request(roles=['admin'])
|
request = unit_test_utils.get_fake_request(roles=['admin'])
|
||||||
|
|
||||||
@ -2048,6 +2136,42 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
|||||||
expected = set([TAG1, TAG2, TAG3])
|
expected = set([TAG1, TAG2, TAG3])
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_tag_create_duplicate_with_pre_existing_tags_with_append(self):
|
||||||
|
request = unit_test_utils.get_fake_request(
|
||||||
|
headers={'X-Openstack-Append': 'True'}, roles=['admin'])
|
||||||
|
|
||||||
|
metadef_tags = tags.MetadefTags()
|
||||||
|
# As TAG1 is already created in setup, just creating other two tags.
|
||||||
|
metadef_tags.tags = _db_tags_fixture([TAG2, TAG3])
|
||||||
|
output = self.tag_controller.create_tags(
|
||||||
|
request, metadef_tags, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(2, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG2, TAG3])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
self.assertNotificationLog(
|
||||||
|
"metadef_tag.create", [
|
||||||
|
{'name': TAG2, 'namespace': NAMESPACE1},
|
||||||
|
{'name': TAG3, 'namespace': NAMESPACE1},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
metadef_tags = tags.MetadefTags()
|
||||||
|
metadef_tags.tags = _db_tags_fixture([TAG4, TAG5, TAG4])
|
||||||
|
self.assertRaises(
|
||||||
|
webob.exc.HTTPConflict,
|
||||||
|
self.tag_controller.create_tags,
|
||||||
|
request, metadef_tags, NAMESPACE1)
|
||||||
|
self.assertNotificationsLog([])
|
||||||
|
|
||||||
|
output = self.tag_controller.index(request, NAMESPACE1)
|
||||||
|
output = output.to_dict()
|
||||||
|
self.assertEqual(3, len(output['tags']))
|
||||||
|
actual = set([tag.name for tag in output['tags']])
|
||||||
|
expected = set([TAG1, TAG2, TAG3])
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
def test_tag_create_conflict(self):
|
def test_tag_create_conflict(self):
|
||||||
request = unit_test_utils.get_fake_request(roles=['admin'])
|
request = unit_test_utils.get_fake_request(roles=['admin'])
|
||||||
self.assertRaises(webob.exc.HTTPConflict,
|
self.assertRaises(webob.exc.HTTPConflict,
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new optional header ``X-Openstack-Append`` has been added to append the
|
||||||
|
new metadef tags to the existing tags. If the header is present it will
|
||||||
|
append the new tags to the existing one, if not then it will default to the
|
||||||
|
old behaviour i.e. overwriting the existing tags with the new one.
|
||||||
|
Fixes:
|
||||||
|
- |
|
||||||
|
* Bug 1939169_: glance md-tag-create-multiple overwrites existing tags
|
||||||
|
|
||||||
|
.. _1939169: https://bugs.launchpad.net/glance/+bug/1939169
|
||||||
|
|
Loading…
Reference in New Issue
Block a user