From 0efb901863304970b8fc5b9518cb10d439e73b65 Mon Sep 17 00:00:00 2001 From: Amol Umbark Date: Mon, 28 Mar 2022 21:01:57 +0530 Subject: [PATCH 01/11] feat: Amol/webhook (#868) webhook receiver enabled for alerts Co-authored-by: Palash gupta --- .../clickhouse-setup/docker-compose.yaml | 11 +- frontend/src/api/channels/createWebhook.ts | 51 +++++++ frontend/src/api/channels/editWebhook.ts | 50 +++++++ frontend/src/components/Graph/index.tsx | 1 + .../src/container/AllAlertChannels/Delete.tsx | 3 +- .../container/CreateAlertChannels/config.ts | 26 +++- .../container/CreateAlertChannels/index.tsx | 101 ++++++++++++-- .../src/container/EditAlertChannels/index.tsx | 65 ++++++++- .../FormAlertChannels/Settings/Webhook.tsx | 59 ++++++++ .../src/container/FormAlertChannels/index.tsx | 22 ++- frontend/src/container/Timeline/index.tsx | 5 +- .../src/pages/AlertChannelCreate/index.tsx | 2 +- frontend/src/pages/ChannelsEdit/index.tsx | 32 ++++- .../src/types/api/channels/createWebhook.ts | 8 ++ .../src/types/api/channels/editWebhook.ts | 10 ++ .../app/clickhouseReader/reader.go | 86 +++--------- pkg/query-service/app/druidReader/reader.go | 5 +- pkg/query-service/app/http_handler.go | 5 +- pkg/query-service/app/interface.go | 5 +- pkg/query-service/constants/constants.go | 14 +- .../integrations/alertManager/manager.go | 129 ++++++++++++++++++ .../integrations/alertManager/model.go | 22 +++ pkg/query-service/model/response.go | 21 --- 23 files changed, 604 insertions(+), 129 deletions(-) create mode 100644 frontend/src/api/channels/createWebhook.ts create mode 100644 frontend/src/api/channels/editWebhook.ts create mode 100644 frontend/src/container/FormAlertChannels/Settings/Webhook.tsx create mode 100644 frontend/src/types/api/channels/createWebhook.ts create mode 100644 frontend/src/types/api/channels/editWebhook.ts create mode 100644 pkg/query-service/integrations/alertManager/manager.go create mode 100644 pkg/query-service/integrations/alertManager/model.go diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index ac69884ff6..c161d1c4d4 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -15,17 +15,20 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.5.0 + image: signoz/alertmanager:0.6.0 volumes: - - ./alertmanager.yml:/prometheus/alertmanager.yml + # we no longer need the config file as query services delivers + # the required config now + # - ./alertmanager.yml:/prometheus/alertmanager.yml - ./data/alertmanager:/data + depends_on: + - query-service command: - - '--config.file=/prometheus/alertmanager.yml' + - '--queryService.url=http://query-service:8080' - '--storage.path=/data' # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` - query-service: image: signoz/query-service:0.7.3 container_name: query-service diff --git a/frontend/src/api/channels/createWebhook.ts b/frontend/src/api/channels/createWebhook.ts new file mode 100644 index 0000000000..9c3c52c943 --- /dev/null +++ b/frontend/src/api/channels/createWebhook.ts @@ -0,0 +1,51 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/channels/createWebhook'; + +const create = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + let httpConfig = {}; + + if (props.username !== '' && props.password !== '') { + httpConfig = { + basic_auth: { + username: props.username, + password: props.password, + }, + }; + } else if (props.username === '' && props.password !== '') { + httpConfig = { + authorization: { + type: 'bearer', + credentials: props.password, + }, + }; + } + + const response = await axios.post('/channels', { + name: props.name, + webhook_configs: [ + { + send_resolved: true, + url: props.api_url, + http_config: httpConfig, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default create; diff --git a/frontend/src/api/channels/editWebhook.ts b/frontend/src/api/channels/editWebhook.ts new file mode 100644 index 0000000000..a574633e4e --- /dev/null +++ b/frontend/src/api/channels/editWebhook.ts @@ -0,0 +1,50 @@ +import axios from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/channels/editWebhook'; + +const editWebhook = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + let httpConfig = {}; + if (props.username !== '' && props.password !== '') { + httpConfig = { + basic_auth: { + username: props.username, + password: props.password, + }, + }; + } else if (props.username === '' && props.password !== '') { + httpConfig = { + authorization: { + type: 'bearer', + credentials: props.password, + }, + }; + } + + const response = await axios.put(`/channels/${props.id}`, { + name: props.name, + webhook_configs: [ + { + send_resolved: true, + url: props.api_url, + http_config: httpConfig, + }, + ], + }); + + return { + statusCode: 200, + error: null, + message: 'Success', + payload: response.data.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default editWebhook; diff --git a/frontend/src/components/Graph/index.tsx b/frontend/src/components/Graph/index.tsx index bdf23e6468..a8f668235d 100644 --- a/frontend/src/components/Graph/index.tsx +++ b/frontend/src/components/Graph/index.tsx @@ -79,6 +79,7 @@ function Graph({ return 'rgba(231,233,237,0.8)'; }, [currentTheme]); + // eslint-disable-next-line sonarjs/cognitive-complexity const buildChart = useCallback(() => { if (lineChartRef.current !== undefined) { lineChartRef.current.destroy(); diff --git a/frontend/src/container/AllAlertChannels/Delete.tsx b/frontend/src/container/AllAlertChannels/Delete.tsx index 4501c916c8..85116fd922 100644 --- a/frontend/src/container/AllAlertChannels/Delete.tsx +++ b/frontend/src/container/AllAlertChannels/Delete.tsx @@ -30,7 +30,8 @@ function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element { } catch (error) { notifications.error({ message: 'Error', - description: error instanceof Error ? error.toString() : 'Something went wrong', + description: + error instanceof Error ? error.toString() : 'Something went wrong', }); setLoading(false); } diff --git a/frontend/src/container/CreateAlertChannels/config.ts b/frontend/src/container/CreateAlertChannels/config.ts index 364d367806..f104a84076 100644 --- a/frontend/src/container/CreateAlertChannels/config.ts +++ b/frontend/src/container/CreateAlertChannels/config.ts @@ -1,10 +1,22 @@ -export interface SlackChannel { - send_resolved: boolean; - api_url: string; - channel: string; - title: string; - text: string; +export interface Channel { + send_resolved?: boolean; name: string; } -export type ChannelType = 'slack' | 'email'; +export interface SlackChannel extends Channel { + api_url?: string; + channel?: string; + title?: string; + text?: string; +} + +export interface WebhookChannel extends Channel { + api_url?: string; + // basic auth + username?: string; + password?: string; +} + +export type ChannelType = 'slack' | 'email' | 'webhook'; +export const SlackType: ChannelType = 'slack'; +export const WebhookType: ChannelType = 'webhook'; diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx index 8a3b3fe606..f999627154 100644 --- a/frontend/src/container/CreateAlertChannels/index.tsx +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -1,17 +1,26 @@ import { Form, notification } from 'antd'; import createSlackApi from 'api/channels/createSlack'; +import createWebhookApi from 'api/channels/createWebhook'; import ROUTES from 'constants/routes'; import FormAlertChannels from 'container/FormAlertChannels'; import history from 'lib/history'; import React, { useCallback, useState } from 'react'; -import { ChannelType, SlackChannel } from './config'; +import { + ChannelType, + SlackChannel, + SlackType, + WebhookChannel, + WebhookType, +} from './config'; function CreateAlertChannels({ preType = 'slack', }: CreateAlertChannelsProps): JSX.Element { const [formInstance] = Form.useForm(); - const [selectedConfig, setSelectedConfig] = useState>({ + const [selectedConfig, setSelectedConfig] = useState< + Partial + >({ text: ` {{ range .Alerts -}} *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} @@ -73,17 +82,93 @@ function CreateAlertChannels({ } setSavingState(false); } catch (error) { + notifications.error({ + message: 'Error', + description: + 'An unexpected error occurred while creating this channel, please try again', + }); setSavingState(false); } }, [notifications, selectedConfig]); + const onWebhookHandler = useCallback(async () => { + // initial api request without auth params + let request: WebhookChannel = { + api_url: selectedConfig?.api_url || '', + name: selectedConfig?.name || '', + send_resolved: true, + }; + + setSavingState(true); + + try { + if (selectedConfig?.username !== '' || selectedConfig?.password !== '') { + if (selectedConfig?.username !== '') { + // if username is not null then password must be passed + if (selectedConfig?.password !== '') { + request = { + ...request, + username: selectedConfig.username, + password: selectedConfig.password, + }; + } else { + notifications.error({ + message: 'Error', + description: 'A Password must be provided with user name', + }); + } + } else if (selectedConfig?.password !== '') { + // only password entered, set bearer token + request = { + ...request, + username: '', + password: selectedConfig.password, + }; + } + } + + const response = await createWebhookApi(request); + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: 'Successfully created the channel', + }); + setTimeout(() => { + history.replace(ROUTES.SETTINGS); + }, 2000); + } else { + notifications.error({ + message: 'Error', + description: response.error || 'Error while creating the channel', + }); + } + } catch (error) { + notifications.error({ + message: 'Error', + description: + 'An unexpected error occurred while creating this channel, please try again', + }); + } + setSavingState(false); + }, [notifications, selectedConfig]); + const onSaveHandler = useCallback( async (value: ChannelType) => { - if (value === 'slack') { - onSlackHandler(); + switch (value) { + case SlackType: + onSlackHandler(); + break; + case WebhookType: + onWebhookHandler(); + break; + default: + notifications.error({ + message: 'Error', + description: 'channel type selected is invalid', + }); } }, - [onSlackHandler], + [onSlackHandler, onWebhookHandler, notifications], ); return ( @@ -108,11 +193,7 @@ function CreateAlertChannels({ } interface CreateAlertChannelsProps { - preType?: ChannelType; + preType: ChannelType; } -CreateAlertChannels.defaultProps = { - preType: undefined, -}; - export default CreateAlertChannels; diff --git a/frontend/src/container/EditAlertChannels/index.tsx b/frontend/src/container/EditAlertChannels/index.tsx index 2d8ac3576d..e4aab19d31 100644 --- a/frontend/src/container/EditAlertChannels/index.tsx +++ b/frontend/src/container/EditAlertChannels/index.tsx @@ -1,9 +1,13 @@ import { Form, notification } from 'antd'; import editSlackApi from 'api/channels/editSlack'; +import editWebhookApi from 'api/channels/editWebhook'; import ROUTES from 'constants/routes'; import { ChannelType, SlackChannel, + SlackType, + WebhookChannel, + WebhookType, } from 'container/CreateAlertChannels/config'; import FormAlertChannels from 'container/FormAlertChannels'; import history from 'lib/history'; @@ -14,14 +18,18 @@ function EditAlertChannels({ initialValue, }: EditAlertChannelsProps): JSX.Element { const [formInstance] = Form.useForm(); - const [selectedConfig, setSelectedConfig] = useState>({ + const [selectedConfig, setSelectedConfig] = useState< + Partial + >({ ...initialValue, }); const [savingState, setSavingState] = useState(false); const [notifications, NotificationElement] = notification.useNotification(); const { id } = useParams<{ id: string }>(); - const [type, setType] = useState('slack'); + const [type, setType] = useState( + initialValue?.type ? (initialValue.type as ChannelType) : SlackType, + ); const onTypeChangeHandler = useCallback((value: string) => { setType(value as ChannelType); @@ -57,13 +65,62 @@ function EditAlertChannels({ setSavingState(false); }, [selectedConfig, notifications, id]); + const onWebhookEditHandler = useCallback(async () => { + setSavingState(true); + const { name, username, password } = selectedConfig; + + const showError = (msg: string): void => { + notifications.error({ + message: 'Error', + description: msg, + }); + }; + + if (selectedConfig?.api_url === '') { + showError('Webhook URL is mandatory'); + setSavingState(false); + return; + } + + if (username && (!password || password === '')) { + showError('Please enter a password'); + setSavingState(false); + return; + } + + const response = await editWebhookApi({ + api_url: selectedConfig?.api_url || '', + name: name || '', + send_resolved: true, + username, + password, + id, + }); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Success', + description: 'Channels Edited Successfully', + }); + + setTimeout(() => { + history.replace(ROUTES.SETTINGS); + }, 2000); + } else { + showError(response.error || 'error while updating the Channels'); + } + setSavingState(false); + }, [selectedConfig, notifications, id]); + const onSaveHandler = useCallback( (value: ChannelType) => { - if (value === 'slack') { + if (value === SlackType) { onSlackEditHandler(); + } else if (value === WebhookType) { + onWebhookEditHandler(); } }, - [onSlackEditHandler], + [onSlackEditHandler, onWebhookEditHandler], ); const onTestHandler = useCallback(() => { diff --git a/frontend/src/container/FormAlertChannels/Settings/Webhook.tsx b/frontend/src/container/FormAlertChannels/Settings/Webhook.tsx new file mode 100644 index 0000000000..1c7748f795 --- /dev/null +++ b/frontend/src/container/FormAlertChannels/Settings/Webhook.tsx @@ -0,0 +1,59 @@ +import { Input } from 'antd'; +import FormItem from 'antd/lib/form/FormItem'; +import React from 'react'; + +import { WebhookChannel } from '../../CreateAlertChannels/config'; + +function WebhookSettings({ setSelectedConfig }: WebhookProps): JSX.Element { + return ( + <> + + { + setSelectedConfig((value) => ({ + ...value, + api_url: event.target.value, + })); + }} + /> + + + { + setSelectedConfig((value) => ({ + ...value, + username: event.target.value, + })); + }} + /> + + + { + setSelectedConfig((value) => ({ + ...value, + password: event.target.value, + })); + }} + /> + + + ); +} + +interface WebhookProps { + setSelectedConfig: React.Dispatch< + React.SetStateAction> + >; +} + +export default WebhookSettings; diff --git a/frontend/src/container/FormAlertChannels/index.tsx b/frontend/src/container/FormAlertChannels/index.tsx index 573be68d00..55aa6e238f 100644 --- a/frontend/src/container/FormAlertChannels/index.tsx +++ b/frontend/src/container/FormAlertChannels/index.tsx @@ -5,11 +5,14 @@ import ROUTES from 'constants/routes'; import { ChannelType, SlackChannel, + SlackType, + WebhookType, } from 'container/CreateAlertChannels/config'; import history from 'lib/history'; import React from 'react'; import SlackSettings from './Settings/Slack'; +import WebhookSettings from './Settings/Webhook'; import { Button } from './styles'; const { Option } = Select; @@ -28,6 +31,16 @@ function FormAlertChannels({ initialValue, nameDisable = false, }: FormAlertChannelsProps): JSX.Element { + const renderSettings = (): React.ReactElement | null => { + switch (type) { + case SlackType: + return ; + case WebhookType: + return ; + default: + return null; + } + }; return ( <> {NotificationElement} @@ -52,14 +65,13 @@ function FormAlertChannels({ + - - {type === 'slack' && ( - - )} - + {renderSettings()} - ); + return Delete; } interface DispatchProps { diff --git a/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx b/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx index fa2ff98991..e54431063f 100644 --- a/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx +++ b/frontend/src/container/ListOfDashboard/TableComponents/Name.tsx @@ -1,10 +1,10 @@ -import { Button } from 'antd'; import ROUTES from 'constants/routes'; import history from 'lib/history'; import React from 'react'; import { generatePath } from 'react-router-dom'; import { Data } from '..'; +import { TableLinkText } from './styles'; function Name(name: Data['name'], data: Data): JSX.Element { const onClickHandler = (): void => { @@ -17,11 +17,7 @@ function Name(name: Data['name'], data: Data): JSX.Element { ); }; - return ( - - ); + return {name}; } export default Name; diff --git a/frontend/src/container/ListOfDashboard/TableComponents/styles.ts b/frontend/src/container/ListOfDashboard/TableComponents/styles.ts new file mode 100644 index 0000000000..78c382700b --- /dev/null +++ b/frontend/src/container/ListOfDashboard/TableComponents/styles.ts @@ -0,0 +1,8 @@ +import { blue } from '@ant-design/colors'; +import { Typography } from 'antd'; +import styled from 'styled-components'; + +export const TableLinkText = styled(Typography.Text)` + color: ${blue.primary} !important; + cursor: pointer; +`; From 3dc94c8da76ff03d1e07646cb91710b47cab4159 Mon Sep 17 00:00:00 2001 From: Amol Umbark Date: Tue, 29 Mar 2022 19:59:40 +0530 Subject: [PATCH 04/11] (fix): Duplicate alerts in triggered alerts (#932) * (fix): Duplicate alerts in triggered alerts fixed by changing source api from /alert/groups to /alerts * (fix): added comments for removed lines of group api call * (fix): restored all getGroup --- frontend/src/api/alerts/getTriggered.ts | 29 +++++++++++++++++++ .../TriggeredAlerts/TriggeredAlert.tsx | 16 +++++----- .../src/container/TriggeredAlerts/index.tsx | 19 +++++++----- frontend/src/types/api/alerts/getTriggered.ts | 10 +++++++ 4 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 frontend/src/api/alerts/getTriggered.ts create mode 100644 frontend/src/types/api/alerts/getTriggered.ts diff --git a/frontend/src/api/alerts/getTriggered.ts b/frontend/src/api/alerts/getTriggered.ts new file mode 100644 index 0000000000..160b9a3b93 --- /dev/null +++ b/frontend/src/api/alerts/getTriggered.ts @@ -0,0 +1,29 @@ +import { AxiosAlertManagerInstance } from 'api'; +import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; +import { AxiosError } from 'axios'; +import convertObjectIntoParams from 'lib/query/convertObjectIntoParams'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { PayloadProps, Props } from 'types/api/alerts/getTriggered'; + +const getTriggered = async ( + props: Props, +): Promise | ErrorResponse> => { + try { + const queryParams = convertObjectIntoParams(props); + + const response = await AxiosAlertManagerInstance.get( + `/alerts?${queryParams}`, + ); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data, + }; + } catch (error) { + return ErrorResponseHandler(error as AxiosError); + } +}; + +export default getTriggered; diff --git a/frontend/src/container/TriggeredAlerts/TriggeredAlert.tsx b/frontend/src/container/TriggeredAlerts/TriggeredAlert.tsx index df3b6ff012..425334e7ba 100644 --- a/frontend/src/container/TriggeredAlerts/TriggeredAlert.tsx +++ b/frontend/src/container/TriggeredAlerts/TriggeredAlert.tsx @@ -1,4 +1,4 @@ -import getGroupApi from 'api/alerts/getGroup'; +import getTriggeredApi from 'api/alerts/getTriggered'; import useInterval from 'hooks/useInterval'; import React, { useState } from 'react'; import { Alerts } from 'types/api/alerts/getAll'; @@ -13,20 +13,22 @@ function TriggeredAlerts({ allAlerts }: TriggeredAlertsProps): JSX.Element { useInterval(() => { (async (): Promise => { - const response = await getGroupApi({ + const response = await getTriggeredApi({ active: true, inhibited: true, silenced: false, }); if (response.statusCode === 200 && response.payload !== null) { - const initialAlerts: Alerts[] = []; + // commented reduce() call as we no longer use /alerts/groups + // from alertmanager which needed re-grouping on client side + // const initialAlerts: Alerts[] = []; - const allAlerts: Alerts[] = response.payload.reduce((acc, cur) => { - return [...acc, ...cur.alerts]; - }, initialAlerts); + // const allAlerts: Alerts[] = response.payload.reduce((acc, cur) => { + // return [...acc, ...cur.alerts]; + // }, initialAlerts); - setInitialAlerts(allAlerts); + setInitialAlerts(response.payload); } })(); }, 30000); diff --git a/frontend/src/container/TriggeredAlerts/index.tsx b/frontend/src/container/TriggeredAlerts/index.tsx index a665adf6dd..88ebb2e138 100644 --- a/frontend/src/container/TriggeredAlerts/index.tsx +++ b/frontend/src/container/TriggeredAlerts/index.tsx @@ -1,9 +1,9 @@ -import getGroupApi from 'api/alerts/getGroup'; +import getTriggeredApi from 'api/alerts/getTriggered'; import Spinner from 'components/Spinner'; import { State } from 'hooks/useFetch'; import React, { useCallback, useEffect, useState } from 'react'; import { Alerts } from 'types/api/alerts/getAll'; -import { PayloadProps } from 'types/api/alerts/getGroups'; +import { PayloadProps } from 'types/api/alerts/getTriggered'; import TriggerComponent from './TriggeredAlert'; @@ -23,7 +23,7 @@ function TriggeredAlerts(): JSX.Element { loading: true, })); - const response = await getGroupApi({ + const response = await getTriggeredApi({ active: true, inhibited: true, silenced: false, @@ -65,13 +65,16 @@ function TriggeredAlerts(): JSX.Element { return ; } - const initialAlerts: Alerts[] = []; + // commented the reduce() call as we no longer use /alerts/groups + // API from alert manager, which returns a group for each receiver - const allAlerts: Alerts[] = groupState.payload.reduce((acc, curr) => { - return [...acc, ...curr.alerts]; - }, initialAlerts); + // const initialAlerts: Alerts[] = []; - return ; + // const allAlerts: Alerts[] = groupState.payload.reduce((acc, curr) => { + // return [...acc, ...curr.alerts]; + // }, initialAlerts); + + return ; } export default TriggeredAlerts; diff --git a/frontend/src/types/api/alerts/getTriggered.ts b/frontend/src/types/api/alerts/getTriggered.ts new file mode 100644 index 0000000000..8b0e50a279 --- /dev/null +++ b/frontend/src/types/api/alerts/getTriggered.ts @@ -0,0 +1,10 @@ +import { Alerts } from './getAll'; + +export interface Props { + silenced: boolean; + inhibited: boolean; + active: boolean; + [key: string]: string | boolean; +} + +export type PayloadProps = Alerts[] | []; From 1002ab553eed39b580d22f88d1c06b76757ea987 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Tue, 29 Mar 2022 22:59:32 +0530 Subject: [PATCH 05/11] =?UTF-8?q?chore(release):=20=F0=9F=93=8C=20pin=200.?= =?UTF-8?q?7.4=20SigNoz=20version=20and=20deployment=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Prashant Shahi --- .../clickhouse-setup/docker-compose.yaml | 15 +++++++------ .../clickhouse-setup/docker-compose.arm.yaml | 17 +++++++------- .../clickhouse-setup/docker-compose.yaml | 14 +++++------- .../tests/test-deploy/docker-compose.arm.yaml | 20 ++++++++--------- .../tests/test-deploy/docker-compose.yaml | 22 ++++++++----------- 5 files changed, 40 insertions(+), 48 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 425dbde5a3..c5d8e6b630 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -17,19 +17,20 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.5.0 + image: signoz/alertmanager:0.6.0 volumes: - - ./alertmanager.yml:/prometheus/alertmanager.yml - ./data/alertmanager:/data command: - - '--config.file=/prometheus/alertmanager.yml' - - '--storage.path=/data' + - --queryService.url=http://query-service:8080 + - --storage.path=/data + depends_on: + - query-service deploy: restart_policy: condition: on-failure query-service: - image: signoz/query-service:0.7.3 + image: signoz/query-service:0.7.4 command: ["-config=/root/config/prometheus.yml"] ports: - "8080:8080" @@ -48,10 +49,10 @@ services: restart_policy: condition: on-failure depends_on: - - clickhouse + - clickhouse frontend: - image: signoz/frontend:0.7.3 + image: signoz/frontend:0.7.4 depends_on: - query-service ports: diff --git a/deploy/docker/clickhouse-setup/docker-compose.arm.yaml b/deploy/docker/clickhouse-setup/docker-compose.arm.yaml index 2d5a638fd9..0540fa2868 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.arm.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.arm.yaml @@ -15,16 +15,17 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.5.0 + image: signoz/alertmanager:0.6.0 volumes: - - ./alertmanager.yml:/prometheus/alertmanager.yml - ./data/alertmanager:/data + depends_on: + - query-service command: - - '--config.file=/prometheus/alertmanager.yml' - - '--storage.path=/data' + - --queryService.url=http://query-service:8080 + - --storage.path=/data query-service: - image: signoz/query-service:0.7.3 + image: signoz/query-service:0.7.4 container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: @@ -44,7 +45,7 @@ services: condition: service_healthy frontend: - image: signoz/frontend:0.7.3 + image: signoz/frontend:0.7.4 container_name: frontend depends_on: - query-service @@ -66,7 +67,7 @@ services: # - "14268:14268" # Jaeger receiver # - "55678:55678" # OpenCensus receiver # - "55679:55679" # zpages extension - # - "55680:55680" # OTLP gRPC legacy port + # - "55680:55680" # OTLP gRPC legacy receiver # - "55681:55681" # OTLP HTTP legacy receiver mem_limit: 2000m restart: on-failure @@ -93,7 +94,7 @@ services: max-file: "3" command: ["all"] environment: - - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces load-hotrod: image: "grubykarol/locust:1.2.3-python3.9-alpine3.12" diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index c161d1c4d4..b9c96c7bdb 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -17,20 +17,17 @@ services: alertmanager: image: signoz/alertmanager:0.6.0 volumes: - # we no longer need the config file as query services delivers - # the required config now - # - ./alertmanager.yml:/prometheus/alertmanager.yml - ./data/alertmanager:/data depends_on: - query-service command: - - '--queryService.url=http://query-service:8080' - - '--storage.path=/data' + - --queryService.url=http://query-service:8080 + - --storage.path=/data # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:0.7.3 + image: signoz/query-service:0.7.4 container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: @@ -43,14 +40,13 @@ services: - GODEBUG=netdns=go - TELEMETRY_ENABLED=true - DEPLOYMENT_TYPE=docker-standalone-amd - restart: on-failure depends_on: clickhouse: condition: service_healthy frontend: - image: signoz/frontend:0.7.3 + image: signoz/frontend:0.7.4 container_name: frontend depends_on: - query-service @@ -72,7 +68,7 @@ services: # - "14268:14268" # Jaeger receiver # - "55678:55678" # OpenCensus receiver # - "55679:55679" # zpages extension - # - "55680:55680" # OTLP gRPC legacy port + # - "55680:55680" # OTLP gRPC legacy receiver # - "55681:55681" # OTLP HTTP legacy receiver mem_limit: 2000m restart: on-failure diff --git a/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml b/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml index d231ab656b..0447c26a35 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.arm.yaml @@ -5,7 +5,6 @@ services: image: altinity/clickhouse-server:21.12.3.32.altinitydev.arm volumes: - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml - - ./data/clickhouse/:/var/lib/clickhouse/ healthcheck: # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"] @@ -14,13 +13,12 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.5.0 - volumes: - - ./alertmanager.yml:/prometheus/alertmanager.yml - - ./data/alertmanager:/data + image: signoz/alertmanager:0.6.0 + depends_on: + - query-service command: - - '--config.file=/prometheus/alertmanager.yml' - - '--storage.path=/data' + - --queryService.url=http://query-service:8080 + - --storage.path=/data query-service: image: signoz/query-service:latest @@ -29,9 +27,9 @@ services: volumes: - ./prometheus.yml:/root/config/prometheus.yml - ../dashboards:/root/config/dashboards - - ./data/signoz/:/var/lib/signoz/ + - ./data:/var/lib/signoz ports: - - "8180:8080" + - "8180:8080" environment: - ClickHouseUrl=tcp://clickhouse:9000 - STORAGE=clickhouse @@ -72,7 +70,7 @@ services: max-file: "3" command: ["all"] environment: - - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces load-hotrod: image: "grubykarol/locust:1.2.3-python3.9-alpine3.12" @@ -87,4 +85,4 @@ services: QUIET_MODE: "${QUIET_MODE:-false}" LOCUST_OPTS: "--headless -u 10 -r 1" volumes: - - ../common/locust-scripts:/locust + - ../../../../deploy/docker/common/locust-scripts:/locust diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 2dbafff539..6edd31b63a 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -13,22 +13,25 @@ services: retries: 3 alertmanager: - image: signoz/alertmanager:0.5.0 - volumes: - - ./alertmanager.yml:/prometheus/alertmanager.yml + image: signoz/alertmanager:0.6.0 + depends_on: + - query-service command: - - '--config.file=/prometheus/alertmanager.yml' - - '--storage.path=/data' + - --queryService.url=http://query-service:8080 + - --storage.path=/data # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` - query-service: image: signoz/query-service:latest + container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: - ./prometheus.yml:/root/config/prometheus.yml - ../dashboards:/root/config/dashboards + - ./data:/var/lib/signoz + ports: + - "8180:8080" environment: - ClickHouseUrl=tcp://clickhouse:9000 - STORAGE=clickhouse @@ -37,13 +40,6 @@ services: depends_on: clickhouse: condition: service_healthy - ports: - - "8180:8080" - volumes: - - type: bind - source: ./data - target: /var/lib/signoz - read_only: false otel-collector: image: signoz/otelcontribcol:0.43.0 From 1d28ceb3d78095801161b4242b651d6fcf08cb57 Mon Sep 17 00:00:00 2001 From: Ahsan Barkati Date: Fri, 1 Apr 2022 11:22:25 +0530 Subject: [PATCH 06/11] feat(query-service): Add cold storage support in getTTL API (#922) * Add cold storage support in getTTL API --- .../app/clickhouseReader/reader.go | 72 ++++++++++-------- pkg/query-service/app/parser.go | 2 +- pkg/query-service/model/response.go | 6 +- pkg/query-service/tests/cold_storage_test.go | 72 ++++++++++++++++++ .../tests/test-deploy/data/signoz.db | Bin 0 -> 32768 bytes 5 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 pkg/query-service/tests/test-deploy/data/signoz.db diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 167aa6236a..de49aa0e49 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "os" + "regexp" "sort" "strconv" "strings" @@ -44,8 +45,8 @@ import ( "github.com/prometheus/prometheus/util/strutil" "go.signoz.io/query-service/constants" - "go.signoz.io/query-service/model" am "go.signoz.io/query-service/integrations/alertManager" + "go.signoz.io/query-service/model" "go.uber.org/zap" ) @@ -75,7 +76,7 @@ type ClickHouseReader struct { remoteStorage *remote.Storage ruleManager *rules.Manager promConfig *config.Config - alertManager am.Manager + alertManager am.Manager } // NewTraceReader returns a TraceReader for the database @@ -95,7 +96,7 @@ func NewReader(localDB *sqlx.DB) *ClickHouseReader { return &ClickHouseReader{ db: db, localDB: localDB, - alertManager: alertManager, + alertManager: alertManager, operationsTable: options.primary.OperationsTable, indexTable: options.primary.IndexTable, errorTable: options.primary.ErrorTable, @@ -850,7 +851,6 @@ func (r *ClickHouseReader) EditChannel(receiver *am.Receiver, id string) (*am.Re } - func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, *model.ApiError) { tx, err := r.localDB.Begin() @@ -860,8 +860,8 @@ func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, * channel_type := getChannelType(receiver) receiverString, _ := json.Marshal(receiver) - - // todo: check if the channel name already exists, raise an error if so + + // todo: check if the channel name already exists, raise an error if so { stmt, err := tx.Prepare(`INSERT INTO notification_channels (created_at, updated_at, name, type, data) VALUES($1,$2,$3,$4,$5);`) @@ -884,7 +884,7 @@ func (r *ClickHouseReader) CreateChannel(receiver *am.Receiver) (*am.Receiver, * tx.Rollback() return nil, apiError } - + err = tx.Commit() if err != nil { zap.S().Errorf("Error in commiting transaction for INSERT to notification_channels\n", err) @@ -2602,7 +2602,6 @@ func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *mo fmt.Errorf("error while getting disks. Err=%v", err)} } - zap.S().Infof("Got response: %+v\n", diskItems) return &diskItems, nil @@ -2610,29 +2609,33 @@ func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *mo func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError) { - parseTTL := func(queryResp string) int { - values := strings.Split(queryResp, " ") - N := len(values) - ttlIdx := -1 + parseTTL := func(queryResp string) (int, int) { - for i := 0; i < N; i++ { - if strings.Contains(values[i], "toIntervalSecond") { - ttlIdx = i - break + zap.S().Debugf("Parsing TTL from: %s", queryResp) + deleteTTLExp := regexp.MustCompile(`toIntervalSecond\(([0-9]*)\)`) + moveTTLExp := regexp.MustCompile(`toIntervalSecond\(([0-9]*)\) TO VOLUME`) + + var delTTL, moveTTL int = -1, -1 + + m := deleteTTLExp.FindStringSubmatch(queryResp) + if len(m) > 1 { + seconds_int, err := strconv.Atoi(m[1]) + if err != nil { + return -1, -1 } - } - if ttlIdx == -1 { - return ttlIdx + delTTL = seconds_int / 3600 } - output := strings.SplitN(values[ttlIdx], "(", 2) - timePart := strings.Trim(output[1], ")") - seconds_int, err := strconv.Atoi(timePart) - if err != nil { - return -1 + m = moveTTLExp.FindStringSubmatch(queryResp) + if len(m) > 1 { + seconds_int, err := strconv.Atoi(m[1]) + if err != nil { + return -1, -1 + } + moveTTL = seconds_int / 3600 } - ttl_hrs := seconds_int / 3600 - return ttl_hrs + + return delTTL, moveTTL } getMetricsTTL := func() (*model.DBResponseTTL, *model.ApiError) { @@ -2671,7 +2674,8 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa return nil, err } - return &model.GetTTLResponseItem{TracesTime: parseTTL(dbResp.EngineFull)}, nil + delTTL, moveTTL := parseTTL(dbResp.EngineFull) + return &model.GetTTLResponseItem{TracesTime: delTTL, TracesMoveTime: moveTTL}, nil case constants.MetricsTTL: dbResp, err := getMetricsTTL() @@ -2679,7 +2683,9 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa return nil, err } - return &model.GetTTLResponseItem{MetricsTime: parseTTL(dbResp.EngineFull)}, nil + delTTL, moveTTL := parseTTL(dbResp.EngineFull) + return &model.GetTTLResponseItem{MetricsTime: delTTL, MetricsMoveTime: moveTTL}, nil + } db1, err := getTracesTTL() if err != nil { @@ -2690,9 +2696,15 @@ func (r *ClickHouseReader) GetTTL(ctx context.Context, ttlParams *model.GetTTLPa if err != nil { return nil, err } + tracesDelTTL, tracesMoveTTL := parseTTL(db1.EngineFull) + metricsDelTTL, metricsMoveTTL := parseTTL(db2.EngineFull) - return &model.GetTTLResponseItem{TracesTime: parseTTL(db1.EngineFull), MetricsTime: parseTTL(db2.EngineFull)}, nil - + return &model.GetTTLResponseItem{ + TracesTime: tracesDelTTL, + TracesMoveTime: tracesMoveTTL, + MetricsTime: metricsDelTTL, + MetricsMoveTime: metricsMoveTTL, + }, nil } func (r *ClickHouseReader) GetErrors(ctx context.Context, queryParams *model.GetErrorsParams) (*[]model.Error, *model.ApiError) { diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 3e3abc6c79..ae9abc5204 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -914,7 +914,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) { if err != nil { return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration) } - if toColdParsed.Seconds() >= durationParsed.Seconds() { + if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() { return nil, fmt.Errorf("Delete TTL should be greater than cold storage move TTL.") } } diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index bbee7224e9..8d4bd4b766 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -283,8 +283,10 @@ type DBResponseTTL struct { } type GetTTLResponseItem struct { - MetricsTime int `json:"metrics_ttl_duration_hrs"` - TracesTime int `json:"traces_ttl_duration_hrs"` + MetricsTime int `json:"metrics_ttl_duration_hrs,omitempty"` + MetricsMoveTime int `json:"metrics_move_ttl_duration_hrs,omitempty"` + TracesTime int `json:"traces_ttl_duration_hrs,omitempty"` + TracesMoveTime int `json:"traces_move_ttl_duration_hrs,omitempty"` } type DBResponseMinMaxDuration struct { diff --git a/pkg/query-service/tests/cold_storage_test.go b/pkg/query-service/tests/cold_storage_test.go index f748db30dc..8159805a56 100644 --- a/pkg/query-service/tests/cold_storage_test.go +++ b/pkg/query-service/tests/cold_storage_test.go @@ -1,6 +1,7 @@ package tests import ( + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -8,6 +9,7 @@ import ( "time" "github.com/stretchr/testify/require" + "go.signoz.io/query-service/model" ) const ( @@ -102,6 +104,76 @@ func TestSetTTL(t *testing.T) { fmt.Printf("=== Found %d objects in Minio\n", count) } +func getTTL(t *testing.T, table string) *model.GetTTLResponseItem { + req := endpoint + fmt.Sprintf("/api/v1/settings/ttl?type=%s", table) + if len(table) == 0 { + req = endpoint + "/api/v1/settings/ttl" + } + + resp, err := client.Get(req) + require.NoError(t, err) + + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + + res := &model.GetTTLResponseItem{} + require.NoError(t, json.Unmarshal(b, res)) + return res +} + +func TestGetTTL(t *testing.T) { + r, err := setTTL("traces", "s3", "3600s", "7200s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp := getTTL(t, "traces") + require.Equal(t, 1, resp.TracesMoveTime) + require.Equal(t, 2, resp.TracesTime) + + r, err = setTTL("metrics", "s3", "3600s", "7200s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "metrics") + require.Equal(t, 1, resp.MetricsMoveTime) + require.Equal(t, 2, resp.MetricsTime) + + r, err = setTTL("traces", "s3", "36000s", "72000s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 10, resp.TracesMoveTime) + require.Equal(t, 20, resp.TracesTime) + require.Equal(t, 1, resp.MetricsMoveTime) + require.Equal(t, 2, resp.MetricsTime) + + r, err = setTTL("metrics", "s3", "15h", "50h") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 10, resp.TracesMoveTime) + require.Equal(t, 20, resp.TracesTime) + require.Equal(t, 15, resp.MetricsMoveTime) + require.Equal(t, 50, resp.MetricsTime) + + r, err = setTTL("metrics", "s3", "0s", "0s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + r, err = setTTL("traces", "s3", "0s", "0s") + require.NoError(t, err) + require.Contains(t, string(r), "successfully set up") + + resp = getTTL(t, "") + require.Equal(t, 0, resp.TracesMoveTime) + require.Equal(t, 0, resp.TracesTime) + require.Equal(t, 0, resp.MetricsMoveTime) + require.Equal(t, 0, resp.MetricsTime) +} + func TestMain(m *testing.M) { if err := startCluster(); err != nil { fmt.Println(err) diff --git a/pkg/query-service/tests/test-deploy/data/signoz.db b/pkg/query-service/tests/test-deploy/data/signoz.db new file mode 100644 index 0000000000000000000000000000000000000000..c19319ab3476b0101d7f73bdf31e884b5e9f3590 GIT binary patch literal 32768 zcmeI(-%i?490%}Lgo+ztvWwmx?;zR@22po6<~m12RBW?(v2+J|h)tly{z>Mg_z=6^ zv+Nmsh-G_#UGB7stcVb@n>G116x#DUJ@ofEr%g_vrzZ`|XQb=6ed?2nur7$A@R|@o z5Yl{I;p=Ew<{RNUTEx-DjL)?2{Pc4!|67or91Ckd^556K=6*f(a^Lta76?E90uX=z z1Rwwb2qa!$NF+&C6!F^gX}iY)kGaOcWnJbnyTiQ6{gu6zTGLdb)!sBzG8rX>)oj)> zNxiA5`)Z3Ex9W$r))_fa&qz(zj_OT*_@Ua=ws^fD;59Y%qehxXeAOF`u+;KuwqsxR zoxra!UwmG4@5c)(q2 ztJ&gkGcCzGJK}W^5!sG!b*&EdEyp%G=hU`Y&zoA9iD6BxvXEHEWjq_x;1i!PE#K<1 zm|YNM`*_@@{D7D^(VO)XT@A_n%fUo2EHzn=@vpq^iu)djg4Cz+_PAC(laW8|h=OIC z?2C8NZ$07p=KmY;|fB*y_009U< z00Izzz&ruW|K|xI4gm;200Izz00bZa0SG_<0uV^P0G|I(ejg$O2tWV=5P$##AOHaf zKmY;|fWSO~+voppg#69CnutRH0uX=z1Rwwb2tWV=5P$##An;!a6vecluVlAbrNpX~ cD& Date: Fri, 1 Apr 2022 15:43:58 +0530 Subject: [PATCH 07/11] chore: links are updated for all dashboard and promql (#908) --- frontend/src/container/ListOfDashboard/index.tsx | 2 +- .../container/NewWidget/LeftContainer/QuerySection/Query.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ListOfDashboard/index.tsx b/frontend/src/container/ListOfDashboard/index.tsx index 8828d748f2..492da5c846 100644 --- a/frontend/src/container/ListOfDashboard/index.tsx +++ b/frontend/src/container/ListOfDashboard/index.tsx @@ -157,7 +157,7 @@ function ListOfAllDashboard(): JSX.Element { diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx index 97aa24d180..916771eece 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/Query.tsx @@ -114,7 +114,7 @@ function Query({ From d085506d3e16f71f46de37d8289c1fd5b762495a Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Fri, 1 Apr 2022 15:47:39 +0530 Subject: [PATCH 08/11] bug: logged in check is added in the useEffect (#921) --- frontend/src/container/AppLayout/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 84f7d237e2..5a9051c215 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -27,6 +27,12 @@ function AppLayout(props: AppLayoutProps): JSX.Element { } }, [isLoggedIn, isSignUpPage]); + useEffect(() => { + if (isLoggedIn) { + history.push(ROUTES.APPLICATION); + } + }, [isLoggedIn]); + return ( {!isSignUpPage && } From a566b5dc971a7971868a0575495d201d936dceff Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Fri, 1 Apr 2022 17:59:44 +0530 Subject: [PATCH 09/11] bug: no service and loading check are added (#934) --- .../src/modules/Servicemap/ServiceMap.tsx | 28 +++++------ frontend/src/store/actions/serviceMap.ts | 46 ++++++++++--------- frontend/src/store/actions/types.ts | 14 ++++-- frontend/src/store/reducers/serviceMap.ts | 7 +++ 4 files changed, 57 insertions(+), 38 deletions(-) diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index 9d07892fa5..7408fe206d 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -1,12 +1,13 @@ /* eslint-disable */ //@ts-nocheck +import { Card } from 'antd'; import Spinner from 'components/Spinner'; import React, { useEffect, useRef } from 'react'; import { ForceGraph2D } from 'react-force-graph'; import { connect } from 'react-redux'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { getDetailedServiceMapItems, getServiceMapItems } from 'store/actions'; +import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions'; import { AppState } from 'store/reducers'; import styled from 'styled-components'; import { GlobalTime } from 'types/actions/globalTime'; @@ -31,9 +32,8 @@ const Container = styled.div` `; interface ServiceMapProps extends RouteComponentProps { - serviceMap: serviceMapStore; + serviceMap: ServiceMapStore; globalTime: GlobalTime; - getServiceMapItems: (time: GlobalTime) => void; getDetailedServiceMapItems: (time: GlobalTime) => void; } interface graphNode { @@ -53,29 +53,32 @@ export interface graphDataType { function ServiceMap(props: ServiceMapProps): JSX.Element { const fgRef = useRef(); - const { - getDetailedServiceMapItems, - getServiceMapItems, - globalTime, - serviceMap, - } = props; + const { getDetailedServiceMapItems, globalTime, serviceMap } = props; useEffect(() => { /* Call the apis only when the route is loaded. Check this issue: https://github.com/SigNoz/signoz/issues/110 */ - getServiceMapItems(globalTime); getDetailedServiceMapItems(globalTime); - }, [globalTime, getServiceMapItems, getDetailedServiceMapItems]); + }, [globalTime, getDetailedServiceMapItems]); useEffect(() => { fgRef.current && fgRef.current.d3Force('charge').strength(-400); }); - if (!serviceMap.items.length || !serviceMap.services.length) { + + if (serviceMap.loading) { return ; } + if (!serviceMap.loading && serviceMap.items.length === 0) { + return ( + + No Service Found + + ); + } + const zoomToService = (value: string): void => { fgRef && fgRef.current && @@ -149,7 +152,6 @@ const mapStateToProps = ( export default withRouter( connect(mapStateToProps, { - getServiceMapItems, getDetailedServiceMapItems, })(ServiceMap), ); diff --git a/frontend/src/store/actions/serviceMap.ts b/frontend/src/store/actions/serviceMap.ts index be5a84a239..a6f079a7eb 100644 --- a/frontend/src/store/actions/serviceMap.ts +++ b/frontend/src/store/actions/serviceMap.ts @@ -7,6 +7,7 @@ import { ActionTypes } from './types'; export interface ServiceMapStore { items: ServicesMapItem[]; services: ServicesItem[]; + loading: boolean; } export interface ServicesItem { @@ -37,38 +38,39 @@ export interface ServicesAction { payload: ServicesItem[]; } -export const getServiceMapItems = (globalTime: GlobalTime) => { - return async (dispatch: Dispatch): Promise => { - dispatch({ - type: ActionTypes.getServiceMapItems, - payload: [], - }); - - const requestString = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`; - - const response = await api.get(requestString); - - dispatch({ - type: ActionTypes.getServiceMapItems, - payload: response.data, - }); +export interface ServiceMapLoading { + type: ActionTypes.serviceMapLoading; + payload: { + loading: ServiceMapStore['loading']; }; -}; +} export const getDetailedServiceMapItems = (globalTime: GlobalTime) => { return async (dispatch: Dispatch): Promise => { - dispatch({ - type: ActionTypes.getServices, - payload: [], - }); - const requestString = `/services?start=${globalTime.minTime}&end=${globalTime.maxTime}`; - const response = await api.get(requestString); + const serviceMapDependencies = `/serviceMapDependencies?start=${globalTime.minTime}&end=${globalTime.maxTime}`; + + const [serviceMapDependenciesResponse, response] = await Promise.all([ + api.get(serviceMapDependencies), + api.get(requestString), + ]); dispatch({ type: ActionTypes.getServices, payload: response.data, }); + + dispatch({ + type: ActionTypes.getServiceMapItems, + payload: serviceMapDependenciesResponse.data, + }); + + dispatch({ + type: ActionTypes.serviceMapLoading, + payload: { + loading: false, + }, + }); }; }; diff --git a/frontend/src/store/actions/types.ts b/frontend/src/store/actions/types.ts index c15ea00286..702997d49b 100644 --- a/frontend/src/store/actions/types.ts +++ b/frontend/src/store/actions/types.ts @@ -1,14 +1,22 @@ -import { ServiceMapItemAction, ServicesAction } from './serviceMap'; +import { + ServiceMapItemAction, + ServiceMapLoading, + ServicesAction, +} from './serviceMap'; import { GetUsageDataAction } from './usage'; export enum ActionTypes { - updateTraceFilters = 'UPDATE_TRACES_FILTER', updateTimeInterval = 'UPDATE_TIME_INTERVAL', getServiceMapItems = 'GET_SERVICE_MAP_ITEMS', getServices = 'GET_SERVICES', getUsageData = 'GET_USAGE_DATE', fetchTraces = 'FETCH_TRACES', fetchTraceItem = 'FETCH_TRACE_ITEM', + serviceMapLoading = 'UPDATE_SERVICE_MAP_LOADING', } -export type Action = GetUsageDataAction | ServicesAction | ServiceMapItemAction; +export type Action = + | GetUsageDataAction + | ServicesAction + | ServiceMapItemAction + | ServiceMapLoading; diff --git a/frontend/src/store/reducers/serviceMap.ts b/frontend/src/store/reducers/serviceMap.ts index ef7cd21496..18ec21a9ec 100644 --- a/frontend/src/store/reducers/serviceMap.ts +++ b/frontend/src/store/reducers/serviceMap.ts @@ -3,6 +3,7 @@ import { Action, ActionTypes, ServiceMapStore } from 'store/actions'; const initialState: ServiceMapStore = { items: [], services: [], + loading: true, }; export const ServiceMapReducer = ( @@ -20,6 +21,12 @@ export const ServiceMapReducer = ( ...state, services: action.payload, }; + case ActionTypes.serviceMapLoading: { + return { + ...state, + loading: action.payload.loading, + }; + } default: return state; } From 53e7037f48ec4d8abf8ac33a1b15c5996d7bb5bd Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Sat, 2 Apr 2022 16:15:03 +0530 Subject: [PATCH 10/11] fix: run go vet to fix some issues with json tag (#936) Co-authored-by: Naman Jain --- pkg/query-service/godruid/queries.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/query-service/godruid/queries.go b/pkg/query-service/godruid/queries.go index 5fb518f908..ffdeee002d 100644 --- a/pkg/query-service/godruid/queries.go +++ b/pkg/query-service/godruid/queries.go @@ -62,7 +62,7 @@ type QueryScan struct { Limit int64 `json:"limit,omitempty"` Offset int64 `json:"offset,omitempty"` BatchSize int64 `json:"batchSize,omitempty"` - Order string `json:"order",omitempty` + Order string `json:"order,omitempty"` ResultFormat string `json:"resultFormat"` Context map[string]interface{} `json:"context,omitempty"` @@ -189,7 +189,7 @@ type TimeBoundaryItem struct { type TimeBoundary struct { MinTime string `json:"minTime"` - MaxTime string `json:"minTime"` + MaxTime string `json:"maxTime"` } func (q *QueryTimeBoundary) setup() { q.QueryType = "timeBoundary" } From 6c4c814b3ff79e137803f629d81a5e560265e48d Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Mon, 4 Apr 2022 10:25:15 +0530 Subject: [PATCH 11/11] bug: pathname check is added (#948) --- frontend/src/container/AppLayout/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 5a9051c215..5230ca5bae 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -28,10 +28,10 @@ function AppLayout(props: AppLayoutProps): JSX.Element { }, [isLoggedIn, isSignUpPage]); useEffect(() => { - if (isLoggedIn) { + if (isLoggedIn && pathname === ROUTES.SIGN_UP) { history.push(ROUTES.APPLICATION); } - }, [isLoggedIn]); + }, [isLoggedIn, pathname]); return (