From 6fc5179b932432c1caeb69063693b39af49b3575 Mon Sep 17 00:00:00 2001 From: Maurice Escher Date: Thu, 6 Feb 2020 17:24:12 +0100 Subject: [PATCH] ccloud: add possibility to set default tag(s) configuration option to set tags that get added to newly created projects Change-Id: Icac8d54506082816bdaeacb73853a20b49735c16 --- keystone/conf/default.py | 12 +++++++ keystone/resource/core.py | 10 ++++++ keystone/tests/unit/core.py | 6 ++++ keystone/tests/unit/test_v3_resource.py | 45 ++++++++++++++++++------- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/keystone/conf/default.py b/keystone/conf/default.py index 25d8731ab64..bdde75db2c0 100644 --- a/keystone/conf/default.py +++ b/keystone/conf/default.py @@ -105,6 +105,17 @@ will default to using the server's host name. """)) +default_tag = cfg.MultiStrOpt( + 'default_tag', + default=[], + help=utils.fmt(""" +Default `tag`(s) for newly created projects. If left undefined, Keystone will +create projects without an initial tag. This field can be set multiple times in +order to set multiple default tags, for example: +default_tag=tag_0 +default_tag=tag_1 +""")) + notification_format = cfg.StrOpt( 'notification_format', default='cadf', @@ -148,6 +159,7 @@ strict_password_check, insecure_debug, default_publisher_id, + default_tag, notification_format, notification_opt_out, ] diff --git a/keystone/resource/core.py b/keystone/resource/core.py index 27f1ec6b9ee..55bb1921aaa 100644 --- a/keystone/resource/core.py +++ b/keystone/resource/core.py @@ -204,6 +204,16 @@ def create_project(self, project_id, project, initiator=None): project['name'] = project['name'].strip() project.setdefault('description', '') + # ccloud add default tag(s) + if CONF.default_tag: + default_tags = [x.strip() for x in CONF.default_tag] + if 'tags' in project: + # a user may have provided a tag, which is a default tag, again + # make unique by converting to a set + project['tags'] = list(set(project['tags'] + default_tags)) + else: + project['tags'] = default_tags + # For regular projects, the controller will ensure we have a valid # domain_id. For projects acting as a domain, the project_id # is, effectively, the domain_id - and for such projects we don't diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index a46826d4692..d7b4ee338f5 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -297,6 +297,12 @@ def new_project_ref(domain_id=None, is_domain=False, **kwargs): return ref +def new_project_without_tags_ref(domain_id=None, is_domain=False, **kwargs): + ref = new_project_ref(domain_id=domain_id, is_domain=is_domain, **kwargs) + ref.pop('tags') + return ref + + def new_user_ref(domain_id, project_id=None, **kwargs): ref = { 'id': uuid.uuid4().hex, diff --git a/keystone/tests/unit/test_v3_resource.py b/keystone/tests/unit/test_v3_resource.py index 8fe6c7e771d..629d11df5ef 100644 --- a/keystone/tests/unit/test_v3_resource.py +++ b/keystone/tests/unit/test_v3_resource.py @@ -29,6 +29,9 @@ CONF = keystone.conf.CONF PROVIDERS = provider_api.ProviderAPIs +_DEFAULT_TAG = ['single_tag'] +_DEFAULT_TAGS = [None, [], ['vc-a-0', 'tag_1', 'tag_2'], _DEFAULT_TAG] + class ResourceTestCase(test_v3.RestfulTestCase, test_v3.AssignmentTestMixin): @@ -777,7 +780,7 @@ def _create_projects_hierarchy(self, hierarchy_size=1): return projects - def _create_project_and_tags(self, num_of_tags=1): + def _create_project_and_tags(self, num_of_tags=1, with_default_tag=False): """Create a project and a number of tags attached to that project. :param num_of_tags: the desired number of tags created with a specified @@ -787,9 +790,19 @@ def _create_project_and_tags(self, num_of_tags=1): random tags """ tags = [uuid.uuid4().hex for i in range(num_of_tags)] - ref = unit.new_project_ref( - domain_id=self.domain_id, - tags=tags) + + if num_of_tags > 0: + if with_default_tag: + # remove the last tag + tags.pop() + # add default tag instead + tags += _DEFAULT_TAG + ref = unit.new_project_ref( + domain_id=self.domain_id, + tags=tags) + else: + ref = unit.new_project_without_tags_ref( + domain_id=self.domain_id) resp = self.post('/projects', body={'project': ref}) return resp.result['project'], tags @@ -1601,14 +1614,22 @@ def test_delete_not_leaf_project(self): expected_status=http_client.FORBIDDEN) def test_create_project_with_tags(self): - project, tags = self._create_project_and_tags(num_of_tags=10) - ref = self.get( - '/projects/%(project_id)s' % { - 'project_id': project['id']}, - expected_status=http_client.OK) - self.assertIn('tags', ref.result['project']) - for tag in tags: - self.assertIn(tag, ref.result['project']['tags']) + for config_setting in _DEFAULT_TAGS: + if config_setting is not None: + self.config_fixture.config(default_tag=config_setting) + for tag_number in [0, 10]: + project, tags = self._create_project_and_tags( + num_of_tags=tag_number, with_default_tag=True) + ref = self.get( + '/projects/%(project_id)s' % { + 'project_id': project['id']}, + expected_status=http_client.OK) + self.assertIn('tags', ref.result['project']) + for tag in tags: + self.assertIn(tag, ref.result['project']['tags']) + if config_setting is not None: + for tag in config_setting: + self.assertIn(tag, ref.result['project']['tags']) def test_update_project_with_tags(self): project, tags = self._create_project_and_tags(num_of_tags=9)