qontract-reconcile 0.10.2.dev135__py3-none-any.whl → 0.10.2.dev136__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.2.dev135.dist-info → qontract_reconcile-0.10.2.dev136.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev135.dist-info → qontract_reconcile-0.10.2.dev136.dist-info}/RECORD +35 -29
- reconcile/aws_ami_share.py +4 -2
- reconcile/aws_ecr_image_pull_secrets.py +15 -7
- reconcile/aws_garbage_collector.py +1 -1
- reconcile/aws_iam_keys.py +24 -17
- reconcile/aws_iam_password_reset.py +4 -2
- reconcile/aws_support_cases_sos.py +19 -6
- reconcile/closedbox_endpoint_monitoring_base.py +4 -3
- reconcile/cna/client.py +3 -3
- reconcile/cna/integration.py +4 -5
- reconcile/cna/state.py +3 -3
- reconcile/email_sender.py +65 -56
- reconcile/gcr_mirror.py +11 -9
- reconcile/github_org.py +57 -52
- reconcile/github_owners.py +8 -5
- reconcile/github_repo_invites.py +1 -1
- reconcile/github_repo_permissions_validator.py +3 -3
- reconcile/github_users.py +16 -12
- reconcile/github_validator.py +1 -1
- reconcile/gitlab_fork_compliance.py +9 -8
- reconcile/gitlab_labeler.py +1 -1
- reconcile/gitlab_mr_sqs_consumer.py +6 -3
- reconcile/gitlab_owners.py +29 -12
- reconcile/gql_definitions/email_sender/__init__.py +0 -0
- reconcile/gql_definitions/email_sender/apps.py +64 -0
- reconcile/gql_definitions/email_sender/emails.py +133 -0
- reconcile/gql_definitions/email_sender/users.py +62 -0
- reconcile/gql_definitions/fragments/email_service.py +32 -0
- reconcile/gql_definitions/fragments/email_user.py +28 -0
- reconcile/queries.py +0 -44
- reconcile/utils/jinja2/utils.py +4 -2
- reconcile/utils/smtp_client.py +1 -1
- {qontract_reconcile-0.10.2.dev135.dist-info → qontract_reconcile-0.10.2.dev136.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev135.dist-info → qontract_reconcile-0.10.2.dev136.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
|
+
from typing import Any, Self
|
3
4
|
|
4
5
|
from gitlab import GitlabGetError
|
5
6
|
from gitlab.const import MAINTAINER_ACCESS
|
@@ -36,7 +37,7 @@ class GitlabForkCompliance:
|
|
36
37
|
ERR_NOT_A_MEMBER = 0x0002
|
37
38
|
ERR_NOT_A_MAINTAINER = 0x0004
|
38
39
|
|
39
|
-
def __init__(self, project_id, mr_id, maintainers_group):
|
40
|
+
def __init__(self, project_id: str, mr_id: str, maintainers_group: str) -> None:
|
40
41
|
self.exit_code = self.OK
|
41
42
|
|
42
43
|
self.maintainers_group = maintainers_group
|
@@ -50,15 +51,15 @@ class GitlabForkCompliance:
|
|
50
51
|
)
|
51
52
|
self.mr = self.gl_cli.get_merge_request(mr_id)
|
52
53
|
|
53
|
-
def __enter__(self):
|
54
|
+
def __enter__(self) -> Self:
|
54
55
|
return self
|
55
56
|
|
56
|
-
def __exit__(self, *exc):
|
57
|
+
def __exit__(self, *exc: Any) -> None:
|
57
58
|
self.gl_cli.cleanup()
|
58
59
|
if hasattr(self, "src") and self.src is not None:
|
59
60
|
self.src.cleanup()
|
60
61
|
|
61
|
-
def run(self):
|
62
|
+
def run(self) -> None:
|
62
63
|
self.exit_code |= self.check_branch()
|
63
64
|
self.exit_code |= self.check_bot_access()
|
64
65
|
if self.exit_code:
|
@@ -86,7 +87,7 @@ class GitlabForkCompliance:
|
|
86
87
|
|
87
88
|
sys.exit(self.exit_code)
|
88
89
|
|
89
|
-
def check_branch(self):
|
90
|
+
def check_branch(self) -> int:
|
90
91
|
# The Merge Request can use the 'master' source branch
|
91
92
|
if self.mr.source_branch == "master":
|
92
93
|
self.handle_error("source branch can not be master", MSG_BRANCH)
|
@@ -94,7 +95,7 @@ class GitlabForkCompliance:
|
|
94
95
|
|
95
96
|
return self.OK
|
96
97
|
|
97
|
-
def check_bot_access(self):
|
98
|
+
def check_bot_access(self) -> int:
|
98
99
|
try:
|
99
100
|
self.src = GitLabApi(
|
100
101
|
self.instance,
|
@@ -113,7 +114,7 @@ class GitlabForkCompliance:
|
|
113
114
|
|
114
115
|
return self.OK
|
115
116
|
|
116
|
-
def handle_error(self, log_msg, mr_msg):
|
117
|
+
def handle_error(self, log_msg: str, mr_msg: str) -> None:
|
117
118
|
LOG.error([log_msg.format(bot=self.gl_cli.user.username)])
|
118
119
|
self.gl_cli.add_label_to_merge_request(self.mr, BLOCKED_BOT_ACCESS)
|
119
120
|
comment = mr_msg.format(
|
@@ -124,6 +125,6 @@ class GitlabForkCompliance:
|
|
124
125
|
self.mr.notes.create({"body": comment})
|
125
126
|
|
126
127
|
|
127
|
-
def run(dry_run, project_id, mr_id, maintainers_group):
|
128
|
+
def run(dry_run: bool, project_id: str, mr_id: str, maintainers_group: str) -> None:
|
128
129
|
with GitlabForkCompliance(project_id, mr_id, maintainers_group) as gfc:
|
129
130
|
gfc.run()
|
reconcile/gitlab_labeler.py
CHANGED
@@ -122,7 +122,7 @@ def guess_labels(
|
|
122
122
|
return guesses
|
123
123
|
|
124
124
|
|
125
|
-
def run(dry_run, gitlab_project_id
|
125
|
+
def run(dry_run: bool, gitlab_project_id: str, gitlab_merge_request_id: str) -> None:
|
126
126
|
instance = queries.get_gitlab_instance()
|
127
127
|
settings = queries.get_app_interface_settings()
|
128
128
|
with GitLabApi(instance, project_id=gitlab_project_id, settings=settings) as gl:
|
@@ -5,6 +5,7 @@ SQS Consumer to create Gitlab merge requests.
|
|
5
5
|
import json
|
6
6
|
import logging
|
7
7
|
import sys
|
8
|
+
from collections.abc import Callable
|
8
9
|
|
9
10
|
from reconcile import queries
|
10
11
|
from reconcile.utils import mr
|
@@ -17,18 +18,20 @@ QONTRACT_INTEGRATION = "gitlab-mr-sqs-consumer"
|
|
17
18
|
|
18
19
|
|
19
20
|
@defer
|
20
|
-
def run(dry_run, gitlab_project_id, defer=None):
|
21
|
+
def run(dry_run: str, gitlab_project_id: str, defer: Callable | None = None) -> None:
|
21
22
|
secret_reader = SecretReader(queries.get_secret_reader_settings())
|
22
23
|
|
23
24
|
accounts = queries.get_queue_aws_accounts()
|
24
25
|
sqs_cli = SQSGateway(accounts, secret_reader)
|
25
|
-
defer
|
26
|
+
if defer:
|
27
|
+
defer(sqs_cli.cleanup)
|
26
28
|
|
27
29
|
instance = queries.get_gitlab_instance()
|
28
30
|
gitlab_cli = GitLabApi(
|
29
31
|
instance, project_id=gitlab_project_id, secret_reader=secret_reader
|
30
32
|
)
|
31
|
-
defer
|
33
|
+
if defer:
|
34
|
+
defer(gitlab_cli.cleanup)
|
32
35
|
|
33
36
|
errors_occured = False
|
34
37
|
while True:
|
reconcile/gitlab_owners.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
import logging
|
2
|
+
from collections.abc import Callable, Mapping
|
3
|
+
from typing import Any
|
2
4
|
|
3
5
|
from dateutil import parser as dateparser
|
6
|
+
from gitlab.v4.objects import ProjectMergeRequest
|
4
7
|
from sretoolbox.utils import threaded
|
5
8
|
|
6
9
|
from reconcile import queries
|
@@ -31,7 +34,14 @@ class MRApproval:
|
|
31
34
|
between the approval messages the the project owners.
|
32
35
|
"""
|
33
36
|
|
34
|
-
def __init__(
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
gitlab_client: GitLabApi,
|
40
|
+
merge_request: ProjectMergeRequest,
|
41
|
+
owners: RepoOwners,
|
42
|
+
dry_run: bool,
|
43
|
+
persistent_lgtm: bool,
|
44
|
+
) -> None:
|
35
45
|
self.gitlab = gitlab_client
|
36
46
|
self.mr = merge_request
|
37
47
|
self.owners = owners
|
@@ -45,7 +55,7 @@ class MRApproval:
|
|
45
55
|
top_commit = next(commits)
|
46
56
|
self.top_commit_created_at = dateparser.parse(top_commit.created_at)
|
47
57
|
|
48
|
-
def get_change_owners_map(self):
|
58
|
+
def get_change_owners_map(self) -> dict[str, dict[str, list[str]]]:
|
49
59
|
"""
|
50
60
|
Maps each change path to the list of owners that can approve
|
51
61
|
that change.
|
@@ -71,7 +81,7 @@ class MRApproval:
|
|
71
81
|
}
|
72
82
|
return change_owners_map
|
73
83
|
|
74
|
-
def get_lgtms(self):
|
84
|
+
def get_lgtms(self) -> list[str]:
|
75
85
|
"""
|
76
86
|
Collects the usernames of all the '/lgtm' comments.
|
77
87
|
"""
|
@@ -101,8 +111,8 @@ class MRApproval:
|
|
101
111
|
members.update(m.username for m in self.gitlab.get_group_members(group))
|
102
112
|
return members.union(owners)
|
103
113
|
|
104
|
-
def get_approval_status(self):
|
105
|
-
approval_status = {"approved": False, "report": None}
|
114
|
+
def get_approval_status(self) -> dict[str, Any]:
|
115
|
+
approval_status: dict[str, Any] = {"approved": False, "report": None}
|
106
116
|
|
107
117
|
try:
|
108
118
|
change_owners_map = self.get_change_owners_map()
|
@@ -114,7 +124,7 @@ class MRApproval:
|
|
114
124
|
if not change_owners_map:
|
115
125
|
return approval_status
|
116
126
|
|
117
|
-
report = {}
|
127
|
+
report: dict[str, Any] = {}
|
118
128
|
lgtms = self.get_lgtms()
|
119
129
|
|
120
130
|
approval_status["approved"] = True
|
@@ -200,18 +210,18 @@ class MRApproval:
|
|
200
210
|
approval_status["report"] = formatted_report
|
201
211
|
return approval_status
|
202
212
|
|
203
|
-
def has_approval_label(self):
|
213
|
+
def has_approval_label(self) -> bool:
|
204
214
|
return APPROVED in self.mr.labels
|
205
215
|
|
206
216
|
@staticmethod
|
207
|
-
def format_report(report):
|
217
|
+
def format_report(report: Mapping[str, Any]) -> str:
|
208
218
|
"""
|
209
219
|
Gets a report dictionary and creates the corresponding Markdown
|
210
220
|
comment message.
|
211
221
|
"""
|
212
222
|
markdown_report = ""
|
213
223
|
|
214
|
-
closest_approvers = []
|
224
|
+
closest_approvers: list[list[str]] = []
|
215
225
|
for owners in report.values():
|
216
226
|
new_group = []
|
217
227
|
|
@@ -323,9 +333,16 @@ class MRApproval:
|
|
323
333
|
|
324
334
|
|
325
335
|
@defer
|
326
|
-
def act(
|
336
|
+
def act(
|
337
|
+
repo: Mapping,
|
338
|
+
dry_run: bool,
|
339
|
+
instance: Mapping,
|
340
|
+
settings: Mapping,
|
341
|
+
defer: Callable | None = None,
|
342
|
+
) -> None:
|
327
343
|
gitlab_cli = GitLabApi(instance, project_url=repo["url"], settings=settings)
|
328
|
-
defer
|
344
|
+
if defer:
|
345
|
+
defer(gitlab_cli.cleanup)
|
329
346
|
project_owners = RepoOwners(
|
330
347
|
git_cli=gitlab_cli, ref=gitlab_cli.project.default_branch
|
331
348
|
)
|
@@ -392,7 +409,7 @@ def act(repo, dry_run, instance, settings, defer=None):
|
|
392
409
|
])
|
393
410
|
|
394
411
|
|
395
|
-
def run(dry_run, thread_pool_size=10):
|
412
|
+
def run(dry_run: bool, thread_pool_size: int = 10) -> None:
|
396
413
|
instance = queries.get_gitlab_instance()
|
397
414
|
settings = queries.get_app_interface_settings()
|
398
415
|
repos = queries.get_repos_gitlab_owner(server=instance["url"])
|
File without changes
|
@@ -0,0 +1,64 @@
|
|
1
|
+
"""
|
2
|
+
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
3
|
+
"""
|
4
|
+
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
5
|
+
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
6
|
+
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
7
|
+
from typing import ( # noqa: F401 # pylint: disable=W0611
|
8
|
+
Any,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
14
|
+
BaseModel,
|
15
|
+
Extra,
|
16
|
+
Field,
|
17
|
+
Json,
|
18
|
+
)
|
19
|
+
|
20
|
+
from reconcile.gql_definitions.fragments.email_service import EmailServiceOwners
|
21
|
+
|
22
|
+
|
23
|
+
DEFINITION = """
|
24
|
+
fragment EmailServiceOwners on App_v1 {
|
25
|
+
serviceOwners {
|
26
|
+
email
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
query EmailApps {
|
31
|
+
apps: apps_v1 {
|
32
|
+
...EmailServiceOwners
|
33
|
+
}
|
34
|
+
}
|
35
|
+
"""
|
36
|
+
|
37
|
+
|
38
|
+
class ConfiguredBaseModel(BaseModel):
|
39
|
+
class Config:
|
40
|
+
smart_union=True
|
41
|
+
extra=Extra.forbid
|
42
|
+
|
43
|
+
|
44
|
+
class EmailAppsQueryData(ConfiguredBaseModel):
|
45
|
+
apps: Optional[list[EmailServiceOwners]] = Field(..., alias="apps")
|
46
|
+
|
47
|
+
|
48
|
+
def query(query_func: Callable, **kwargs: Any) -> EmailAppsQueryData:
|
49
|
+
"""
|
50
|
+
This is a convenience function which queries and parses the data into
|
51
|
+
concrete types. It should be compatible with most GQL clients.
|
52
|
+
You do not have to use it to consume the generated data classes.
|
53
|
+
Alternatively, you can also mime and alternate the behavior
|
54
|
+
of this function in the caller.
|
55
|
+
|
56
|
+
Parameters:
|
57
|
+
query_func (Callable): Function which queries your GQL Server
|
58
|
+
kwargs: optional arguments that will be passed to the query function
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
EmailAppsQueryData: queried data parsed into generated classes
|
62
|
+
"""
|
63
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
64
|
+
return EmailAppsQueryData(**raw_data)
|
@@ -0,0 +1,133 @@
|
|
1
|
+
"""
|
2
|
+
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
3
|
+
"""
|
4
|
+
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
5
|
+
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
6
|
+
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
7
|
+
from typing import ( # noqa: F401 # pylint: disable=W0611
|
8
|
+
Any,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
14
|
+
BaseModel,
|
15
|
+
Extra,
|
16
|
+
Field,
|
17
|
+
Json,
|
18
|
+
)
|
19
|
+
|
20
|
+
from reconcile.gql_definitions.fragments.email_service import EmailServiceOwners
|
21
|
+
from reconcile.gql_definitions.fragments.email_user import EmailUser
|
22
|
+
|
23
|
+
|
24
|
+
DEFINITION = """
|
25
|
+
fragment EmailServiceOwners on App_v1 {
|
26
|
+
serviceOwners {
|
27
|
+
email
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
fragment EmailUser on User_v1 {
|
32
|
+
org_username
|
33
|
+
}
|
34
|
+
|
35
|
+
query Emails {
|
36
|
+
emails: app_interface_emails_v1 {
|
37
|
+
name
|
38
|
+
subject
|
39
|
+
to {
|
40
|
+
aliases
|
41
|
+
services {
|
42
|
+
... EmailServiceOwners
|
43
|
+
}
|
44
|
+
clusters {
|
45
|
+
name
|
46
|
+
}
|
47
|
+
namespaces {
|
48
|
+
name
|
49
|
+
}
|
50
|
+
aws_accounts {
|
51
|
+
accountOwners {
|
52
|
+
email
|
53
|
+
}
|
54
|
+
}
|
55
|
+
roles {
|
56
|
+
users {
|
57
|
+
... EmailUser
|
58
|
+
}
|
59
|
+
}
|
60
|
+
users {
|
61
|
+
... EmailUser
|
62
|
+
}
|
63
|
+
}
|
64
|
+
body
|
65
|
+
}
|
66
|
+
}
|
67
|
+
"""
|
68
|
+
|
69
|
+
|
70
|
+
class ConfiguredBaseModel(BaseModel):
|
71
|
+
class Config:
|
72
|
+
smart_union=True
|
73
|
+
extra=Extra.forbid
|
74
|
+
|
75
|
+
|
76
|
+
class ClusterV1(ConfiguredBaseModel):
|
77
|
+
name: str = Field(..., alias="name")
|
78
|
+
|
79
|
+
|
80
|
+
class NamespaceV1(ConfiguredBaseModel):
|
81
|
+
name: str = Field(..., alias="name")
|
82
|
+
|
83
|
+
|
84
|
+
class OwnerV1(ConfiguredBaseModel):
|
85
|
+
email: str = Field(..., alias="email")
|
86
|
+
|
87
|
+
|
88
|
+
class AWSAccountV1(ConfiguredBaseModel):
|
89
|
+
account_owners: list[OwnerV1] = Field(..., alias="accountOwners")
|
90
|
+
|
91
|
+
|
92
|
+
class RoleV1(ConfiguredBaseModel):
|
93
|
+
users: list[EmailUser] = Field(..., alias="users")
|
94
|
+
|
95
|
+
|
96
|
+
class AppInterfaceEmailAudienceV1(ConfiguredBaseModel):
|
97
|
+
aliases: Optional[list[str]] = Field(..., alias="aliases")
|
98
|
+
services: Optional[list[EmailServiceOwners]] = Field(..., alias="services")
|
99
|
+
clusters: Optional[list[ClusterV1]] = Field(..., alias="clusters")
|
100
|
+
namespaces: Optional[list[NamespaceV1]] = Field(..., alias="namespaces")
|
101
|
+
aws_accounts: Optional[list[AWSAccountV1]] = Field(..., alias="aws_accounts")
|
102
|
+
roles: Optional[list[RoleV1]] = Field(..., alias="roles")
|
103
|
+
users: Optional[list[EmailUser]] = Field(..., alias="users")
|
104
|
+
|
105
|
+
|
106
|
+
class AppInterfaceEmailV1(ConfiguredBaseModel):
|
107
|
+
name: str = Field(..., alias="name")
|
108
|
+
subject: str = Field(..., alias="subject")
|
109
|
+
q_to: AppInterfaceEmailAudienceV1 = Field(..., alias="to")
|
110
|
+
body: str = Field(..., alias="body")
|
111
|
+
|
112
|
+
|
113
|
+
class EmailsQueryData(ConfiguredBaseModel):
|
114
|
+
emails: Optional[list[AppInterfaceEmailV1]] = Field(..., alias="emails")
|
115
|
+
|
116
|
+
|
117
|
+
def query(query_func: Callable, **kwargs: Any) -> EmailsQueryData:
|
118
|
+
"""
|
119
|
+
This is a convenience function which queries and parses the data into
|
120
|
+
concrete types. It should be compatible with most GQL clients.
|
121
|
+
You do not have to use it to consume the generated data classes.
|
122
|
+
Alternatively, you can also mime and alternate the behavior
|
123
|
+
of this function in the caller.
|
124
|
+
|
125
|
+
Parameters:
|
126
|
+
query_func (Callable): Function which queries your GQL Server
|
127
|
+
kwargs: optional arguments that will be passed to the query function
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
EmailsQueryData: queried data parsed into generated classes
|
131
|
+
"""
|
132
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
133
|
+
return EmailsQueryData(**raw_data)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
"""
|
2
|
+
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
3
|
+
"""
|
4
|
+
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
5
|
+
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
6
|
+
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
7
|
+
from typing import ( # noqa: F401 # pylint: disable=W0611
|
8
|
+
Any,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
14
|
+
BaseModel,
|
15
|
+
Extra,
|
16
|
+
Field,
|
17
|
+
Json,
|
18
|
+
)
|
19
|
+
|
20
|
+
from reconcile.gql_definitions.fragments.email_user import EmailUser
|
21
|
+
|
22
|
+
|
23
|
+
DEFINITION = """
|
24
|
+
fragment EmailUser on User_v1 {
|
25
|
+
org_username
|
26
|
+
}
|
27
|
+
|
28
|
+
query EmailUsers {
|
29
|
+
users: users_v1 {
|
30
|
+
... EmailUser
|
31
|
+
}
|
32
|
+
}
|
33
|
+
"""
|
34
|
+
|
35
|
+
|
36
|
+
class ConfiguredBaseModel(BaseModel):
|
37
|
+
class Config:
|
38
|
+
smart_union=True
|
39
|
+
extra=Extra.forbid
|
40
|
+
|
41
|
+
|
42
|
+
class EmailUsersQueryData(ConfiguredBaseModel):
|
43
|
+
users: Optional[list[EmailUser]] = Field(..., alias="users")
|
44
|
+
|
45
|
+
|
46
|
+
def query(query_func: Callable, **kwargs: Any) -> EmailUsersQueryData:
|
47
|
+
"""
|
48
|
+
This is a convenience function which queries and parses the data into
|
49
|
+
concrete types. It should be compatible with most GQL clients.
|
50
|
+
You do not have to use it to consume the generated data classes.
|
51
|
+
Alternatively, you can also mime and alternate the behavior
|
52
|
+
of this function in the caller.
|
53
|
+
|
54
|
+
Parameters:
|
55
|
+
query_func (Callable): Function which queries your GQL Server
|
56
|
+
kwargs: optional arguments that will be passed to the query function
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
EmailUsersQueryData: queried data parsed into generated classes
|
60
|
+
"""
|
61
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
62
|
+
return EmailUsersQueryData(**raw_data)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""
|
2
|
+
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
3
|
+
"""
|
4
|
+
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
5
|
+
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
6
|
+
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
7
|
+
from typing import ( # noqa: F401 # pylint: disable=W0611
|
8
|
+
Any,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
14
|
+
BaseModel,
|
15
|
+
Extra,
|
16
|
+
Field,
|
17
|
+
Json,
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class ConfiguredBaseModel(BaseModel):
|
22
|
+
class Config:
|
23
|
+
smart_union=True
|
24
|
+
extra=Extra.forbid
|
25
|
+
|
26
|
+
|
27
|
+
class OwnerV1(ConfiguredBaseModel):
|
28
|
+
email: str = Field(..., alias="email")
|
29
|
+
|
30
|
+
|
31
|
+
class EmailServiceOwners(ConfiguredBaseModel):
|
32
|
+
service_owners: Optional[list[OwnerV1]] = Field(..., alias="serviceOwners")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
3
|
+
"""
|
4
|
+
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
5
|
+
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
6
|
+
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
7
|
+
from typing import ( # noqa: F401 # pylint: disable=W0611
|
8
|
+
Any,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
14
|
+
BaseModel,
|
15
|
+
Extra,
|
16
|
+
Field,
|
17
|
+
Json,
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class ConfiguredBaseModel(BaseModel):
|
22
|
+
class Config:
|
23
|
+
smart_union=True
|
24
|
+
extra=Extra.forbid
|
25
|
+
|
26
|
+
|
27
|
+
class EmailUser(ConfiguredBaseModel):
|
28
|
+
org_username: str = Field(..., alias="org_username")
|
reconcile/queries.py
CHANGED
@@ -124,50 +124,6 @@ def get_app_interface_settings():
|
|
124
124
|
return None
|
125
125
|
|
126
126
|
|
127
|
-
APP_INTERFACE_EMAILS_QUERY = """
|
128
|
-
{
|
129
|
-
emails: app_interface_emails_v1 {
|
130
|
-
name
|
131
|
-
subject
|
132
|
-
to {
|
133
|
-
aliases
|
134
|
-
services {
|
135
|
-
serviceOwners {
|
136
|
-
email
|
137
|
-
}
|
138
|
-
}
|
139
|
-
clusters {
|
140
|
-
name
|
141
|
-
}
|
142
|
-
namespaces {
|
143
|
-
name
|
144
|
-
}
|
145
|
-
aws_accounts {
|
146
|
-
accountOwners {
|
147
|
-
email
|
148
|
-
}
|
149
|
-
}
|
150
|
-
roles {
|
151
|
-
users {
|
152
|
-
org_username
|
153
|
-
}
|
154
|
-
}
|
155
|
-
users {
|
156
|
-
org_username
|
157
|
-
}
|
158
|
-
}
|
159
|
-
body
|
160
|
-
}
|
161
|
-
}
|
162
|
-
"""
|
163
|
-
|
164
|
-
|
165
|
-
def get_app_interface_emails():
|
166
|
-
"""Returns Email resources defined in app-interface"""
|
167
|
-
gqlapi = gql.get_api()
|
168
|
-
return gqlapi.query(APP_INTERFACE_EMAILS_QUERY)["emails"]
|
169
|
-
|
170
|
-
|
171
127
|
CREDENTIALS_REQUESTS_QUERY = """
|
172
128
|
{
|
173
129
|
credentials_requests: credentials_requests_v1 {
|
reconcile/utils/jinja2/utils.py
CHANGED
@@ -116,8 +116,10 @@ def lookup_github_file_content(
|
|
116
116
|
)
|
117
117
|
|
118
118
|
gh = init_github()
|
119
|
-
|
120
|
-
|
119
|
+
content = gh.get_repo(repo).get_contents(path, ref)
|
120
|
+
if isinstance(content, list):
|
121
|
+
raise Exception(f"multiple files found for {repo}/{path}/{ref}")
|
122
|
+
return content.decoded_content.decode("utf-8")
|
121
123
|
|
122
124
|
|
123
125
|
def lookup_graphql_query_results(query: str, **kwargs: dict[str, Any]) -> list[Any]:
|
reconcile/utils/smtp_client.py
CHANGED
@@ -70,7 +70,7 @@ class SmtpClient:
|
|
70
70
|
self.send_mail([name], subject, body)
|
71
71
|
|
72
72
|
@retry()
|
73
|
-
def send_mail(self, names: str, subject: str, body: str) -> None:
|
73
|
+
def send_mail(self, names: Iterable[str], subject: str, body: str) -> None:
|
74
74
|
msg = MIMEMultipart()
|
75
75
|
from_name = str(Header("App SRE team automation", "utf-8"))
|
76
76
|
msg["From"] = formataddr((from_name, self.user))
|
{qontract_reconcile-0.10.2.dev135.dist-info → qontract_reconcile-0.10.2.dev136.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|