qontract-reconcile 0.10.1rc735__py3-none-any.whl → 0.10.1rc736__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.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/RECORD +11 -11
- reconcile/gql_definitions/templating/template_collection.py +6 -0
- reconcile/gql_definitions/templating/templates.py +2 -0
- reconcile/templating/lib/merge_request_manager.py +17 -3
- reconcile/templating/lib/model.py +4 -3
- reconcile/templating/renderer.py +38 -29
- reconcile/templating/validator.py +5 -1
- {qontract_reconcile-0.10.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc735.dist-info → qontract_reconcile-0.10.1rc736.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.1rc736
|
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.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/RECORD
RENAMED
@@ -331,8 +331,8 @@ reconcile/gql_definitions/status_board/status_board.py,sha256=vHEzncabujkqbjJ-ib
|
|
331
331
|
reconcile/gql_definitions/statuspage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
332
332
|
reconcile/gql_definitions/statuspage/statuspages.py,sha256=gxDb42H93nwtBg7oFRb6Gk9pbAZpsWk_y4Y0s3_g3nE,3520
|
333
333
|
reconcile/gql_definitions/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
334
|
-
reconcile/gql_definitions/templating/template_collection.py,sha256=
|
335
|
-
reconcile/gql_definitions/templating/templates.py,sha256=
|
334
|
+
reconcile/gql_definitions/templating/template_collection.py,sha256=lS0vzEKV2ZrzOqOEriqpy0yBgKjb2Ftrzgx6PIH46_4,3310
|
335
|
+
reconcile/gql_definitions/templating/templates.py,sha256=ejAvQ13zfNMQTz3FWtRUic6dSvio3aAgBKEqt600hbk,2821
|
336
336
|
reconcile/gql_definitions/terraform_cloudflare_dns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
337
337
|
reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py,sha256=eyGX9HcTF6MZbOYZ6Kl6Mg3k6nJTUtwqs9gDxBP_8Dk,1920
|
338
338
|
reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py,sha256=uVZYu5EUcvdAQYBK5YKD0mjoMKDb5inSuCJrrOD5KpE,5704
|
@@ -419,11 +419,11 @@ reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9
|
|
419
419
|
reconcile/templates/rosa-classic-cluster-creation.sh.j2,sha256=0UHfYtXRVJqP07VJQx456cRI6EbZNBgamtP_8nb4WPY,2353
|
420
420
|
reconcile/templates/rosa-hcp-cluster-creation.sh.j2,sha256=O7Bf3WQIJhsZoEqaYA0wRktUO4yXXCb4BQkuvvp-C80,2385
|
421
421
|
reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
422
|
-
reconcile/templating/renderer.py,sha256=
|
423
|
-
reconcile/templating/validator.py,sha256=
|
422
|
+
reconcile/templating/renderer.py,sha256=2FWbnefT2siozQpXXkuvVKUo6cePMqLY4BMYpqXg6xM,10652
|
423
|
+
reconcile/templating/validator.py,sha256=pvDEc6veznEZzjypkoRJUGMMFLWosU-zd7i3j7JeNjE,4670
|
424
424
|
reconcile/templating/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
425
|
-
reconcile/templating/lib/merge_request_manager.py,sha256=
|
426
|
-
reconcile/templating/lib/model.py,sha256=
|
425
|
+
reconcile/templating/lib/merge_request_manager.py,sha256=JUkfF3smaQ8onzKF5F7UpmA7MWaQpftANy6dDo1FCug,5464
|
426
|
+
reconcile/templating/lib/model.py,sha256=fb6FYYLQjmoh2DjVKO7TEWCuDPf1Q34xmOx0M9Z07ek,324
|
427
427
|
reconcile/templating/lib/rendering.py,sha256=_BVQ2gqip8K1AgLYfaTWh8NKJFTW6VjUZ6rBI_GH30E,5061
|
428
428
|
reconcile/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
429
429
|
reconcile/test/conftest.py,sha256=rQousYrxUz-EwAIbsYO6bIwR1B4CrOz9y_zaUVo2lfI,4466
|
@@ -770,8 +770,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
770
770
|
tools/test/test_qontract_cli.py,sha256=w2l4BHB09k1d-BGJ1jBUNCqDv7zkqYrMHojQXg-21kQ,4155
|
771
771
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
772
772
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
773
|
-
qontract_reconcile-0.10.
|
774
|
-
qontract_reconcile-0.10.
|
775
|
-
qontract_reconcile-0.10.
|
776
|
-
qontract_reconcile-0.10.
|
777
|
-
qontract_reconcile-0.10.
|
773
|
+
qontract_reconcile-0.10.1rc736.dist-info/METADATA,sha256=HGDJxGb77YYojenQNeOYhLfp3EOLLqkOwx8Tat8ONWA,2382
|
774
|
+
qontract_reconcile-0.10.1rc736.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
775
|
+
qontract_reconcile-0.10.1rc736.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
776
|
+
qontract_reconcile-0.10.1rc736.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
777
|
+
qontract_reconcile-0.10.1rc736.dist-info/RECORD,,
|
@@ -22,7 +22,9 @@ DEFINITION = """
|
|
22
22
|
query TemplateCollection_v1 {
|
23
23
|
template_collection_v1 {
|
24
24
|
name
|
25
|
+
additionalMrLabels
|
25
26
|
description
|
27
|
+
enableAutoApproval
|
26
28
|
variables {
|
27
29
|
static
|
28
30
|
dynamic {
|
@@ -32,6 +34,7 @@ query TemplateCollection_v1 {
|
|
32
34
|
}
|
33
35
|
templates {
|
34
36
|
name
|
37
|
+
autoApproved
|
35
38
|
condition
|
36
39
|
targetPath
|
37
40
|
patch {
|
@@ -68,6 +71,7 @@ class TemplatePatchV1(ConfiguredBaseModel):
|
|
68
71
|
|
69
72
|
class TemplateV1(ConfiguredBaseModel):
|
70
73
|
name: str = Field(..., alias="name")
|
74
|
+
auto_approved: Optional[bool] = Field(..., alias="autoApproved")
|
71
75
|
condition: Optional[str] = Field(..., alias="condition")
|
72
76
|
target_path: str = Field(..., alias="targetPath")
|
73
77
|
patch: Optional[TemplatePatchV1] = Field(..., alias="patch")
|
@@ -76,7 +80,9 @@ class TemplateV1(ConfiguredBaseModel):
|
|
76
80
|
|
77
81
|
class TemplateCollectionV1(ConfiguredBaseModel):
|
78
82
|
name: str = Field(..., alias="name")
|
83
|
+
additional_mr_labels: Optional[list[str]] = Field(..., alias="additionalMrLabels")
|
79
84
|
description: str = Field(..., alias="description")
|
85
|
+
enable_auto_approval: Optional[bool] = Field(..., alias="enableAutoApproval")
|
80
86
|
variables: Optional[TemplateCollectionVariablesV1] = Field(..., alias="variables")
|
81
87
|
templates: list[TemplateV1] = Field(..., alias="templates")
|
82
88
|
|
@@ -22,6 +22,7 @@ DEFINITION = """
|
|
22
22
|
query Templatev1 {
|
23
23
|
template_v1 {
|
24
24
|
name
|
25
|
+
autoApproved
|
25
26
|
condition
|
26
27
|
patch {
|
27
28
|
path
|
@@ -64,6 +65,7 @@ class TemplateTestV1(ConfiguredBaseModel):
|
|
64
65
|
|
65
66
|
class TemplateV1(ConfiguredBaseModel):
|
66
67
|
name: str = Field(..., alias="name")
|
68
|
+
auto_approved: Optional[bool] = Field(..., alias="autoApproved")
|
67
69
|
condition: Optional[str] = Field(..., alias="condition")
|
68
70
|
patch: Optional[TemplatePatchV1] = Field(..., alias="patch")
|
69
71
|
target_path: str = Field(..., alias="targetPath")
|
@@ -13,6 +13,7 @@ from reconcile.utils.merge_request_manager.parser import (
|
|
13
13
|
Parser,
|
14
14
|
)
|
15
15
|
from reconcile.utils.mr import MergeRequestBase
|
16
|
+
from reconcile.utils.mr.labels import AUTO_MERGE
|
16
17
|
from reconcile.utils.vcs import VCS
|
17
18
|
|
18
19
|
DATA_SEPARATOR = (
|
@@ -120,16 +121,23 @@ class TemplateRenderingMR(MergeRequestBase):
|
|
120
121
|
)
|
121
122
|
|
122
123
|
|
124
|
+
class MrData(BaseModel):
|
125
|
+
data: list[TemplateOutput]
|
126
|
+
auto_approved: bool
|
127
|
+
|
128
|
+
|
123
129
|
class MergeRequestManager(MergeRequestManagerBase[TemplateInfo]):
|
124
130
|
def __init__(self, vcs: VCS, parser: Parser):
|
125
131
|
super().__init__(vcs, parser, TR_LABEL)
|
126
132
|
|
127
|
-
def create_merge_request(self,
|
133
|
+
def create_merge_request(self, data: MrData) -> None:
|
128
134
|
if not self._housekeeping_ran:
|
129
135
|
self.housekeeping()
|
130
136
|
|
131
|
-
|
132
|
-
|
137
|
+
output = data.data
|
138
|
+
collections = {o.input.collection for o in output}
|
139
|
+
collection_hashes = {o.input.collection_hash for o in output}
|
140
|
+
additional_labels = {label for o in output for label in o.input.labels}
|
133
141
|
# From the way the code is written, we can assert that there is only one collection and one template hash
|
134
142
|
assert len(collections) == 1
|
135
143
|
assert len(collection_hashes) == 1
|
@@ -158,6 +166,12 @@ class MergeRequestManager(MergeRequestManagerBase[TemplateInfo]):
|
|
158
166
|
logging.info("Opening MR for %s with hash (%s)", collection, collection_hash)
|
159
167
|
mr_labels = [TR_LABEL]
|
160
168
|
|
169
|
+
if data.auto_approved:
|
170
|
+
mr_labels.append(AUTO_MERGE)
|
171
|
+
|
172
|
+
if additional_labels:
|
173
|
+
mr_labels.extend(additional_labels)
|
174
|
+
|
161
175
|
self._vcs.open_app_interface_merge_request(
|
162
176
|
mr=TemplateRenderingMR(
|
163
177
|
title=title,
|
@@ -1,15 +1,16 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
1
|
from pydantic import BaseModel
|
4
2
|
|
5
3
|
|
6
4
|
class TemplateInput(BaseModel):
|
7
5
|
collection: str
|
8
6
|
collection_hash: str
|
7
|
+
enable_auto_approval: bool = False
|
8
|
+
labels: list[str] = []
|
9
9
|
|
10
10
|
|
11
11
|
class TemplateOutput(BaseModel):
|
12
|
-
input:
|
12
|
+
input: TemplateInput
|
13
13
|
is_new: bool = False
|
14
14
|
path: str
|
15
15
|
content: str
|
16
|
+
auto_approved: bool = False
|
reconcile/templating/renderer.py
CHANGED
@@ -17,6 +17,7 @@ from reconcile.gql_definitions.templating.template_collection import (
|
|
17
17
|
)
|
18
18
|
from reconcile.templating.lib.merge_request_manager import (
|
19
19
|
MergeRequestManager,
|
20
|
+
MrData,
|
20
21
|
create_parser,
|
21
22
|
)
|
22
23
|
from reconcile.templating.lib.model import TemplateInput, TemplateOutput
|
@@ -59,6 +60,19 @@ class FilePersistence(ABC):
|
|
59
60
|
def read(self, path: str) -> Optional[str]:
|
60
61
|
pass
|
61
62
|
|
63
|
+
@staticmethod
|
64
|
+
def _read_local_file(path: str) -> Optional[str]:
|
65
|
+
try:
|
66
|
+
with open(
|
67
|
+
path,
|
68
|
+
"r",
|
69
|
+
encoding="utf-8",
|
70
|
+
) as f:
|
71
|
+
return f.read()
|
72
|
+
except FileNotFoundError:
|
73
|
+
logging.debug(f"File not found: {path}, need to create it")
|
74
|
+
return None
|
75
|
+
|
62
76
|
|
63
77
|
class LocalFilePersistence(FilePersistence):
|
64
78
|
"""
|
@@ -80,16 +94,7 @@ class LocalFilePersistence(FilePersistence):
|
|
80
94
|
f.write(output.content)
|
81
95
|
|
82
96
|
def read(self, path: str) -> Optional[str]:
|
83
|
-
|
84
|
-
with open(
|
85
|
-
f"{join_path(self.app_interface_data_path, path)}",
|
86
|
-
"r",
|
87
|
-
encoding="utf-8",
|
88
|
-
) as f:
|
89
|
-
return f.read()
|
90
|
-
except FileNotFoundError:
|
91
|
-
logging.debug(f"File not found: {path}, need to create it")
|
92
|
-
return None
|
97
|
+
return self._read_local_file(join_path(self.app_interface_data_path, path))
|
93
98
|
|
94
99
|
|
95
100
|
class PersistenceTransaction(FilePersistence):
|
@@ -127,6 +132,8 @@ class ClonedRepoGitlabPersistence(FilePersistence):
|
|
127
132
|
"""
|
128
133
|
This class is used to persist the rendered templates in a cloned gitlab repo
|
129
134
|
Reads are from the local filesystem, writes are done via utils.VCS abstraction
|
135
|
+
|
136
|
+
Only one MR is created per run. Auto-approval MRs are prefered.
|
130
137
|
"""
|
131
138
|
|
132
139
|
def __init__(self, local_path: str, vcs: VCS, mr_manager: MergeRequestManager):
|
@@ -136,19 +143,19 @@ class ClonedRepoGitlabPersistence(FilePersistence):
|
|
136
143
|
|
137
144
|
def write(self, outputs: list[TemplateOutput]) -> None:
|
138
145
|
self.mr_manager.housekeeping()
|
139
|
-
|
146
|
+
|
147
|
+
if any([o.input.enable_auto_approval for o in outputs]):
|
148
|
+
auto_approved = [o for o in outputs if o.auto_approved]
|
149
|
+
if auto_approved:
|
150
|
+
self.mr_manager.create_merge_request(
|
151
|
+
MrData(data=auto_approved, auto_approved=True)
|
152
|
+
)
|
153
|
+
return
|
154
|
+
|
155
|
+
self.mr_manager.create_merge_request(MrData(data=outputs, auto_approved=False))
|
140
156
|
|
141
157
|
def read(self, path: str) -> Optional[str]:
|
142
|
-
|
143
|
-
with open(
|
144
|
-
f"{join_path(self.local_path, path)}",
|
145
|
-
"r",
|
146
|
-
encoding="utf-8",
|
147
|
-
) as f:
|
148
|
-
return f.read()
|
149
|
-
except FileNotFoundError:
|
150
|
-
logging.debug(f"File not found: {path}, need to create it")
|
151
|
-
return None
|
158
|
+
return self._read_local_file(join_path(self.local_path, path))
|
152
159
|
|
153
160
|
|
154
161
|
def unpack_static_variables(
|
@@ -187,6 +194,7 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
187
194
|
variables: dict,
|
188
195
|
persistence: FilePersistence,
|
189
196
|
ruaml_instance: yaml.YAML,
|
197
|
+
template_input: TemplateInput,
|
190
198
|
) -> Optional[TemplateOutput]:
|
191
199
|
r = create_renderer(
|
192
200
|
template,
|
@@ -217,6 +225,8 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
217
225
|
path=target_path,
|
218
226
|
content=output,
|
219
227
|
is_new=current_str is None,
|
228
|
+
auto_approved=template.auto_approved or False,
|
229
|
+
input=template_input,
|
220
230
|
)
|
221
231
|
return None
|
222
232
|
|
@@ -244,18 +254,17 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
244
254
|
).hexdigest()
|
245
255
|
|
246
256
|
with PersistenceTransaction(persistence, dry_run) as p:
|
257
|
+
input = TemplateInput(
|
258
|
+
collection=c.name,
|
259
|
+
collection_hash=template_hash,
|
260
|
+
enable_auto_approval=c.enable_auto_approval or False,
|
261
|
+
labels=c.additional_mr_labels or [],
|
262
|
+
)
|
247
263
|
for template in c.templates:
|
248
264
|
output = self.process_template(
|
249
|
-
template,
|
250
|
-
variables,
|
251
|
-
p,
|
252
|
-
ruamel_instance,
|
265
|
+
template, variables, p, ruamel_instance, input
|
253
266
|
)
|
254
267
|
if output:
|
255
|
-
output.input = TemplateInput(
|
256
|
-
collection=c.name,
|
257
|
-
collection_hash=template_hash,
|
258
|
-
)
|
259
268
|
outputs.append(output)
|
260
269
|
|
261
270
|
if not dry_run:
|
@@ -127,7 +127,11 @@ class TemplateValidatorIntegration(QontractReconcileIntegration):
|
|
127
127
|
if diffs:
|
128
128
|
for diff in diffs:
|
129
129
|
logging.error(f"template: {diff.template}, test: {diff.test}")
|
130
|
-
|
130
|
+
# This log should never be added except for local debugging.
|
131
|
+
# Credentials could be leaked, i.e. creating an MR with a diff,
|
132
|
+
# using a template, that uses the vault function.
|
133
|
+
# Use template-validator CLI instead.
|
134
|
+
# logging.debug(diff.diff)
|
131
135
|
raise ValueError("Template validation failed")
|
132
136
|
|
133
137
|
@property
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc735.dist-info → qontract_reconcile-0.10.1rc736.dist-info}/top_level.txt
RENAMED
File without changes
|