Skip to content

Commit 3c5f510

Browse files
chore: make reset_gitlab() better
Saw issues in the CI where reset_gitlab() would fail. It would fail to delete the group that is created when GitLab starts up. Extending the timeout didn't fix the issue. Changed the code so now it will check if the item is deleted and if it isn't it will call the delete() method again to ensure that GitLab knows it should be deleted. Since making this change I have not been able to reproduce the failure in reset_gitlab(). Also added some logging functionality that can be seen if logging is turned on in pytest.
1 parent f26bf7d commit 3c5f510

File tree

2 files changed

+89
-11
lines changed

2 files changed

+89
-11
lines changed

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,10 @@ disable = [
8787
"useless-object-inheritance",
8888

8989
]
90+
91+
[tool.pytest.ini_options]
92+
log_cli = true
93+
log_cli_level = "INFO"
94+
log_cli_format = "%(asctime)s.%(msecs)03d [%(levelname)8s] (%(filename)s:%(funcName)s:L%(lineno)s) %(message)s"
95+
log_cli_date_format = "%Y-%m-%d %H:%M:%S"
96+
addopts = "--color=yes"

tests/functional/conftest.py

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import tempfile
23
import time
34
import uuid
@@ -10,54 +11,119 @@
1011
import gitlab.base
1112

1213
SLEEP_INTERVAL = 0.1
13-
TIMEOUT = 60 # seconds before timeout will occur
14+
TIMEOUT = 3 * 60 # seconds before timeout will occur
1415

1516

1617
@pytest.fixture(scope="session")
1718
def fixture_dir(test_dir):
1819
return test_dir / "functional" / "fixtures"
1920

2021

21-
def reset_gitlab(gl):
22-
# previously tools/reset_gitlab.py
22+
def reset_gitlab(gl: gitlab.Gitlab) -> None:
23+
# Mark our resources for deletion. It takes time for them to actually be deleted
24+
# though.
25+
_reset_gitlab_delete_resources(gl=gl)
26+
27+
# Wait for all resources to be deleted
28+
_reset_gitlab_wait_deletion_finish(gl=gl)
29+
30+
31+
def _reset_gitlab_delete_resources(gl: gitlab.Gitlab) -> None:
32+
"""Mark for deletion, resources (such as projects, groups, users) that shouldn't
33+
exist. Once marked they will still take time to be deleted."""
2334
for project in gl.projects.list():
35+
logging.info(f"Mark for deletion project: {project.name!r}")
2436
for deploy_token in project.deploytokens.list():
37+
logging.info(
38+
f"Mark for deletion token: {deploy_token.name!r} in "
39+
f"project: {project.name!r}"
40+
)
2541
deploy_token.delete()
2642
project.delete()
2743
for group in gl.groups.list():
44+
logging.info(f"Mark for deletion group: {group.name!r}")
2845
for deploy_token in group.deploytokens.list():
46+
logging.info(
47+
f"Mark for deletion token: {deploy_token.name!r} in "
48+
f"group: {group.name!r}"
49+
)
2950
deploy_token.delete()
3051
group.delete()
3152
for variable in gl.variables.list():
53+
logging.info(f"Mark for deletion variable: {variable.name!r}")
3254
variable.delete()
3355
for user in gl.users.list():
3456
if user.username != "root":
57+
logging.info(f"Mark for deletion user: {user.username!r}")
3558
user.delete(hard_delete=True)
3659

60+
61+
def _reset_gitlab_wait_deletion_finish(gl: gitlab.Gitlab) -> None:
62+
"""Wait for all of our resources to be deleted.
63+
64+
If anything exists then mark it again for deletion in case initial call to delete
65+
didn't work, which has been seen :("""
66+
3767
max_iterations = int(TIMEOUT / SLEEP_INTERVAL)
3868

3969
# Ensure everything has been reset
4070
start_time = time.perf_counter()
4171

4272
def wait_for_maximum_list_length(
43-
rest_manager: gitlab.base.RESTManager, description: str, max_length: int = 0
73+
rest_manager: gitlab.base.RESTManager,
74+
description: str,
75+
max_length: int = 0,
76+
should_delete_func=lambda x: True,
77+
delete_kwargs={},
4478
) -> None:
4579
"""Wait for the list() length to be no greater than expected maximum or fail
4680
test if timeout is exceeded"""
47-
for _ in range(max_iterations):
48-
if len(rest_manager.list()) <= max_length:
81+
for count in range(max_iterations):
82+
items = rest_manager.list()
83+
logging.info(
84+
f"Iteration: {count}: items in {description}: {[x.name for x in items]}"
85+
)
86+
for item in items:
87+
if should_delete_func(item):
88+
logging.info(
89+
f"Marking again for deletion {description}: {item.name!r}"
90+
)
91+
try:
92+
item.delete(**delete_kwargs)
93+
except gitlab.exceptions.GitlabDeleteError as exc:
94+
logging.info(
95+
f"Already marked for deletion: {item.name!r} {exc}"
96+
)
97+
if len(items) <= max_length:
4998
break
5099
time.sleep(SLEEP_INTERVAL)
51-
assert len(rest_manager.list()) <= max_length, (
100+
items = rest_manager.list()
101+
elapsed_time = time.perf_counter() - start_time
102+
if len(items) > max_length:
103+
logging.error(
104+
f"Too many items still remaining and timeout exceeded: {elapsed_time}"
105+
)
106+
assert len(items) <= max_length, (
52107
f"Did not delete required items for {description}. "
53-
f"Elapsed_time: {time.perf_counter() - start_time}"
108+
f"Elapsed_time: {elapsed_time}\n"
109+
f"items: {[str(x) for x in items]!r}"
54110
)
55111

56112
wait_for_maximum_list_length(rest_manager=gl.projects, description="projects")
57113
wait_for_maximum_list_length(rest_manager=gl.groups, description="groups")
58114
wait_for_maximum_list_length(rest_manager=gl.variables, description="variables")
115+
116+
def should_delete_user(user):
117+
if user.username == "root":
118+
return False
119+
return True
120+
59121
wait_for_maximum_list_length(
60-
rest_manager=gl.users, description="users", max_length=1
122+
rest_manager=gl.users,
123+
description="users",
124+
max_length=1,
125+
should_delete_func=should_delete_user,
126+
delete_kwargs={"hard_delete": True},
61127
)
62128

63129

@@ -144,7 +210,7 @@ def wait_for_sidekiq(gl):
144210
"""
145211

146212
def _wait(timeout=30, step=0.5):
147-
for _ in range(timeout):
213+
for count in range(timeout):
148214
time.sleep(step)
149215
busy = False
150216
processes = gl.sidekiq.process_metrics()["processes"]
@@ -153,6 +219,7 @@ def _wait(timeout=30, step=0.5):
153219
busy = True
154220
if not busy:
155221
return True
222+
logging.info(f"sidekiq busy {count} of {timeout}")
156223
return False
157224

158225
return _wait
@@ -163,9 +230,11 @@ def gitlab_config(check_is_alive, docker_ip, docker_services, temp_dir, fixture_
163230
config_file = temp_dir / "python-gitlab.cfg"
164231
port = docker_services.port_for("gitlab", 80)
165232

233+
logging.info("Waiting for GitLab container to become ready.")
166234
docker_services.wait_until_responsive(
167235
timeout=200, pause=5, check=lambda: check_is_alive("gitlab-test")
168236
)
237+
logging.info("GitLab container is now ready.")
169238

170239
token = set_token("gitlab-test", fixture_dir=fixture_dir)
171240

@@ -188,8 +257,10 @@ def gitlab_config(check_is_alive, docker_ip, docker_services, temp_dir, fixture_
188257
def gl(gitlab_config):
189258
"""Helper instance to make fixtures and asserts directly via the API."""
190259

260+
logging.info("Create python-gitlab gitlab.Gitlab object")
191261
instance = gitlab.Gitlab.from_config("local", [gitlab_config])
192-
reset_gitlab(instance)
262+
263+
reset_gitlab(gl=instance)
193264

194265
return instance
195266

0 commit comments

Comments
 (0)