qontract-reconcile 0.10.1rc605__py3-none-any.whl → 0.10.1rc607__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.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/METADATA +2 -1
- {qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/RECORD +10 -9
- {qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/entry_points.txt +1 -0
- reconcile/aus/version_gate_approver.py +4 -1
- reconcile/templating/rendering.py +11 -5
- reconcile/templating/validator.py +85 -56
- reconcile/utils/rosa/session.py +6 -0
- tools/template_validation.py +107 -0
- {qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.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.1rc607
|
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
|
@@ -62,4 +62,5 @@ Requires-Dist: requests-oauthlib ~=1.3
|
|
62
62
|
Requires-Dist: dt ==1.1.61
|
63
63
|
Requires-Dist: jsonpatch ~=1.33
|
64
64
|
Requires-Dist: jsonpointer ~=2.4
|
65
|
+
Requires-Dist: yamllint ==1.34.0
|
65
66
|
|
{qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/RECORD
RENAMED
@@ -128,7 +128,7 @@ reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=fshslI27hrqT40qrVsVOQaW
|
|
128
128
|
reconcile/aus/ocm_upgrade_scheduler.py,sha256=7cK2SakCFkl5EdnqUEAYdUo4pUnnf-SsUR10uytAGyE,3058
|
129
129
|
reconcile/aus/ocm_upgrade_scheduler_org.py,sha256=OBgE5mnVdQQV4tMH0AE2V_PDt9Gy6d-LyuPceqjORts,2331
|
130
130
|
reconcile/aus/upgrades.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
131
|
-
reconcile/aus/version_gate_approver.py,sha256=
|
131
|
+
reconcile/aus/version_gate_approver.py,sha256=sZEVB0qVpAXpqymHxavFprkqmqt29fZAIuzSjPHLLrQ,7239
|
132
132
|
reconcile/aus/version_gates/__init__.py,sha256=fWx-IvS132Wpa4gWNIuoNvFwqhkuUuFWYWq5-xiLklI,362
|
133
133
|
reconcile/aus/version_gates/handler.py,sha256=S_isQPYHbG4DERiUEvQBZ6ngiFX3uMmATA-Q_eNKmFk,839
|
134
134
|
reconcile/aus/version_gates/ocp_gate_handler.py,sha256=RW1ppDaCZXVegV9AzzqYXxDUu_Z_7d43Z5h2Pk_piKc,716
|
@@ -393,8 +393,8 @@ reconcile/templates/email.yml.j2,sha256=OZgczNRgXPj2gVYTgwQyHAQrMGu7xp-e4W1rX19G
|
|
393
393
|
reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9igCUQhCnFDYh6xw-zcIbU,570
|
394
394
|
reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
395
395
|
reconcile/templating/renderer.py,sha256=IOZN0ASSKqp4JGXZ1BDba4XosD3nCuqhStxHMGBcxDI,5119
|
396
|
-
reconcile/templating/rendering.py,sha256=
|
397
|
-
reconcile/templating/validator.py,sha256=
|
396
|
+
reconcile/templating/rendering.py,sha256=msMGqCNh7M0c6A1l_7KBmT3GRHH9gsB8htN_9bGtwe4,4949
|
397
|
+
reconcile/templating/validator.py,sha256=6ZZs5cREIndgAUHiR1SeDGwaeSLJTJBKHww0nXzgqhQ,4141
|
398
398
|
reconcile/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
399
399
|
reconcile/test/conftest.py,sha256=rQousYrxUz-EwAIbsYO6bIwR1B4CrOz9y_zaUVo2lfI,4466
|
400
400
|
reconcile/test/fixtures.py,sha256=9SDWAUlSd1rCx7z3GhULHcpr-I6FyCsXxaFAZIqYQsQ,591
|
@@ -670,7 +670,7 @@ reconcile/utils/ocm/syncsets.py,sha256=zvji9qWvInIRTdoybMaM9-VUhq4L1VewWfWCQmp4u
|
|
670
670
|
reconcile/utils/ocm/upgrades.py,sha256=ZWDfg3VrmRGx7Re-JjecRjwmn7Rh-dsuLA3OljbCByg,6616
|
671
671
|
reconcile/utils/rosa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
672
672
|
reconcile/utils/rosa/rosa_cli.py,sha256=zuXBRh-cJK4yOPjtiSRbUQRURemFiLlWdjj3XteuQ-4,10475
|
673
|
-
reconcile/utils/rosa/session.py,sha256=
|
673
|
+
reconcile/utils/rosa/session.py,sha256=nPEmSNos0_ATQEVg2KckAlVyIEBvxUVo7pa0WMACoOU,4150
|
674
674
|
reconcile/utils/runtime/__init__.py,sha256=sfk92MGfsBh9tKYHl_FH17NdEsrGBwgDFTb7KNKoIfY,107
|
675
675
|
reconcile/utils/runtime/desired_state_diff.py,sha256=finZnWoVDCiYTgu4lGk8G4QOFAGgiIDhD3fcnVqYoxM,8108
|
676
676
|
reconcile/utils/runtime/environment.py,sha256=JLptHJoYyeLdMBghJppttP3wZ5HxHLMLgUcfGjIiKLM,2087
|
@@ -700,6 +700,7 @@ tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QE
|
|
700
700
|
tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
|
701
701
|
tools/qontract_cli.py,sha256=cnZLJJS-FRUQFUAD90ivt-WEv8NfaemqBvZ9CaFV_kI,110957
|
702
702
|
tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
|
703
|
+
tools/template_validation.py,sha256=XZ6i1iTN2Lknh-8uS-kcbTmYBzJhzl2tLYN6hP33N2Q,3244
|
703
704
|
tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
704
705
|
tools/cli_commands/gpg_encrypt.py,sha256=w8hl4jIEWk5wKbEFN6fVEOwUJGmdlvOqYodW3XSN7mU,4978
|
705
706
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
@@ -709,8 +710,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
709
710
|
tools/test/test_qontract_cli.py,sha256=OvalpVRfY4pNmpMaWHHYqBjV68b1eGQjX8SCyTAXb1w,3501
|
710
711
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
711
712
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
712
|
-
qontract_reconcile-0.10.
|
713
|
-
qontract_reconcile-0.10.
|
714
|
-
qontract_reconcile-0.10.
|
715
|
-
qontract_reconcile-0.10.
|
716
|
-
qontract_reconcile-0.10.
|
713
|
+
qontract_reconcile-0.10.1rc607.dist-info/METADATA,sha256=YA_uMko_z5JCYKe6FRbUNu7lVLlorRq_4HKpwJDrj3Q,2382
|
714
|
+
qontract_reconcile-0.10.1rc607.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
715
|
+
qontract_reconcile-0.10.1rc607.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
716
|
+
qontract_reconcile-0.10.1rc607.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
717
|
+
qontract_reconcile-0.10.1rc607.dist-info/RECORD,,
|
@@ -5,3 +5,4 @@ glitchtip-access-reporter = tools.glitchtip_access_reporter:main
|
|
5
5
|
glitchtip-access-revalidation = tools.glitchtip_access_revalidation:main
|
6
6
|
qontract-cli = tools.qontract_cli:root
|
7
7
|
qontract-reconcile = reconcile.cli:integration
|
8
|
+
template-validation = tools.template_validation:main
|
@@ -178,6 +178,9 @@ class VersionGateApprover(QontractReconcileIntegration[VersionGateApproverParams
|
|
178
178
|
for gate in gates:
|
179
179
|
if gate.label in self.handlers and gate.label not in enabled_gate_handlers:
|
180
180
|
continue
|
181
|
+
logging.info(
|
182
|
+
f"handle gate {gate.label} for cluster {cluster.name} {{gate_id = {gate.id}), version = {gate.version_raw_id_prefix}, cluster_id = {cluster.id}, org_id = {ocm_org_id}}}"
|
183
|
+
)
|
181
184
|
success = self.handlers[gate.label].handle(
|
182
185
|
ocm_api=ocm_api,
|
183
186
|
ocm_org_id=ocm_org_id,
|
@@ -189,7 +192,7 @@ class VersionGateApprover(QontractReconcileIntegration[VersionGateApproverParams
|
|
189
192
|
create_version_agreement(ocm_api, gate.id, cluster.id)
|
190
193
|
elif not success:
|
191
194
|
logging.error(
|
192
|
-
f"
|
195
|
+
f"failed to handle gate {gate.label} for cluster {cluster.name} {{gate_id = {gate.id}), version = {gate.version_raw_id_prefix}, cluster_id = {cluster.id}, org_id = {ocm_org_id}}}"
|
193
196
|
)
|
194
197
|
|
195
198
|
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import logging
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
+
from io import StringIO
|
3
4
|
from typing import Any, Optional, Protocol
|
4
5
|
|
5
6
|
from pydantic import BaseModel
|
6
|
-
from ruamel import yaml
|
7
7
|
|
8
8
|
from reconcile.utils.jinja2.utils import Jinja2TemplateError, process_jinja2_template
|
9
9
|
from reconcile.utils.jsonpath import parse_jsonpath
|
10
|
+
from reconcile.utils.ruamel import create_ruamel_instance
|
10
11
|
from reconcile.utils.secret_reader import SecretReaderBase
|
11
12
|
|
12
13
|
|
@@ -40,11 +41,12 @@ class Renderer(ABC):
|
|
40
41
|
self,
|
41
42
|
template: Template,
|
42
43
|
data: TemplateData,
|
43
|
-
secret_reader: SecretReaderBase,
|
44
|
+
secret_reader: Optional[SecretReaderBase] = None,
|
44
45
|
):
|
45
46
|
self.template = template
|
46
47
|
self.data = data
|
47
48
|
self.secret_reader = secret_reader
|
49
|
+
self.ruaml_instace = create_ruamel_instance()
|
48
50
|
|
49
51
|
def _jinja2_render_kwargs(self) -> dict[str, Any]:
|
50
52
|
return {**self.data.variables, "current": self.data.current}
|
@@ -108,7 +110,9 @@ class PatchRenderer(Renderer):
|
|
108
110
|
)
|
109
111
|
matched_value = matched_values[0]
|
110
112
|
|
111
|
-
data_to_add =
|
113
|
+
data_to_add = self.ruaml_instace.load(
|
114
|
+
self._render_template(self.template.template)
|
115
|
+
)
|
112
116
|
|
113
117
|
if isinstance(matched_value, list):
|
114
118
|
if not self.template.patch.identifier:
|
@@ -136,13 +140,15 @@ class PatchRenderer(Renderer):
|
|
136
140
|
else:
|
137
141
|
matched_value.update(data_to_add)
|
138
142
|
|
139
|
-
|
143
|
+
with StringIO() as s:
|
144
|
+
self.ruaml_instace.dump(self.data.current, s)
|
145
|
+
return s.getvalue()
|
140
146
|
|
141
147
|
|
142
148
|
def create_renderer(
|
143
149
|
template: Template,
|
144
150
|
data: TemplateData,
|
145
|
-
secret_reader: SecretReaderBase,
|
151
|
+
secret_reader: Optional[SecretReaderBase] = None,
|
146
152
|
) -> Renderer:
|
147
153
|
if template.patch:
|
148
154
|
return PatchRenderer(template=template, data=data, secret_reader=secret_reader)
|
@@ -5,13 +5,17 @@ from typing import Callable, Optional
|
|
5
5
|
from pydantic import BaseModel
|
6
6
|
from ruamel import yaml
|
7
7
|
|
8
|
-
from reconcile.gql_definitions.templating.templates import
|
9
|
-
|
8
|
+
from reconcile.gql_definitions.templating.templates import (
|
9
|
+
TemplateTestV1,
|
10
|
+
TemplateV1,
|
11
|
+
query,
|
12
|
+
)
|
13
|
+
from reconcile.templating.rendering import Renderer, TemplateData, create_renderer
|
10
14
|
from reconcile.utils import gql
|
11
15
|
from reconcile.utils.runtime.integration import (
|
12
16
|
QontractReconcileIntegration,
|
13
|
-
RunParamsTypeVar,
|
14
17
|
)
|
18
|
+
from reconcile.utils.secret_reader import SecretReaderBase
|
15
19
|
|
16
20
|
QONTRACT_INTEGRATION = "template-validator"
|
17
21
|
|
@@ -31,70 +35,95 @@ class TemplateDiff(BaseModel):
|
|
31
35
|
|
32
36
|
|
33
37
|
class TemplateValidatorIntegration(QontractReconcileIntegration):
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
@staticmethod
|
39
|
+
def _create_renderer(
|
40
|
+
template: TemplateV1,
|
41
|
+
template_test: TemplateTestV1,
|
42
|
+
secret_reader: Optional[SecretReaderBase] = None,
|
43
|
+
) -> Renderer:
|
44
|
+
return create_renderer(
|
45
|
+
template,
|
46
|
+
TemplateData(
|
47
|
+
variables=template_test.variables or {},
|
48
|
+
current=yaml.load(
|
49
|
+
template_test.current or "", Loader=yaml.RoundTripLoader
|
50
|
+
),
|
51
|
+
),
|
52
|
+
secret_reader=secret_reader,
|
53
|
+
)
|
54
|
+
|
55
|
+
@staticmethod
|
56
|
+
def validate_template(
|
57
|
+
template: TemplateV1,
|
58
|
+
template_test: TemplateTestV1,
|
59
|
+
secret_reader: Optional[SecretReaderBase] = None,
|
60
|
+
) -> list[TemplateDiff]:
|
61
|
+
diffs: list[TemplateDiff] = []
|
62
|
+
|
63
|
+
r = TemplateValidatorIntegration._create_renderer(
|
64
|
+
template, template_test, secret_reader=secret_reader
|
45
65
|
)
|
46
|
-
|
47
|
-
|
48
|
-
|
66
|
+
|
67
|
+
# Check target path
|
68
|
+
if template_test.expected_target_path:
|
69
|
+
rendered_target_path = r.render_target_path().strip()
|
70
|
+
if rendered_target_path != template_test.expected_target_path.strip():
|
71
|
+
diffs.append(
|
72
|
+
TemplateDiff(
|
73
|
+
template=template.name,
|
74
|
+
test=template_test.name,
|
75
|
+
diff=f"Target path mismatch, got: {rendered_target_path}, expected: {template_test.expected_target_path}",
|
76
|
+
)
|
77
|
+
)
|
78
|
+
|
79
|
+
# Check condition
|
80
|
+
should_render = r.render_condition()
|
81
|
+
if (
|
82
|
+
template_test.expected_to_render is not None
|
83
|
+
and template_test.expected_to_render != should_render
|
84
|
+
):
|
85
|
+
diffs.append(
|
86
|
+
TemplateDiff(
|
87
|
+
template=template.name,
|
88
|
+
test=template_test.name,
|
89
|
+
diff=f"Condition mismatch, got: {should_render}, expected: {template_test.expected_to_render}",
|
90
|
+
)
|
49
91
|
)
|
50
92
|
|
93
|
+
# Check template output
|
94
|
+
if should_render:
|
95
|
+
output = r.render_output()
|
96
|
+
diff = list(
|
97
|
+
context_diff(
|
98
|
+
output.splitlines(keepends=True),
|
99
|
+
template_test.expected_output.splitlines(keepends=True),
|
100
|
+
)
|
101
|
+
)
|
102
|
+
if diff:
|
103
|
+
diffs.append(
|
104
|
+
TemplateDiff(
|
105
|
+
template=template.name,
|
106
|
+
test=template_test.name,
|
107
|
+
diff="".join(diff),
|
108
|
+
)
|
109
|
+
)
|
110
|
+
|
111
|
+
return diffs
|
112
|
+
|
51
113
|
def run(self, dry_run: bool) -> None:
|
114
|
+
diffs: list[TemplateDiff] = []
|
115
|
+
|
52
116
|
for template in get_templates():
|
53
117
|
for test in template.template_test:
|
54
118
|
logging.info(f"Running test {test.name} for template {template.name}")
|
55
|
-
|
56
|
-
template,
|
57
|
-
TemplateData(
|
58
|
-
variables=test.variables or {},
|
59
|
-
current=yaml.load(
|
60
|
-
test.current or "", Loader=yaml.RoundTripLoader
|
61
|
-
),
|
62
|
-
),
|
63
|
-
secret_reader=self.secret_reader,
|
64
|
-
)
|
65
|
-
if test.expected_target_path:
|
66
|
-
self.diff_result(
|
67
|
-
template.name,
|
68
|
-
test.name,
|
69
|
-
r.render_target_path().strip(),
|
70
|
-
test.expected_target_path.strip(),
|
71
|
-
)
|
72
|
-
should_render = r.render_condition()
|
73
|
-
if (
|
74
|
-
test.expected_to_render is not None
|
75
|
-
and test.expected_to_render != should_render
|
76
|
-
):
|
77
|
-
self.diffs.append(
|
78
|
-
TemplateDiff(
|
79
|
-
template=template.name,
|
80
|
-
test=test.name,
|
81
|
-
diff=f"Condition mismatch, got: {should_render}, expected: {test.expected_to_render}",
|
82
|
-
)
|
83
|
-
)
|
84
|
-
if should_render:
|
85
|
-
self.diff_result(
|
86
|
-
template.name,
|
87
|
-
test.name,
|
88
|
-
r.render_output().strip(),
|
89
|
-
test.expected_output.strip(),
|
90
|
-
)
|
119
|
+
diffs.extend(self.validate_template(template, test, self.secret_reader))
|
91
120
|
|
92
|
-
if
|
93
|
-
for diff in
|
121
|
+
if diffs:
|
122
|
+
for diff in diffs:
|
94
123
|
logging.error(
|
95
124
|
f"template: {diff.template}, test: {diff.test}: {diff.diff}"
|
96
125
|
)
|
97
|
-
raise ValueError("Template validation")
|
126
|
+
raise ValueError("Template validation failed")
|
98
127
|
|
99
128
|
@property
|
100
129
|
def name(self) -> str:
|
reconcile/utils/rosa/session.py
CHANGED
@@ -89,6 +89,9 @@ class RosaSession:
|
|
89
89
|
def upgrade_account_roles(
|
90
90
|
self, role_prefix: str, minor_version: str, channel_group: str, dry_run: bool
|
91
91
|
) -> None:
|
92
|
+
logging.info(
|
93
|
+
f"Upgrade account roles in AWS account {self.aws_account_id} to {minor_version} ({channel_group})"
|
94
|
+
)
|
92
95
|
if not dry_run:
|
93
96
|
result = self.cli_execute(
|
94
97
|
f"rosa upgrade account-roles --prefix {role_prefix} --version {minor_version} --channel-group {channel_group} -y -m=auto"
|
@@ -104,6 +107,9 @@ class RosaSession:
|
|
104
107
|
Upgrades the operator roles of a cluster to match the latest
|
105
108
|
policy versions available for the cluster.
|
106
109
|
"""
|
110
|
+
logging.info(
|
111
|
+
f"Upgrade operator roles in AWS account {self.aws_account_id} for cluster {cluster_id}"
|
112
|
+
)
|
107
113
|
if not dry_run:
|
108
114
|
result = self.cli_execute(
|
109
115
|
cmd=f"rosa upgrade operator-roles --cluster {cluster_id} -y -m=auto",
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import json
|
2
|
+
import os.path
|
3
|
+
import sys
|
4
|
+
|
5
|
+
import click
|
6
|
+
from yamllint import linter # type: ignore
|
7
|
+
from yamllint.config import YamlLintConfig # type: ignore
|
8
|
+
|
9
|
+
from reconcile.gql_definitions.templating.templates import TemplateV1
|
10
|
+
from reconcile.templating.validator import TemplateDiff, TemplateValidatorIntegration
|
11
|
+
from reconcile.utils.models import data_default_none
|
12
|
+
from reconcile.utils.ruamel import create_ruamel_instance
|
13
|
+
|
14
|
+
|
15
|
+
def load_clean_yaml(ai_path: str, path: str) -> dict:
|
16
|
+
if not path.startswith("/data/"):
|
17
|
+
to_load = os.path.join(ai_path, "data", path.lstrip("/"))
|
18
|
+
else:
|
19
|
+
to_load = os.path.join(ai_path, path.lstrip("/"))
|
20
|
+
|
21
|
+
y = load_yaml(to_load)
|
22
|
+
|
23
|
+
if "$schema" in y:
|
24
|
+
del y["$schema"]
|
25
|
+
return y
|
26
|
+
|
27
|
+
|
28
|
+
def load_yaml(to_load: str) -> dict:
|
29
|
+
ruamel_instance = create_ruamel_instance()
|
30
|
+
with open(to_load, "r", encoding="utf-8") as file:
|
31
|
+
return ruamel_instance.load(file)
|
32
|
+
|
33
|
+
|
34
|
+
def print_lint_problems(lint_problems: list[linter.LintProblem]) -> None:
|
35
|
+
print("--------- LINTING ISSUES ---------")
|
36
|
+
print(f"Found {len(lint_problems)} lint problems:")
|
37
|
+
for problem in lint_problems:
|
38
|
+
print(f"Lint error in line: {problem.line}, {problem.desc}")
|
39
|
+
print("--------- END LINTING ISSUES ---------")
|
40
|
+
|
41
|
+
|
42
|
+
def print_test_diffs(diffs: list[TemplateDiff]) -> None:
|
43
|
+
print("--------- TEMPLATE TEST DIFF ---------")
|
44
|
+
print("Template validation failed")
|
45
|
+
for d in diffs:
|
46
|
+
for line in d.diff.splitlines():
|
47
|
+
if line:
|
48
|
+
print(f"{line}")
|
49
|
+
print("--------- END TEMPLATE TEST DIFF ---------")
|
50
|
+
|
51
|
+
|
52
|
+
@click.command()
|
53
|
+
@click.option(
|
54
|
+
"--ai-path",
|
55
|
+
help="Path to the bundle file",
|
56
|
+
default=None,
|
57
|
+
required=True,
|
58
|
+
)
|
59
|
+
@click.option(
|
60
|
+
"--template-path",
|
61
|
+
help="Path to the template file",
|
62
|
+
default=None,
|
63
|
+
required=True,
|
64
|
+
)
|
65
|
+
def main(ai_path: str, template_path: str) -> None:
|
66
|
+
okay = True
|
67
|
+
templateRaw = load_clean_yaml(ai_path, template_path)
|
68
|
+
|
69
|
+
tests = []
|
70
|
+
for testRaw in templateRaw["templateTest"]:
|
71
|
+
test_yaml = load_clean_yaml(ai_path, testRaw["$ref"])
|
72
|
+
variables = json.dumps(test_yaml["variables"])
|
73
|
+
test_yaml["variables"] = variables
|
74
|
+
tests.append(test_yaml)
|
75
|
+
|
76
|
+
templateRaw["templateTest"] = tests
|
77
|
+
template: TemplateV1 = TemplateV1(**data_default_none(TemplateV1, templateRaw))
|
78
|
+
|
79
|
+
# templates_to_validate = {}
|
80
|
+
for test in template.template_test:
|
81
|
+
diffs: list[TemplateDiff] = []
|
82
|
+
print("Running tests:", test.name)
|
83
|
+
diffs.extend(TemplateValidatorIntegration.validate_template(template, test))
|
84
|
+
|
85
|
+
renderer = TemplateValidatorIntegration._create_renderer(template, test)
|
86
|
+
if renderer.render_condition():
|
87
|
+
output = renderer.render_output()
|
88
|
+
lint_problems = list(
|
89
|
+
linter.run(output, YamlLintConfig(file=f"{ai_path}/.yamllint"), "")
|
90
|
+
)
|
91
|
+
if lint_problems:
|
92
|
+
okay = False
|
93
|
+
print_lint_problems(lint_problems)
|
94
|
+
|
95
|
+
if diffs:
|
96
|
+
okay = False
|
97
|
+
print_test_diffs(diffs)
|
98
|
+
|
99
|
+
if okay:
|
100
|
+
print("... passed")
|
101
|
+
sys.exit(0)
|
102
|
+
print("... failed")
|
103
|
+
sys.exit(1)
|
104
|
+
|
105
|
+
|
106
|
+
if __name__ == "__main__":
|
107
|
+
main() # pylint: disable=no-value-for-parameter
|
File without changes
|
{qontract_reconcile-0.10.1rc605.dist-info → qontract_reconcile-0.10.1rc607.dist-info}/top_level.txt
RENAMED
File without changes
|