qontract-reconcile 0.10.1rc731__py3-none-any.whl → 0.10.1rc733__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.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc731
3
+ Version: 0.10.1rc733
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
@@ -607,7 +607,7 @@ reconcile/utils/helpers.py,sha256=k9svgFFZG7H5FvHYY0g5jJyvgvh2UDZxf0Ib221teag,11
607
607
  reconcile/utils/imap_client.py,sha256=byFAJATbITJPsGECSbvXBOcCnoeTUpDFiEjzOAxLm_U,1975
608
608
  reconcile/utils/instrumented_wrappers.py,sha256=eVwMoa6FCrYxLv3RML3WpZF9qKVfCTjMxphgVXG03OM,1073
609
609
  reconcile/utils/jenkins_api.py,sha256=MyJSB_S3uYf3sXnt9t03-gZNQ7tbdd7Wusv3MoF2fRc,7113
610
- reconcile/utils/jira_client.py,sha256=geozBG45AM_z2Fisrhqw-fSkmvikyrKIb9XARn-Q-AE,7186
610
+ reconcile/utils/jira_client.py,sha256=2m512wPgbKCq8PIXRmqsowsQ2nm7FBXoMkIzRcIYnFs,7197
611
611
  reconcile/utils/jjb_client.py,sha256=Pdy0dLCFvD6GPCaC0tZydYgkVJPOxYXIiwWECZaFJBU,14551
612
612
  reconcile/utils/jsonpath.py,sha256=NRpAEijKN4cMDjo7qivNPqpm0__GQQ1TiE0PBEBO45s,5572
613
613
  reconcile/utils/jump_host.py,sha256=AdwmCZYNhRe53VwV2iAsUdVyUdVtSd4REmdThJDkM5w,4973
@@ -617,7 +617,7 @@ reconcile/utils/lean_terraform_client.py,sha256=zReyNPJbr2uOdrdh8Qfe-OZQBoRwxb5Z
617
617
  reconcile/utils/make.py,sha256=QaEwucrzbl8-VHS66Wfdjfo0ubmAcvt_hZGpiGsKU50,231
618
618
  reconcile/utils/metrics.py,sha256=7nXdctmZ0UtGMHPpS3V55sfH4xpMPqdYaJ3JKAUc_sM,18474
619
619
  reconcile/utils/models.py,sha256=It_Q1WNIvw_EDCsiSWzIgpSPr_X9jMgbJI-DR3N23xY,4677
620
- reconcile/utils/oauth2_backend_application_session.py,sha256=kWUX2LTwKniD01-0a7x-kV9ud3Q30DpLgh1xDbUhLSI,3298
620
+ reconcile/utils/oauth2_backend_application_session.py,sha256=6W16sMpnWEPFDUX7qi5Cui2yOnmLfpgUxWtB3Ii35D0,4177
621
621
  reconcile/utils/oc.py,sha256=ILAlP-AZMtWeyAepLoMnYbDJfyyMs-Z0fOEo9JXQfkE,65490
622
622
  reconcile/utils/oc_connection_parameters.py,sha256=85slrnDigYwYmzhyceVkMElWzFArp4ge1d-fHXVqh0w,9729
623
623
  reconcile/utils/oc_filters.py,sha256=R2Lf3fo0jQCeE62Ygeo_KN24XbAosq0QbjimYG6qHI4,1402
@@ -635,7 +635,7 @@ reconcile/utils/promtool.py,sha256=kT2rFZSBaRqW7SSHAuYzGZzQxM5Dzk8KW1NnEUYZU_s,2
635
635
  reconcile/utils/quay_api.py,sha256=EuOegpb-7ntEjkKLFwM2Oo4Nw7SyFtmyl3sQ9aXMtrM,8152
636
636
  reconcile/utils/raw_github_api.py,sha256=ZHC-SZuAyRe1zaMoOU7Krt1-zecDxENd9c_NzQYqK9g,2968
637
637
  reconcile/utils/repo_owners.py,sha256=j-pUjc9PuDzq7KpjNLpnhqfU8tUG4nj2WMhFp4ick7g,6629
638
- reconcile/utils/rest_api_base.py,sha256=uM4fGDNx5exDky0Ls56dJsta2PpceAO_uHc10vQCjOI,3914
638
+ reconcile/utils/rest_api_base.py,sha256=uXLdXocNmRCxJsYFWOdUNLaTEu-dcxMLJg_CwRefETE,3970
639
639
  reconcile/utils/ruamel.py,sha256=FzL4_L0FnMOUZmgThrZSMJs5MTdXwiy-E9MZWfk8bh8,397
640
640
  reconcile/utils/secret_reader.py,sha256=2DeYAAQFjUULEKlLw3UDAUoND6gbqvCh9uKPtlc-0us,10403
641
641
  reconcile/utils/semver_helper.py,sha256=-WfPOMSA2v1h7hT3PwVf-Htg7wOsoKlQC1JdmDX2Ars,1268
@@ -747,26 +747,26 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
747
747
  tools/app_interface_reporter.py,sha256=upA-J-n-HXHKVDINRuMR7vTt-iJvQORKUVi9D3leQto,17738
748
748
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
749
749
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
750
- tools/qontract_cli.py,sha256=eZONmZCLrSgMQFKHKxElDmw2_4Ld-Ug-FEFR2c8QnXI,116018
750
+ tools/qontract_cli.py,sha256=ilm3Ezw86rgFKLBgL-FV0xSRLHaFHfG0CKmYZfDe1VU,116022
751
751
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
752
752
  tools/template_validation.py,sha256=-U-lTGeLaci8yWPEblCJeev2DOlY1jM9QOOh-O1zts8,3376
753
753
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
754
754
  tools/cli_commands/gpg_encrypt.py,sha256=w8hl4jIEWk5wKbEFN6fVEOwUJGmdlvOqYodW3XSN7mU,4978
755
755
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
756
756
  tools/cli_commands/cost_report/command.py,sha256=ecT4SjCwhoWNFF9Xb1spqYzI7QMwX9EFWz6XFSW4bas,6317
757
- tools/cli_commands/cost_report/cost_management_api.py,sha256=IuPbrtNpgt1wvzHAPQjGPdCobXIkvkusqP8uXv3G204,1743
757
+ tools/cli_commands/cost_report/cost_management_api.py,sha256=o-qz-ykT7R6pMjrLVl030W7JuYAYSCdOXTO4B06aV3s,1771
758
758
  tools/cli_commands/cost_report/model.py,sha256=blNk52T6KIDy-7w4V6qvZhGFWplxgDfZ5jRZimPDYJo,593
759
759
  tools/cli_commands/cost_report/response.py,sha256=lOmIohSuwJBxoSAC9LCwihBsyWgutHCIW-nvd6_HX5Y,867
760
- tools/cli_commands/cost_report/view.py,sha256=LtM5AVv1pZ9yT1C_IcTrHHyXlQkRWeS3nnDf5XQVoVQ,8554
760
+ tools/cli_commands/cost_report/view.py,sha256=2a6LOuUEbFQYw57Mkc3R2onLOT3oObizHtKFyj9stMU,8558
761
761
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
762
762
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
763
763
  tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
764
764
  tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvftCWEEf-g1mfXOtgCog-g,1271
765
- tools/test/test_qontract_cli.py,sha256=UEwAW7PA_GIrbqzaLxpkCxbuVjEFLNvnVG-6VyoCGIc,4147
765
+ tools/test/test_qontract_cli.py,sha256=w2l4BHB09k1d-BGJ1jBUNCqDv7zkqYrMHojQXg-21kQ,4155
766
766
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
767
767
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
768
- qontract_reconcile-0.10.1rc731.dist-info/METADATA,sha256=sursdK9csIPxAOvipsmyKrcSuaHrms1FagDc5yNyBRI,2382
769
- qontract_reconcile-0.10.1rc731.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
770
- qontract_reconcile-0.10.1rc731.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
771
- qontract_reconcile-0.10.1rc731.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
772
- qontract_reconcile-0.10.1rc731.dist-info/RECORD,,
768
+ qontract_reconcile-0.10.1rc733.dist-info/METADATA,sha256=vcNc4Ashu0DlVjZ-3ZtZRXdI1pZFea0gCObIeSf-MLg,2382
769
+ qontract_reconcile-0.10.1rc733.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
770
+ qontract_reconcile-0.10.1rc733.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
771
+ qontract_reconcile-0.10.1rc733.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
772
+ qontract_reconcile-0.10.1rc733.dist-info/RECORD,,
@@ -96,14 +96,11 @@ class JiraClient:
96
96
  if settings and settings["jiraWatcher"]:
97
97
  read_timeout = settings["jiraWatcher"]["readTimeout"]
98
98
  connect_timeout = settings["jiraWatcher"]["connectTimeout"]
99
+ self.timeout = (read_timeout, connect_timeout)
99
100
  if not self.server:
100
101
  raise RuntimeError("JiraClient.server is not set.")
101
102
 
102
- self.jira = JIRA(
103
- self.server,
104
- token_auth=token_auth,
105
- timeout=(read_timeout, connect_timeout),
106
- )
103
+ self.jira = JIRA(self.server, token_auth=token_auth, timeout=self.timeout)
107
104
 
108
105
  @staticmethod
109
106
  def create(
@@ -218,7 +215,7 @@ class JiraClient:
218
215
  raise RuntimeError("JiraClient.server is not set.")
219
216
 
220
217
  # use anonymous access to get public projects
221
- jira_api_anon = JIRA(server=self.server)
218
+ jira_api_anon = JIRA(server=self.server, timeout=self.timeout)
222
219
  return [project.key for project in jira_api_anon.projects()]
223
220
 
224
221
  def components(self) -> list[str]:
@@ -1,9 +1,10 @@
1
1
  import threading
2
- from collections.abc import Mapping
2
+ from collections.abc import Mapping, MutableMapping
3
3
  from typing import Any, Self
4
4
 
5
5
  from oauthlib.oauth2 import BackendApplicationClient, TokenExpiredError
6
6
  from requests import Response
7
+ from requests.adapters import BaseAdapter
7
8
  from requests_oauthlib import OAuth2Session
8
9
 
9
10
  FETCH_TOKEN_HEADERS = {
@@ -39,9 +40,6 @@ class OAuth2BackendApplicationSession:
39
40
  def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
40
41
  self.close()
41
42
 
42
- def close(self) -> None:
43
- self.session.close()
44
-
45
43
  def fetch_token(self) -> dict:
46
44
  """
47
45
  Fetch token from token_url and store it in the session.
@@ -100,3 +98,35 @@ class OAuth2BackendApplicationSession:
100
98
  client_secret=client_secret,
101
99
  **kwargs,
102
100
  )
101
+
102
+ # Delegates for ApiBase
103
+
104
+ def close(self) -> None:
105
+ self.session.close()
106
+
107
+ def mount(self, prefix: str, adapter: BaseAdapter) -> None:
108
+ self.session.mount(prefix, adapter)
109
+
110
+ @property
111
+ def headers(self) -> MutableMapping:
112
+ return self.session.headers
113
+
114
+ @property
115
+ def auth(self) -> Any:
116
+ return self.session.auth
117
+
118
+ @auth.setter
119
+ def auth(self, value: Any) -> None:
120
+ self.session.auth = value
121
+
122
+ def get(self, url: str, **kwargs: Any) -> Response:
123
+ return self.session.get(url, **kwargs)
124
+
125
+ def post(self, url: str, **kwargs: Any) -> Response:
126
+ return self.session.post(url, **kwargs)
127
+
128
+ def put(self, url: str, **kwargs: Any) -> Response:
129
+ return self.session.put(url, **kwargs)
130
+
131
+ def delete(self, url: str, **kwargs: Any) -> Response:
132
+ return self.session.delete(url, **kwargs)
@@ -2,6 +2,11 @@ from typing import Any, Self
2
2
  from urllib.parse import urljoin
3
3
 
4
4
  import requests
5
+ from urllib3 import Retry
6
+
7
+ from reconcile.utils.oauth2_backend_application_session import (
8
+ OAuth2BackendApplicationSession,
9
+ )
5
10
 
6
11
 
7
12
  def get_next_url(links: dict[str, dict[str, str]]) -> str | None:
@@ -40,26 +45,24 @@ class ApiBase:
40
45
  self,
41
46
  host: str,
42
47
  auth: requests.auth.AuthBase | None = None,
43
- max_retries: int | None = None,
48
+ max_retries: int | Retry | None = None,
44
49
  read_timeout: float | None = None,
45
- session: requests.Session | None = None,
50
+ session: requests.Session | OAuth2BackendApplicationSession | None = None,
46
51
  ) -> None:
47
52
  self.host = host
48
53
  self.max_retries = max_retries if max_retries is not None else 3
49
54
  self.read_timeout = read_timeout if read_timeout is not None else 30
50
55
  self.session = session or requests.Session()
51
- if not session:
52
- if auth:
53
- self.session.auth = auth
54
- self.session.mount(
55
- "https://", requests.adapters.HTTPAdapter(max_retries=self.max_retries)
56
- )
56
+ if auth:
57
+ self.session.auth = auth
58
+ for prefix in ["http://", "https://"]:
57
59
  self.session.mount(
58
- "http://", requests.adapters.HTTPAdapter(max_retries=self.max_retries)
60
+ prefix,
61
+ requests.adapters.HTTPAdapter(max_retries=self.max_retries),
59
62
  )
60
- self.session.headers.update({
61
- "Content-Type": "application/json",
62
- })
63
+ self.session.headers.update({
64
+ "Content-Type": "application/json",
65
+ })
63
66
 
64
67
  def __enter__(self) -> Self:
65
68
  return self
@@ -1,14 +1,17 @@
1
- from typing import Any, List, Self
1
+ from typing import List
2
+
3
+ from urllib3.util import Retry
2
4
 
3
5
  from reconcile.utils.oauth2_backend_application_session import (
4
6
  OAuth2BackendApplicationSession,
5
7
  )
8
+ from reconcile.utils.rest_api_base import ApiBase
6
9
  from tools.cli_commands.cost_report.response import ReportCostResponse
7
10
 
8
11
  REQUEST_TIMEOUT = 60
9
12
 
10
13
 
11
- class CostManagementApi:
14
+ class CostManagementApi(ApiBase):
12
15
  def __init__(
13
16
  self,
14
17
  base_url: str,
@@ -16,25 +19,24 @@ class CostManagementApi:
16
19
  client_id: str,
17
20
  client_secret: str,
18
21
  scope: List[str] | None = None,
19
- request_timeout: int | None = REQUEST_TIMEOUT,
20
22
  ) -> None:
21
- self.base_url = base_url
22
- self.request_timeout = request_timeout
23
- self.session = OAuth2BackendApplicationSession(
23
+ session = OAuth2BackendApplicationSession(
24
24
  client_id=client_id,
25
25
  client_secret=client_secret,
26
26
  token_url=token_url,
27
27
  scope=scope,
28
28
  )
29
-
30
- def __enter__(self) -> Self:
31
- return self
32
-
33
- def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
34
- self.cleanup()
35
-
36
- def cleanup(self) -> None:
37
- self.session.close()
29
+ max_retries = Retry(
30
+ total=3,
31
+ backoff_factor=15, # large backoff required for server-side processing
32
+ status_forcelist=[500, 502, 503, 504],
33
+ )
34
+ super().__init__(
35
+ host=base_url,
36
+ session=session,
37
+ read_timeout=REQUEST_TIMEOUT,
38
+ max_retries=max_retries,
39
+ )
38
40
 
39
41
  def get_aws_costs_report(self, app: str) -> ReportCostResponse:
40
42
  params = {
@@ -48,10 +50,9 @@ class CostManagementApi:
48
50
  }
49
51
  response = self.session.request(
50
52
  method="GET",
51
- url=f"{self.base_url}/reports/aws/costs/",
52
- headers={"Content-Type": "application/json"},
53
+ url=f"{self.host}/reports/aws/costs/",
53
54
  params=params,
54
- timeout=self.request_timeout,
55
+ timeout=self.read_timeout,
55
56
  )
56
57
  response.raise_for_status()
57
58
  return ReportCostResponse.parse_obj(response.json())
@@ -16,7 +16,7 @@ LAYOUT = """\
16
16
  """
17
17
 
18
18
  HEADER = """\
19
- # Cost Report
19
+ # AWS Cost Report
20
20
  """
21
21
 
22
22
  SUMMARY = """\
tools/qontract_cli.py CHANGED
@@ -2600,7 +2600,7 @@ def alerts(ctx, file_path):
2600
2600
 
2601
2601
  @get.command()
2602
2602
  @click.pass_context
2603
- def cost_report(ctx):
2603
+ def aws_cost_report(ctx):
2604
2604
  command = CostReportCommand.create()
2605
2605
  print(command.execute())
2606
2606
 
@@ -132,12 +132,12 @@ def mock_cost_report_command(mocker):
132
132
  return mocker.patch("tools.qontract_cli.CostReportCommand", autospec=True)
133
133
 
134
134
 
135
- def test_get_cost_report(env_vars, mock_queries, mock_cost_report_command):
135
+ def test_get_aws_cost_report(env_vars, mock_queries, mock_cost_report_command):
136
136
  mock_cost_report_command.create.return_value.execute.return_value = "some report"
137
137
  runner = CliRunner()
138
138
  result = runner.invoke(
139
139
  qontract_cli.get,
140
- "cost-report",
140
+ "aws-cost-report",
141
141
  obj={},
142
142
  )
143
143