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.
- {qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/RECORD +12 -12
- reconcile/utils/jira_client.py +3 -6
- reconcile/utils/oauth2_backend_application_session.py +34 -4
- reconcile/utils/rest_api_base.py +15 -12
- tools/cli_commands/cost_report/cost_management_api.py +19 -18
- tools/cli_commands/cost_report/view.py +1 -1
- tools/qontract_cli.py +1 -1
- tools/test/test_qontract_cli.py +2 -2
- {qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.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.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
|
{qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/RECORD
RENAMED
@@ -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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
769
|
-
qontract_reconcile-0.10.
|
770
|
-
qontract_reconcile-0.10.
|
771
|
-
qontract_reconcile-0.10.
|
772
|
-
qontract_reconcile-0.10.
|
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,,
|
reconcile/utils/jira_client.py
CHANGED
@@ -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)
|
reconcile/utils/rest_api_base.py
CHANGED
@@ -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
|
52
|
-
|
53
|
-
|
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
|
-
|
60
|
+
prefix,
|
61
|
+
requests.adapters.HTTPAdapter(max_retries=self.max_retries),
|
59
62
|
)
|
60
|
-
|
61
|
-
|
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
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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.
|
52
|
-
headers={"Content-Type": "application/json"},
|
53
|
+
url=f"{self.host}/reports/aws/costs/",
|
53
54
|
params=params,
|
54
|
-
timeout=self.
|
55
|
+
timeout=self.read_timeout,
|
55
56
|
)
|
56
57
|
response.raise_for_status()
|
57
58
|
return ReportCostResponse.parse_obj(response.json())
|
tools/qontract_cli.py
CHANGED
tools/test/test_qontract_cli.py
CHANGED
@@ -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
|
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
|
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc731.dist-info → qontract_reconcile-0.10.1rc733.dist-info}/top_level.txt
RENAMED
File without changes
|