qontract-reconcile 0.10.1rc495__py3-none-any.whl → 0.10.1rc497__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/RECORD +13 -13
- reconcile/saas_auto_promotions_manager/integration.py +0 -1
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager.py +68 -28
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +7 -2
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +3 -0
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +1 -0
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -3
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/{test_housekeeping.py → test_mr_parsing.py} +96 -10
- reconcile/utils/vcs.py +47 -5
- {qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc497
|
4
4
|
Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
|
5
5
|
Home-page: https://github.com/app-sre/qontract-reconcile
|
6
6
|
Author: Red Hat App-SRE Team
|
{qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/RECORD
RENAMED
@@ -340,13 +340,13 @@ reconcile/rhidp/sso_client/base.py,sha256=EfQ2ewcOKh5idg46UKAkY6z0m_nGQfvnQKffa2
|
|
340
340
|
reconcile/rhidp/sso_client/integration.py,sha256=kA8g7c38ZBSdrRtyfEqy_WgSreD1PbwY7ZIN-3tZRPc,2221
|
341
341
|
reconcile/rhidp/sso_client/metrics.py,sha256=Tq7tSOsqL3XdcPUdozxqzSPIodUeOV87UCTqpuuqqhw,1013
|
342
342
|
reconcile/saas_auto_promotions_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
343
|
-
reconcile/saas_auto_promotions_manager/integration.py,sha256=
|
343
|
+
reconcile/saas_auto_promotions_manager/integration.py,sha256=lRDMLekNMhPaMJFKS8e2ueK3EpYTC3T199uMoJck_zA,5503
|
344
344
|
reconcile/saas_auto_promotions_manager/publisher.py,sha256=4_M9Oykhj-kEZPUn05E2DY5gD6-x32Dgf7K3NPOkGEg,2029
|
345
345
|
reconcile/saas_auto_promotions_manager/subscriber.py,sha256=cLhPlkT71J2LIice3SLmH1WpsqzV46gd0peMxrnqyRw,7452
|
346
346
|
reconcile/saas_auto_promotions_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
347
347
|
reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request.py,sha256=PNu7sE-tDUY61E03z5w0b93fdowZ8auCl0S4_vhYOKQ,1333
|
348
|
-
reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager.py,sha256=
|
349
|
-
reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=
|
348
|
+
reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager.py,sha256=9boB6yp5Fin6XUCtNNsFVIOExi2o7IfbRbG9o1ZAe5Q,12797
|
349
|
+
reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=Sj3mwmBcml6t-49bPnXdq7snT9N0WKrPk2chU_0ylyE,7188
|
350
350
|
reconcile/saas_auto_promotions_manager/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
351
351
|
reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py,sha256=mihuWynroB1Cea1Lsvf6V8Nb8PGiBdcLC0uhCX_52Y0,6966
|
352
352
|
reconcile/skupper_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -446,10 +446,10 @@ reconcile/test/saas_auto_promotions_manager/conftest.py,sha256=tZNs35EuWulP53Cqt
|
|
446
446
|
reconcile/test/saas_auto_promotions_manager/test_integration_test.py,sha256=qip-D7a97VnX_-vvDdqJDXeyGDBSG2YvIjI3j7MY1hQ,3901
|
447
447
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
448
448
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
449
|
-
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py,sha256=
|
450
|
-
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py,sha256=
|
451
|
-
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/
|
452
|
-
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/
|
449
|
+
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py,sha256=9N5M_uZn6GGbUDfqLkj9RarrKo_qUZVJDp26bH6m2Gw,3597
|
450
|
+
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py,sha256=vkBpEBkx9mcYN4VkzijTJobfSUbgg5TxHOFpWbs0ztQ,535
|
451
|
+
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py,sha256=16QPUcg0TR1gXS_ELVcJ3frpYb9PBW55c6FRbS5i0y8,6078
|
452
|
+
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_mr_parsing.py,sha256=HC-uyM3VVgA02Fkc0T0XE-eg47noyA9MB_gbKMeufh4,8829
|
453
453
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
454
454
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py,sha256=2rCSstewp4LPoEJHm5N7dGJexEtY8ndLHvoGZYjmpsc,1678
|
455
455
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/data_keys.py,sha256=beHYQ9kgDLeBZgC2FvxQA3tHx1PO-RAMN8_kVcSdikI,90
|
@@ -576,7 +576,7 @@ reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM
|
|
576
576
|
reconcile/utils/unleash.py,sha256=1D56CsZfE3ShDtN3IErE1T2eeIwNmxhK-yYbCotJ99E,3601
|
577
577
|
reconcile/utils/vault.py,sha256=S0eHqvZ9N3fya1E8YDaUffEvLk_fdtpzL4rvWn6f828,14991
|
578
578
|
reconcile/utils/vaultsecretref.py,sha256=3Ed2uBy36TzSvL0B-l4FoWQqB2SbBKDKEuUPIO608Bo,931
|
579
|
-
reconcile/utils/vcs.py,sha256=
|
579
|
+
reconcile/utils/vcs.py,sha256=o1r0n_IrU2El75CED_6sjR2GZGM-exuWsj5F7jONaMU,6779
|
580
580
|
reconcile/utils/cloud_resource_best_practice/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
581
581
|
reconcile/utils/cloud_resource_best_practice/aws_rds.py,sha256=EvE6XKLsrZ531MJptKqPht2lOETrOjySTHXk6CzMgo0,2279
|
582
582
|
reconcile/utils/glitchtip/__init__.py,sha256=FT6iBhGqoe7KExFdbgL8AYUb64iW_4snF5__Dcl7yt0,258
|
@@ -653,8 +653,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
653
653
|
tools/test/test_qontract_cli.py,sha256=d18KrdhtUGqoC7_kWZU128U0-VJEj-0rjFkLVufcI6I,2755
|
654
654
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
655
655
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
656
|
-
qontract_reconcile-0.10.
|
657
|
-
qontract_reconcile-0.10.
|
658
|
-
qontract_reconcile-0.10.
|
659
|
-
qontract_reconcile-0.10.
|
660
|
-
qontract_reconcile-0.10.
|
656
|
+
qontract_reconcile-0.10.1rc497.dist-info/METADATA,sha256=IvyDujhOGuhQNFmCJDWvjg78ZPUpVBwcJwKlcVVWBlQ,2349
|
657
|
+
qontract_reconcile-0.10.1rc497.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
658
|
+
qontract_reconcile-0.10.1rc497.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
|
659
|
+
qontract_reconcile-0.10.1rc497.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
660
|
+
qontract_reconcile-0.10.1rc497.dist-info/RECORD,,
|
@@ -81,7 +81,6 @@ class SaasAutoPromotionsManager:
|
|
81
81
|
self._fetch_publisher_real_world_states()
|
82
82
|
self._compute_desired_subscriber_states()
|
83
83
|
subscribers_with_diff = self._get_subscribers_with_diff()
|
84
|
-
self._merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
85
84
|
self._merge_request_manager.housekeeping()
|
86
85
|
self._merge_request_manager.create_promotion_merge_requests(
|
87
86
|
subscribers=subscribers_with_diff
|
@@ -13,6 +13,7 @@ from reconcile.saas_auto_promotions_manager.merge_request_manager.merge_request
|
|
13
13
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
14
14
|
CHANNELS_REF,
|
15
15
|
CONTENT_HASHES,
|
16
|
+
IS_BATCHABLE,
|
16
17
|
PROMOTION_DATA_SEPARATOR,
|
17
18
|
SAPM_LABEL,
|
18
19
|
SAPM_VERSION,
|
@@ -20,14 +21,18 @@ from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer impor
|
|
20
21
|
Renderer,
|
21
22
|
)
|
22
23
|
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
23
|
-
from reconcile.utils.vcs import VCS
|
24
|
+
from reconcile.utils.vcs import VCS, MRCheckStatus
|
25
|
+
|
26
|
+
ITEM_SEPARATOR = ","
|
24
27
|
|
25
28
|
|
26
29
|
@dataclass
|
27
30
|
class OpenMergeRequest:
|
28
31
|
raw: ProjectMergeRequest
|
29
|
-
|
32
|
+
content_hashes: str
|
30
33
|
channels: str
|
34
|
+
failed_mr_check: bool
|
35
|
+
is_batchable: bool
|
31
36
|
|
32
37
|
|
33
38
|
class MergeRequestManager:
|
@@ -38,13 +43,10 @@ class MergeRequestManager:
|
|
38
43
|
|
39
44
|
The idea is that for every channel combination there exists
|
40
45
|
maximum one open MR in app-interface. I.e., all changes for
|
41
|
-
a channel combination are batched in a single MR.
|
42
|
-
description is used to
|
43
|
-
|
44
|
-
|
45
|
-
open a new MR with the new content. We might need to change this
|
46
|
-
batching approach in the future to have even less promotion MRs,
|
47
|
-
but for now this is sufficient.
|
46
|
+
a channel combination at a minumum are batched in a single MR.
|
47
|
+
The MR description is used to store current state for SAPM.
|
48
|
+
Channels can also be batched together into a single MR to reduce the overall
|
49
|
+
amount of MRs.
|
48
50
|
"""
|
49
51
|
|
50
52
|
def __init__(self, vcs: VCS, renderer: Renderer):
|
@@ -53,6 +55,7 @@ class MergeRequestManager:
|
|
53
55
|
self._version_ref_regex = re.compile(rf"{VERSION_REF}: (.*)$", re.MULTILINE)
|
54
56
|
self._content_hash_regex = re.compile(rf"{CONTENT_HASHES}: (.*)$", re.MULTILINE)
|
55
57
|
self._channels_regex = re.compile(rf"{CHANNELS_REF}: (.*)$", re.MULTILINE)
|
58
|
+
self._is_batchable_regex = re.compile(rf"{IS_BATCHABLE}: (.*)$", re.MULTILINE)
|
56
59
|
self._open_mrs: list[OpenMergeRequest] = []
|
57
60
|
self._open_mrs_with_problems: list[OpenMergeRequest] = []
|
58
61
|
self._open_raw_mrs: list[ProjectMergeRequest] = []
|
@@ -66,21 +69,22 @@ class MergeRequestManager:
|
|
66
69
|
return ""
|
67
70
|
return groups[0]
|
68
71
|
|
69
|
-
def
|
72
|
+
def _fetch_sapm_managed_open_merge_requests(self) -> None:
|
70
73
|
all_open_mrs = self._vcs.get_open_app_interface_merge_requests()
|
71
74
|
self._open_raw_mrs = [
|
72
75
|
mr for mr in all_open_mrs if SAPM_LABEL in mr.attributes.get("labels")
|
73
76
|
]
|
74
77
|
|
75
|
-
def
|
78
|
+
def _parse_raw_mrs(self) -> None:
|
76
79
|
"""
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
We store state in MR descriptions.
|
81
|
+
This function parses the state and stores a list of valid, parsed open MRs (current state).
|
82
|
+
If any issue is encountered during parsing, we consider this MR
|
83
|
+
to be broken and close it. Information we want to parse includes:
|
84
|
+
- SAPM_VERSION -> Close if it doesnt match current version
|
85
|
+
- CHANNELS
|
86
|
+
- CONTENT_HASHES
|
87
|
+
- IS_BATCHABLE flag
|
84
88
|
"""
|
85
89
|
seen: set[tuple[str, str, str]] = set()
|
86
90
|
for mr in self._open_raw_mrs:
|
@@ -134,10 +138,10 @@ class MergeRequestManager:
|
|
134
138
|
)
|
135
139
|
continue
|
136
140
|
|
137
|
-
|
141
|
+
content_hashes = self._apply_regex(
|
138
142
|
pattern=self._content_hash_regex, promotion_data=promotion_data
|
139
143
|
)
|
140
|
-
if not
|
144
|
+
if not content_hashes:
|
141
145
|
logging.info(
|
142
146
|
"Bad %s format. Closing %s",
|
143
147
|
CONTENT_HASHES,
|
@@ -148,10 +152,10 @@ class MergeRequestManager:
|
|
148
152
|
)
|
149
153
|
continue
|
150
154
|
|
151
|
-
|
155
|
+
channels_refs = self._apply_regex(
|
152
156
|
pattern=self._channels_regex, promotion_data=promotion_data
|
153
157
|
)
|
154
|
-
if not
|
158
|
+
if not channels_refs:
|
155
159
|
logging.info(
|
156
160
|
"Bad %s format. Closing %s",
|
157
161
|
CHANNELS_REF,
|
@@ -162,7 +166,7 @@ class MergeRequestManager:
|
|
162
166
|
)
|
163
167
|
continue
|
164
168
|
|
165
|
-
key = (version_ref,
|
169
|
+
key = (version_ref, channels_refs, content_hashes)
|
166
170
|
if key in seen:
|
167
171
|
logging.info(
|
168
172
|
"Duplicate MR detected. Closing %s",
|
@@ -175,14 +179,49 @@ class MergeRequestManager:
|
|
175
179
|
continue
|
176
180
|
seen.add(key)
|
177
181
|
|
182
|
+
is_batchable_str = self._apply_regex(
|
183
|
+
pattern=self._is_batchable_regex, promotion_data=promotion_data
|
184
|
+
)
|
185
|
+
if is_batchable_str not in set(["True", "False"]):
|
186
|
+
logging.info(
|
187
|
+
"Bad %s format. Closing %s",
|
188
|
+
IS_BATCHABLE,
|
189
|
+
mr.attributes.get("web_url", "NO_WEBURL"),
|
190
|
+
)
|
191
|
+
self._vcs.close_app_interface_mr(
|
192
|
+
mr, f"Closing this MR because of bad {IS_BATCHABLE} format."
|
193
|
+
)
|
194
|
+
continue
|
195
|
+
|
196
|
+
mr_check_status = self._vcs.get_gitlab_mr_check_status(mr)
|
197
|
+
|
178
198
|
self._open_mrs.append(
|
179
199
|
OpenMergeRequest(
|
180
200
|
raw=mr,
|
181
|
-
|
182
|
-
channels=
|
201
|
+
content_hashes=content_hashes,
|
202
|
+
channels=channels_refs,
|
203
|
+
failed_mr_check=mr_check_status == MRCheckStatus.FAILED,
|
204
|
+
is_batchable=bool(is_batchable_str),
|
183
205
|
)
|
184
206
|
)
|
185
207
|
|
208
|
+
def _unbatch_failed_mrs(self) -> None:
|
209
|
+
"""
|
210
|
+
We optimistically batch MRs together that didnt run through MR check yet.
|
211
|
+
Vast majority of auto-promotion MRs are succeeding checks, so we can stay optimistic.
|
212
|
+
In the rare case of an MR failing the check, we want to unbatch it.
|
213
|
+
I.e., we open a dedicated MR for each channel in the batched MR, mark the new MRs as non-batchable
|
214
|
+
and close the old batched MR. By doing so, we ensure that unrelated MRs are not blocking each other.
|
215
|
+
Unbatched MRs are marked and will never be batched again.
|
216
|
+
"""
|
217
|
+
# TODO: implemented in follow-up MR
|
218
|
+
pass
|
219
|
+
|
220
|
+
def housekeeping(self) -> None:
|
221
|
+
self._fetch_sapm_managed_open_merge_requests()
|
222
|
+
self._parse_raw_mrs()
|
223
|
+
self._unbatch_failed_mrs()
|
224
|
+
|
186
225
|
def _aggregate_subscribers_per_channel_combo(
|
187
226
|
self, subscribers: Iterable[Subscriber]
|
188
227
|
) -> dict[str, list[Subscriber]]:
|
@@ -196,7 +235,7 @@ class MergeRequestManager:
|
|
196
235
|
return any(
|
197
236
|
True
|
198
237
|
for mr in self._open_mrs
|
199
|
-
if content_hash in mr.
|
238
|
+
if content_hash in mr.content_hashes and channels in mr.channels
|
200
239
|
)
|
201
240
|
|
202
241
|
def create_promotion_merge_requests(
|
@@ -223,7 +262,7 @@ class MergeRequestManager:
|
|
223
262
|
for mr in self._open_mrs:
|
224
263
|
if channel_combo not in mr.channels:
|
225
264
|
continue
|
226
|
-
if combined_content_hash not in mr.
|
265
|
+
if combined_content_hash not in mr.content_hashes:
|
227
266
|
logging.info(
|
228
267
|
"Closing MR %s because it has out-dated content",
|
229
268
|
mr.raw.attributes.get("web_url", "NO_WEBURL"),
|
@@ -261,8 +300,9 @@ class MergeRequestManager:
|
|
261
300
|
continue
|
262
301
|
|
263
302
|
description = self._renderer.render_description(
|
264
|
-
|
303
|
+
content_hashes=combined_content_hash,
|
265
304
|
channels=channel_combo,
|
305
|
+
is_batchable=True,
|
266
306
|
)
|
267
307
|
title = self._renderer.render_title(channels=channel_combo)
|
268
308
|
logging.info(
|
@@ -22,6 +22,7 @@ SAPM_VERSION = "1.1.0"
|
|
22
22
|
SAPM_LABEL = "SAPM"
|
23
23
|
CONTENT_HASHES = "content_hashes"
|
24
24
|
CHANNELS_REF = "channels"
|
25
|
+
IS_BATCHABLE = "is_batchable"
|
25
26
|
VERSION_REF = "sapm_version"
|
26
27
|
SAPM_DESC = f"""
|
27
28
|
This is an auto-promotion triggered by app-interface's [saas-auto-promotions-manager](https://github.com/app-sre/qontract-reconcile/tree/master/reconcile/saas_auto_promotions_manager) (SAPM).
|
@@ -134,7 +135,9 @@ class Renderer:
|
|
134
135
|
new_content += stream.getvalue() or ""
|
135
136
|
return new_content
|
136
137
|
|
137
|
-
def render_description(
|
138
|
+
def render_description(
|
139
|
+
self, content_hashes: str, channels: str, is_batchable: bool
|
140
|
+
) -> str:
|
138
141
|
return f"""
|
139
142
|
{SAPM_DESC}
|
140
143
|
|
@@ -142,7 +145,9 @@ class Renderer:
|
|
142
145
|
|
143
146
|
{CHANNELS_REF}: {channels}
|
144
147
|
|
145
|
-
{CONTENT_HASHES}: {
|
148
|
+
{CONTENT_HASHES}: {content_hashes}
|
149
|
+
|
150
|
+
{IS_BATCHABLE}: {is_batchable}
|
146
151
|
|
147
152
|
{VERSION_REF}: {SAPM_VERSION}
|
148
153
|
"""
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py
CHANGED
@@ -13,6 +13,7 @@ from reconcile.gql_definitions.fragments.saas_target_namespace import (
|
|
13
13
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
14
14
|
CHANNELS_REF,
|
15
15
|
CONTENT_HASHES,
|
16
|
+
IS_BATCHABLE,
|
16
17
|
PROMOTION_DATA_SEPARATOR,
|
17
18
|
SAPM_LABEL,
|
18
19
|
SAPM_VERSION,
|
@@ -30,6 +31,7 @@ from .data_keys import (
|
|
30
31
|
HAS_CONFLICTS,
|
31
32
|
LABELS,
|
32
33
|
OPEN_MERGE_REQUESTS,
|
34
|
+
SUBSCRIBER_BATCHABLE,
|
33
35
|
SUBSCRIBER_CHANNELS,
|
34
36
|
SUBSCRIBER_CONTENT_HASH,
|
35
37
|
SUBSCRIBER_DESIRED_CONFIG_HASHES,
|
@@ -52,6 +54,7 @@ def mr_builder() -> Callable[[Mapping], ProjectMergeRequest]:
|
|
52
54
|
{VERSION_REF}: {data.get(VERSION_REF, SAPM_VERSION)}
|
53
55
|
{CHANNELS_REF}: {data.get(SUBSCRIBER_CHANNELS, "some_channel")}
|
54
56
|
{CONTENT_HASHES}: {data.get(SUBSCRIBER_CONTENT_HASH, "content_hash")}
|
57
|
+
{IS_BATCHABLE}: {data.get(SUBSCRIBER_BATCHABLE, "True")}
|
55
58
|
""",
|
56
59
|
"web_url": "http://localhost",
|
57
60
|
"has_conflicts": False,
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py
CHANGED
@@ -10,5 +10,6 @@ SUBSCRIBER_TARGET_PATH = "target_path"
|
|
10
10
|
SUBSCRIBER_DESIRED_CONFIG_HASHES = "config_hashes"
|
11
11
|
SUBSCRIBER_DESIRED_REF = "desired_ref"
|
12
12
|
SUBSCRIBER_CHANNELS = "sub_channels"
|
13
|
+
SUBSCRIBER_BATCHABLE = "sub_batchable"
|
13
14
|
HAS_CONFLICTS = "has_conflicts"
|
14
15
|
SUBSCRIBER_TARGET_NAMESPACE = "subscriber_target_namespace"
|
@@ -53,7 +53,6 @@ def test_close_old_content(
|
|
53
53
|
vcs=vcs,
|
54
54
|
renderer=renderer,
|
55
55
|
)
|
56
|
-
merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
57
56
|
merge_request_manager.housekeeping()
|
58
57
|
merge_request_manager.create_promotion_merge_requests(subscribers=subscribers)
|
59
58
|
|
@@ -112,7 +111,6 @@ def test_merge_request_already_opened(
|
|
112
111
|
vcs=vcs,
|
113
112
|
renderer=renderer,
|
114
113
|
)
|
115
|
-
merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
116
114
|
merge_request_manager.housekeeping()
|
117
115
|
merge_request_manager.create_promotion_merge_requests(subscribers=subscribers)
|
118
116
|
|
@@ -170,7 +168,6 @@ def test_ignore_unrelated_channels(
|
|
170
168
|
vcs=vcs,
|
171
169
|
renderer=renderer,
|
172
170
|
)
|
173
|
-
merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
174
171
|
merge_request_manager.housekeeping()
|
175
172
|
merge_request_manager.create_promotion_merge_requests(subscribers=subscribers)
|
176
173
|
|
@@ -9,6 +9,7 @@ from reconcile.saas_auto_promotions_manager.merge_request_manager.merge_request_
|
|
9
9
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
10
10
|
CHANNELS_REF,
|
11
11
|
CONTENT_HASHES,
|
12
|
+
IS_BATCHABLE,
|
12
13
|
PROMOTION_DATA_SEPARATOR,
|
13
14
|
SAPM_LABEL,
|
14
15
|
SAPM_VERSION,
|
@@ -25,16 +26,30 @@ from .data_keys import (
|
|
25
26
|
)
|
26
27
|
|
27
28
|
|
28
|
-
def test_labels_filter(
|
29
|
+
def test_labels_filter(
|
30
|
+
vcs_builder: Callable[[Mapping], VCS], renderer: Renderer
|
31
|
+
) -> None:
|
29
32
|
vcs = vcs_builder({
|
30
33
|
OPEN_MERGE_REQUESTS: [
|
31
34
|
{
|
32
35
|
LABELS: ["OtherLabel"],
|
33
|
-
DESCRIPTION: "
|
36
|
+
DESCRIPTION: f"""
|
37
|
+
Blabla
|
38
|
+
{PROMOTION_DATA_SEPARATOR}
|
39
|
+
{VERSION_REF}: {SAPM_VERSION}
|
40
|
+
{CHANNELS_REF}: some-channel
|
41
|
+
{CONTENT_HASHES}: some_hash
|
42
|
+
""",
|
34
43
|
},
|
35
44
|
{
|
36
45
|
LABELS: [SAPM_LABEL, "OtherLabel"],
|
37
|
-
DESCRIPTION: "
|
46
|
+
DESCRIPTION: f"""
|
47
|
+
Blabla
|
48
|
+
{PROMOTION_DATA_SEPARATOR}
|
49
|
+
{VERSION_REF}: {SAPM_VERSION}
|
50
|
+
{CHANNELS_REF}: other-channel
|
51
|
+
{CONTENT_HASHES}: other_hash
|
52
|
+
""",
|
38
53
|
},
|
39
54
|
]
|
40
55
|
})
|
@@ -42,11 +57,13 @@ def test_labels_filter(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer
|
|
42
57
|
vcs=vcs,
|
43
58
|
renderer=renderer,
|
44
59
|
)
|
45
|
-
merge_request_manager.
|
60
|
+
merge_request_manager.housekeeping()
|
46
61
|
assert len(merge_request_manager._open_raw_mrs) == 1
|
47
62
|
|
48
63
|
|
49
|
-
def test_valid_description(
|
64
|
+
def test_valid_description(
|
65
|
+
vcs_builder: Callable[[Mapping], VCS], renderer: Renderer
|
66
|
+
) -> None:
|
50
67
|
vcs = vcs_builder({
|
51
68
|
OPEN_MERGE_REQUESTS: [
|
52
69
|
{
|
@@ -57,6 +74,7 @@ def test_valid_description(vcs_builder: Callable[[Mapping], VCS], renderer: Rend
|
|
57
74
|
{VERSION_REF}: {SAPM_VERSION}
|
58
75
|
{CHANNELS_REF}: some-channel
|
59
76
|
{CONTENT_HASHES}: some_hash
|
77
|
+
{IS_BATCHABLE}: True
|
60
78
|
""",
|
61
79
|
}
|
62
80
|
]
|
@@ -65,13 +83,50 @@ def test_valid_description(vcs_builder: Callable[[Mapping], VCS], renderer: Rend
|
|
65
83
|
vcs=vcs,
|
66
84
|
renderer=renderer,
|
67
85
|
)
|
68
|
-
merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
69
86
|
merge_request_manager.housekeeping()
|
70
87
|
vcs.close_app_interface_mr.assert_not_called() # type: ignore[attr-defined]
|
71
88
|
assert len(merge_request_manager._open_mrs) == 1
|
72
89
|
|
73
90
|
|
74
|
-
def
|
91
|
+
def test_valid_batching(
|
92
|
+
vcs_builder: Callable[[Mapping], VCS], renderer: Renderer
|
93
|
+
) -> None:
|
94
|
+
vcs = vcs_builder({
|
95
|
+
OPEN_MERGE_REQUESTS: [
|
96
|
+
{
|
97
|
+
LABELS: [SAPM_LABEL],
|
98
|
+
DESCRIPTION: f"""
|
99
|
+
Blabla
|
100
|
+
{PROMOTION_DATA_SEPARATOR}
|
101
|
+
{VERSION_REF}: {SAPM_VERSION}
|
102
|
+
{CHANNELS_REF}: some-channel
|
103
|
+
{CONTENT_HASHES}: some_hash
|
104
|
+
{IS_BATCHABLE}: False
|
105
|
+
""",
|
106
|
+
},
|
107
|
+
{
|
108
|
+
LABELS: [SAPM_LABEL],
|
109
|
+
DESCRIPTION: f"""
|
110
|
+
Blabla
|
111
|
+
{PROMOTION_DATA_SEPARATOR}
|
112
|
+
{VERSION_REF}: {SAPM_VERSION}
|
113
|
+
{CHANNELS_REF}: other-channel
|
114
|
+
{CONTENT_HASHES}: other_hash
|
115
|
+
{IS_BATCHABLE}: True
|
116
|
+
""",
|
117
|
+
},
|
118
|
+
]
|
119
|
+
})
|
120
|
+
merge_request_manager = MergeRequestManager(
|
121
|
+
vcs=vcs,
|
122
|
+
renderer=renderer,
|
123
|
+
)
|
124
|
+
merge_request_manager.housekeeping()
|
125
|
+
vcs.close_app_interface_mr.assert_not_called() # type: ignore[attr-defined]
|
126
|
+
assert len(merge_request_manager._open_mrs) == 2
|
127
|
+
|
128
|
+
|
129
|
+
def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer) -> None:
|
75
130
|
vcs = vcs_builder({
|
76
131
|
OPEN_MERGE_REQUESTS: [
|
77
132
|
{
|
@@ -82,6 +137,7 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
82
137
|
missing-version: some_version
|
83
138
|
{CHANNELS_REF}: some-channel
|
84
139
|
{CONTENT_HASHES}: hash_1
|
140
|
+
{IS_BATCHABLE}: True
|
85
141
|
""",
|
86
142
|
},
|
87
143
|
{
|
@@ -91,6 +147,7 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
91
147
|
{PROMOTION_DATA_SEPARATOR}
|
92
148
|
{VERSION_REF}: {SAPM_VERSION}
|
93
149
|
{CHANNELS_REF}: some-channel
|
150
|
+
{IS_BATCHABLE}: True
|
94
151
|
missing-content-hash-key: some_hash
|
95
152
|
""",
|
96
153
|
},
|
@@ -101,6 +158,7 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
101
158
|
missing-data-separator
|
102
159
|
{VERSION_REF}: {SAPM_VERSION}
|
103
160
|
{CONTENT_HASHES}: hash_3
|
161
|
+
{IS_BATCHABLE}: True
|
104
162
|
""",
|
105
163
|
},
|
106
164
|
{
|
@@ -111,6 +169,7 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
111
169
|
{PROMOTION_DATA_SEPARATOR}
|
112
170
|
{CHANNELS_REF}: some-channel
|
113
171
|
{CONTENT_HASHES}: hash_4
|
172
|
+
{IS_BATCHABLE}: True
|
114
173
|
""",
|
115
174
|
},
|
116
175
|
{
|
@@ -123,6 +182,7 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
123
182
|
{VERSION_REF}: {SAPM_VERSION}
|
124
183
|
{CHANNELS_REF}: some-channel
|
125
184
|
{CONTENT_HASHES}: hash_5
|
185
|
+
{IS_BATCHABLE}: True
|
126
186
|
""",
|
127
187
|
},
|
128
188
|
{
|
@@ -133,6 +193,7 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
133
193
|
{VERSION_REF}: outdated-version
|
134
194
|
{CHANNELS_REF}: some-channel
|
135
195
|
{CONTENT_HASHES}: hash_6
|
196
|
+
{IS_BATCHABLE}: True
|
136
197
|
""",
|
137
198
|
},
|
138
199
|
{
|
@@ -143,6 +204,29 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
143
204
|
{VERSION_REF}: {SAPM_VERSION}
|
144
205
|
bad_channel_ref: some-channel
|
145
206
|
{CONTENT_HASHES}: hash_7
|
207
|
+
{IS_BATCHABLE}: True
|
208
|
+
""",
|
209
|
+
},
|
210
|
+
{
|
211
|
+
LABELS: [SAPM_LABEL],
|
212
|
+
DESCRIPTION: f"""
|
213
|
+
Blabla
|
214
|
+
{PROMOTION_DATA_SEPARATOR}
|
215
|
+
{VERSION_REF}: {SAPM_VERSION}
|
216
|
+
{CHANNELS_REF}: some-channel
|
217
|
+
{CONTENT_HASHES}: hash_8
|
218
|
+
missing-batchable-key
|
219
|
+
""",
|
220
|
+
},
|
221
|
+
{
|
222
|
+
LABELS: [SAPM_LABEL],
|
223
|
+
DESCRIPTION: f"""
|
224
|
+
Blabla
|
225
|
+
{PROMOTION_DATA_SEPARATOR}
|
226
|
+
{VERSION_REF}: {SAPM_VERSION}
|
227
|
+
{CHANNELS_REF}: some-channel
|
228
|
+
{CONTENT_HASHES}: hash_9
|
229
|
+
{IS_BATCHABLE}: Something-non-bool
|
146
230
|
""",
|
147
231
|
},
|
148
232
|
]
|
@@ -151,13 +235,14 @@ def test_bad_mrs(vcs_builder: Callable[[Mapping], VCS], renderer: Renderer):
|
|
151
235
|
vcs=vcs,
|
152
236
|
renderer=renderer,
|
153
237
|
)
|
154
|
-
merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
155
238
|
merge_request_manager.housekeeping()
|
156
239
|
vcs.close_app_interface_mr.assert_called() # type: ignore[attr-defined]
|
157
240
|
assert len(merge_request_manager._open_mrs) == 0
|
158
241
|
|
159
242
|
|
160
|
-
def test_remove_duplicates(
|
243
|
+
def test_remove_duplicates(
|
244
|
+
vcs_builder: Callable[[Mapping], VCS], renderer: Renderer
|
245
|
+
) -> None:
|
161
246
|
vcs = vcs_builder({
|
162
247
|
OPEN_MERGE_REQUESTS: [
|
163
248
|
{
|
@@ -168,6 +253,7 @@ def test_remove_duplicates(vcs_builder: Callable[[Mapping], VCS], renderer: Rend
|
|
168
253
|
{VERSION_REF}: {SAPM_VERSION}
|
169
254
|
{CHANNELS_REF}: some_channel
|
170
255
|
{CONTENT_HASHES}: same_hash
|
256
|
+
{IS_BATCHABLE}: True
|
171
257
|
""",
|
172
258
|
},
|
173
259
|
{
|
@@ -178,6 +264,7 @@ def test_remove_duplicates(vcs_builder: Callable[[Mapping], VCS], renderer: Rend
|
|
178
264
|
{VERSION_REF}: {SAPM_VERSION}
|
179
265
|
{CHANNELS_REF}: some_channel
|
180
266
|
{CONTENT_HASHES}: same_hash
|
267
|
+
{IS_BATCHABLE}: True
|
181
268
|
""",
|
182
269
|
},
|
183
270
|
]
|
@@ -186,7 +273,6 @@ def test_remove_duplicates(vcs_builder: Callable[[Mapping], VCS], renderer: Rend
|
|
186
273
|
vcs=vcs,
|
187
274
|
renderer=renderer,
|
188
275
|
)
|
189
|
-
merge_request_manager.fetch_sapm_managed_open_merge_requests()
|
190
276
|
merge_request_manager.housekeeping()
|
191
277
|
vcs.close_app_interface_mr.assert_called_once() # type: ignore[attr-defined]
|
192
278
|
assert len(merge_request_manager._open_mrs) == 1
|
reconcile/utils/vcs.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import logging
|
2
4
|
import re
|
3
5
|
from collections.abc import Iterable
|
6
|
+
from enum import Enum
|
4
7
|
from typing import Optional
|
5
8
|
|
6
9
|
from gitlab.v4.objects import ProjectMergeRequest
|
@@ -19,6 +22,13 @@ from reconcile.utils.secret_reader import (
|
|
19
22
|
)
|
20
23
|
|
21
24
|
|
25
|
+
class MRCheckStatus(Enum):
|
26
|
+
NONE = 0
|
27
|
+
SUCCESS = 1
|
28
|
+
FAILED = 2
|
29
|
+
RUNNING = 3
|
30
|
+
|
31
|
+
|
22
32
|
class VCS:
|
23
33
|
"""
|
24
34
|
Abstraction layer for aggregating different Version Control Systems.
|
@@ -38,17 +48,32 @@ class VCS:
|
|
38
48
|
dry_run: bool,
|
39
49
|
allow_deleting_mrs: bool,
|
40
50
|
allow_opening_mrs: bool,
|
51
|
+
gitlab_instance: Optional[GitLabApi] = None,
|
52
|
+
default_gh_token: Optional[str] = None,
|
53
|
+
app_interface_api: Optional[GitLabApi] = None,
|
41
54
|
):
|
42
55
|
self._dry_run = dry_run
|
43
56
|
self._allow_deleting_mrs = allow_deleting_mrs
|
44
57
|
self._allow_opening_mrs = allow_opening_mrs
|
45
58
|
self._secret_reader = secret_reader
|
46
59
|
self._gh_per_repo_url: dict[str, GithubRepositoryApi] = {}
|
47
|
-
self._default_gh_token =
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
self._default_gh_token = (
|
61
|
+
default_gh_token
|
62
|
+
if default_gh_token
|
63
|
+
else self._get_default_gh_token(github_orgs=github_orgs)
|
64
|
+
)
|
65
|
+
self._gitlab_instance = (
|
66
|
+
gitlab_instance
|
67
|
+
if gitlab_instance
|
68
|
+
else self._gitlab_api(gitlab_instances=gitlab_instances)
|
69
|
+
)
|
70
|
+
self._app_interface_api = (
|
71
|
+
app_interface_api
|
72
|
+
if app_interface_api
|
73
|
+
else self._init_app_interface_api(
|
74
|
+
gitlab_instances=gitlab_instances,
|
75
|
+
app_interface_repo_url=app_interface_repo_url,
|
76
|
+
)
|
52
77
|
)
|
53
78
|
self._is_commit_sha_regex = re.compile(r"^[0-9a-f]{40}$")
|
54
79
|
|
@@ -104,6 +129,23 @@ class VCS:
|
|
104
129
|
project_url=app_interface_repo_url,
|
105
130
|
)
|
106
131
|
|
132
|
+
def get_gitlab_mr_check_status(self, mr: ProjectMergeRequest) -> MRCheckStatus:
|
133
|
+
pipelines = self._gitlab_instance.get_merge_request_pipelines(mr)
|
134
|
+
if not pipelines:
|
135
|
+
return MRCheckStatus.NONE
|
136
|
+
# available status codes https://docs.gitlab.com/ee/api/pipelines.html
|
137
|
+
last_pipeline_result = pipelines[0]["status"]
|
138
|
+
match last_pipeline_result:
|
139
|
+
case "success":
|
140
|
+
return MRCheckStatus.SUCCESS
|
141
|
+
case "running":
|
142
|
+
return MRCheckStatus.RUNNING
|
143
|
+
case "failed":
|
144
|
+
return MRCheckStatus.FAILED
|
145
|
+
case _:
|
146
|
+
# Lets assume all other states as non-present
|
147
|
+
return MRCheckStatus.NONE
|
148
|
+
|
107
149
|
def get_commit_sha(
|
108
150
|
self, repo_url: str, ref: str, auth_code: Optional[HasSecret]
|
109
151
|
) -> str:
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc495.dist-info → qontract_reconcile-0.10.1rc497.dist-info}/top_level.txt
RENAMED
File without changes
|