1+ import logging
12import tempfile
23import time
34import uuid
1011import gitlab .base
1112
1213SLEEP_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" )
1718def fixture_dir (test_dir ):
1819return 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."""
2334for project in gl .projects .list ():
35+ logging .info (f"Mark for deletion project:{ project .name !r} " )
2436for 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+ )
2541deploy_token .delete ()
2642project .delete ()
2743for group in gl .groups .list ():
44+ logging .info (f"Mark for deletion group:{ group .name !r} " )
2845for 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+ )
2950deploy_token .delete ()
3051group .delete ()
3152for variable in gl .variables .list ():
53+ logging .info (f"Mark for deletion variable:{ variable .name !r} " )
3254variable .delete ()
3355for user in gl .users .list ():
3456if user .username != "root" :
57+ logging .info (f"Mark for deletion user:{ user .username !r} " )
3558user .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+
3767max_iterations = int (TIMEOUT / SLEEP_INTERVAL )
3868
3969# Ensure everything has been reset
4070start_time = time .perf_counter ()
4171
4272def 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 :
4998break
5099time .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 , (
52107f"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
56112wait_for_maximum_list_length (rest_manager = gl .projects ,description = "projects" )
57113wait_for_maximum_list_length (rest_manager = gl .groups ,description = "groups" )
58114wait_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+
59121wait_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
146212def _wait (timeout = 30 ,step = 0.5 ):
147- for _ in range (timeout ):
213+ for count in range (timeout ):
148214time .sleep (step )
149215busy = False
150216processes = gl .sidekiq .process_metrics ()["processes" ]
@@ -153,6 +219,7 @@ def _wait(timeout=30, step=0.5):
153219busy = True
154220if not busy :
155221return True
222+ logging .info (f"sidekiq busy{ count } of{ timeout } " )
156223return False
157224
158225return _wait
@@ -188,8 +255,10 @@ def gitlab_config(check_is_alive, docker_ip, docker_services, temp_dir, fixture_
188255def gl (gitlab_config ):
189256"""Helper instance to make fixtures and asserts directly via the API."""
190257
258+ logging .info ("Create python-gitlab gitlab.Gitlab object" )
191259instance = gitlab .Gitlab .from_config ("local" , [gitlab_config ])
192- reset_gitlab (instance )
260+
261+ reset_gitlab (gl = instance )
193262
194263return instance
195264