qontract-reconcile 0.10.1rc718__py3-none-any.whl → 0.10.1rc719__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.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/RECORD +8 -8
- reconcile/cli.py +9 -3
- reconcile/templating/renderer.py +100 -54
- reconcile/utils/git.py +8 -5
- {qontract_reconcile-0.10.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc718.dist-info → qontract_reconcile-0.10.1rc719.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.1rc719
|
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.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/RECORD
RENAMED
@@ -9,7 +9,7 @@ reconcile/aws_iam_password_reset.py,sha256=NwErtrqgBiXr7eGCAHdtGGOx0S7-4JnSc29Ie
|
|
9
9
|
reconcile/aws_support_cases_sos.py,sha256=Jk6_XjDeJSYxgRGqcEAOcynt9qJF2r5HPIPcSKmoBv8,2974
|
10
10
|
reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
|
11
11
|
reconcile/checkpoint.py,sha256=R2WFXUXLTB4sWMi4GeA4eegsuf_1-Q4vH8M0Toh3Ij4,5036
|
12
|
-
reconcile/cli.py,sha256=
|
12
|
+
reconcile/cli.py,sha256=HLfkpQXhNA9YDwNWJUYg8zjm03sCEUIVB6-Ly4FOh48,96572
|
13
13
|
reconcile/closedbox_endpoint_monitoring_base.py,sha256=SMhkcQqprWvThrIJa3U_3uh5w1h-alleW1QnCJFY4Qw,4909
|
14
14
|
reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
|
15
15
|
reconcile/dashdotdb_base.py,sha256=a5aPLVxyqPSbjdB0Ty-uliOtxwvEbbEljHJKxdK3-Zk,4813
|
@@ -416,7 +416,7 @@ reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9
|
|
416
416
|
reconcile/templates/rosa-classic-cluster-creation.sh.j2,sha256=0UHfYtXRVJqP07VJQx456cRI6EbZNBgamtP_8nb4WPY,2353
|
417
417
|
reconcile/templates/rosa-hcp-cluster-creation.sh.j2,sha256=O7Bf3WQIJhsZoEqaYA0wRktUO4yXXCb4BQkuvvp-C80,2385
|
418
418
|
reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
419
|
-
reconcile/templating/renderer.py,sha256=
|
419
|
+
reconcile/templating/renderer.py,sha256=wWsuCUtmC3vdUHP9ZYZgafbQxuAxPGpXiMYWuewPEyg,10156
|
420
420
|
reconcile/templating/validator.py,sha256=QGH2VSk7sVBuojhk9quRAbj_XBykHN-KZ53DbEneUJs,4391
|
421
421
|
reconcile/templating/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
422
422
|
reconcile/templating/lib/merge_request_manager.py,sha256=El3ufdVjHP4EW-LztX7zPiI9syJBJ6ETGRmiM9K4Nqw,5112
|
@@ -595,7 +595,7 @@ reconcile/utils/extended_early_exit.py,sha256=QSktrmfw37zSRMNk930tDbQsVeKxaPPPD4
|
|
595
595
|
reconcile/utils/external_resource_spec.py,sha256=IRY8MCsyWKzt-Qj_hXiFKgkCvZu6VVyj6IYtqEb05BA,6618
|
596
596
|
reconcile/utils/external_resources.py,sha256=a2CkJ3KLociYBnc_9F2VWfZGWMhzDl6fDNhwo2U-MWU,7501
|
597
597
|
reconcile/utils/filtering.py,sha256=zZnHH0u0SaTDyzuFXZ_mREURGLvjEqQIQy4z-7QBVlc,419
|
598
|
-
reconcile/utils/git.py,sha256=
|
598
|
+
reconcile/utils/git.py,sha256=BdxXFgQ1XOZpS-4qb3qMsKTCFDG8MlE26rv1jAhvCkM,1560
|
599
599
|
reconcile/utils/git_secrets.py,sha256=0wGNL5mvDtVPRuu3vEQgld1Am64gIDJHtmu1_ZKxMAI,1973
|
600
600
|
reconcile/utils/github_api.py,sha256=_bttNxYKeam_tLVe27L7O4gKqSn6CeyuFnJn8tSaUVY,2488
|
601
601
|
reconcile/utils/gitlab_api.py,sha256=UMqWIHR_GkxEn5lgvxHLrjcwprRvRuKowxkwvu1xvvM,27241
|
@@ -764,8 +764,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
764
764
|
tools/test/test_qontract_cli.py,sha256=UEwAW7PA_GIrbqzaLxpkCxbuVjEFLNvnVG-6VyoCGIc,4147
|
765
765
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
766
766
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
767
|
-
qontract_reconcile-0.10.
|
768
|
-
qontract_reconcile-0.10.
|
769
|
-
qontract_reconcile-0.10.
|
770
|
-
qontract_reconcile-0.10.
|
771
|
-
qontract_reconcile-0.10.
|
767
|
+
qontract_reconcile-0.10.1rc719.dist-info/METADATA,sha256=c4dAaDQmMj1M0mTRWNFxv-fhaoYqat8QpFMThH9GqUw,2382
|
768
|
+
qontract_reconcile-0.10.1rc719.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
769
|
+
qontract_reconcile-0.10.1rc719.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
770
|
+
qontract_reconcile-0.10.1rc719.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
771
|
+
qontract_reconcile-0.10.1rc719.dist-info/RECORD,,
|
reconcile/cli.py
CHANGED
@@ -1968,12 +1968,17 @@ def template_validator(ctx):
|
|
1968
1968
|
@integration.command(short_help="Render datafile templates in app-interface.")
|
1969
1969
|
@click.option(
|
1970
1970
|
"--app-interface-data-path",
|
1971
|
-
help="Path to app-interface
|
1971
|
+
help="Path to data dir in app-interface repo. Use this for local rendering or in MR checks.",
|
1972
1972
|
required=False,
|
1973
1973
|
envvar="APP_INTERFACE_DATA_PATH",
|
1974
1974
|
)
|
1975
|
+
@click.option(
|
1976
|
+
"--clone-repo",
|
1977
|
+
help="Path to a folder app-interface repo should be cloned to. Use this for regular integration run.",
|
1978
|
+
default=False,
|
1979
|
+
)
|
1975
1980
|
@click.pass_context
|
1976
|
-
def template_renderer(ctx, app_interface_data_path):
|
1981
|
+
def template_renderer(ctx, app_interface_data_path, clone_repo):
|
1977
1982
|
from reconcile.templating.renderer import (
|
1978
1983
|
TemplateRendererIntegration,
|
1979
1984
|
TemplateRendererIntegrationParams,
|
@@ -1982,7 +1987,8 @@ def template_renderer(ctx, app_interface_data_path):
|
|
1982
1987
|
run_class_integration(
|
1983
1988
|
integration=TemplateRendererIntegration(
|
1984
1989
|
TemplateRendererIntegrationParams(
|
1985
|
-
app_interface_data_path=app_interface_data_path
|
1990
|
+
app_interface_data_path=app_interface_data_path,
|
1991
|
+
clone_repo=clone_repo,
|
1986
1992
|
)
|
1987
1993
|
),
|
1988
1994
|
ctx=ctx.obj,
|
reconcile/templating/renderer.py
CHANGED
@@ -2,12 +2,11 @@ import hashlib
|
|
2
2
|
import json
|
3
3
|
import logging
|
4
4
|
import os
|
5
|
+
import tempfile
|
5
6
|
from abc import ABC, abstractmethod
|
6
7
|
from collections.abc import Callable
|
7
|
-
from
|
8
|
-
from typing import Any, Optional
|
8
|
+
from typing import Any, Optional, Self
|
9
9
|
|
10
|
-
import gitlab
|
11
10
|
from ruamel import yaml
|
12
11
|
|
13
12
|
from reconcile.gql_definitions.templating.template_collection import (
|
@@ -29,20 +28,17 @@ from reconcile.typed_queries.app_interface_repo_url import get_app_interface_rep
|
|
29
28
|
from reconcile.typed_queries.github_orgs import get_github_orgs
|
30
29
|
from reconcile.typed_queries.gitlab_instances import get_gitlab_instances
|
31
30
|
from reconcile.utils import gql
|
31
|
+
from reconcile.utils.git import clone
|
32
32
|
from reconcile.utils.gql import init_from_config
|
33
33
|
from reconcile.utils.ruamel import create_ruamel_instance
|
34
34
|
from reconcile.utils.runtime.integration import (
|
35
35
|
PydanticRunParams,
|
36
36
|
QontractReconcileIntegration,
|
37
37
|
)
|
38
|
-
from reconcile.utils.state import State, init_state
|
39
38
|
from reconcile.utils.vcs import VCS
|
40
39
|
|
41
40
|
QONTRACT_INTEGRATION = "template-renderer"
|
42
41
|
|
43
|
-
# Renders templates again to detect drift after one day
|
44
|
-
CACHE_TTL_MINUTES = 24 * 60
|
45
|
-
|
46
42
|
APP_INTERFACE_PATH_SEPERATOR = "/"
|
47
43
|
|
48
44
|
|
@@ -65,7 +61,13 @@ class FilePersistence(ABC):
|
|
65
61
|
|
66
62
|
|
67
63
|
class LocalFilePersistence(FilePersistence):
|
64
|
+
"""
|
65
|
+
This class provides a simple file persistence implementation for local files.
|
66
|
+
"""
|
67
|
+
|
68
68
|
def __init__(self, app_interface_data_path: str) -> None:
|
69
|
+
if not app_interface_data_path.endswith("/data"):
|
70
|
+
raise ValueError("app_interface_data_path should end with /data")
|
69
71
|
self.app_interface_data_path = app_interface_data_path
|
70
72
|
|
71
73
|
def write(self, outputs: list[TemplateOutput]) -> None:
|
@@ -90,8 +92,45 @@ class LocalFilePersistence(FilePersistence):
|
|
90
92
|
return None
|
91
93
|
|
92
94
|
|
93
|
-
class
|
94
|
-
|
95
|
+
class PersistenceTransaction(FilePersistence):
|
96
|
+
"""
|
97
|
+
This class provides a context manager to make read/write operations
|
98
|
+
consistent. Reads/writes are beeing cached and writes are beeing delayed
|
99
|
+
until the context is left.
|
100
|
+
"""
|
101
|
+
|
102
|
+
def __init__(self, persistence: FilePersistence, dry_run: bool) -> None:
|
103
|
+
self.persistence = persistence
|
104
|
+
self.dry_run = dry_run
|
105
|
+
self.content_cache: dict[str, Optional[str]] = {}
|
106
|
+
self.output_cache: dict[str, TemplateOutput] = {}
|
107
|
+
|
108
|
+
def write(self, outputs: list[TemplateOutput]) -> None:
|
109
|
+
for output in outputs:
|
110
|
+
self.content_cache[output.path] = output.content
|
111
|
+
self.output_cache[output.path] = output
|
112
|
+
|
113
|
+
def read(self, path: str) -> Optional[str]:
|
114
|
+
if path not in self.content_cache:
|
115
|
+
self.content_cache[path] = self.persistence.read(path)
|
116
|
+
return self.content_cache[path]
|
117
|
+
|
118
|
+
def __enter__(self) -> Self:
|
119
|
+
return self
|
120
|
+
|
121
|
+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
122
|
+
if not self.dry_run:
|
123
|
+
self.persistence.write(list(self.output_cache.values()))
|
124
|
+
|
125
|
+
|
126
|
+
class ClonedRepoGitlabPersistence(FilePersistence):
|
127
|
+
"""
|
128
|
+
This class is used to persist the rendered templates in a cloned gitlab repo
|
129
|
+
Reads are from the local filesystem, writes are done via utils.VCS abstraction
|
130
|
+
"""
|
131
|
+
|
132
|
+
def __init__(self, local_path: str, vcs: VCS, mr_manager: MergeRequestManager):
|
133
|
+
self.local_path = join_path(local_path, "data")
|
95
134
|
self.vcs = vcs
|
96
135
|
self.mr_manager = mr_manager
|
97
136
|
|
@@ -101,10 +140,15 @@ class GitlabFilePersistence(FilePersistence):
|
|
101
140
|
|
102
141
|
def read(self, path: str) -> Optional[str]:
|
103
142
|
try:
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
108
152
|
|
109
153
|
|
110
154
|
def unpack_static_variables(
|
@@ -125,6 +169,7 @@ def unpack_dynamic_variables(
|
|
125
169
|
|
126
170
|
|
127
171
|
class TemplateRendererIntegrationParams(PydanticRunParams):
|
172
|
+
clone_repo: bool = False
|
128
173
|
app_interface_data_path: Optional[str]
|
129
174
|
|
130
175
|
|
@@ -180,7 +225,6 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
180
225
|
dry_run: bool,
|
181
226
|
persistence: FilePersistence,
|
182
227
|
ruamel_instance: yaml.YAML,
|
183
|
-
state: Optional[State] = None,
|
184
228
|
) -> None:
|
185
229
|
gql_no_validation = init_from_config(validate_schemas=False)
|
186
230
|
|
@@ -199,39 +243,23 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
199
243
|
).encode("utf-8")
|
200
244
|
).hexdigest()
|
201
245
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
for template in c.templates:
|
211
|
-
output = self.process_template(
|
212
|
-
template,
|
213
|
-
variables,
|
214
|
-
persistence,
|
215
|
-
ruamel_instance,
|
216
|
-
)
|
217
|
-
if output:
|
218
|
-
output.input = TemplateInput(
|
219
|
-
collection=c.name,
|
220
|
-
collection_hash=template_hash,
|
221
|
-
)
|
222
|
-
outputs.append(output)
|
223
|
-
|
224
|
-
if not dry_run:
|
225
|
-
persistence.write(outputs)
|
226
|
-
if state:
|
227
|
-
state.add(
|
228
|
-
c.name,
|
229
|
-
{
|
230
|
-
"hash": template_hash,
|
231
|
-
"timestamp": datetime.utcnow().isoformat(),
|
232
|
-
},
|
233
|
-
force=True,
|
246
|
+
with PersistenceTransaction(persistence, dry_run) as p:
|
247
|
+
for template in c.templates:
|
248
|
+
output = self.process_template(
|
249
|
+
template,
|
250
|
+
variables,
|
251
|
+
p,
|
252
|
+
ruamel_instance,
|
234
253
|
)
|
254
|
+
if output:
|
255
|
+
output.input = TemplateInput(
|
256
|
+
collection=c.name,
|
257
|
+
collection_hash=template_hash,
|
258
|
+
)
|
259
|
+
outputs.append(output)
|
260
|
+
|
261
|
+
if not dry_run:
|
262
|
+
p.write(outputs)
|
235
263
|
|
236
264
|
@property
|
237
265
|
def name(self) -> str:
|
@@ -239,14 +267,18 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
239
267
|
|
240
268
|
def run(self, dry_run: bool) -> None:
|
241
269
|
persistence: FilePersistence
|
242
|
-
|
243
|
-
|
270
|
+
ruaml_instance = create_ruamel_instance(explicit_start=True)
|
271
|
+
|
272
|
+
if not self.params.clone_repo and self.params.app_interface_data_path:
|
244
273
|
persistence = LocalFilePersistence(self.params.app_interface_data_path)
|
245
|
-
|
274
|
+
self.reconcile(dry_run, persistence, ruaml_instance)
|
275
|
+
|
276
|
+
elif self.params.clone_repo:
|
277
|
+
gitlab_instances = get_gitlab_instances()
|
246
278
|
vcs = VCS(
|
247
279
|
secret_reader=self.secret_reader,
|
248
280
|
github_orgs=get_github_orgs(),
|
249
|
-
gitlab_instances=
|
281
|
+
gitlab_instances=gitlab_instances,
|
250
282
|
app_interface_repo_url=get_app_interface_repo_url(),
|
251
283
|
dry_run=dry_run,
|
252
284
|
allow_deleting_mrs=True,
|
@@ -256,10 +288,24 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
|
|
256
288
|
vcs=vcs,
|
257
289
|
parser=create_parser(),
|
258
290
|
)
|
259
|
-
|
291
|
+
url = get_app_interface_repo_url()
|
292
|
+
|
293
|
+
ssl_verify = next(
|
294
|
+
g.ssl_verify for g in gitlab_instances if url.startswith(g.url)
|
295
|
+
)
|
260
296
|
|
261
|
-
|
297
|
+
with tempfile.TemporaryDirectory(
|
298
|
+
prefix=f"{QONTRACT_INTEGRATION}-",
|
299
|
+
) as temp_dir:
|
300
|
+
logging.debug(f"Cloning {url} to {temp_dir}")
|
262
301
|
|
263
|
-
|
302
|
+
clone(url, temp_dir, depth=1, verify=ssl_verify)
|
264
303
|
|
265
|
-
|
304
|
+
persistence = ClonedRepoGitlabPersistence(
|
305
|
+
temp_dir, vcs, merge_request_manager
|
306
|
+
)
|
307
|
+
|
308
|
+
self.reconcile(dry_run, persistence, ruaml_instance)
|
309
|
+
|
310
|
+
else:
|
311
|
+
raise ValueError("App-interface-data-path must be set")
|
reconcile/utils/git.py
CHANGED
@@ -6,9 +6,14 @@ class GitError(Exception):
|
|
6
6
|
pass
|
7
7
|
|
8
8
|
|
9
|
-
def clone(repo_url, wd):
|
10
|
-
|
11
|
-
|
9
|
+
def clone(repo_url, wd, depth=None, verify=True):
|
10
|
+
cmd = ["git"]
|
11
|
+
if not verify:
|
12
|
+
cmd += ["-c", "http.sslVerify=false"]
|
13
|
+
cmd += ["clone"]
|
14
|
+
if depth:
|
15
|
+
cmd += ["--depth", str(depth)]
|
16
|
+
cmd += [repo_url, wd]
|
12
17
|
result = subprocess.run(
|
13
18
|
cmd, cwd=wd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
|
14
19
|
)
|
@@ -17,7 +22,6 @@ def clone(repo_url, wd):
|
|
17
22
|
|
18
23
|
|
19
24
|
def checkout(commit, wd):
|
20
|
-
# pylint: disable=subprocess-run-check
|
21
25
|
cmd = ["git", "checkout", commit]
|
22
26
|
result = subprocess.run(
|
23
27
|
cmd, cwd=wd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
|
@@ -29,7 +33,6 @@ def checkout(commit, wd):
|
|
29
33
|
def is_file_in_git_repo(file_path):
|
30
34
|
real_path = os.path.realpath(file_path)
|
31
35
|
dir_path = os.path.dirname(real_path)
|
32
|
-
# pylint: disable=subprocess-run-check
|
33
36
|
cmd = ["git", "rev-parse", "--is-inside-work-tree"]
|
34
37
|
result = subprocess.run(
|
35
38
|
cmd, cwd=dir_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc718.dist-info → qontract_reconcile-0.10.1rc719.dist-info}/top_level.txt
RENAMED
File without changes
|