qontract-reconcile 0.10.2.dev294__py3-none-any.whl → 0.10.2.dev296__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.dev294.dist-info → qontract_reconcile-0.10.2.dev296.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev294.dist-info → qontract_reconcile-0.10.2.dev296.dist-info}/RECORD +13 -13
- reconcile/automated_actions/config/integration.py +14 -0
- reconcile/aws_iam_password_reset.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +45 -2
- reconcile/gql_definitions/introspection.json +247 -0
- reconcile/ocm_additional_routers.py +7 -3
- reconcile/queries.py +227 -103
- reconcile/utils/ocm/ocm.py +3 -3
- tools/app_interface_reporter.py +36 -28
- tools/qontract_cli.py +28 -16
- {qontract_reconcile-0.10.2.dev294.dist-info → qontract_reconcile-0.10.2.dev296.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev294.dist-info → qontract_reconcile-0.10.2.dev296.dist-info}/entry_points.txt +0 -0
reconcile/utils/ocm/ocm.py
CHANGED
@@ -403,7 +403,7 @@ class OCM:
|
|
403
403
|
api = (
|
404
404
|
f"{CS_API_BASE}/v1/clusters/{cluster_id}" + "/external_configuration/labels"
|
405
405
|
)
|
406
|
-
items = self._get_json(api).get("items")
|
406
|
+
items = self._get_json(api).get("items", [])
|
407
407
|
item = [item for item in items if label.items() <= item.items()]
|
408
408
|
if not item:
|
409
409
|
return
|
@@ -597,14 +597,14 @@ class OCM:
|
|
597
597
|
def _init_addons(self):
|
598
598
|
"""Returns a list of Addons"""
|
599
599
|
api = f"{CS_API_BASE}/v1/addons"
|
600
|
-
self.addons = self._get_json(api).get("items")
|
600
|
+
self.addons = self._get_json(api).get("items", [])
|
601
601
|
|
602
602
|
def _init_version_gates(self):
|
603
603
|
"""Returns a list of version gates"""
|
604
604
|
if self.version_gates:
|
605
605
|
return
|
606
606
|
api = f"{CS_API_BASE}/v1/version_gates"
|
607
|
-
self.version_gates = self._get_json(api).get("items")
|
607
|
+
self.version_gates = self._get_json(api).get("items", [])
|
608
608
|
|
609
609
|
def get_addon(self, id):
|
610
610
|
for addon in self.addons:
|
tools/app_interface_reporter.py
CHANGED
@@ -275,17 +275,17 @@ def get_apps_data(
|
|
275
275
|
cluster = sample.labels["cluster"]
|
276
276
|
if app_namespace["cluster"]["name"] != cluster:
|
277
277
|
continue
|
278
|
-
|
279
|
-
if app_namespace["name"] !=
|
278
|
+
sample_namespace = sample.labels["namespace"]
|
279
|
+
if app_namespace["name"] != sample_namespace:
|
280
280
|
continue
|
281
281
|
severity = sample.labels["severity"]
|
282
282
|
if cluster not in vuln_mx:
|
283
283
|
vuln_mx[cluster] = {}
|
284
|
-
if
|
285
|
-
vuln_mx[cluster][
|
286
|
-
if severity not in vuln_mx[cluster][
|
284
|
+
if sample_namespace not in vuln_mx[cluster]:
|
285
|
+
vuln_mx[cluster][sample_namespace] = {}
|
286
|
+
if severity not in vuln_mx[cluster][sample_namespace]:
|
287
287
|
value = int(sample.value)
|
288
|
-
vuln_mx[cluster][
|
288
|
+
vuln_mx[cluster][sample_namespace][severity] = value
|
289
289
|
for family in text_string_to_metric_families(validt_metrics):
|
290
290
|
for sample in family.samples:
|
291
291
|
if sample.name == "deploymentvalidation_total":
|
@@ -293,8 +293,8 @@ def get_apps_data(
|
|
293
293
|
cluster = sample.labels["cluster"]
|
294
294
|
if app_namespace["cluster"]["name"] != cluster:
|
295
295
|
continue
|
296
|
-
|
297
|
-
if app_namespace["name"] !=
|
296
|
+
sample_namespace = sample.labels["namespace"]
|
297
|
+
if app_namespace["name"] != sample_namespace:
|
298
298
|
continue
|
299
299
|
validation = sample.labels["validation"]
|
300
300
|
# dvo: fail == 1, pass == 0, py: true == 1, false == 0
|
@@ -302,14 +302,19 @@ def get_apps_data(
|
|
302
302
|
status = ("Passed", "Failed")[int(sample.labels["status"])]
|
303
303
|
if cluster not in validt_mx:
|
304
304
|
validt_mx[cluster] = {}
|
305
|
-
if
|
306
|
-
validt_mx[cluster][
|
307
|
-
if validation not in validt_mx[cluster][
|
308
|
-
validt_mx[cluster][
|
309
|
-
if
|
310
|
-
|
305
|
+
if sample_namespace not in validt_mx[cluster]:
|
306
|
+
validt_mx[cluster][sample_namespace] = {}
|
307
|
+
if validation not in validt_mx[cluster][sample_namespace]:
|
308
|
+
validt_mx[cluster][sample_namespace][validation] = {}
|
309
|
+
if (
|
310
|
+
status
|
311
|
+
not in validt_mx[cluster][sample_namespace][validation]
|
312
|
+
):
|
313
|
+
validt_mx[cluster][sample_namespace][validation][
|
314
|
+
status
|
315
|
+
] = {}
|
311
316
|
value = int(sample.value)
|
312
|
-
validt_mx[cluster][
|
317
|
+
validt_mx[cluster][sample_namespace][validation][status] = value
|
313
318
|
for family in text_string_to_metric_families(slo_metrics):
|
314
319
|
for sample in family.samples:
|
315
320
|
if sample.name == "serviceslometrics":
|
@@ -317,25 +322,28 @@ def get_apps_data(
|
|
317
322
|
cluster = sample.labels["cluster"]
|
318
323
|
if app_namespace["cluster"]["name"] != cluster:
|
319
324
|
continue
|
320
|
-
|
321
|
-
if app_namespace["name"] !=
|
325
|
+
sample_namespace = sample.labels["namespace"]
|
326
|
+
if app_namespace["name"] != sample_namespace:
|
322
327
|
continue
|
323
328
|
slo_doc_name = sample.labels["slodoc"]
|
324
329
|
slo_name = sample.labels["name"]
|
325
330
|
if cluster not in slo_mx:
|
326
331
|
slo_mx[cluster] = {}
|
327
|
-
if
|
328
|
-
slo_mx[cluster][
|
329
|
-
if slo_doc_name not in slo_mx[cluster][
|
330
|
-
slo_mx[cluster][
|
331
|
-
if
|
332
|
-
|
333
|
-
|
334
|
-
|
332
|
+
if sample_namespace not in slo_mx[cluster]:
|
333
|
+
slo_mx[cluster][sample_namespace] = {}
|
334
|
+
if slo_doc_name not in slo_mx[cluster][sample_namespace]:
|
335
|
+
slo_mx[cluster][sample_namespace][slo_doc_name] = {}
|
336
|
+
if (
|
337
|
+
slo_name
|
338
|
+
not in slo_mx[cluster][sample_namespace][slo_doc_name]
|
339
|
+
):
|
340
|
+
slo_mx[cluster][sample_namespace][slo_doc_name][
|
341
|
+
slo_name
|
342
|
+
] = {sample.labels["type"]: sample.value}
|
335
343
|
else:
|
336
|
-
slo_mx[cluster][
|
337
|
-
|
338
|
-
})
|
344
|
+
slo_mx[cluster][sample_namespace][slo_doc_name][
|
345
|
+
slo_name
|
346
|
+
].update({sample.labels["type"]: sample.value})
|
339
347
|
app["container_vulnerabilities"] = vuln_mx
|
340
348
|
app["deployment_validations"] = validt_mx
|
341
349
|
app["service_slo"] = slo_mx
|
tools/qontract_cli.py
CHANGED
@@ -1715,7 +1715,10 @@ def add_resource(item: dict, resource: Mapping, columns: list[str]) -> None:
|
|
1715
1715
|
@click.pass_context
|
1716
1716
|
def cluster_openshift_resources(ctx: click.Context) -> None:
|
1717
1717
|
gqlapi = gql.get_api()
|
1718
|
-
|
1718
|
+
data = gqlapi.query(orb.NAMESPACES_QUERY)
|
1719
|
+
if not data:
|
1720
|
+
raise ValueError("no namespaces found")
|
1721
|
+
namespaces = data.get("namespaces", [])
|
1719
1722
|
columns = ["name", "total"]
|
1720
1723
|
results: dict = {}
|
1721
1724
|
for ns_info in namespaces:
|
@@ -1912,6 +1915,7 @@ def rds_recommendations(ctx: click.Context) -> None:
|
|
1912
1915
|
print("[TOC]")
|
1913
1916
|
for account in accounts:
|
1914
1917
|
account_name = account.get("name")
|
1918
|
+
assert account_name is not None # make mypy happy
|
1915
1919
|
account_deployment_regions = account.get("supportedDeploymentRegions")
|
1916
1920
|
for region in account_deployment_regions or []:
|
1917
1921
|
with AWSApi(1, [account], settings=settings, init_users=False) as aws:
|
@@ -1926,9 +1930,9 @@ def rds_recommendations(ctx: click.Context) -> None:
|
|
1926
1930
|
recommendations = [
|
1927
1931
|
{
|
1928
1932
|
**rec,
|
1929
|
-
"ResourceName": rec
|
1933
|
+
"ResourceName": rec.get("ResourceArn", "").split(":")[-1],
|
1930
1934
|
# The Description field has \n that are causing issues with the markdown table
|
1931
|
-
"Description": rec
|
1935
|
+
"Description": rec.get("Description", "").replace("\n", " "),
|
1932
1936
|
}
|
1933
1937
|
for rec in db_recommendations
|
1934
1938
|
if rec.get("Status") not in ignored_statuses
|
@@ -2710,16 +2714,16 @@ def ec2_jenkins_workers(
|
|
2710
2714
|
)
|
2711
2715
|
continue
|
2712
2716
|
instance = ec2.Instance(i["InstanceId"])
|
2713
|
-
state = instance.state
|
2717
|
+
state = instance.state.get("Name")
|
2714
2718
|
if state != "running":
|
2715
2719
|
continue
|
2716
2720
|
os = ""
|
2717
2721
|
url = ""
|
2718
2722
|
for t in instance.tags:
|
2719
2723
|
if t.get("Key") == "os":
|
2720
|
-
os = t
|
2724
|
+
os = t.get("Value", "")
|
2721
2725
|
if t.get("Key") == "jenkins_controller":
|
2722
|
-
url = f"https://{t
|
2726
|
+
url = f"https://{t.get('Value', '').replace('-', '.')}.devshift.net/computer/{instance.id}"
|
2723
2727
|
image = ec2.Image(instance.image_id)
|
2724
2728
|
commit_url = ""
|
2725
2729
|
for t in image.tags:
|
@@ -3633,7 +3637,10 @@ def template(
|
|
3633
3637
|
secret_reader: str,
|
3634
3638
|
) -> None:
|
3635
3639
|
gqlapi = gql.get_api()
|
3636
|
-
|
3640
|
+
data = gqlapi.query(orb.NAMESPACES_QUERY)
|
3641
|
+
if not data:
|
3642
|
+
raise ValueError("no namespaces found")
|
3643
|
+
namespaces = data.get("namespaces", [])
|
3637
3644
|
namespaces_info = [
|
3638
3645
|
n
|
3639
3646
|
for n in namespaces
|
@@ -3725,7 +3732,7 @@ def run_prometheus_test(
|
|
3725
3732
|
ptr.run_test(test=test, alerting_services=get_alerting_services())
|
3726
3733
|
|
3727
3734
|
print(test.result)
|
3728
|
-
if
|
3735
|
+
if test.result is None:
|
3729
3736
|
sys.exit(1)
|
3730
3737
|
|
3731
3738
|
|
@@ -3804,7 +3811,10 @@ def alert_to_receiver(
|
|
3804
3811
|
additional_labels[key] = value
|
3805
3812
|
|
3806
3813
|
gqlapi = gql.get_api()
|
3807
|
-
|
3814
|
+
data = gqlapi.query(orb.NAMESPACES_QUERY)
|
3815
|
+
if not data:
|
3816
|
+
raise ValueError("no namespaces found")
|
3817
|
+
namespaces = data.get("namespaces", [])
|
3808
3818
|
cluster_namespaces = [n for n in namespaces if n["cluster"]["name"] == cluster]
|
3809
3819
|
|
3810
3820
|
if len(cluster_namespaces) == 0:
|
@@ -4027,15 +4037,16 @@ def query(output: str, query: str) -> None:
|
|
4027
4037
|
def promquery(cluster: str, query: str) -> None:
|
4028
4038
|
"""Run a PromQL query"""
|
4029
4039
|
config_data = config.get_config()
|
4030
|
-
|
4040
|
+
prom_auth = {"path": config_data["promql-auth"]["secret_path"], "field": "token"}
|
4031
4041
|
settings = queries.get_app_interface_settings()
|
4032
4042
|
secret_reader = SecretReader(settings=settings)
|
4033
|
-
|
4034
|
-
prom_auth = requests.auth.HTTPBasicAuth(*prom_auth_creds.split(":"))
|
4043
|
+
prom_user, prom_pass = secret_reader.read(prom_auth).split(":")
|
4035
4044
|
|
4036
4045
|
url = f"https://prometheus.{cluster}.devshift.net/api/v1/query"
|
4037
4046
|
|
4038
|
-
response = requests.get(
|
4047
|
+
response = requests.get(
|
4048
|
+
url, params={"query": query}, auth=(prom_user, prom_pass), timeout=60
|
4049
|
+
)
|
4039
4050
|
response.raise_for_status()
|
4040
4051
|
|
4041
4052
|
print(json.dumps(response.json(), indent=4))
|
@@ -4092,11 +4103,12 @@ def sre_checkpoint_metadata(
|
|
4092
4103
|
board_info = queries.get_simple_jira_boards(jiradef)
|
4093
4104
|
else:
|
4094
4105
|
board_info = app["escalationPolicy"]["channels"]["jiraBoard"]
|
4095
|
-
board_info
|
4106
|
+
assert board_info # make mypy happy
|
4107
|
+
board = board_info[0]
|
4096
4108
|
# Overrides for easier testing
|
4097
4109
|
if jiraboard:
|
4098
|
-
|
4099
|
-
report_invalid_metadata(app, app_path,
|
4110
|
+
board["name"] = jiraboard
|
4111
|
+
report_invalid_metadata(app, app_path, board, settings, parent_ticket, dry_run)
|
4100
4112
|
|
4101
4113
|
|
4102
4114
|
@root.command()
|
{qontract_reconcile-0.10.2.dev294.dist-info → qontract_reconcile-0.10.2.dev296.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|