From e3d60b238e79b42b5d082187d80a42f4e3011e46 Mon Sep 17 00:00:00 2001 From: Aspect13 Date: Wed, 19 Jul 2023 13:12:31 +0400 Subject: [PATCH 1/5] refactored configs various improvements and bugfixes interceptor needs rebuild --- api/v1/charts.py | 2 +- api/v1/loki_url.py | 16 ++++++---------- connectors/loki_connector.py | 11 ++++++----- static/js/backend_performance.js | 15 ++++++++------- utils/report_utils.py | 16 +++++++++------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/api/v1/charts.py b/api/v1/charts.py index b8a4d6a..c73a88a 100644 --- a/api/v1/charts.py +++ b/api/v1/charts.py @@ -66,7 +66,7 @@ def _get_connector(self, args, source): return MinioConnector(**args) else: if source == "errors": - log.info('Using LokiConnector') + log.info('Using LokiConnector %s', args) return LokiConnector(**args) elif source == "engine_health": project_id = Report.query.with_entities(Report.project_id).filter( diff --git a/api/v1/loki_url.py b/api/v1/loki_url.py index 29bc455..6335a7c 100644 --- a/api/v1/loki_url.py +++ b/api/v1/loki_url.py @@ -1,7 +1,7 @@ from ...models.reports import Report from flask import request -from tools import api_tools, constants as c +from tools import api_tools, LokiLogFetcher class API(api_tools.APIBase): @@ -17,15 +17,11 @@ def get(self, project_id: int, **kwargs): if not report_id: return {"message": ""}, 404 - build_id = Report.query.get_or_404(report_id).build_id - - websocket_base_url = c.APP_HOST.replace("http://", "ws://").replace("https://", "wss://") - websocket_base_url += "/loki/api/v1/tail" - logs_query = "{" + f'report_id="{report_id}",project="{project_id}",build_id="{build_id}"' + "}" + project = self.module.context.rpc_manager.call.project_get_or_404(project_id=project_id) + websocket_base_url = LokiLogFetcher.from_project(project).get_websocket_url(project) - logs_start = 0 logs_limit = 10000000000 + build_id = Report.query.get_or_404(report_id).build_id + logs_query = '{report_id="%s",project="%s",build_id="%s"}' % (report_id, project_id, build_id) - return { - "websocket_url": f"{websocket_base_url}?query={logs_query}&start={logs_start}&limit={logs_limit}" - }, 200 + return {'websocket_url': f"{websocket_base_url}?query={logs_query}&start=0&limit={logs_limit}"}, 200 diff --git a/connectors/loki_connector.py b/connectors/loki_connector.py index 73288ac..84cc7dc 100644 --- a/connectors/loki_connector.py +++ b/connectors/loki_connector.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional import requests @@ -22,11 +23,11 @@ class LokiConnector: - def __init__(self, **args) -> None: - self.test_name = args['test_name'] - self.query_range_url = LokiLogFetcher.make_url() # todo: here we should consider making url from project - self.start_time, self.end_time = timeframe(args, time_as_ts=True) - + def __init__(self, *, test_name: str, project_id: Optional[int] = None, **kwargs) -> None: + self.test_name = test_name + self.query_range_url = LokiLogFetcher.make_url(project_id) # todo: here we should consider making url from project + kwargs['test_name'] = self.test_name + self.start_time, self.end_time = timeframe(kwargs, time_as_ts=True) def get_issues(self) -> list: data = { diff --git a/static/js/backend_performance.js b/static/js/backend_performance.js index 7496591..65309b2 100644 --- a/static/js/backend_performance.js +++ b/static/js/backend_performance.js @@ -371,16 +371,17 @@ const TestCreateModal = {
-
-

Entrypoint

-

File for jMeter and class for gatling

- +

Entrypoint

+

File for jMeter and class for gatling

+ -
[[ get_error_msg('entrypoint') ]]
-
+ > +
[[ get_error_msg('entrypoint') ]]
+
diff --git a/utils/report_utils.py b/utils/report_utils.py index fd5edef..d3eebc5 100644 --- a/utils/report_utils.py +++ b/utils/report_utils.py @@ -2,15 +2,16 @@ from collections import defaultdict from datetime import datetime, timezone from typing import Tuple, Union, Optional, List -from pydantic import BaseModel, validator, ValidationError -from tools import data_tools, MinioClient, rpc_tools +from influxdb.exceptions import InfluxDBClientError +from pydantic import BaseModel, validator, ValidationError from pylon.core.tools import log from ..models.baselines import Baseline from ..models.reports import Report -# from influxdb.exceptions import InfluxDBClientError + +from tools import data_tools, MinioClient, rpc_tools def _create_dataset_for_users(timeline, data, scope, metric, axe): @@ -290,6 +291,7 @@ def timeframe(args: dict, time_as_ts: bool = False) -> tuple: def delete_project_reports(project: Union['Project', int], report_ids: List[int]) -> None: + from ..connectors.influx_connector import InfluxConnector if isinstance(project, int): project = rpc_tools.RpcMixin().rpc.call.project_get_or_404( project_id=project) @@ -305,10 +307,10 @@ def delete_project_reports(project: Union['Project', int], report_ids: List[int] minio_delete_build_ids = dict() for build_id, name, lg_type, test_config in query_result: # delete influx tables - # try: - # InfluxConnector(build_id=build_id, test_name=name, lg_type=lg_type).delete_test_data() - # except InfluxDBClientError as e: - # log.warning('InfluxDBClientError %s', e) + try: + InfluxConnector(build_id=build_id, test_name=name, lg_type=lg_type).delete_test_data() + except InfluxDBClientError as e: + log.warning('InfluxDBClientError %s', e) # collect s3 data for deletion s3_settings = test_config.get( From 604fe981abe6a7569302290aca7c6215ab4b255c Mon Sep 17 00:00:00 2001 From: Aspect13 Date: Wed, 19 Jul 2023 15:56:42 +0400 Subject: [PATCH 2/5] fixing some tasks apis fixing various typos fixing classes inheritance --- models/pd/quality_gate.py | 13 +++---------- models/pd/thresholds.py | 36 ++++++++++++++++++------------------ static/js/quality_gate.js | 18 +++++++++--------- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/models/pd/quality_gate.py b/models/pd/quality_gate.py index 254a803..ec15e76 100644 --- a/models/pd/quality_gate.py +++ b/models/pd/quality_gate.py @@ -5,9 +5,8 @@ class QualityGateSLA(BaseModel): checked: bool -class QualityGateBaseline(BaseModel): - checked: bool - rt_baseline_comparison_mecric: str +class QualityGateBaseline(QualityGateSLA): + rt_baseline_comparison_metric: str class QualityGateSettingsSummary(BaseModel): @@ -19,13 +18,7 @@ class QualityGateSettingsSummary(BaseModel): throughput_deviation: PositiveInt -class QualityGateSettingsRequests(BaseModel): - check_response_time: bool - response_time_deviation: PositiveInt - check_error_rate: bool - error_rate_deviation: conint(ge=0, le=100) - check_throughput: bool - throughput_deviation: PositiveInt +class QualityGateSettingsRequests(QualityGateSettingsSummary): percentage_of_failed_requests: conint(ge=0, le=100) diff --git a/models/pd/thresholds.py b/models/pd/thresholds.py index efe6eec..8bfba56 100644 --- a/models/pd/thresholds.py +++ b/models/pd/thresholds.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Literal from pydantic import BaseModel, validator, AnyUrl, parse_obj_as, root_validator, constr from ..tests import Test @@ -10,9 +10,9 @@ class ThresholdPD(BaseModel): test: str environment: str scope: str - target: str - aggregation: str - comparison: str + target: Literal['throughput', 'error_rate', 'response_time'] + aggregation: Literal['max', 'min', 'avg', 'pct95', 'pct50'] + comparison: Literal['gte', 'lte', 'lt', 'gt', 'eq'] value: float @validator('test') @@ -42,17 +42,17 @@ def validate_scope_exists(cls, value: str, values: dict): ).first(), 'Such scope does not exist' return value - @validator('target') - def validate_target(cls, value: str): - assert value in {'throughput', 'error_rate', 'response_time'}, f'Target {value} is not supported' - return value - - @validator('aggregation') - def validate_aggregation(cls, value: str): - assert value in {'max', 'min', 'avg', 'pct95', 'pct50'}, f'Aggregation {value} is not supported' - return value - - @validator('comparison') - def validate_comparison(cls, value: str): - assert value in {'gte', 'lte', 'lt', 'gt', 'eq'}, f'Comparison {value} is not supported' - return value + # @validator('target') + # def validate_target(cls, value: str): + # assert value in {'throughput', 'error_rate', 'response_time'}, f'Target {value} is not supported' + # return value + # + # @validator('aggregation') + # def validate_aggregation(cls, value: str): + # assert value in {'max', 'min', 'avg', 'pct95', 'pct50'}, f'Aggregation {value} is not supported' + # return value + # + # @validator('comparison') + # def validate_comparison(cls, value: str): + # assert value in {'gte', 'lte', 'lt', 'gt', 'eq'}, f'Comparison {value} is not supported' + # return value diff --git a/static/js/quality_gate.js b/static/js/quality_gate.js index 02ba993..79aa1ca 100644 --- a/static/js/quality_gate.js +++ b/static/js/quality_gate.js @@ -27,7 +27,7 @@ const QualityGate = { request_error_rate_deviation, request_throughput_deviation, percentage_of_failed_requests, - rt_baseline_comparison_mecric, + rt_baseline_comparison_metric, } = this return { SLA: { @@ -35,7 +35,7 @@ const QualityGate = { }, baseline: { checked: baseline, - rt_baseline_comparison_mecric: rt_baseline_comparison_mecric + rt_baseline_comparison_metric: rt_baseline_comparison_metric }, settings: { summary_results: { @@ -91,7 +91,7 @@ const QualityGate = { request_error_rate_deviation: data.settings.per_request_results.error_rate_deviation, request_throughput_deviation: data.settings.per_request_results.throughput_deviation, percentage_of_failed_requests: data.settings.per_request_results.percentage_of_failed_requests, - rt_baseline_comparison_mecric: data.baseline.rt_baseline_comparison_mecric + rt_baseline_comparison_metric: data.baseline.rt_baseline_comparison_metric } Object.assign(this.$data, {...this.initialState(), ...data_to_load}) }, @@ -120,8 +120,8 @@ const QualityGate = { request_error_rate_deviation: 1, request_throughput_deviation: 1, percentage_of_failed_requests: 20, - rt_baseline_comparison_mecric: "pct95", - is_adv_settins_open: false, + rt_baseline_comparison_metric: "pct95", + is_adv_settings_open: false, errors: {}, }) @@ -151,12 +151,12 @@ const QualityGate = { role="button" aria-expanded="false" aria-controls="selector_advanced_settings" - @click="is_adv_settins_open = !is_adv_settins_open" + @click="is_adv_settings_open = !is_adv_settings_open" >

ADVANCED SETTINGS

@@ -335,14 +335,14 @@ const QualityGate = {
-
[[ errors.baseline_rt_baseline_comparison_mecric ]]
+
[[ errors.baseline_rt_baseline_comparison_metric ]]
From bff67a6d1595d90f8c7b1f0d7ad3b8d0ad7221ff Mon Sep 17 00:00:00 2001 From: Aspect13 Date: Wed, 19 Jul 2023 16:37:44 +0400 Subject: [PATCH 3/5] fixing summary legend --- static/js/chart_legend.js | 5 ++++- static/js/results.js | 8 ++++---- templates/results/summary.html | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/static/js/chart_legend.js b/static/js/chart_legend.js index d009f11..b1880fe 100644 --- a/static/js/chart_legend.js +++ b/static/js/chart_legend.js @@ -24,7 +24,7 @@ const ChartLegend = { components: { LegendItem: LegendItem }, - props: ['chart_object_name', 'select_all_enabled', 'item_container_classes'], + props: ['chart_object_name', 'select_all_enabled', 'item_container_classes', 'chart_data_loaded'], data() { return { all_selected: true, @@ -52,6 +52,9 @@ const ChartLegend = { this.handle_chart_changes(i) }) this.chart_object.update() + }, + chart_data_loaded(new_value) { + new_value && this.reload() } }, template: ` diff --git a/static/js/results.js b/static/js/results.js index 4f5cd8c..dc7bf24 100644 --- a/static/js/results.js +++ b/static/js/results.js @@ -67,14 +67,13 @@ const SummaryController = { samplers: [], sampler_type: 'REQUEST', status_type: 'all', - // todo: change to 'auto' - // aggregator: '30s', aggregator: 'auto', update_interval: 0, auto_update_id: null, status_percentage: 0, active_tab_id: undefined, - current_chart: 'presetLine' + current_chart: 'presetLine', + chart_data_loaded: false } }, async mounted() { @@ -250,6 +249,7 @@ const SummaryController = { } }, async load_request_data(url, y_label) { + this.chart_data_loaded = false $('#chart-loader').show(); const $preset = $("#preset") if (!$preset.is(":visible")) { @@ -281,11 +281,11 @@ const SummaryController = { window.presetLine.data = data window.presetLine.update() } - $('#chart-loader').hide(); } else { // todo: handle fetch error } + this.chart_data_loaded = true }, handle_download(event) { console.log('todo handle download') diff --git a/templates/results/summary.html b/templates/results/summary.html index 9b1d7e5..096f900 100644 --- a/templates/results/summary.html +++ b/templates/results/summary.html @@ -135,8 +135,9 @@ From 47be9b5e02a1f90b70cf99d6c7508b474cae7d14 Mon Sep 17 00:00:00 2001 From: Aspect13 Date: Fri, 21 Jul 2023 18:10:28 +0400 Subject: [PATCH 4/5] fixing permissions to results pages --- slots/results.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/slots/results.py b/slots/results.py index 3348ac9..9b1b1c3 100644 --- a/slots/results.py +++ b/slots/results.py @@ -30,6 +30,11 @@ def content(self, context, slot, payload): result_id = payload.request.args.get('result_id') if result_id: report = Report.query.get_or_404(result_id) + if not self.context.rpc_manager.call.admin_check_user_in_project( + project_id=report.project_id, + user_id=payload.auth.id + ): + return theme.access_denied_part test_data = report.to_json() test_data["is_baseline_report"] = report.is_baseline_report try: From 3f576fd6be9441983c7586c87a9e0f2ef4c6f115 Mon Sep 17 00:00:00 2001 From: Aspect13 Date: Fri, 21 Jul 2023 18:27:40 +0400 Subject: [PATCH 5/5] painless patching beta --- api/v1/loki_url.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/api/v1/loki_url.py b/api/v1/loki_url.py index 6335a7c..29bc455 100644 --- a/api/v1/loki_url.py +++ b/api/v1/loki_url.py @@ -1,7 +1,7 @@ from ...models.reports import Report from flask import request -from tools import api_tools, LokiLogFetcher +from tools import api_tools, constants as c class API(api_tools.APIBase): @@ -17,11 +17,15 @@ def get(self, project_id: int, **kwargs): if not report_id: return {"message": ""}, 404 - project = self.module.context.rpc_manager.call.project_get_or_404(project_id=project_id) - websocket_base_url = LokiLogFetcher.from_project(project).get_websocket_url(project) + build_id = Report.query.get_or_404(report_id).build_id + + websocket_base_url = c.APP_HOST.replace("http://", "ws://").replace("https://", "wss://") + websocket_base_url += "/loki/api/v1/tail" + logs_query = "{" + f'report_id="{report_id}",project="{project_id}",build_id="{build_id}"' + "}" + logs_start = 0 logs_limit = 10000000000 - build_id = Report.query.get_or_404(report_id).build_id - logs_query = '{report_id="%s",project="%s",build_id="%s"}' % (report_id, project_id, build_id) - return {'websocket_url': f"{websocket_base_url}?query={logs_query}&start=0&limit={logs_limit}"}, 200 + return { + "websocket_url": f"{websocket_base_url}?query={logs_query}&start={logs_start}&limit={logs_limit}" + }, 200