qontract-reconcile 0.10.2.dev49__py3-none-any.whl → 0.10.2.dev51__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.dev49
3
+ Version: 0.10.2.dev51
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
@@ -599,7 +599,7 @@ reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSb
599
599
  reconcile/utils/instrumented_wrappers.py,sha256=aAO4q6LMpHjJIaFdrouwCEIEl5u3thCCBPSuoEloC60,1082
600
600
  reconcile/utils/jenkins_api.py,sha256=RaKuZmO7_lbI-hE6c_Pq2a6CQdmBVj7BcP2jR68cIbI,7081
601
601
  reconcile/utils/jira_client.py,sha256=oWi7rcAP1C59oIBTPg6kRntI25Zm4e7FyvdVYvZ9RZ8,7881
602
- reconcile/utils/jjb_client.py,sha256=T6G4n2K44nEFqnh-ASzrqiDkJDPnVJoyIyHwH-U9M40,14997
602
+ reconcile/utils/jjb_client.py,sha256=4YqeXEkO4p6QtJE_fkaD1XuLKbe9l3g0W7AVpcjJ3yg,15187
603
603
  reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
604
604
  reconcile/utils/jump_host.py,sha256=gi8vGUDgdTVwJvROvRVauFxtL0YAramhbWvG70L7AY8,5137
605
605
  reconcile/utils/keycloak.py,sha256=YWSEUGrOVqFaJUk055dKUWpLDPdDRvhcmvR-lfbmxdE,3388
@@ -616,7 +616,7 @@ reconcile/utils/oc_map.py,sha256=ougQ-Wlsa8ymoE_lPQ7g2LlpsUOsHVeRCLYW_6fjeWU,897
616
616
  reconcile/utils/ocm_base_client.py,sha256=t5kxhklEqOpenXPkXiwQIk8d3D7hIUndBm5qGusS0bc,6681
617
617
  reconcile/utils/openshift_resource.py,sha256=DI-e04f4NqEUFJ_9HzjY-QMhFt7o2XVauM09mzMC5Vs,24716
618
618
  reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
619
- reconcile/utils/output.py,sha256=xh2Not-Xm97KeRx_J5nc2PP5tDwpDLZBeCrWI0S-al4,2034
619
+ reconcile/utils/output.py,sha256=dj3tE_sj3x8ssnAV6hCGmsRI9ttqUUFsIH1r7RujNA8,2038
620
620
  reconcile/utils/pagerduty_api.py,sha256=_24i9S_4X7nlvHb-7clXRE0p1BG4ODjOzKxWO-F9WgI,7627
621
621
  reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv9s8ffbTSY,1840
622
622
  reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
@@ -746,18 +746,18 @@ reconcile/utils/unleash/client.py,sha256=YrJnauxjcy1ml7W2AHg7dzIH_fVK_GugoRu7IFm
746
746
  reconcile/utils/unleash/server.py,sha256=907gDh9Ee8UxLqusnfpzE-7LUnttB38D4xhVJ0vMf_M,4439
747
747
  tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
748
748
  tools/app_interface_metrics_exporter.py,sha256=f1qwTmQfEcs98uBVRyBa0k7GQXdiSwd7w1hDVjhdGcQ,2303
749
- tools/app_interface_reporter.py,sha256=gR2EgHmgSIxzK5xxDW1SduFU6OkPaf2LlAQjhV3NYIg,17623
749
+ tools/app_interface_reporter.py,sha256=0_oq1-mL0UOh1x5G8CoKaEVJqK-XTKE7PX7IbRTovBc,17224
750
750
  tools/app_sre_tekton_access_reporter.py,sha256=o9prLUgQpwO3msRWc2as1xT1y9OB3znkpgvLr0Ys8_M,3146
751
751
  tools/app_sre_tekton_access_revalidation.py,sha256=66nHEaY-bIqxIhpcmwN8AvQZu6ZXenfkg4Fut0pVZRM,2726
752
752
  tools/glitchtip_access_reporter.py,sha256=o01A6b88t3Wie6tj_tJWWVo2J01LxQ_a9giGm4UzEaU,2901
753
753
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
754
- tools/qontract_cli.py,sha256=157wUZMwEpCkLj68zCJX96AnhEDqoIiMEFK9BKFw5OU,148485
754
+ tools/qontract_cli.py,sha256=j18Tm-jgWSFxjbZGAeuEqng_oJf6klKyugFapoITKDc,152214
755
755
  tools/sd_app_sre_alert_report.py,sha256=jQpJdXVID68bSNtJNOGDh0-ei1CfEUS4Itr4MAaBNFA,5062
756
756
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
757
757
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
758
758
  tools/cli_commands/container_images_report.py,sha256=8fG9XU-eEhJ7hKCdQzBcdPpvIJR-8WGkHOgFEulpfYQ,5213
759
759
  tools/cli_commands/erv2.py,sha256=VxUlNXllo947UwmtvS-42IeI9x_t_X3MHrrSI3K_GRo,23274
760
- tools/cli_commands/gpg_encrypt.py,sha256=NhzwN49UN7P5_FJgTUN5A4BIwNbFokIE4lwDax2iP5k,4891
760
+ tools/cli_commands/gpg_encrypt.py,sha256=JWwds_Qg7KhSJMIGUh8TfI5-Jf17iUtmaEi4kWJxfVE,4907
761
761
  tools/cli_commands/systems_and_tools.py,sha256=EMHOF1AtUDaoSk0bbjl6oUKYAz4rTZjIBaF-6E6GspM,16816
762
762
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
763
763
  tools/cli_commands/cost_report/aws.py,sha256=JtwDfhaYLfa4Uz1LR6OfSBh_3nBlb90kQq6i3MV_ims,4563
@@ -778,7 +778,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
778
778
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
779
779
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
780
780
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
781
- qontract_reconcile-0.10.2.dev49.dist-info/METADATA,sha256=wWmWGxP1x0eJkaoCP0dPkmUYEYYHnG3FFPvJxlE4gAs,24665
782
- qontract_reconcile-0.10.2.dev49.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
783
- qontract_reconcile-0.10.2.dev49.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
784
- qontract_reconcile-0.10.2.dev49.dist-info/RECORD,,
781
+ qontract_reconcile-0.10.2.dev51.dist-info/METADATA,sha256=Ph2jCMJCtVblXRo35uNM-E14lmcVz4skONoYVLbn9i8,24665
782
+ qontract_reconcile-0.10.2.dev51.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
783
+ qontract_reconcile-0.10.2.dev51.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
784
+ qontract_reconcile-0.10.2.dev51.dist-info/RECORD,,
@@ -186,7 +186,11 @@ class JJB: # pylint: disable=too-many-public-methods
186
186
  if equal:
187
187
  continue
188
188
 
189
- instance, item, _ = f.replace(replace_path + "/", "").split("/")
189
+ instance, *items, _ = f.replace(replace_path + "/", "").split("/")
190
+ if len(items) != 1:
191
+ name = "/".join(items)
192
+ raise ValueError(f"Invalid job name contains '/' in {instance}: {name}")
193
+ item = items[0]
190
194
  item_type = et.parse(f).getroot().tag
191
195
  item_type = item_type.replace("hudson.model.ListView", "view")
192
196
  item_type = item_type.replace("project", "job")
reconcile/utils/output.py CHANGED
@@ -1,9 +1,6 @@
1
1
  import json
2
2
  import re
3
- from collections.abc import (
4
- Iterable,
5
- Mapping,
6
- )
3
+ from collections.abc import Iterable, Mapping
7
4
 
8
5
  import yaml
9
6
  from tabulate import tabulate
@@ -11,11 +8,11 @@ from tabulate import tabulate
11
8
 
12
9
  def print_output(
13
10
  options: Mapping[str, str | bool],
14
- content: list[dict],
11
+ content: Iterable[dict],
15
12
  columns: Iterable[str] = (),
16
13
  ) -> str | None:
17
14
  if options["sort"]:
18
- content.sort(key=lambda c: tuple(c.values()))
15
+ content = sorted(content, key=lambda c: tuple(c.values()))
19
16
  if options.get("to_string"):
20
17
  for c in content:
21
18
  for k, v in c.items():
@@ -2,6 +2,7 @@ import contextlib
2
2
  import logging
3
3
  import os
4
4
  import textwrap
5
+ from collections.abc import Mapping, MutableMapping
5
6
  from datetime import (
6
7
  UTC,
7
8
  datetime,
@@ -38,50 +39,14 @@ DASHDOTDB_SECRET = os.environ.get(
38
39
  )
39
40
 
40
41
 
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
-
74
42
  class Report:
75
- def __init__(self, app, date):
43
+ def __init__(self, app: Mapping, date: datetime) -> None:
76
44
  settings = queries.get_app_interface_settings()
77
45
  self.secret_reader = SecretReader(settings=settings)
78
- # standard date format
79
- if hasattr(date, "strftime"):
80
- date = date.strftime("%Y-%m-%d")
81
-
82
46
  self.app = app
83
- self.date = date
84
- self.report_sections = {}
47
+ # standard date format
48
+ self.date = date.strftime("%Y-%m-%d")
49
+ self.report_sections: dict = {}
85
50
 
86
51
  # promotions
87
52
  self.add_report_section("promotions", self.app.get("promotions"))
@@ -108,10 +73,10 @@ class Report:
108
73
  )
109
74
 
110
75
  @property
111
- def path(self):
112
- return "data/reports/{}/{}.yml".format(self.app["name"], self.date)
76
+ def path(self) -> str:
77
+ return f"data/reports/{self.app['name']}/{self.date}.yml"
113
78
 
114
- def content(self):
79
+ def content(self) -> dict:
115
80
  return {
116
81
  "$schema": "/app-sre/report-1.yml",
117
82
  "labels": {"app": self.app["name"]},
@@ -122,21 +87,20 @@ class Report:
122
87
  "content": yaml.safe_dump(self.report_sections, sort_keys=False),
123
88
  }
124
89
 
125
- def to_yaml(self):
90
+ def to_yaml(self) -> str:
126
91
  return yaml.safe_dump(self.content(), sort_keys=False)
127
92
 
128
- def to_message(self):
93
+ def to_message(self) -> dict:
129
94
  return {"file_path": self.path, "content": self.to_yaml()}
130
95
 
131
- def add_report_section(self, header, content):
132
- if not content:
133
- content = None
134
-
135
- self.report_sections[header] = content
96
+ def add_report_section(self, header: str, content: list[dict] | None) -> None:
97
+ self.report_sections[header] = content or None
136
98
 
137
99
  @staticmethod
138
- def get_vulnerability_content(container_vulnerabilities):
139
- parsed_metrics = []
100
+ def get_vulnerability_content(
101
+ container_vulnerabilities: Mapping | None,
102
+ ) -> list[dict]:
103
+ parsed_metrics: list[dict] = []
140
104
  if not container_vulnerabilities:
141
105
  return parsed_metrics
142
106
 
@@ -150,8 +114,8 @@ class Report:
150
114
  return parsed_metrics
151
115
 
152
116
  @staticmethod
153
- def get_post_deploy_jobs_content(post_deploy_jobs):
154
- results = []
117
+ def get_post_deploy_jobs_content(post_deploy_jobs: Mapping | None) -> list[dict]:
118
+ results: list[dict] = []
155
119
  if not post_deploy_jobs:
156
120
  return results
157
121
 
@@ -165,8 +129,8 @@ class Report:
165
129
  return results
166
130
 
167
131
  @staticmethod
168
- def get_validations_content(deployment_validations):
169
- parsed_metrics = []
132
+ def get_validations_content(deployment_validations: Mapping | None) -> list[dict]:
133
+ parsed_metrics: list[dict] = []
170
134
  if not deployment_validations:
171
135
  return parsed_metrics
172
136
 
@@ -180,8 +144,8 @@ class Report:
180
144
  return parsed_metrics
181
145
 
182
146
  @staticmethod
183
- def get_slo_content(service_slo):
184
- parsed_metrics = []
147
+ def get_slo_content(service_slo: Mapping | None) -> list[dict]:
148
+ parsed_metrics: list[dict] = []
185
149
  if not service_slo:
186
150
  return parsed_metrics
187
151
 
@@ -200,7 +164,7 @@ class Report:
200
164
  return parsed_metrics
201
165
 
202
166
  @staticmethod
203
- def get_activity_content(activity):
167
+ def get_activity_content(activity: Mapping) -> list[dict]:
204
168
  if not activity:
205
169
  return []
206
170
 
@@ -214,7 +178,9 @@ class Report:
214
178
  ]
215
179
 
216
180
 
217
- def get_apps_data(date, month_delta=1, thread_pool_size=10):
181
+ def get_apps_data(
182
+ date: datetime, month_delta: int = 1, thread_pool_size: int = 10
183
+ ) -> list[dict]:
218
184
  settings = queries.get_app_interface_settings()
219
185
  secret_reader = SecretReader(settings)
220
186
 
@@ -297,9 +263,9 @@ def get_apps_data(date, month_delta=1, thread_pool_size=10):
297
263
  if namespace["app"]["name"] != app["name"]:
298
264
  continue
299
265
  app_namespaces.append(namespace)
300
- vuln_mx = {}
301
- validt_mx = {}
302
- slo_mx = {}
266
+ vuln_mx: dict = {}
267
+ validt_mx: dict = {}
268
+ slo_mx: dict = {}
303
269
  for family in text_string_to_metric_families(vuln_metrics):
304
270
  for sample in family.samples:
305
271
  if sample.name == "imagemanifestvuln_total":
@@ -375,7 +341,7 @@ def get_apps_data(date, month_delta=1, thread_pool_size=10):
375
341
  return apps
376
342
 
377
343
 
378
- def get_build_history(job):
344
+ def get_build_history(job: MutableMapping) -> MutableMapping:
379
345
  try:
380
346
  logging.info(f"getting build history for {job['name']}")
381
347
  job["build_history"] = job["jenkins"].get_build_history(
@@ -386,7 +352,9 @@ def get_build_history(job):
386
352
  return job
387
353
 
388
354
 
389
- def get_build_history_pool(jenkins_map, jobs, timestamp_limit, thread_pool_size):
355
+ def get_build_history_pool(
356
+ jenkins_map: Mapping, jobs: Mapping, timestamp_limit: int, thread_pool_size: int
357
+ ) -> dict:
390
358
  history_to_get = []
391
359
  for instance, _jobs in jobs.items():
392
360
  jenkins = jenkins_map[instance]
@@ -411,7 +379,7 @@ def get_build_history_pool(jenkins_map, jobs, timestamp_limit, thread_pool_size)
411
379
  return history
412
380
 
413
381
 
414
- def get_repo_url(job):
382
+ def get_repo_url(job: Mapping) -> str:
415
383
  repo_url_raw = job["properties"][0]["github"]["url"]
416
384
  return repo_url_raw.strip("/").replace(".git", "")
417
385
 
@@ -424,8 +392,13 @@ def get_repo_url(job):
424
392
  @gitlab_project_id
425
393
  @click.option("--reports-path", help="path to write reports")
426
394
  def main(
427
- configfile, dry_run, log_level, gitlab_project_id, reports_path, thread_pool_size
428
- ):
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:
429
402
  init_env(log_level=log_level, config_file=configfile)
430
403
 
431
404
  now = datetime.now()
@@ -476,8 +449,9 @@ def main(
476
449
  gitlab_project_id=gitlab_project_id, sqs_or_gitlab="gitlab"
477
450
  ) as mr_cli:
478
451
  result = mr.submit(cli=mr_cli)
479
- logging.info(["created_mr", result.web_url])
452
+ if result:
453
+ logging.info(["created_mr", result.web_url])
480
454
 
481
455
 
482
456
  if __name__ == "__main__":
483
- main() # pylint: disable=no-value-for-parameter
457
+ main()
@@ -132,7 +132,7 @@ class GPGEncryptCommand:
132
132
 
133
133
  return user["public_gpg_key"]
134
134
 
135
- def _output(self, content: str):
135
+ def _output(self, content: str) -> None:
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):
143
+ def execute(self) -> None:
144
144
  secret = self._fetch_secret()
145
145
  gpg_key = self._get_gpg_key()
146
146
  encrypted_content = gpg.gpg_encrypt(