Skip to content

Commit 40182f2

Browse files
authored
Merge pull request #35 from timson/html-upload
add html ingestion pipeline
2 parents 669bebe + 1064597 commit 40182f2

File tree

27 files changed

+396
-42
lines changed

27 files changed

+396
-42
lines changed

src/gprofiler-dev/gprofiler_dev/s3_profile_dal.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16-
16+
import gzip
1717
from datetime import datetime
1818
from io import BytesIO
1919
from typing import Callable, Optional
@@ -72,6 +72,14 @@ def download_file(self, src_file_path: str, dest_file_path: str) -> None:
7272
raise FileNotFoundError("Requested file does not exist", src_file_path) from error
7373
raise
7474

75+
def get_object(self, s3_path: str, is_gzip=False) -> str:
76+
s3_response = self._s3_client.get_object(Bucket=self.bucket_name, Key=s3_path)
77+
response_body = s3_response["Body"].read()
78+
if is_gzip:
79+
with gzip.GzipFile(fileobj=BytesIO(response_body)) as gz:
80+
response_body = gz.read()
81+
return response_body.decode("utf-8")
82+
7583
def write_file(self, file_path: str, content: bytes) -> None:
7684
io_content = BytesIO(content)
7785
self._s3_client.upload_fileobj(Bucket=self.bucket_name, Fileobj=io_content, Key=file_path)

src/gprofiler/backend/models/metrics_models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@ class MetricK8s(CamelModel):
8383
cores: Optional[int] = None
8484
cpu: Optional[float] = None
8585
memory: Optional[float] = None
86+
87+
88+
class HTMLMetadata(CamelModel):
89+
content: str

src/gprofiler/backend/routers/metrics_routes.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from logging import getLogger
2020
from typing import List, Optional
2121

22+
from botocore.exceptions import ClientError
23+
2224
from backend.models.filters_models import FilterTypes
2325
from backend.models.flamegraph_models import FGParamsBaseModel
2426
from backend.models.metrics_models import (
@@ -30,11 +32,14 @@
3032
MetricNodesCoresSummary,
3133
MetricSummary,
3234
SampleCount,
35+
HTMLMetadata,
3336
)
3437
from backend.utils.filters_utils import get_rql_first_eq_key, get_rql_only_for_one_key
3538
from backend.utils.request_utils import flamegraph_base_request_params, get_metrics_response, get_query_response
36-
from fastapi import APIRouter, Depends, Query
39+
from fastapi import APIRouter, Depends, Query, HTTPException
3740
from fastapi.responses import Response
41+
42+
from gprofiler_dev import S3ProfileDal
3843
from gprofiler_dev.postgres.db_manager import DBManager
3944

4045
logger = getLogger(__name__)
@@ -212,3 +217,21 @@ def calculate_trend_in_cpu(
212217
)
213218

214219
return response
220+
221+
222+
@router.get("/html_metadata", response_model=HTMLMetadata)
223+
def get_html_metadata(
224+
fg_params: FGParamsBaseModel = Depends(flamegraph_base_request_params),
225+
):
226+
host_name_value = get_rql_first_eq_key(fg_params.filter, FilterTypes.HOSTNAME_KEY)
227+
if not host_name_value:
228+
raise HTTPException(400, detail="Must filter by hostname to get the html metadata")
229+
s3_path = get_metrics_response(fg_params, lookup_for="lasthtml")
230+
if not s3_path:
231+
raise HTTPException(404, detail="The html metadata path not found in CH")
232+
s3_dal = S3ProfileDal(logger)
233+
try:
234+
html_content = s3_dal.get_object(s3_path, is_gzip=True)
235+
except ClientError:
236+
raise HTTPException(status_code=404, detail="The html metadata file not found in S3")
237+
return HTMLMetadata(content=html_content)

src/gprofiler/backend/utils/request_utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def get_flamegraph_request(db_api_params, stream):
111111
return response
112112

113113

114-
def _common_fg_rest_response(response: Response, db_api_params: Dict) -> Union[List, Dict]:
114+
def _common_fg_rest_response(response: Response, db_api_params: Dict) -> Union[List, Dict, str]:
115115
if response.status_code == 401:
116116
logger.error(f"{response.request.url} request is unauthorized")
117117
if response.status_code >= 300:
@@ -138,7 +138,7 @@ def _common_fg_rest_response(response: Response, db_api_params: Dict) -> Union[L
138138

139139
def get_query_response(
140140
fg_params: FGParamsBaseModel, lookup_for: str = "time", resolution=None, interval=None
141-
) -> Union[List, Dict]:
141+
) -> Union[List, Dict, str]:
142142
fg_filter = fg_params.filter.json().encode() if fg_params.filter else None
143143
db_api_params = get_api_params(
144144
fg_params.service_name,
@@ -169,8 +169,7 @@ def get_metrics_response(
169169
interval=None,
170170
compared_start_datetime=None,
171171
compared_end_datetime=None,
172-
m2m: Optional[bool] = False,
173-
) -> Union[List, Dict]:
172+
) -> Union[List, Dict, str]:
174173
fg_filter = fg_params.filter.json().encode() if fg_params.filter else None
175174
db_api_params = get_api_params(
176175
fg_params.service_name,

src/gprofiler/frontend/src/api/hooks/useGetFgMetrics.jsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import _ from 'lodash';
2020
import { stringify } from 'query-string';
2121
import { useContext, useState } from 'react';
2222

23+
import { isFilterTypeExist } from '@/components/filters/utils';
24+
import { FILTER_TYPES } from '@/utils/filtersUtils';
25+
2326
import { FilterTagsContext } from '../../states/filters/FiltersTagsContext';
2427
import { SelectorsContext } from '../../states/selectors';
2528
import { PROFILES_VIEWS } from '../../utils/consts';
@@ -35,6 +38,7 @@ const useGetFgMetrics = ({ customTimeSelection, customService, disableCoreNodesR
3538
const { selectedService, timeSelection, viewMode, ignoreZeros } = useContext(SelectorsContext);
3639
const { activeFilterTag } = useContext(FilterTagsContext);
3740
const [metricsData, setMetricsData] = useState(undefined);
41+
const [lastHtmlData, setLastHtmlData] = useState(undefined);
3842
const [coresNodesCountData, setCoresNodesCountData] = useState(undefined);
3943
const [instanceTypeData, setInstanceTypeData] = useState(undefined);
4044
const timeParams = getStartEndDateTimeFromSelection(customTimeSelection || timeSelection);
@@ -66,6 +70,30 @@ const useGetFgMetrics = ({ customTimeSelection, customService, disableCoreNodesR
6670
}
6771
);
6872

73+
const isHostNameFilterActive = isFilterTypeExist(FILTER_TYPES.HostName.value, activeFilterTag);
74+
const { loading: lastHtmlLoading } = useFetchWithRequest(
75+
{
76+
url: DATA_URLS.GET_LAST_HTML + '?' + stringify(metricsParams),
77+
},
78+
{
79+
refreshDeps: [
80+
customService,
81+
selectedService,
82+
customTimeSelection ? customTimeSelection : timeSelection,
83+
activeFilterTag,
84+
],
85+
ready:
86+
areParamsDefined(customService || selectedService, customTimeSelection || timeSelection) &&
87+
isHostNameFilterActive,
88+
onSuccess: (result) => {
89+
setLastHtmlData(result?.content);
90+
},
91+
onError: () => {
92+
setLastHtmlData(undefined);
93+
},
94+
}
95+
);
96+
6997
const isServiceView = viewMode === PROFILES_VIEWS.service;
7098
const serviceAndTimeParams = {
7199
serviceName: customService || selectedService,
@@ -117,6 +145,8 @@ const useGetFgMetrics = ({ customTimeSelection, customService, disableCoreNodesR
117145
return {
118146
metricsData,
119147
metricsLoading,
148+
lastHtmlData,
149+
lastHtmlLoading,
120150
coresNodesCountData: coresNodesCountData,
121151
coresNodesCountLoading,
122152
instanceTypeData,

src/gprofiler/frontend/src/api/urls.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const DATA_URLS = {
3232
GET_METRICS: `${API_PREFIX}/metrics/summary`,
3333
GET_METRICS_CPU_AND_MEMORY_TREND: `${API_PREFIX}/metrics/cpu_trend`,
3434
GET_NODES_AND_CORES: `${API_PREFIX}/metrics/nodes_cores/summary`,
35+
GET_LAST_HTML: `${API_PREFIX}/metrics/html_metadata`,
3536
GET_INSTANCE_TYPE: `${API_PREFIX}/metrics/instance_type_count`,
3637
GET_GRAPH_METRICS: `${API_PREFIX}/metrics/graph`,
3738
GET_FUCNTION_CPU_GRAPH: `${API_PREFIX}/metrics/function_cpu`,

src/gprofiler/frontend/src/components/common/icon/iconsData.js

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/gprofiler/frontend/src/components/filters/utils.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818

1919
import { FILTER_OPERATIONS } from '../../utils/filtersUtils';
2020

21-
const isFilterTypeExist = (type, filter) => {
21+
export const isFilterTypeExist = (type, filter) => {
22+
if (!filter?.filter) return false;
2223
const [, rules] = Object.entries(filter.filter)[0];
2324
return rules?.some((rule) => {
2425
const [ruleType] = Object.entries(rule)[0];

src/gprofiler/frontend/src/components/profiles/ProfilesViews.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import _ from 'lodash';
2020
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
2121

22+
import useGetFgMetrics from '@/api/hooks/useGetFgMetrics';
23+
import HtmlView from '@/components/profiles/views/htmlView/HtmlView';
24+
2225
import {
2326
countZoomedSearchMatches,
2427
FgContext,
@@ -52,6 +55,7 @@ import TableView from './views/table/TableView';
5255
const ProfilesViews = () => {
5356
const { fgOriginData, lastWeekOriginData, zoomedFgData, setZoomedFgData, framesSelected, setIsFGEmpty, isFGEmpty } =
5457
useContext(FgContext);
58+
const { lastHtmlData } = useGetFgMetrics({});
5559

5660
const { viewMode, selectedService, timeSelection } = useContext(SelectorsContext);
5761
const { setHoverData } = useContext(FgHoverContext);
@@ -243,6 +247,8 @@ const ProfilesViews = () => {
243247
<ServiceView />
244248
) : viewMode === PROFILES_VIEWS.table ? (
245249
<TableView timeSelection={timeSelection} rows={tableViewData} filteredData={filteredData} />
250+
) : viewMode === PROFILES_VIEWS.html ? (
251+
<HtmlView lastHtml={lastHtmlData} />
246252
) : !isFGEmpty ? (
247253
<FlamegraphView
248254
flameGraphData={flameGraphData}

src/gprofiler/frontend/src/components/profiles/header/ProfilesTopPanel.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,12 @@ const ProfilesTopPanel = () => {
8989
</Flexbox>
9090
</Flexbox>
9191
</Box>
92-
<Flexbox sx={{ px: 5, width: '100%' }} spacing={3}>
93-
<MineSweeper />
94-
<ProfilesMetricsPanel />
95-
</Flexbox>
92+
{viewMode !== PROFILES_VIEWS.html && (
93+
<Flexbox sx={{ px: 5, width: '100%' }} spacing={3}>
94+
<MineSweeper />
95+
<ProfilesMetricsPanel />
96+
</Flexbox>
97+
)}
9698
</>
9799
)}
98100
</Flexbox>

0 commit comments

Comments
 (0)