qontract-reconcile 0.10.2.dev56__py3-none-any.whl → 0.10.2.dev58__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev56
3
+ Version: 0.10.2.dev58
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -615,7 +615,7 @@ reconcile/utils/oc_map.py,sha256=ougQ-Wlsa8ymoE_lPQ7g2LlpsUOsHVeRCLYW_6fjeWU,897
615
615
  reconcile/utils/ocm_base_client.py,sha256=t5kxhklEqOpenXPkXiwQIk8d3D7hIUndBm5qGusS0bc,6681
616
616
  reconcile/utils/openshift_resource.py,sha256=DI-e04f4NqEUFJ_9HzjY-QMhFt7o2XVauM09mzMC5Vs,24716
617
617
  reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
618
- reconcile/utils/output.py,sha256=dj3tE_sj3x8ssnAV6hCGmsRI9ttqUUFsIH1r7RujNA8,2038
618
+ reconcile/utils/output.py,sha256=xh2Not-Xm97KeRx_J5nc2PP5tDwpDLZBeCrWI0S-al4,2034
619
619
  reconcile/utils/pagerduty_api.py,sha256=_24i9S_4X7nlvHb-7clXRE0p1BG4ODjOzKxWO-F9WgI,7627
620
620
  reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv9s8ffbTSY,1840
621
621
  reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
@@ -745,18 +745,18 @@ reconcile/utils/unleash/client.py,sha256=YrJnauxjcy1ml7W2AHg7dzIH_fVK_GugoRu7IFm
745
745
  reconcile/utils/unleash/server.py,sha256=907gDh9Ee8UxLqusnfpzE-7LUnttB38D4xhVJ0vMf_M,4439
746
746
  tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
747
747
  tools/app_interface_metrics_exporter.py,sha256=f1qwTmQfEcs98uBVRyBa0k7GQXdiSwd7w1hDVjhdGcQ,2303
748
- tools/app_interface_reporter.py,sha256=0_oq1-mL0UOh1x5G8CoKaEVJqK-XTKE7PX7IbRTovBc,17224
748
+ tools/app_interface_reporter.py,sha256=gR2EgHmgSIxzK5xxDW1SduFU6OkPaf2LlAQjhV3NYIg,17623
749
749
  tools/app_sre_tekton_access_reporter.py,sha256=o9prLUgQpwO3msRWc2as1xT1y9OB3znkpgvLr0Ys8_M,3146
750
750
  tools/app_sre_tekton_access_revalidation.py,sha256=66nHEaY-bIqxIhpcmwN8AvQZu6ZXenfkg4Fut0pVZRM,2726
751
751
  tools/glitchtip_access_reporter.py,sha256=o01A6b88t3Wie6tj_tJWWVo2J01LxQ_a9giGm4UzEaU,2901
752
752
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
753
- tools/qontract_cli.py,sha256=HAN-3m8udB8Ea1B4fIpmJoJjLWQERDhIF-9LU9XBCnQ,152477
753
+ tools/qontract_cli.py,sha256=DCgIaGxZlYG5TVWUifAQGfp81aop9SLu0ezVVg4x2NM,148792
754
754
  tools/sd_app_sre_alert_report.py,sha256=jQpJdXVID68bSNtJNOGDh0-ei1CfEUS4Itr4MAaBNFA,5062
755
755
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
756
756
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
757
757
  tools/cli_commands/container_images_report.py,sha256=8fG9XU-eEhJ7hKCdQzBcdPpvIJR-8WGkHOgFEulpfYQ,5213
758
758
  tools/cli_commands/erv2.py,sha256=VxUlNXllo947UwmtvS-42IeI9x_t_X3MHrrSI3K_GRo,23274
759
- tools/cli_commands/gpg_encrypt.py,sha256=JWwds_Qg7KhSJMIGUh8TfI5-Jf17iUtmaEi4kWJxfVE,4907
759
+ tools/cli_commands/gpg_encrypt.py,sha256=NhzwN49UN7P5_FJgTUN5A4BIwNbFokIE4lwDax2iP5k,4891
760
760
  tools/cli_commands/systems_and_tools.py,sha256=EMHOF1AtUDaoSk0bbjl6oUKYAz4rTZjIBaF-6E6GspM,16816
761
761
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
762
762
  tools/cli_commands/cost_report/aws.py,sha256=JtwDfhaYLfa4Uz1LR6OfSBh_3nBlb90kQq6i3MV_ims,4563
@@ -777,7 +777,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
777
777
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
778
778
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
779
779
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
780
- qontract_reconcile-0.10.2.dev56.dist-info/METADATA,sha256=kWky6Hw9eg7YODzjcwI6GeUg7BUdpELD6uvYOH6ByOQ,24665
781
- qontract_reconcile-0.10.2.dev56.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
782
- qontract_reconcile-0.10.2.dev56.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
783
- qontract_reconcile-0.10.2.dev56.dist-info/RECORD,,
780
+ qontract_reconcile-0.10.2.dev58.dist-info/METADATA,sha256=rQJYMzaII94AgfVybASnf6jWezt2vOg4-p1vRIuKKyc,24665
781
+ qontract_reconcile-0.10.2.dev58.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
782
+ qontract_reconcile-0.10.2.dev58.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
783
+ qontract_reconcile-0.10.2.dev58.dist-info/RECORD,,
reconcile/utils/output.py CHANGED
@@ -1,6 +1,9 @@
1
1
  import json
2
2
  import re
3
- from collections.abc import Iterable, Mapping
3
+ from collections.abc import (
4
+ Iterable,
5
+ Mapping,
6
+ )
4
7
 
5
8
  import yaml
6
9
  from tabulate import tabulate
@@ -8,11 +11,11 @@ from tabulate import tabulate
8
11
 
9
12
  def print_output(
10
13
  options: Mapping[str, str | bool],
11
- content: Iterable[dict],
14
+ content: list[dict],
12
15
  columns: Iterable[str] = (),
13
16
  ) -> str | None:
14
17
  if options["sort"]:
15
- content = sorted(content, key=lambda c: tuple(c.values()))
18
+ content.sort(key=lambda c: tuple(c.values()))
16
19
  if options.get("to_string"):
17
20
  for c in content:
18
21
  for k, v in c.items():
@@ -2,7 +2,6 @@ import contextlib
2
2
  import logging
3
3
  import os
4
4
  import textwrap
5
- from collections.abc import Mapping, MutableMapping
6
5
  from datetime import (
7
6
  UTC,
8
7
  datetime,
@@ -39,14 +38,50 @@ DASHDOTDB_SECRET = os.environ.get(
39
38
  )
40
39
 
41
40
 
41
+ def promql(url, query, auth=None):
42
+ """
43
+ Run an instant-query on the prometheus instance.
44
+
45
+ The returned structure is documented here:
46
+ https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries
47
+
48
+ :param url: base prometheus url (not the API endpoint).
49
+ :type url: string
50
+ :param query: this is a second value
51
+ :type query: string
52
+ :param auth: auth object
53
+ :type auth: requests.auth
54
+ :return: structure with the metrics
55
+ :rtype: dictionary
56
+ """
57
+
58
+ url = os.path.join(url, "api/v1/query")
59
+
60
+ if auth is None:
61
+ auth = {}
62
+
63
+ params = {"query": query}
64
+
65
+ response = requests.get(url, params=params, auth=auth, timeout=60)
66
+
67
+ response.raise_for_status()
68
+ response = response.json()
69
+
70
+ # TODO ensure len response == 1
71
+ return response["data"]["result"]
72
+
73
+
42
74
  class Report:
43
- def __init__(self, app: Mapping, date: datetime) -> None:
75
+ def __init__(self, app, date):
44
76
  settings = queries.get_app_interface_settings()
45
77
  self.secret_reader = SecretReader(settings=settings)
46
- self.app = app
47
78
  # standard date format
48
- self.date = date.strftime("%Y-%m-%d")
49
- self.report_sections: dict = {}
79
+ if hasattr(date, "strftime"):
80
+ date = date.strftime("%Y-%m-%d")
81
+
82
+ self.app = app
83
+ self.date = date
84
+ self.report_sections = {}
50
85
 
51
86
  # promotions
52
87
  self.add_report_section("promotions", self.app.get("promotions"))
@@ -73,10 +108,10 @@ class Report:
73
108
  )
74
109
 
75
110
  @property
76
- def path(self) -> str:
77
- return f"data/reports/{self.app['name']}/{self.date}.yml"
111
+ def path(self):
112
+ return "data/reports/{}/{}.yml".format(self.app["name"], self.date)
78
113
 
79
- def content(self) -> dict:
114
+ def content(self):
80
115
  return {
81
116
  "$schema": "/app-sre/report-1.yml",
82
117
  "labels": {"app": self.app["name"]},
@@ -87,20 +122,21 @@ class Report:
87
122
  "content": yaml.safe_dump(self.report_sections, sort_keys=False),
88
123
  }
89
124
 
90
- def to_yaml(self) -> str:
125
+ def to_yaml(self):
91
126
  return yaml.safe_dump(self.content(), sort_keys=False)
92
127
 
93
- def to_message(self) -> dict:
128
+ def to_message(self):
94
129
  return {"file_path": self.path, "content": self.to_yaml()}
95
130
 
96
- def add_report_section(self, header: str, content: list[dict] | None) -> None:
97
- self.report_sections[header] = content or None
131
+ def add_report_section(self, header, content):
132
+ if not content:
133
+ content = None
134
+
135
+ self.report_sections[header] = content
98
136
 
99
137
  @staticmethod
100
- def get_vulnerability_content(
101
- container_vulnerabilities: Mapping | None,
102
- ) -> list[dict]:
103
- parsed_metrics: list[dict] = []
138
+ def get_vulnerability_content(container_vulnerabilities):
139
+ parsed_metrics = []
104
140
  if not container_vulnerabilities:
105
141
  return parsed_metrics
106
142
 
@@ -114,8 +150,8 @@ class Report:
114
150
  return parsed_metrics
115
151
 
116
152
  @staticmethod
117
- def get_post_deploy_jobs_content(post_deploy_jobs: Mapping | None) -> list[dict]:
118
- results: list[dict] = []
153
+ def get_post_deploy_jobs_content(post_deploy_jobs):
154
+ results = []
119
155
  if not post_deploy_jobs:
120
156
  return results
121
157
 
@@ -129,8 +165,8 @@ class Report:
129
165
  return results
130
166
 
131
167
  @staticmethod
132
- def get_validations_content(deployment_validations: Mapping | None) -> list[dict]:
133
- parsed_metrics: list[dict] = []
168
+ def get_validations_content(deployment_validations):
169
+ parsed_metrics = []
134
170
  if not deployment_validations:
135
171
  return parsed_metrics
136
172
 
@@ -144,8 +180,8 @@ class Report:
144
180
  return parsed_metrics
145
181
 
146
182
  @staticmethod
147
- def get_slo_content(service_slo: Mapping | None) -> list[dict]:
148
- parsed_metrics: list[dict] = []
183
+ def get_slo_content(service_slo):
184
+ parsed_metrics = []
149
185
  if not service_slo:
150
186
  return parsed_metrics
151
187
 
@@ -164,7 +200,7 @@ class Report:
164
200
  return parsed_metrics
165
201
 
166
202
  @staticmethod
167
- def get_activity_content(activity: Mapping) -> list[dict]:
203
+ def get_activity_content(activity):
168
204
  if not activity:
169
205
  return []
170
206
 
@@ -178,9 +214,7 @@ class Report:
178
214
  ]
179
215
 
180
216
 
181
- def get_apps_data(
182
- date: datetime, month_delta: int = 1, thread_pool_size: int = 10
183
- ) -> list[dict]:
217
+ def get_apps_data(date, month_delta=1, thread_pool_size=10):
184
218
  settings = queries.get_app_interface_settings()
185
219
  secret_reader = SecretReader(settings)
186
220
 
@@ -263,9 +297,9 @@ def get_apps_data(
263
297
  if namespace["app"]["name"] != app["name"]:
264
298
  continue
265
299
  app_namespaces.append(namespace)
266
- vuln_mx: dict = {}
267
- validt_mx: dict = {}
268
- slo_mx: dict = {}
300
+ vuln_mx = {}
301
+ validt_mx = {}
302
+ slo_mx = {}
269
303
  for family in text_string_to_metric_families(vuln_metrics):
270
304
  for sample in family.samples:
271
305
  if sample.name == "imagemanifestvuln_total":
@@ -341,7 +375,7 @@ def get_apps_data(
341
375
  return apps
342
376
 
343
377
 
344
- def get_build_history(job: MutableMapping) -> MutableMapping:
378
+ def get_build_history(job):
345
379
  try:
346
380
  logging.info(f"getting build history for {job['name']}")
347
381
  job["build_history"] = job["jenkins"].get_build_history(
@@ -352,9 +386,7 @@ def get_build_history(job: MutableMapping) -> MutableMapping:
352
386
  return job
353
387
 
354
388
 
355
- def get_build_history_pool(
356
- jenkins_map: Mapping, jobs: Mapping, timestamp_limit: int, thread_pool_size: int
357
- ) -> dict:
389
+ def get_build_history_pool(jenkins_map, jobs, timestamp_limit, thread_pool_size):
358
390
  history_to_get = []
359
391
  for instance, _jobs in jobs.items():
360
392
  jenkins = jenkins_map[instance]
@@ -379,7 +411,7 @@ def get_build_history_pool(
379
411
  return history
380
412
 
381
413
 
382
- def get_repo_url(job: Mapping) -> str:
414
+ def get_repo_url(job):
383
415
  repo_url_raw = job["properties"][0]["github"]["url"]
384
416
  return repo_url_raw.strip("/").replace(".git", "")
385
417
 
@@ -392,13 +424,8 @@ def get_repo_url(job: Mapping) -> str:
392
424
  @gitlab_project_id
393
425
  @click.option("--reports-path", help="path to write reports")
394
426
  def main(
395
- configfile: str,
396
- dry_run: bool,
397
- log_level: str,
398
- gitlab_project_id: str,
399
- reports_path: str,
400
- thread_pool_size: int,
401
- ) -> None:
427
+ configfile, dry_run, log_level, gitlab_project_id, reports_path, thread_pool_size
428
+ ):
402
429
  init_env(log_level=log_level, config_file=configfile)
403
430
 
404
431
  now = datetime.now()
@@ -449,9 +476,8 @@ def main(
449
476
  gitlab_project_id=gitlab_project_id, sqs_or_gitlab="gitlab"
450
477
  ) as mr_cli:
451
478
  result = mr.submit(cli=mr_cli)
452
- if result:
453
- logging.info(["created_mr", result.web_url])
479
+ logging.info(["created_mr", result.web_url])
454
480
 
455
481
 
456
482
  if __name__ == "__main__":
457
- main()
483
+ main() # pylint: disable=no-value-for-parameter
@@ -132,7 +132,7 @@ class GPGEncryptCommand:
132
132
 
133
133
  return user["public_gpg_key"]
134
134
 
135
- def _output(self, content: str) -> None:
135
+ def _output(self, content: str):
136
136
  output = self._command_data.output
137
137
  if not output:
138
138
  print(content)
@@ -140,7 +140,7 @@ class GPGEncryptCommand:
140
140
  with open(output, "w", encoding="locale") as f:
141
141
  f.write(content)
142
142
 
143
- def execute(self) -> None:
143
+ def execute(self):
144
144
  secret = self._fetch_secret()
145
145
  gpg_key = self._get_gpg_key()
146
146
  encrypted_content = gpg.gpg_encrypt(