qontract-reconcile 0.10.1rc528__py3-none-any.whl → 0.10.1rc530__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.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/RECORD +8 -8
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +12 -0
- reconcile/utils/gitlab_api.py +44 -16
- reconcile/utils/terrascript_aws_client.py +24 -0
- {qontract_reconcile-0.10.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc528.dist-info → qontract_reconcile-0.10.1rc530.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.1rc530
|
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.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/RECORD
RENAMED
@@ -303,7 +303,7 @@ reconcile/gql_definitions/terraform_repo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
303
303
|
reconcile/gql_definitions/terraform_repo/terraform_repo.py,sha256=pefTRhb0ZcWm_j6dYz6m6qNqZFteqgrQ25SgUqRAVaI,3173
|
304
304
|
reconcile/gql_definitions/terraform_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
305
305
|
reconcile/gql_definitions/terraform_resources/database_access_manager.py,sha256=yv0_YC-LmhaKD_gyGG3le1w5BtypBjlsO894-Zgdg4U,4813
|
306
|
-
reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py,sha256=
|
306
|
+
reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py,sha256=GMV0sch1Mdznjd3Vwgj_gMS1fb56d5xa2dFtOM57yUA,41126
|
307
307
|
reconcile/gql_definitions/terraform_tgw_attachments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
308
308
|
reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py,sha256=GjCuLHOgm3eHbkpK7Q2i7l6tori5Y62uFlz3M89BYtA,2602
|
309
309
|
reconcile/gql_definitions/vault_instances/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -535,7 +535,7 @@ reconcile/utils/filtering.py,sha256=zZnHH0u0SaTDyzuFXZ_mREURGLvjEqQIQy4z-7QBVlc,
|
|
535
535
|
reconcile/utils/git.py,sha256=Qad7mfPuS9s7eKODeWSewehwSGgJPCbQuLda1qg_6GA,1522
|
536
536
|
reconcile/utils/git_secrets.py,sha256=0wGNL5mvDtVPRuu3vEQgld1Am64gIDJHtmu1_ZKxMAI,1973
|
537
537
|
reconcile/utils/github_api.py,sha256=_bttNxYKeam_tLVe27L7O4gKqSn6CeyuFnJn8tSaUVY,2488
|
538
|
-
reconcile/utils/gitlab_api.py,sha256=
|
538
|
+
reconcile/utils/gitlab_api.py,sha256=7gImzen-LzEcdyTbRNcvuLOczzm03sEI78NmKBSMAl4,27228
|
539
539
|
reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
|
540
540
|
reconcile/utils/gql.py,sha256=bzIYYYYGIO_4Db4sA-mnZUOPVNBELZD5EcIUSTbYG1o,13604
|
541
541
|
reconcile/utils/grouping.py,sha256=kWKivD14eAkiDneH_VIl_XyUdcVVQgiaKA9sLsuD2dw,441
|
@@ -581,7 +581,7 @@ reconcile/utils/state.py,sha256=SAa6QLHu9lr0yqLCBy2AypNx1IPCJWlrRBrvlzAKsOU,1450
|
|
581
581
|
reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
|
582
582
|
reconcile/utils/template.py,sha256=wTvRU4AnAV_o042tD4Mwls2dwWMuk7MKnde3MaCjaYg,331
|
583
583
|
reconcile/utils/terraform_client.py,sha256=V7AMQOEU4tvUOT-LQN2cXLqcphD5L93PMGMfurQQyPY,31753
|
584
|
-
reconcile/utils/terrascript_aws_client.py,sha256=
|
584
|
+
reconcile/utils/terrascript_aws_client.py,sha256=wA6AcAqbS2tIuqh8soMfpdpuf0IbAJWjONMmffJ-0vA,265467
|
585
585
|
reconcile/utils/three_way_diff_strategy.py,sha256=nyqeQsLCoPI6e16k2CF3b9KNgQLU-rPf5RtfdUfVMwE,4468
|
586
586
|
reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
|
587
587
|
reconcile/utils/unleash.py,sha256=1D56CsZfE3ShDtN3IErE1T2eeIwNmxhK-yYbCotJ99E,3601
|
@@ -668,8 +668,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
668
668
|
tools/test/test_qontract_cli.py,sha256=d18KrdhtUGqoC7_kWZU128U0-VJEj-0rjFkLVufcI6I,2755
|
669
669
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
670
670
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
671
|
-
qontract_reconcile-0.10.
|
672
|
-
qontract_reconcile-0.10.
|
673
|
-
qontract_reconcile-0.10.
|
674
|
-
qontract_reconcile-0.10.
|
675
|
-
qontract_reconcile-0.10.
|
671
|
+
qontract_reconcile-0.10.1rc530.dist-info/METADATA,sha256=vBKtmSp2hYuK2N6ks8hGTMvpT7L7s6ZFD3yMu1NpS8w,2349
|
672
|
+
qontract_reconcile-0.10.1rc530.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
673
|
+
qontract_reconcile-0.10.1rc530.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
|
674
|
+
qontract_reconcile-0.10.1rc530.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
675
|
+
qontract_reconcile-0.10.1rc530.dist-info/RECORD,,
|
@@ -168,6 +168,11 @@ query TerraformResourcesNamespaces {
|
|
168
168
|
role_policy
|
169
169
|
output_resource_name
|
170
170
|
annotations
|
171
|
+
lifecycle {
|
172
|
+
create_before_destroy
|
173
|
+
prevent_destroy
|
174
|
+
ignore_changes
|
175
|
+
}
|
171
176
|
}
|
172
177
|
... on NamespaceTerraformResourceSQS_v1 {
|
173
178
|
region
|
@@ -614,6 +619,12 @@ class AssumeRoleV1(ConfiguredBaseModel):
|
|
614
619
|
federated: Optional[str] = Field(..., alias="Federated")
|
615
620
|
|
616
621
|
|
622
|
+
class NamespaceTerraformResourceLifecycleV1(ConfiguredBaseModel):
|
623
|
+
create_before_destroy: Optional[bool] = Field(..., alias="create_before_destroy")
|
624
|
+
prevent_destroy: Optional[bool] = Field(..., alias="prevent_destroy")
|
625
|
+
ignore_changes: Optional[list[str]] = Field(..., alias="ignore_changes")
|
626
|
+
|
627
|
+
|
617
628
|
class NamespaceTerraformResourceRoleV1(NamespaceTerraformResourceAWSV1):
|
618
629
|
identifier: str = Field(..., alias="identifier")
|
619
630
|
assume_role: AssumeRoleV1 = Field(..., alias="assume_role")
|
@@ -623,6 +634,7 @@ class NamespaceTerraformResourceRoleV1(NamespaceTerraformResourceAWSV1):
|
|
623
634
|
role_policy: Optional[str] = Field(..., alias="role_policy")
|
624
635
|
output_resource_name: Optional[str] = Field(..., alias="output_resource_name")
|
625
636
|
annotations: Optional[str] = Field(..., alias="annotations")
|
637
|
+
lifecycle: Optional[NamespaceTerraformResourceLifecycleV1] = Field(..., alias="lifecycle")
|
626
638
|
|
627
639
|
|
628
640
|
class KeyValueV1(ConfiguredBaseModel):
|
reconcile/utils/gitlab_api.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
+
import re
|
3
4
|
from collections.abc import (
|
4
5
|
Iterable,
|
5
6
|
Set,
|
@@ -12,6 +13,7 @@ from operator import (
|
|
12
13
|
from typing import (
|
13
14
|
Any,
|
14
15
|
Optional,
|
16
|
+
TypedDict,
|
15
17
|
)
|
16
18
|
from urllib.parse import urlparse
|
17
19
|
|
@@ -19,6 +21,8 @@ import gitlab
|
|
19
21
|
import urllib3
|
20
22
|
from gitlab.v4.objects import (
|
21
23
|
CurrentUser,
|
24
|
+
Group,
|
25
|
+
Project,
|
22
26
|
ProjectIssue,
|
23
27
|
ProjectMergeRequest,
|
24
28
|
ProjectMergeRequestNote,
|
@@ -66,12 +70,19 @@ class MRStatus:
|
|
66
70
|
CANNOT_BE_MERGED_RECHECK = "cannot_be_merged_recheck"
|
67
71
|
|
68
72
|
|
73
|
+
GROUP_BOT_NAME_REGEX = re.compile(r"group_.+_bot_.+")
|
74
|
+
|
75
|
+
|
76
|
+
class GLGroupMember(TypedDict):
|
77
|
+
user: str
|
78
|
+
access_level: str
|
79
|
+
|
80
|
+
|
69
81
|
class GitLabApi: # pylint: disable=too-many-public-methods
|
70
82
|
def __init__(
|
71
83
|
self,
|
72
84
|
instance,
|
73
85
|
project_id=None,
|
74
|
-
ssl_verify=True,
|
75
86
|
settings=None,
|
76
87
|
secret_reader=None,
|
77
88
|
project_url=None,
|
@@ -205,14 +216,14 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
205
216
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
206
217
|
return self.project.mergerequests.create(data)
|
207
218
|
|
208
|
-
def mr_exists(self, title):
|
219
|
+
def mr_exists(self, title: str) -> bool:
|
209
220
|
mrs = self.get_merge_requests(state=MRState.OPENED)
|
210
221
|
# since we are using a naming convention for these MRs
|
211
222
|
# we can determine if a pending MR exists based on the title
|
212
223
|
return any(mr.title == title for mr in mrs)
|
213
224
|
|
214
225
|
@retry()
|
215
|
-
def get_project_maintainers(self, repo_url=None):
|
226
|
+
def get_project_maintainers(self, repo_url: str | None = None) -> list[str] | None:
|
216
227
|
if repo_url is None:
|
217
228
|
project = self.project
|
218
229
|
else:
|
@@ -227,25 +238,42 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
227
238
|
app_sre_group = self.gl.groups.get("app-sre")
|
228
239
|
return self.get_items(app_sre_group.members.list)
|
229
240
|
|
230
|
-
def get_group_if_exists(self, group_name):
|
241
|
+
def get_group_if_exists(self, group_name: str) -> Group | None:
|
231
242
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
232
243
|
try:
|
233
244
|
return self.gl.groups.get(group_name)
|
234
245
|
except gitlab.exceptions.GitlabGetError:
|
235
246
|
return None
|
236
247
|
|
237
|
-
|
248
|
+
@staticmethod
|
249
|
+
def _is_bot_username(username: str) -> bool:
|
250
|
+
"""crudely checking for the username
|
251
|
+
|
252
|
+
as gitlab-python require a major upgrade to use the billable members apis
|
253
|
+
https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#id11 lists the api
|
254
|
+
billable_membersis the attribute that provides billable members of groups
|
255
|
+
|
256
|
+
the second api is https://python-gitlab.readthedocs.io/en/stable/gl_objects/group_access_tokens.html
|
257
|
+
which provides a list of access tokens as well as their assigned users
|
258
|
+
|
259
|
+
those apis are not avaliable in python-gitlab v1.x
|
260
|
+
"""
|
261
|
+
return GROUP_BOT_NAME_REGEX.match(username) is not None
|
262
|
+
|
263
|
+
def get_group_members(self, group_name: str) -> list[GLGroupMember]:
|
238
264
|
group = self.get_group_if_exists(group_name)
|
239
|
-
if
|
265
|
+
if group is None:
|
240
266
|
logging.error(group_name + " group not found")
|
241
267
|
return []
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
268
|
+
else:
|
269
|
+
return [
|
270
|
+
{
|
271
|
+
"user": m.username,
|
272
|
+
"access_level": self.get_access_level_string(m.access_level),
|
273
|
+
}
|
274
|
+
for m in self.get_items(group.members.list)
|
275
|
+
if not self._is_bot_username(m.username)
|
276
|
+
]
|
249
277
|
|
250
278
|
def add_project_member(self, repo_url, user, access="maintainer"):
|
251
279
|
project = self.get_project(repo_url)
|
@@ -323,7 +351,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
323
351
|
if access == "guest":
|
324
352
|
return gitlab.GUEST_ACCESS
|
325
353
|
|
326
|
-
def get_group_id_and_projects(self, group_name):
|
354
|
+
def get_group_id_and_projects(self, group_name: str) -> tuple[str, list[str]]:
|
327
355
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
328
356
|
group = self.gl.groups.get(group_name)
|
329
357
|
return group.id, [p.name for p in self.get_items(group.projects.list)]
|
@@ -336,7 +364,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
336
364
|
return f"{self.server}/{group}/{project}"
|
337
365
|
|
338
366
|
@retry()
|
339
|
-
def get_project(self, repo_url):
|
367
|
+
def get_project(self, repo_url: str) -> Project | None:
|
340
368
|
repo = repo_url.replace(self.server + "/", "")
|
341
369
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
342
370
|
try:
|
@@ -748,5 +776,5 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
748
776
|
self, repo_url: str, ref_from: str, ref_to: str
|
749
777
|
) -> list[dict[str, Any]]:
|
750
778
|
project = self.get_project(repo_url)
|
751
|
-
response = project.repository_compare(ref_from, ref_to)
|
779
|
+
response: Any = project.repository_compare(ref_from, ref_to)
|
752
780
|
return response.get("commits", [])
|
@@ -143,6 +143,9 @@ import reconcile.openshift_resources_base as orb
|
|
143
143
|
import reconcile.utils.aws_helper as awsh
|
144
144
|
from reconcile import queries
|
145
145
|
from reconcile.github_org import get_default_config
|
146
|
+
from reconcile.gql_definitions.terraform_resources.terraform_resources_namespaces import (
|
147
|
+
NamespaceTerraformResourceLifecycleV1,
|
148
|
+
)
|
146
149
|
from reconcile.utils import gql
|
147
150
|
from reconcile.utils.aws_api import (
|
148
151
|
AmiTag,
|
@@ -238,6 +241,7 @@ VARIABLE_KEYS = [
|
|
238
241
|
"subscriptions",
|
239
242
|
"records",
|
240
243
|
"extra_tags",
|
244
|
+
"lifecycle",
|
241
245
|
]
|
242
246
|
|
243
247
|
EMAIL_REGEX = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
@@ -870,6 +874,23 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
|
|
870
874
|
role_name = awsh.get_id_from_arn(account["assume_role"])
|
871
875
|
return f"account-{uid}-{role_name}"
|
872
876
|
|
877
|
+
@staticmethod
|
878
|
+
def get_resource_lifecycle(
|
879
|
+
common_values: dict[str, Any],
|
880
|
+
) -> Optional[dict[str, Any]]:
|
881
|
+
if lifecycle := common_values.get("lifecycle"):
|
882
|
+
lifecycle = NamespaceTerraformResourceLifecycleV1(**lifecycle)
|
883
|
+
if lifecycle.create_before_destroy is None:
|
884
|
+
lifecycle.create_before_destroy = False
|
885
|
+
if lifecycle.prevent_destroy is None:
|
886
|
+
lifecycle.prevent_destroy = False
|
887
|
+
if lifecycle.ignore_changes is None:
|
888
|
+
lifecycle.ignore_changes = []
|
889
|
+
if "all" in lifecycle.ignore_changes:
|
890
|
+
lifecycle.ignore_changes = "all"
|
891
|
+
return lifecycle.dict(by_alias=True)
|
892
|
+
return None
|
893
|
+
|
873
894
|
def populate_additional_providers(self, infra_account_name: str, accounts):
|
874
895
|
for account in accounts:
|
875
896
|
account_name = account["name"]
|
@@ -2459,6 +2480,9 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
|
|
2459
2480
|
if inline_policy:
|
2460
2481
|
values["inline_policy"] = {"name": identifier, "policy": inline_policy}
|
2461
2482
|
|
2483
|
+
if lifecycle := self.get_resource_lifecycle(common_values):
|
2484
|
+
values["lifecycle"] = lifecycle
|
2485
|
+
|
2462
2486
|
role_tf_resource = aws_iam_role(identifier, **values)
|
2463
2487
|
tf_resources.append(role_tf_resource)
|
2464
2488
|
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc528.dist-info → qontract_reconcile-0.10.1rc530.dist-info}/top_level.txt
RENAMED
File without changes
|