Update project depth check

Now `domain` is considered as the first level of project depth.

Update the check function to make sure the depth check is correct.

Change-Id: I38411001271518b9c2f58d53b2f654c361e952e8
bp: domain-level-limit
This commit is contained in:
wangxiyuan 2018-12-10 11:53:07 +08:00
parent 7e98d297ae
commit 8667f42beb
2 changed files with 49 additions and 43 deletions

View File

@ -278,52 +278,53 @@ class Resource(base.ResourceDriverBase):
obj_list = []
# Using db table self outerjoin to find the project descendants.
#
# We'll only outerjoin the project table (max_depth + 1) times to
# We'll only outerjoin the project table `max_depth` times to
# check whether current project tree exceed the max depth limit.
#
# Note one more time here is for project act as domain.
# For example:
#
# for example:
# If max_depth is 2, we will take the outerjoin 3 times, then the
# If max_depth is 2, we will take the outerjoin 2 times, then the
# SQL result may be like:
# +----+-------------+-------------+-------------+-------------+
# | No | project1_id | project2_id | project3_id | project4_id |
# +----+-------------+-------------+-------------+-------------+
# | 1 | project_a | | | |
# +----+-------------+-------------+-------------+-------------+
# | 2 | domain_x | project_a | | |
# +----+-------------+-------------+-------------+-------------+
# | 3 | project_b | project_c | | |
# +----+-------------+-------------+-------------+-------------+
# | 4 | domain_x | project_b | project_c | |
# +----+-------------+-------------+-------------+-------------+
# | 5 | project_d | project_e | project_f | |
# +----+-------------+-------------+-------------+-------------+
# | 6 | domain_x | project_d | project_e | project_f |
# +----+-------------+-------------+-------------+-------------+
#
# project1 is the root. It is a project or a domain. If project1 is
# a project, there must exist a line that project1 is its domain.
# +---- +-------------+-------------+-------------+
# | No. | project1_id | project2_id | project3_id |
# +--- -+-------------+-------------+-------------+
# | 1 | domain_x | | |
# +- ---+-------------+-------------+-------------+
# | 2 | project_a | | |
# +- ---+-------------+-------------+-------------+
# | 3 | domain_y | project_a | |
# +- ---+-------------+-------------+-------------+
# | 4 | project_b | project_c | |
# +- ---+-------------+-------------+-------------+
# | 5 | domain_y | project_b | project_c |
# +- ---+-------------+-------------+-------------+
#
# we got 6 lines here.
# `project1_id` column is the root. It is a project or a domain.
# If `project1_id` is a project, there must exist a line that
# `project1` is its domain.
#
# 1). the 1, 2 line means project project_a has no child, the depth
# is 1.
# 2). the 3, 4 line means project project_a has a child, the depth
# is 2.
# 3). the 5, 6 line means project project_a has a grandchild, the
# depth is 3. this tree hit the max depth.
# So we can see that if column "project4_id" has value, it means
# We got 5 lines here. It includes three scenarios:
#
# 1). The No.1 line means there is a domain `domain_x` which has no
# children. The depth is 1.
#
# 2). The No.2 and No.3 lines mean project `project_a` has no child
# and its parent is domain `domain_y`. The depth is 2.
#
# 3). The No.4 and No.5 lines mean project `project_b` has a child
# `project_c` and its parent is domain `domain_y`. The depth is
# 3. This tree hit the max depth
#
# So we can see that if column "project3_id" has value, it means
# some trees hit the max depth limit.
outerjoin_obj_number = max_depth + 2
for _ in range(outerjoin_obj_number):
for _ in range(max_depth + 1):
obj_list.append(orm.aliased(Project))
query = session.query(*obj_list)
outerjoin_count = max_depth + 1
for index in range(outerjoin_count):
for index in range(max_depth):
query = query.outerjoin(
obj_list[index + 1],
obj_list[index].id == obj_list[index + 1].parent_id)
@ -331,7 +332,7 @@ class Resource(base.ResourceDriverBase):
obj_list[-1].id != expression.null())
if exceeded_lines:
return [line[max_depth + 1].id for line in exceeded_lines]
return [line[max_depth].id for line in exceeded_lines]
class Project(sql.ModelBase, sql.ModelDictMixinWithExtras):

View File

@ -718,15 +718,20 @@ class SqlIdentity(SqlTests,
self.assertEqual(first_call_counter, counter.calls)
def test_check_project_depth(self):
# create a 3 level project tree
ref = unit.new_project_ref(domain_id=CONF.identity.default_domain_id)
PROVIDERS.resource_api.create_project(ref['id'], ref)
ref_1 = unit.new_project_ref(domain_id=CONF.identity.default_domain_id,
parent_id=ref['id'])
PROVIDERS.resource_api.create_project(ref_1['id'], ref_1)
ref_2 = unit.new_project_ref(domain_id=CONF.identity.default_domain_id,
parent_id=ref_1['id'])
PROVIDERS.resource_api.create_project(ref_2['id'], ref_2)
# Create a 3 level project tree:
#
# default_domain
# |
# project_1
# |
# project_2
project_1 = unit.new_project_ref(
domain_id=CONF.identity.default_domain_id)
PROVIDERS.resource_api.create_project(project_1['id'], project_1)
project_2 = unit.new_project_ref(
domain_id=CONF.identity.default_domain_id,
parent_id=project_1['id'])
PROVIDERS.resource_api.create_project(project_2['id'], project_2)
# if max_depth is None or >= current project depth, return nothing.
resp = PROVIDERS.resource_api.check_project_depth(max_depth=None)