qontract-reconcile 0.10.1rc1150__py3-none-any.whl → 0.10.1rc1151__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.1rc1150.dist-info → qontract_reconcile-0.10.1rc1151.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc1150.dist-info → qontract_reconcile-0.10.1rc1151.dist-info}/RECORD +25 -16
- reconcile/gql_definitions/cost_report/cost_namespaces.py +2 -0
- reconcile/typed_queries/cost_report/cost_namespaces.py +7 -4
- tools/cli_commands/cost_report/aws.py +12 -25
- tools/cli_commands/cost_report/cost_management_api.py +79 -6
- tools/cli_commands/cost_report/model.py +21 -0
- tools/cli_commands/cost_report/openshift.py +7 -20
- tools/cli_commands/cost_report/openshift_cost_optimization.py +187 -0
- tools/cli_commands/cost_report/response.py +56 -1
- tools/cli_commands/cost_report/util.py +13 -0
- tools/cli_commands/cost_report/view.py +128 -2
- tools/cli_commands/test/__init__.py +0 -0
- tools/cli_commands/test/conftest.py +332 -0
- tools/cli_commands/test/test_aws_cost_report.py +258 -0
- tools/cli_commands/test/test_cost_management_api.py +326 -0
- tools/cli_commands/test/test_gpg_encrypt.py +235 -0
- tools/cli_commands/test/test_openshift_cost_optimization_report.py +255 -0
- tools/cli_commands/test/test_openshift_cost_report.py +295 -0
- tools/cli_commands/test/test_util.py +70 -0
- tools/qontract_cli.py +10 -0
- tools/test/test_qontract_cli.py +24 -0
- {qontract_reconcile-0.10.1rc1150.dist-info → qontract_reconcile-0.10.1rc1151.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc1150.dist-info → qontract_reconcile-0.10.1rc1151.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc1150.dist-info → qontract_reconcile-0.10.1rc1151.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,258 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from decimal import Decimal
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
from pytest_mock import MockerFixture
|
7
|
+
|
8
|
+
from reconcile.typed_queries.cost_report.app_names import App
|
9
|
+
from tools.cli_commands.cost_report.aws import AwsCostReportCommand
|
10
|
+
from tools.cli_commands.cost_report.model import ChildAppReport, Report, ReportItem
|
11
|
+
from tools.cli_commands.cost_report.response import AwsReportCostResponse
|
12
|
+
from tools.cli_commands.test.conftest import (
|
13
|
+
COST_REPORT_SECRET,
|
14
|
+
)
|
15
|
+
|
16
|
+
COST_MANAGEMENT_CONSOLE_BASE_URL = (
|
17
|
+
"https://console.redhat.com/openshift/cost-management"
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.fixture
|
22
|
+
def mock_gql(mocker: MockerFixture) -> Any:
|
23
|
+
return mocker.patch("tools.cli_commands.cost_report.aws.gql")
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture
|
27
|
+
def mock_cost_management_api(mocker: MockerFixture) -> Any:
|
28
|
+
return mocker.patch(
|
29
|
+
"tools.cli_commands.cost_report.aws.CostManagementApi",
|
30
|
+
autospec=True,
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
@pytest.fixture
|
35
|
+
def mock_fetch_cost_report_secret(mocker: MockerFixture) -> Any:
|
36
|
+
return mocker.patch(
|
37
|
+
"tools.cli_commands.cost_report.aws.fetch_cost_report_secret",
|
38
|
+
return_value=COST_REPORT_SECRET,
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
def test_aws_cost_report_create(
|
43
|
+
mock_gql: Any,
|
44
|
+
mock_cost_management_api: Any,
|
45
|
+
mock_fetch_cost_report_secret: Any,
|
46
|
+
) -> None:
|
47
|
+
cost_report_command = AwsCostReportCommand.create()
|
48
|
+
|
49
|
+
assert isinstance(cost_report_command, AwsCostReportCommand)
|
50
|
+
assert cost_report_command.gql_api == mock_gql.get_api.return_value
|
51
|
+
assert (
|
52
|
+
cost_report_command.cost_management_console_base_url
|
53
|
+
== COST_MANAGEMENT_CONSOLE_BASE_URL
|
54
|
+
)
|
55
|
+
assert (
|
56
|
+
cost_report_command.cost_management_api
|
57
|
+
== mock_cost_management_api.create_from_secret.return_value
|
58
|
+
)
|
59
|
+
assert cost_report_command.thread_pool_size == 10
|
60
|
+
mock_cost_management_api.create_from_secret.assert_called_once_with(
|
61
|
+
COST_REPORT_SECRET
|
62
|
+
)
|
63
|
+
mock_fetch_cost_report_secret.assert_called_once_with(mock_gql.get_api.return_value)
|
64
|
+
|
65
|
+
|
66
|
+
@pytest.fixture
|
67
|
+
def aws_cost_report_command(
|
68
|
+
mock_gql: Any,
|
69
|
+
mock_cost_management_api: Any,
|
70
|
+
mock_fetch_cost_report_secret: Any,
|
71
|
+
) -> AwsCostReportCommand:
|
72
|
+
return AwsCostReportCommand.create()
|
73
|
+
|
74
|
+
|
75
|
+
@pytest.fixture
|
76
|
+
def mock_get_app_names(mocker: MockerFixture) -> Any:
|
77
|
+
return mocker.patch("tools.cli_commands.cost_report.aws.get_app_names")
|
78
|
+
|
79
|
+
|
80
|
+
PARENT_APP = App(name="parent", parent_app_name=None)
|
81
|
+
CHILD_APP = App(name="child", parent_app_name="parent")
|
82
|
+
|
83
|
+
|
84
|
+
def test_aws_cost_report_execute(
|
85
|
+
aws_cost_report_command: AwsCostReportCommand,
|
86
|
+
mock_get_app_names: Any,
|
87
|
+
fx: Callable,
|
88
|
+
) -> None:
|
89
|
+
expected_output = fx("empty_aws_cost_report.md")
|
90
|
+
mock_get_app_names.return_value = []
|
91
|
+
|
92
|
+
output = aws_cost_report_command.execute()
|
93
|
+
|
94
|
+
assert output.rstrip() == expected_output.rstrip()
|
95
|
+
|
96
|
+
|
97
|
+
def test_aws_cost_report_get_apps(
|
98
|
+
aws_cost_report_command: AwsCostReportCommand,
|
99
|
+
mock_get_app_names: Any,
|
100
|
+
) -> None:
|
101
|
+
expected_apps = [PARENT_APP, CHILD_APP]
|
102
|
+
mock_get_app_names.return_value = expected_apps
|
103
|
+
|
104
|
+
apps = aws_cost_report_command.get_apps()
|
105
|
+
|
106
|
+
assert apps == expected_apps
|
107
|
+
|
108
|
+
|
109
|
+
def aws_report_cost_response_builder(
|
110
|
+
delta_value: int,
|
111
|
+
delta_percent: int,
|
112
|
+
total: int,
|
113
|
+
service: str,
|
114
|
+
) -> AwsReportCostResponse:
|
115
|
+
return AwsReportCostResponse.parse_obj({
|
116
|
+
"meta": {
|
117
|
+
"delta": {
|
118
|
+
"value": delta_value,
|
119
|
+
"percent": delta_percent,
|
120
|
+
},
|
121
|
+
"total": {
|
122
|
+
"cost": {
|
123
|
+
"total": {
|
124
|
+
"value": total,
|
125
|
+
"units": "USD",
|
126
|
+
}
|
127
|
+
}
|
128
|
+
},
|
129
|
+
},
|
130
|
+
"data": [
|
131
|
+
{
|
132
|
+
"date": "2024-02",
|
133
|
+
"services": [
|
134
|
+
{
|
135
|
+
"service": service,
|
136
|
+
"values": [
|
137
|
+
{
|
138
|
+
"delta_value": delta_value,
|
139
|
+
"delta_percent": delta_percent,
|
140
|
+
"cost": {
|
141
|
+
"total": {
|
142
|
+
"value": total,
|
143
|
+
"units": "USD",
|
144
|
+
}
|
145
|
+
},
|
146
|
+
}
|
147
|
+
],
|
148
|
+
},
|
149
|
+
],
|
150
|
+
}
|
151
|
+
],
|
152
|
+
})
|
153
|
+
|
154
|
+
|
155
|
+
PARENT_APP_COST_RESPONSE = aws_report_cost_response_builder(
|
156
|
+
delta_value=100,
|
157
|
+
delta_percent=10,
|
158
|
+
total=1000,
|
159
|
+
service="service1",
|
160
|
+
)
|
161
|
+
|
162
|
+
CHILD_APP_COST_RESPONSE = aws_report_cost_response_builder(
|
163
|
+
delta_value=200,
|
164
|
+
delta_percent=20,
|
165
|
+
total=2000,
|
166
|
+
service="service2",
|
167
|
+
)
|
168
|
+
|
169
|
+
PARENT_APP_REPORT = Report(
|
170
|
+
app_name="parent",
|
171
|
+
parent_app_name=None,
|
172
|
+
child_apps=[
|
173
|
+
ChildAppReport(name="child", total=Decimal(2000)),
|
174
|
+
],
|
175
|
+
child_apps_total=Decimal(2000),
|
176
|
+
date="2024-02",
|
177
|
+
items=[
|
178
|
+
ReportItem(
|
179
|
+
name="service1",
|
180
|
+
delta_value=Decimal(100),
|
181
|
+
delta_percent=10,
|
182
|
+
total=Decimal(1000),
|
183
|
+
)
|
184
|
+
],
|
185
|
+
items_total=Decimal(1000),
|
186
|
+
items_delta_value=Decimal(100),
|
187
|
+
items_delta_percent=10,
|
188
|
+
total=Decimal(3000),
|
189
|
+
)
|
190
|
+
|
191
|
+
CHILD_APP_REPORT = Report(
|
192
|
+
app_name="child",
|
193
|
+
parent_app_name="parent",
|
194
|
+
child_apps=[],
|
195
|
+
child_apps_total=Decimal(0),
|
196
|
+
date="2024-02",
|
197
|
+
items=[
|
198
|
+
ReportItem(
|
199
|
+
name="service2",
|
200
|
+
delta_value=Decimal(200),
|
201
|
+
delta_percent=20,
|
202
|
+
total=Decimal(2000),
|
203
|
+
)
|
204
|
+
],
|
205
|
+
items_total=Decimal(2000),
|
206
|
+
items_delta_value=Decimal(200),
|
207
|
+
items_delta_percent=20,
|
208
|
+
total=Decimal(2000),
|
209
|
+
)
|
210
|
+
|
211
|
+
|
212
|
+
def test_aws_cost_report_get_reports(
|
213
|
+
aws_cost_report_command: AwsCostReportCommand,
|
214
|
+
mock_cost_management_api: Any,
|
215
|
+
) -> None:
|
216
|
+
mocked_api = mock_cost_management_api.create_from_secret.return_value
|
217
|
+
mocked_api.get_aws_costs_report.return_value = PARENT_APP_COST_RESPONSE
|
218
|
+
|
219
|
+
reports = aws_cost_report_command.get_reports([PARENT_APP])
|
220
|
+
|
221
|
+
assert reports == {
|
222
|
+
"parent": PARENT_APP_COST_RESPONSE,
|
223
|
+
}
|
224
|
+
mocked_api.get_aws_costs_report.assert_called_once_with("parent")
|
225
|
+
|
226
|
+
|
227
|
+
def test_aws_cost_report_process_reports(
|
228
|
+
aws_cost_report_command: AwsCostReportCommand,
|
229
|
+
) -> None:
|
230
|
+
expected_reports = {
|
231
|
+
"parent": PARENT_APP_REPORT,
|
232
|
+
"child": CHILD_APP_REPORT,
|
233
|
+
}
|
234
|
+
|
235
|
+
reports = aws_cost_report_command.process_reports(
|
236
|
+
[PARENT_APP, CHILD_APP],
|
237
|
+
{
|
238
|
+
"parent": PARENT_APP_COST_RESPONSE,
|
239
|
+
"child": CHILD_APP_COST_RESPONSE,
|
240
|
+
},
|
241
|
+
)
|
242
|
+
|
243
|
+
assert reports == expected_reports
|
244
|
+
|
245
|
+
|
246
|
+
def test_aws_cost_report_render(
|
247
|
+
aws_cost_report_command: AwsCostReportCommand,
|
248
|
+
fx: Callable,
|
249
|
+
) -> None:
|
250
|
+
expected_output = fx("aws_cost_report.md")
|
251
|
+
reports = {
|
252
|
+
"parent": PARENT_APP_REPORT,
|
253
|
+
"child": CHILD_APP_REPORT,
|
254
|
+
}
|
255
|
+
|
256
|
+
output = aws_cost_report_command.render(reports)
|
257
|
+
|
258
|
+
assert output == expected_output
|
@@ -0,0 +1,326 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from decimal import Decimal
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
import requests
|
7
|
+
from pytest_httpserver import HTTPServer
|
8
|
+
from pytest_mock import MockerFixture
|
9
|
+
from requests import HTTPError
|
10
|
+
|
11
|
+
from tools.cli_commands.cost_report.cost_management_api import CostManagementApi
|
12
|
+
from tools.cli_commands.cost_report.response import (
|
13
|
+
AwsReportCostResponse,
|
14
|
+
CostResponse,
|
15
|
+
CostTotalResponse,
|
16
|
+
DeltaResponse,
|
17
|
+
MoneyResponse,
|
18
|
+
OpenShiftCostResponse,
|
19
|
+
OpenShiftReportCostResponse,
|
20
|
+
ProjectCostResponse,
|
21
|
+
ProjectCostValueResponse,
|
22
|
+
ReportMetaResponse,
|
23
|
+
ServiceCostResponse,
|
24
|
+
ServiceCostValueResponse,
|
25
|
+
TotalMetaResponse,
|
26
|
+
)
|
27
|
+
from tools.cli_commands.test.conftest import (
|
28
|
+
COST_MANAGEMENT_API_HOST,
|
29
|
+
COST_REPORT_SECRET,
|
30
|
+
OPENSHIFT_COST_OPTIMIZATION_RESPONSE,
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
@pytest.fixture
|
35
|
+
def mock_session(mocker: MockerFixture) -> Any:
|
36
|
+
return mocker.patch(
|
37
|
+
"tools.cli_commands.cost_report.cost_management_api.OAuth2BackendApplicationSession",
|
38
|
+
autospec=True,
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.fixture
|
43
|
+
def base_url(httpserver: HTTPServer) -> str:
|
44
|
+
return httpserver.url_for("/")
|
45
|
+
|
46
|
+
|
47
|
+
TOKEN_URL = "token_url"
|
48
|
+
CLIENT_ID = COST_REPORT_SECRET["client_id"]
|
49
|
+
CLIENT_SECRET = COST_REPORT_SECRET["client_secret"]
|
50
|
+
SCOPE = ["scope"]
|
51
|
+
|
52
|
+
|
53
|
+
def test_cost_management_api_create_from_secret(
|
54
|
+
mock_session: Any,
|
55
|
+
) -> None:
|
56
|
+
api = CostManagementApi.create_from_secret(COST_REPORT_SECRET)
|
57
|
+
|
58
|
+
assert api.host == COST_MANAGEMENT_API_HOST
|
59
|
+
assert api.base_url == COST_REPORT_SECRET["api_base_url"]
|
60
|
+
assert api.session == mock_session.return_value
|
61
|
+
mock_session.assert_called_once_with(
|
62
|
+
client_id=CLIENT_ID,
|
63
|
+
client_secret=CLIENT_SECRET,
|
64
|
+
token_url=TOKEN_URL,
|
65
|
+
scope=SCOPE,
|
66
|
+
)
|
67
|
+
|
68
|
+
|
69
|
+
def test_cost_management_api_init(mock_session: Any, base_url: str) -> None:
|
70
|
+
with CostManagementApi(
|
71
|
+
base_url=base_url,
|
72
|
+
token_url=TOKEN_URL,
|
73
|
+
client_id=CLIENT_ID,
|
74
|
+
client_secret=CLIENT_SECRET,
|
75
|
+
scope=SCOPE,
|
76
|
+
) as api:
|
77
|
+
pass
|
78
|
+
|
79
|
+
assert api.base_url == base_url
|
80
|
+
assert api.session == mock_session.return_value
|
81
|
+
|
82
|
+
mock_session.assert_called_once_with(
|
83
|
+
client_id=CLIENT_ID,
|
84
|
+
client_secret=CLIENT_SECRET,
|
85
|
+
token_url=TOKEN_URL,
|
86
|
+
scope=SCOPE,
|
87
|
+
)
|
88
|
+
assert mock_session.return_value.mount.call_count == 2
|
89
|
+
mock_session.return_value.close.assert_called_once_with()
|
90
|
+
|
91
|
+
|
92
|
+
@pytest.fixture
|
93
|
+
def cost_management_api(mock_session: Any, base_url: str) -> CostManagementApi:
|
94
|
+
# swap to requests.request to skip oauth2 logic
|
95
|
+
mock_session.return_value.request.side_effect = requests.request
|
96
|
+
return CostManagementApi(
|
97
|
+
base_url=base_url,
|
98
|
+
token_url=TOKEN_URL,
|
99
|
+
client_id=CLIENT_ID,
|
100
|
+
client_secret=CLIENT_SECRET,
|
101
|
+
scope=SCOPE,
|
102
|
+
)
|
103
|
+
|
104
|
+
|
105
|
+
EXPECTED_REPORT_COST_RESPONSE = AwsReportCostResponse(
|
106
|
+
meta=ReportMetaResponse(
|
107
|
+
delta=DeltaResponse(
|
108
|
+
value=Decimal(100),
|
109
|
+
percent=10,
|
110
|
+
),
|
111
|
+
total=TotalMetaResponse(
|
112
|
+
cost=CostTotalResponse(
|
113
|
+
total=MoneyResponse(
|
114
|
+
value=Decimal(1000),
|
115
|
+
units="USD",
|
116
|
+
)
|
117
|
+
)
|
118
|
+
),
|
119
|
+
),
|
120
|
+
data=[
|
121
|
+
CostResponse(
|
122
|
+
date="2024-02",
|
123
|
+
services=[
|
124
|
+
ServiceCostResponse(
|
125
|
+
service="AmazonEC2",
|
126
|
+
values=[
|
127
|
+
ServiceCostValueResponse(
|
128
|
+
delta_percent=10,
|
129
|
+
delta_value=Decimal(200),
|
130
|
+
cost=CostTotalResponse(
|
131
|
+
total=MoneyResponse(
|
132
|
+
value=Decimal(800),
|
133
|
+
units="USD",
|
134
|
+
)
|
135
|
+
),
|
136
|
+
)
|
137
|
+
],
|
138
|
+
),
|
139
|
+
ServiceCostResponse(
|
140
|
+
service="AmazonS3",
|
141
|
+
values=[
|
142
|
+
ServiceCostValueResponse(
|
143
|
+
delta_percent=-10,
|
144
|
+
delta_value=Decimal(-100),
|
145
|
+
cost=CostTotalResponse(
|
146
|
+
total=MoneyResponse(
|
147
|
+
value=Decimal(200),
|
148
|
+
units="USD",
|
149
|
+
)
|
150
|
+
),
|
151
|
+
)
|
152
|
+
],
|
153
|
+
),
|
154
|
+
],
|
155
|
+
),
|
156
|
+
],
|
157
|
+
)
|
158
|
+
|
159
|
+
|
160
|
+
def test_get_aws_costs_report(
|
161
|
+
cost_management_api: CostManagementApi,
|
162
|
+
fx: Callable,
|
163
|
+
httpserver: HTTPServer,
|
164
|
+
base_url: str,
|
165
|
+
) -> None:
|
166
|
+
response_body = fx("aws_cost_report.json")
|
167
|
+
httpserver.expect_request(
|
168
|
+
"/reports/aws/costs/",
|
169
|
+
query_string={
|
170
|
+
"cost_type": "calculated_amortized_cost",
|
171
|
+
"delta": "cost",
|
172
|
+
"filter[resolution]": "monthly",
|
173
|
+
"filter[tag:app]": "test",
|
174
|
+
"filter[time_scope_units]": "month",
|
175
|
+
"filter[time_scope_value]": "-2",
|
176
|
+
"group_by[service]": "*",
|
177
|
+
},
|
178
|
+
).respond_with_data(response_body)
|
179
|
+
|
180
|
+
report_cost_response = cost_management_api.get_aws_costs_report(app="test")
|
181
|
+
|
182
|
+
assert report_cost_response == EXPECTED_REPORT_COST_RESPONSE
|
183
|
+
|
184
|
+
|
185
|
+
def test_get_aws_costs_report_error(
|
186
|
+
cost_management_api: CostManagementApi,
|
187
|
+
fx: Callable,
|
188
|
+
httpserver: HTTPServer,
|
189
|
+
) -> None:
|
190
|
+
httpserver.expect_request("/reports/aws/costs/").respond_with_data(status=500)
|
191
|
+
|
192
|
+
with pytest.raises(HTTPError) as error:
|
193
|
+
cost_management_api.get_aws_costs_report(app="test")
|
194
|
+
|
195
|
+
assert error.value.response.status_code == 500
|
196
|
+
|
197
|
+
|
198
|
+
EXPECTED_OPENSHIFT_REPORT_COST_RESPONSE = OpenShiftReportCostResponse(
|
199
|
+
meta=ReportMetaResponse(
|
200
|
+
delta=DeltaResponse(
|
201
|
+
value=Decimal(100),
|
202
|
+
percent=10,
|
203
|
+
),
|
204
|
+
total=TotalMetaResponse(
|
205
|
+
cost=CostTotalResponse(
|
206
|
+
total=MoneyResponse(
|
207
|
+
value=Decimal(1000),
|
208
|
+
units="USD",
|
209
|
+
)
|
210
|
+
)
|
211
|
+
),
|
212
|
+
),
|
213
|
+
data=[
|
214
|
+
OpenShiftCostResponse(
|
215
|
+
date="2024-02",
|
216
|
+
projects=[
|
217
|
+
ProjectCostResponse(
|
218
|
+
project="some-project",
|
219
|
+
values=[
|
220
|
+
ProjectCostValueResponse(
|
221
|
+
delta_percent=10,
|
222
|
+
delta_value=Decimal(100),
|
223
|
+
clusters=["some-cluster"],
|
224
|
+
cost=CostTotalResponse(
|
225
|
+
total=MoneyResponse(
|
226
|
+
value=Decimal(1000),
|
227
|
+
units="USD",
|
228
|
+
)
|
229
|
+
),
|
230
|
+
)
|
231
|
+
],
|
232
|
+
),
|
233
|
+
],
|
234
|
+
),
|
235
|
+
],
|
236
|
+
)
|
237
|
+
|
238
|
+
|
239
|
+
def test_get_openshift_costs_report(
|
240
|
+
cost_management_api: CostManagementApi,
|
241
|
+
fx: Callable,
|
242
|
+
httpserver: HTTPServer,
|
243
|
+
) -> None:
|
244
|
+
response_body = fx("openshift_cost_report.json")
|
245
|
+
project = "some-project"
|
246
|
+
cluster = "some-cluster-uuid"
|
247
|
+
httpserver.expect_request(
|
248
|
+
"/reports/openshift/costs/",
|
249
|
+
query_string={
|
250
|
+
"delta": "cost",
|
251
|
+
"filter[resolution]": "monthly",
|
252
|
+
"filter[cluster]": cluster,
|
253
|
+
"filter[exact:project]": project,
|
254
|
+
"filter[time_scope_units]": "month",
|
255
|
+
"filter[time_scope_value]": "-2",
|
256
|
+
"group_by[project]": "*",
|
257
|
+
},
|
258
|
+
).respond_with_data(response_body)
|
259
|
+
|
260
|
+
report_cost_response = cost_management_api.get_openshift_costs_report(
|
261
|
+
cluster=cluster,
|
262
|
+
project=project,
|
263
|
+
)
|
264
|
+
|
265
|
+
assert report_cost_response == EXPECTED_OPENSHIFT_REPORT_COST_RESPONSE
|
266
|
+
|
267
|
+
|
268
|
+
def test_get_openshift_costs_report_error(
|
269
|
+
cost_management_api: CostManagementApi,
|
270
|
+
fx: Callable,
|
271
|
+
httpserver: HTTPServer,
|
272
|
+
) -> None:
|
273
|
+
httpserver.expect_request("/reports/openshift/costs/").respond_with_data(status=500)
|
274
|
+
|
275
|
+
with pytest.raises(HTTPError) as error:
|
276
|
+
cost_management_api.get_openshift_costs_report(
|
277
|
+
cluster="some-cluster",
|
278
|
+
project="some-project",
|
279
|
+
)
|
280
|
+
|
281
|
+
assert error.value.response.status_code == 500
|
282
|
+
|
283
|
+
|
284
|
+
def test_get_openshift_cost_optimization_report(
|
285
|
+
cost_management_api: CostManagementApi,
|
286
|
+
fx: Callable,
|
287
|
+
httpserver: HTTPServer,
|
288
|
+
) -> None:
|
289
|
+
response_body = fx("openshift_cost_optimization_report.json")
|
290
|
+
project = "some-project"
|
291
|
+
cluster = "some-cluster-uuid"
|
292
|
+
httpserver.expect_request(
|
293
|
+
"/recommendations/openshift",
|
294
|
+
query_string={
|
295
|
+
"cluster": cluster,
|
296
|
+
"project": project,
|
297
|
+
"limit": "100",
|
298
|
+
"memory-unit": "MiB",
|
299
|
+
"cpu-unit": "millicores",
|
300
|
+
},
|
301
|
+
).respond_with_data(response_body)
|
302
|
+
|
303
|
+
report_cost_response = cost_management_api.get_openshift_cost_optimization_report(
|
304
|
+
cluster=cluster,
|
305
|
+
project=project,
|
306
|
+
)
|
307
|
+
|
308
|
+
assert report_cost_response == OPENSHIFT_COST_OPTIMIZATION_RESPONSE
|
309
|
+
|
310
|
+
|
311
|
+
def test_get_openshift_cost_optimization_report_error(
|
312
|
+
cost_management_api: CostManagementApi,
|
313
|
+
fx: Callable,
|
314
|
+
httpserver: HTTPServer,
|
315
|
+
) -> None:
|
316
|
+
httpserver.expect_request("/recommendations/openshift").respond_with_data(
|
317
|
+
status=500
|
318
|
+
)
|
319
|
+
|
320
|
+
with pytest.raises(HTTPError) as error:
|
321
|
+
cost_management_api.get_openshift_cost_optimization_report(
|
322
|
+
cluster="some-cluster",
|
323
|
+
project="some-project",
|
324
|
+
)
|
325
|
+
|
326
|
+
assert error.value.response.status_code == 500
|