Merge pull request #2531 from SigNoz/release/v0.18.0

Release/v0.18.0
This commit is contained in:
Ankit Nayan 2023-03-31 18:15:08 +05:30 committed by GitHub
commit 80171eddea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
149 changed files with 6780 additions and 1224 deletions

View File

@ -11,21 +11,23 @@ jobs:
environment: staging
steps:
- name: Executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v0.1.6
uses: appleboy/ssh-action@v0.1.8
env:
GITHUB_BRANCH: develop
GITHUB_SHA: ${{ github.sha }}
with:
host: ${{ secrets.HOST_DNS }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
key: ${{ secrets.SSH_KEY }}
envs: GITHUB_BRANCH,GITHUB_SHA
command_timeout: 60m
script: |
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export OTELCOL_TAG="main"
docker system prune --force
docker pull signoz/signoz-otel-collector:main
cd ~/signoz
git status
git add .

View File

@ -11,14 +11,14 @@ jobs:
if: ${{ github.event.label.name == 'testing-deploy' }}
steps:
- name: Executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v0.1.6
uses: appleboy/ssh-action@v0.1.8
env:
GITHUB_BRANCH: ${{ github.head_ref || github.ref_name }}
GITHUB_SHA: ${{ github.sha }}
with:
host: ${{ secrets.HOST_DNS }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
key: ${{ secrets.SSH_KEY }}
envs: GITHUB_BRANCH,GITHUB_SHA
command_timeout: 60m
script: |

View File

@ -35,14 +35,37 @@ SigNoz helps developers monitor applications and troubleshoot problems in their
👉 Filter and query logs, build dashboards and alerts based on attributes in logs
![screenzy-1670570187181](https://user-images.githubusercontent.com/504541/206646629-829fdafe-70e2-4503-a9c4-1301b7918586.png)
<br />
![screenzy-1670570193901](https://user-images.githubusercontent.com/504541/206646676-a676fdeb-331c-4847-aea9-d1cabf7c47e1.png)
<br />
![screenzy-1670570199026](https://user-images.githubusercontent.com/504541/206646754-28c5534f-0377-428c-9c6e-5c7c0d9dd22d.png)
<br />
![screenzy-1670569888865](https://user-images.githubusercontent.com/504541/206645819-1e865a56-71b4-4fde-80cc-fbdb137a4da5.png)
👉 Record exceptions automatically in Python, Java, Ruby, and Javascript
👉 Easy to set alerts with DIY query builder
### Application Metrics
![application_metrics](https://user-images.githubusercontent.com/83692067/226637410-900dbc5e-6705-4b11-a10c-bd0faeb2a92f.png)
### Distributed Tracing
<img width="2068" alt="distributed_tracing_2 2" src="https://user-images.githubusercontent.com/83692067/226536447-bae58321-6a22-4ed3-af80-e3e964cb3489.png">
<img width="2068" alt="distributed_tracing_1" src="https://user-images.githubusercontent.com/83692067/226536462-939745b6-4f9d-45a6-8016-814837e7f7b4.png">
### Logs Management
<img width="2068" alt="logs_management" src="https://user-images.githubusercontent.com/83692067/226536482-b8a5c4af-b69c-43d5-969c-338bd5eaf1a5.png">
### Infrastructure Monitoring
<img width="2068" alt="infrastructure_monitoring" src="https://user-images.githubusercontent.com/83692067/226536496-f38c4dbf-e03c-4158-8be0-32d4a61158c7.png">
### Exceptions Monitoring
![exceptions_light](https://user-images.githubusercontent.com/83692067/226637967-4188d024-3ac9-4799-be95-f5ea9c45436f.png)
### Alerts
<img width="2068" alt="alerts_management" src="https://user-images.githubusercontent.com/83692067/226536548-2c81e2e8-c12d-47e8-bad7-c6be79055def.png">
<br /><br />
@ -65,6 +88,10 @@ Come say Hi to us on [Slack](https://signoz.io/slack) 👋
- See exact request trace to figure out issues in downstream services, slow DB queries, call to 3rd party services like payment gateways, etc
- Filter traces by service name, operation, latency, error, tags/annotations.
- Run aggregates on trace data (events/spans) to get business relevant metrics. e.g. You can get error rate and 99th percentile latency of `customer_type: gold` or `deployment_version: v2` or `external_call: paypal`
- Native support for OpenTelemetry Logs, advanced log query builder, and automatic log collection from k8s cluster
- Lightening quick log analytics ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/))
- End-to-End visibility into infrastructure performance, ingest metrics from all kinds of host environments
- Easy to set alerts with DIY query builder
<br /><br />

View File

@ -137,7 +137,7 @@ services:
condition: on-failure
query-service:
image: signoz/query-service:0.17.0
image: signoz/query-service:0.18.0
command: ["-config=/root/config/prometheus.yml"]
# ports:
# - "6060:6060" # pprof port
@ -166,7 +166,7 @@ services:
<<: *clickhouse-depend
frontend:
image: signoz/frontend:0.17.0
image: signoz/frontend:0.18.0
deploy:
restart_policy:
condition: on-failure
@ -179,7 +179,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/signoz-otel-collector:0.66.6
image: signoz/signoz-otel-collector:0.66.7
command: ["--config=/etc/otel-collector-config.yaml"]
user: root # required for reading docker container logs
volumes:
@ -208,7 +208,7 @@ services:
<<: *clickhouse-depend
otel-collector-metrics:
image: signoz/signoz-otel-collector:0.66.6
image: signoz/signoz-otel-collector:0.66.7
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
volumes:
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml

View File

@ -41,7 +41,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
otel-collector:
container_name: otel-collector
image: signoz/signoz-otel-collector:0.66.6
image: signoz/signoz-otel-collector:0.66.7
command: ["--config=/etc/otel-collector-config.yaml"]
# user: root # required for reading docker container logs
volumes:
@ -67,7 +67,7 @@ services:
otel-collector-metrics:
container_name: otel-collector-metrics
image: signoz/signoz-otel-collector:0.66.6
image: signoz/signoz-otel-collector:0.66.7
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
volumes:
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml

View File

@ -153,7 +153,7 @@ services:
# 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:${DOCKER_TAG:-0.17.0}
image: signoz/query-service:${DOCKER_TAG:-0.18.0}
container_name: query-service
command: ["-config=/root/config/prometheus.yml"]
# ports:
@ -181,7 +181,7 @@ services:
<<: *clickhouse-depend
frontend:
image: signoz/frontend:${DOCKER_TAG:-0.17.0}
image: signoz/frontend:${DOCKER_TAG:-0.18.0}
container_name: frontend
restart: on-failure
depends_on:
@ -193,7 +193,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.6}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.7}
command: ["--config=/etc/otel-collector-config.yaml"]
user: root # required for reading docker container logs
volumes:
@ -219,7 +219,7 @@ services:
<<: *clickhouse-depend
otel-collector-metrics:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.6}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.7}
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
volumes:
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml

View File

@ -25,16 +25,18 @@ import (
licensepkg "go.signoz.io/signoz/ee/query-service/license"
"go.signoz.io/signoz/ee/query-service/usage"
"go.signoz.io/signoz/pkg/query-service/agentConf"
baseapp "go.signoz.io/signoz/pkg/query-service/app"
"go.signoz.io/signoz/pkg/query-service/app/dashboards"
"go.signoz.io/signoz/pkg/query-service/app/explorer"
baseexplorer "go.signoz.io/signoz/pkg/query-service/app/explorer"
"go.signoz.io/signoz/pkg/query-service/app/opamp"
opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
baseauth "go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/constants"
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/healthcheck"
basealm "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
"go.signoz.io/signoz/pkg/query-service/model"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
pqle "go.signoz.io/signoz/pkg/query-service/pqlEngine"
rules "go.signoz.io/signoz/pkg/query-service/rules"
"go.signoz.io/signoz/pkg/query-service/telemetry"
@ -42,6 +44,8 @@ import (
"go.uber.org/zap"
)
const AppDbEngine = "sqlite"
type ServerOptions struct {
PromConfigPath string
HTTPHostPort string
@ -85,8 +89,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
return nil, err
}
baseexplorer.InitWithDSN(baseconst.RELATIONAL_DATASOURCE_PATH)
localDB, err := dashboards.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH)
explorer.InitWithDSN(constants.RELATIONAL_DATASOURCE_PATH)
if err != nil {
return nil, err
@ -127,6 +132,17 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
return nil, err
}
// initiate opamp
_, err = opAmpModel.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH)
if err != nil {
return nil, err
}
// initiate agent config handler
if err := agentConf.Initiate(localDB, AppDbEngine); err != nil {
return nil, err
}
// start the usagemanager
usageManager, err := usage.New("sqlite", localDB, lm.GetRepo(), reader.GetConn())
if err != nil {
@ -208,7 +224,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e
r := mux.NewRouter()
getUserFromRequest := func(r *http.Request) (*model.UserPayload, error) {
getUserFromRequest := func(r *http.Request) (*basemodel.UserPayload, error) {
patToken := r.Header.Get("SIGNOZ-API-KEY")
if len(patToken) > 0 {
zap.S().Debugf("Received a non-zero length PAT token")
@ -299,7 +315,7 @@ func extractDashboardMetaData(path string, r *http.Request) (map[string]interfac
pathToExtractBodyFrom := "/api/v2/metrics/query_range"
data := map[string]interface{}{}
var postData *model.QueryRangeParamsV2
var postData *basemodel.QueryRangeParamsV2
if path == pathToExtractBodyFrom && (r.Method == "POST") {
if r.Body != nil {
@ -472,7 +488,7 @@ func (s *Server) Start() error {
if port, err := utils.GetPort(s.privateConn.Addr()); err == nil {
privatePort = port
}
fmt.Println("starting private http")
go func() {
zap.S().Info("Starting Private HTTP server", zap.Int("port", privatePort), zap.String("addr", s.serverOptions.PrivateHostPort))
@ -488,6 +504,37 @@ func (s *Server) Start() error {
}()
go func() {
zap.S().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint))
err := opamp.InitalizeServer(baseconst.OpAmpWsEndpoint, &opAmpModel.AllAgents)
if err != nil {
zap.S().Info("opamp ws server failed to start", err)
s.unavailableChannel <- healthcheck.Unavailable
}
}()
return nil
}
func (s *Server) Stop() error {
if s.httpServer != nil {
if err := s.httpServer.Shutdown(context.Background()); err != nil {
return err
}
}
if s.privateHTTP != nil {
if err := s.privateHTTP.Shutdown(context.Background()); err != nil {
return err
}
}
opamp.StopServer()
if s.ruleManager != nil {
s.ruleManager.Stop()
}
return nil
}

View File

@ -4,7 +4,9 @@ import Spinner from 'components/Spinner';
import AppLayout from 'container/AppLayout';
import { useThemeConfig } from 'hooks/useDarkMode';
import { NotificationProvider } from 'hooks/useNotifications';
import { ResourceProvider } from 'hooks/useResourceAttribute';
import history from 'lib/history';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import React, { Suspense } from 'react';
import { Route, Router, Switch } from 'react-router-dom';
@ -16,28 +18,32 @@ function App(): JSX.Element {
return (
<ConfigProvider theme={themeConfig}>
<NotificationProvider>
<Router history={history}>
<Router history={history}>
<NotificationProvider>
<PrivateRoute>
<AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch>
{routes.map(({ path, component, exact }) => (
<Route
key={`${path}`}
exact={exact}
path={path}
component={component}
/>
))}
<ResourceProvider>
<QueryBuilderProvider>
<AppLayout>
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
<Switch>
{routes.map(({ path, component, exact }) => (
<Route
key={`${path}`}
exact={exact}
path={path}
component={component}
/>
))}
<Route path="*" component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
<Route path="*" component={NotFound} />
</Switch>
</Suspense>
</AppLayout>
</QueryBuilderProvider>
</ResourceProvider>
</PrivateRoute>
</Router>
</NotificationProvider>
</NotificationProvider>
</Router>
</ConfigProvider>
);
}

View File

@ -1,7 +1,6 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import createQueryParams from 'lib/createQueryParams';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/errors/getAll';
@ -9,11 +8,17 @@ const getAll = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.get(
`/listErrors?${createQueryParams({
...props,
})}`,
);
const response = await axios.post(`/listErrors`, {
start: `${props.start}`,
end: `${props.end}`,
order: props.order,
orderParam: props.orderParam,
limit: props.limit,
offset: props.offset,
exceptionType: props.exceptionType,
serviceName: props.serviceName,
tags: props.tags,
});
return {
statusCode: 200,

View File

@ -1,7 +1,6 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import createQueryParams from 'lib/createQueryParams';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/errors/getErrorCounts';
@ -9,11 +8,13 @@ const getErrorCounts = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.get(
`/countErrors?${createQueryParams({
...props,
})}`,
);
const response = await axios.post(`/countErrors`, {
start: `${props.start}`,
end: `${props.end}`,
exceptionType: props.exceptionType,
serviceName: props.serviceName,
tags: props.tags,
});
return {
statusCode: 200,

View File

@ -10,7 +10,7 @@ const getSpans = async (
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const updatedSelectedTags = props.selectedTags.map((e) => ({
Key: e.Key,
Key: `${e.Key}.(string)`,
Operator: e.Operator,
StringValues: e.StringValues,
NumberValues: e.NumberValues,

View File

@ -28,7 +28,7 @@ const getSpanAggregate = async (
});
const updatedSelectedTags = props.selectedTags.map((e) => ({
Key: e.Key,
Key: `${e.Key}.(string)`,
Operator: e.Operator,
StringValues: e.StringValues,
NumberValues: e.NumberValues,

View File

@ -1,6 +1,6 @@
import MEditor, { EditorProps } from '@monaco-editor/react';
import { useIsDarkMode } from 'hooks/useDarkMode';
import React from 'react';
import React, { useMemo } from 'react';
function Editor({
value,
@ -11,16 +11,24 @@ function Editor({
options,
}: MEditorProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const onChangeHandler = (newValue?: string): void => {
if (typeof newValue === 'string' && onChange) onChange(newValue);
};
const editorOptions = useMemo(
() => ({ fontSize: 16, automaticLayout: true, readOnly, ...options }),
[options, readOnly],
);
return (
<MEditor
theme={isDarkMode ? 'vs-dark' : 'vs-light'}
language={language}
value={value}
options={{ fontSize: 16, automaticLayout: true, readOnly, ...options }}
options={editorOptions}
height={height}
onChange={(newValue): void => {
if (typeof newValue === 'string') onChange(newValue);
}}
onChange={onChangeHandler}
/>
);
}
@ -28,7 +36,7 @@ function Editor({
interface MEditorProps {
value: string;
language?: string;
onChange: (value: string) => void;
onChange?: (value: string) => void;
readOnly?: boolean;
height?: string;
options?: EditorProps['options'];
@ -39,6 +47,7 @@ Editor.defaultProps = {
readOnly: false,
height: '40vh',
options: {},
onChange: (): void => {},
};
export default Editor;

View File

@ -2,8 +2,6 @@ import { Tabs, TabsProps } from 'antd';
import history from 'lib/history';
import React from 'react';
const { TabPane } = Tabs;
function RouteTab({
routes,
activeKey,
@ -22,29 +20,23 @@ function RouteTab({
}
};
const items = routes.map(({ Component, name, route }) => ({
label: name,
key: name,
tabKey: route,
children: <Component />,
}));
return (
<Tabs
onChange={onChange}
destroyInactiveTabPane
activeKey={activeKey}
animated
items={items}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
>
{routes.map(
({ Component, name, route }): JSX.Element => (
<TabPane
tabKey={route}
animated
destroyInactiveTabPane
tab={name}
key={name}
>
<Component />
</TabPane>
),
)}
</Tabs>
/>
);
}

View File

@ -1,23 +1,40 @@
/* eslint-disable react/no-unstable-nested-components */
import { grey } from '@ant-design/colors';
import { QuestionCircleFilled } from '@ant-design/icons';
import { Tooltip } from 'antd';
import React from 'react';
import { themeColors } from 'constants/theme';
import { useIsDarkMode } from 'hooks/useDarkMode';
import React, { useMemo } from 'react';
import { style } from './styles';
function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const overlay = useMemo(
() => (
<div>
{`${text} `}
{url && (
<a href={url} rel="noopener noreferrer" target="_blank">
here
</a>
)}
</div>
),
[text, url],
);
const iconStyle = useMemo(
() => ({
...style,
color: isDarkMode ? themeColors.whiteCream : grey[0],
}),
[isDarkMode],
);
return (
<Tooltip
overlay={(): JSX.Element => (
<div>
{`${text} `}
{url && (
<a href={url} rel="noopener noreferrer" target="_blank">
here
</a>
)}
</div>
)}
>
<QuestionCircleFilled style={{ fontSize: '1.3125rem' }} />
<Tooltip overlay={overlay}>
<QuestionCircleFilled style={iconStyle} />
</Tooltip>
);
}

View File

@ -0,0 +1 @@
export const style = { fontSize: '1.3125rem' };

View File

@ -1,9 +1,9 @@
import { Button, Dropdown, Menu } from 'antd';
import { Button, Dropdown } from 'antd';
import TimeItems, {
timePreferance,
timePreferenceType,
} from 'container/NewWidget/RightContainer/timeItems';
import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import { menuItems } from './config';
import { TextContainer } from './styles';
@ -22,11 +22,17 @@ function TimePreference({
[setSelectedTime],
);
const menu = useMemo(
() => ({
items: menuItems,
onClick: timeMenuItemOnChangeHandler,
}),
[timeMenuItemOnChangeHandler],
);
return (
<TextContainer noButtonMargin>
<Dropdown
overlay={<Menu onClick={timeMenuItemOnChangeHandler} items={menuItems} />}
>
<Dropdown menu={menu}>
<Button>{selectedTime.name}</Button>
</Dropdown>
</TextContainer>

View File

@ -18,6 +18,8 @@ import { ResizeTable } from 'components/ResizeTable';
import ROUTES from 'constants/routes';
import dayjs from 'dayjs';
import { useNotifications } from 'hooks/useNotifications';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import useUrlQuery from 'hooks/useUrlQuery';
import createQueryParams from 'lib/createQueryParams';
import history from 'lib/history';
@ -93,9 +95,11 @@ function AllErrors(): JSX.Element {
],
);
const { queries } = useResourceAttribute();
const [{ isLoading, data }, errorCountResponse] = useQueries([
{
queryKey: ['getAllErrors', updatedPath, maxTime, minTime],
queryKey: ['getAllErrors', updatedPath, maxTime, minTime, queries],
queryFn: (): Promise<SuccessResponse<PayloadProps> | ErrorResponse> =>
getAll({
end: maxTime,
@ -106,6 +110,7 @@ function AllErrors(): JSX.Element {
orderParam: getUpdatedParams,
exceptionType: getUpdatedExceptionType,
serviceName: getUpdatedServiceName,
tags: convertRawQueriesToTraceSelectedTags(queries),
}),
enabled: !loading,
},
@ -116,6 +121,7 @@ function AllErrors(): JSX.Element {
minTime,
getUpdatedExceptionType,
getUpdatedServiceName,
queries,
],
queryFn: (): Promise<ErrorResponse | SuccessResponse<number>> =>
getErrorCounts({
@ -123,6 +129,7 @@ function AllErrors(): JSX.Element {
start: minTime,
exceptionType: getUpdatedExceptionType,
serviceName: getUpdatedServiceName,
tags: convertRawQueriesToTraceSelectedTags(queries),
}),
enabled: !loading,
},

View File

@ -1,17 +0,0 @@
import { Menu } from 'antd';
import styled from 'styled-components';
export const MenuDropdown = styled(Menu)`
&&& {
.ant-dropdown,
.ant-dropdown-menu,
.ant-dropdown-menu-item {
padding: 0px;
}
.ant-menu-item {
height: 1.75rem;
display: flex;
align-items: center;
}
}
`;

View File

@ -13,7 +13,6 @@ import { ConfigProps } from 'types/api/dynamicConfigs/getDynamicConfigs';
import AppReducer from 'types/reducer/app';
import HelpToolTip from './Config';
import { MenuDropdown } from './Config/styles';
function DynamicConfigDropdown({
frontendId,
@ -34,13 +33,15 @@ function DynamicConfigDropdown({
setIsHelpDropDownOpen(!isHelpDropDownOpen);
};
const menuItems = useMemo(
() => [
{
key: '1',
label: <HelpToolTip config={config as ConfigProps} />,
},
],
const menu = useMemo(
() => ({
items: [
{
key: '1',
label: <HelpToolTip config={config as ConfigProps} />,
},
],
}),
[config],
);
@ -53,10 +54,10 @@ function DynamicConfigDropdown({
return (
<Dropdown
onVisibleChange={onToggleHandler}
onOpenChange={onToggleHandler}
trigger={['click']}
overlay={<MenuDropdown items={menuItems} />}
visible={isHelpDropDownOpen}
menu={menu}
open={isHelpDropDownOpen}
>
<Space align="center">
<Icon

View File

@ -23,7 +23,6 @@ import PromqlSection from './PromqlSection';
import { FormContainer, QueryButton, StepHeading } from './styles';
import { toIMetricsBuilderQuery } from './utils';
const { TabPane } = Tabs;
function QuerySection({
queryCategory,
setQueryCategory,
@ -282,6 +281,24 @@ function QuerySection({
runQuery();
};
const tabs = [
{
label: t('tab_qb'),
key: EQueryType.QUERY_BUILDER.toString(),
disabled: true,
},
{
label: t('tab_chquery'),
key: EQueryType.CLICKHOUSE.toString(),
},
];
const items = [
{ label: t('tab_qb'), key: EQueryType.QUERY_BUILDER.toString() },
{ label: t('tab_chquery'), key: EQueryType.CLICKHOUSE.toString() },
{ label: t('tab_promql'), key: EQueryType.PROM.toString() },
];
const renderTabs = (typ: AlertTypes): JSX.Element | null => {
switch (typ) {
case AlertTypes.TRACES_BASED_ALERT:
@ -303,14 +320,8 @@ function QuerySection({
)}
</span>
}
>
<TabPane
tab={t('tab_qb')}
key={EQueryType.QUERY_BUILDER.toString()}
disabled
/>
<TabPane tab={t('tab_chquery')} key={EQueryType.CLICKHOUSE.toString()} />
</Tabs>
items={tabs}
/>
);
case AlertTypes.METRICS_BASED_ALERT:
default:
@ -330,11 +341,8 @@ function QuerySection({
)}
</span>
}
>
<TabPane tab={t('tab_qb')} key={EQueryType.QUERY_BUILDER.toString()} />
<TabPane tab={t('tab_chquery')} key={EQueryType.CLICKHOUSE.toString()} />
<TabPane tab={t('tab_promql')} key={EQueryType.PROM.toString()} />
</Tabs>
items={items}
/>
);
}
};

View File

@ -1,16 +1,21 @@
import { Avatar, Typography } from 'antd';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import React from 'react';
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import { AvatarContainer, ManageAccountLink, Wrapper } from '../styles';
function SignedInAS(): JSX.Element {
function SignedIn({ onToggle }: SignedInProps): JSX.Element {
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
const onManageAccountClick = useCallback(() => {
onToggle();
history.push(ROUTES.MY_SETTINGS);
}, [onToggle]);
if (!user) {
return <div />;
}
@ -30,11 +35,7 @@ function SignedInAS(): JSX.Element {
<Typography>{email}</Typography>
</div>
</AvatarContainer>
<ManageAccountLink
onClick={(): void => {
history.push(ROUTES.MY_SETTINGS);
}}
>
<ManageAccountLink onClick={onManageAccountClick}>
Manage Account
</ManageAccountLink>
</Wrapper>
@ -42,4 +43,8 @@ function SignedInAS(): JSX.Element {
);
}
export default SignedInAS;
interface SignedInProps {
onToggle: VoidFunction;
}
export default SignedIn;

View File

@ -3,12 +3,19 @@ import {
CaretUpFilled,
LogoutOutlined,
} from '@ant-design/icons';
import { Divider, Dropdown, Menu, Space, Typography } from 'antd';
import type { MenuProps } from 'antd';
import { Divider, Dropdown, Space, Typography } from 'antd';
import { Logout } from 'api/utils';
import ROUTES from 'constants/routes';
import Config from 'container/ConfigDropdown';
import { useIsDarkMode, useThemeMode } from 'hooks/useDarkMode';
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
import React, {
Dispatch,
SetStateAction,
useCallback,
useMemo,
useState,
} from 'react';
import { useSelector } from 'react-redux';
import { NavLink } from 'react-router-dom';
import { AppState } from 'store/reducers';
@ -16,7 +23,7 @@ import AppReducer from 'types/reducer/app';
import CurrentOrganization from './CurrentOrganization';
import ManageLicense from './ManageLicense';
import SignedInAS from './SignedInAs';
import SignedIn from './SignedIn';
import {
AvatarWrapper,
Container,
@ -43,32 +50,45 @@ function HeaderContainer(): JSX.Element {
[],
);
const menu = (
<Menu style={{ padding: '1rem' }}>
<Menu.ItemGroup>
<SignedInAS />
<Divider />
<CurrentOrganization onToggle={onToggleHandler(setIsUserDropDownOpen)} />
<Divider />
<ManageLicense onToggle={onToggleHandler(setIsUserDropDownOpen)} />
<Divider />
<LogoutContainer>
<LogoutOutlined />
<div
tabIndex={0}
onKeyDown={(e): void => {
if (e.key === 'Enter' || e.key === 'Space') {
Logout();
}
}}
role="button"
onClick={Logout}
>
<Typography.Link>Logout</Typography.Link>
</div>
</LogoutContainer>
</Menu.ItemGroup>
</Menu>
const onLogoutKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' || e.key === 'Space') {
Logout();
}
},
[],
);
const menu: MenuProps = useMemo(
() => ({
items: [
{
key: 'main-menu',
label: (
<div>
<SignedIn onToggle={onToggleHandler(setIsUserDropDownOpen)} />
<Divider />
<CurrentOrganization onToggle={onToggleHandler(setIsUserDropDownOpen)} />
<Divider />
<ManageLicense onToggle={onToggleHandler(setIsUserDropDownOpen)} />
<Divider />
<LogoutContainer>
<LogoutOutlined />
<div
tabIndex={0}
onKeyDown={onLogoutKeyDown}
role="button"
onClick={Logout}
>
<Typography.Link>Logout</Typography.Link>
</div>
</LogoutContainer>
</div>
),
},
],
}),
[onToggleHandler, onLogoutKeyDown],
);
return (
@ -98,10 +118,10 @@ function HeaderContainer(): JSX.Element {
/>
<Dropdown
onVisibleChange={onToggleHandler(setIsUserDropDownOpen)}
onOpenChange={onToggleHandler(setIsUserDropDownOpen)}
trigger={['click']}
overlay={menu}
visible={isUserDropDownOpen}
menu={menu}
open={isUserDropDownOpen}
>
<Space>
<AvatarWrapper shape="circle">{user?.name[0]}</AvatarWrapper>

View File

@ -8,8 +8,6 @@ import { useQuery } from 'react-query';
import ApplyLicenseForm from './ApplyLicenseForm';
import ListLicenses from './ListLicenses';
const { TabPane } = Tabs;
function Licenses(): JSX.Element {
const { t } = useTranslation(['licenses']);
const { data, isError, isLoading, refetch } = useQuery({
@ -28,17 +26,21 @@ function Licenses(): JSX.Element {
const allValidLicense =
data?.payload?.filter((license) => license.isCurrent) || [];
return (
<Tabs destroyInactiveTabPane defaultActiveKey="licenses">
<TabPane tabKey="licenses" tab={t('tab_current_license')} key="licenses">
<ApplyLicenseForm licenseRefetch={refetch} />
<ListLicenses licenses={allValidLicense} />
</TabPane>
const tabs = [
{
label: t('tab_current_license'),
key: 'licenses',
children: <ApplyLicenseForm licenseRefetch={refetch} />,
},
{
label: t('tab_license_history'),
key: 'history',
children: <ListLicenses licenses={allValidLicense} />,
},
];
<TabPane tabKey="history" tab={t('tab_license_history')} key="history">
<ListLicenses licenses={allValidLicense} />
</TabPane>
</Tabs>
return (
<Tabs destroyInactiveTabPane defaultActiveKey="licenses" items={tabs} />
);
}

View File

@ -1,5 +1,12 @@
import { PlusOutlined } from '@ant-design/icons';
import { Card, Dropdown, Menu, Row, TableColumnProps, Typography } from 'antd';
import {
Card,
Dropdown,
MenuProps,
Row,
TableColumnProps,
Typography,
} from 'antd';
import { ItemType } from 'antd/es/menu/hooks/useItems';
import createDashboard from 'api/dashboard/create';
import { AxiosError } from 'axios';
@ -47,10 +54,12 @@ function ListOfAllDashboard(): JSX.Element {
);
const { t } = useTranslation('dashboard');
const [
isImportJSONModalVisible,
setIsImportJSONModalVisible,
] = useState<boolean>(false);
const [uploadedGrafana, setUploadedGrafana] = useState<boolean>(false);
const [filteredDashboards, setFilteredDashboards] = useState<Dashboard[]>();
@ -58,6 +67,7 @@ function ListOfAllDashboard(): JSX.Element {
useEffect(() => {
setFilteredDashboards(dashboards);
}, [dashboards]);
const [newDashboardState, setNewDashboardState] = useState({
loading: false,
error: false,
@ -215,7 +225,12 @@ function ListOfAllDashboard(): JSX.Element {
return menuItems;
}, [createNewDashboard, loading, onNewDashboardHandler, t]);
const menuItems = getMenuItems();
const menu: MenuProps = useMemo(
() => ({
items: getMenuItems(),
}),
[getMenuItems],
);
const GetHeader = useMemo(
() => (
@ -230,7 +245,7 @@ function ListOfAllDashboard(): JSX.Element {
}}
/>
{newDashboard && (
<Dropdown trigger={['click']} overlay={<Menu items={menuItems} />}>
<Dropdown trigger={['click']} menu={menu}>
<NewDashboardButton
icon={<PlusOutlined />}
type="primary"
@ -249,7 +264,7 @@ function ListOfAllDashboard(): JSX.Element {
newDashboard,
newDashboardState.error,
newDashboardState.loading,
menuItems,
menu,
],
);

View File

@ -1,14 +1,18 @@
import { blue, orange } from '@ant-design/colors';
import { Input } from 'antd';
import { ColumnsType } from 'antd/es/table';
import Editor from 'components/Editor';
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import { ResizeTable } from 'components/ResizeTable';
import flatten from 'flat';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { isEmpty } from 'lodash-es';
import React, { useMemo, useState } from 'react';
import { ILog } from 'types/api/logs/log';
import ActionItem from './ActionItem';
import { recursiveParseJSON } from './utils';
// Fields which should be restricted from adding it to query
const RESTRICTED_FIELDS = ['timestamp'];
@ -41,10 +45,10 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
return null;
}
const columns = [
const columns: ColumnsType<DataType> = [
{
title: 'Action',
width: 100,
width: 15,
render: (fieldData: Record<string, string>): JSX.Element | null => {
const fieldKey = fieldData.field.split('.').slice(-1);
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
@ -57,7 +61,8 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
title: 'Field',
dataIndex: 'field',
key: 'field',
width: 100,
width: 30,
ellipsis: true,
render: (field: string): JSX.Element => {
const fieldKey = field.split('.').slice(-1);
const renderedField = <span style={{ color: blue[4] }}>{field}</span>;
@ -78,16 +83,36 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
key: 'value',
width: 80,
ellipsis: false,
render: (field: never): JSX.Element => (
<CopyClipboardHOC textToCopy={field}>
<span style={{ color: orange[6] }}>{field}</span>
</CopyClipboardHOC>
),
render: (field, record): JSX.Element => {
if (record.field === 'body') {
const parsedBody = recursiveParseJSON(field);
if (!isEmpty(parsedBody)) {
return (
<Editor
value={JSON.stringify(parsedBody, null, 2)}
readOnly
height="70vh"
options={{
minimap: {
enabled: false,
},
}}
/>
);
}
}
return (
<CopyClipboardHOC textToCopy={field}>
<span style={{ color: orange[6] }}>{field}</span>
</CopyClipboardHOC>
);
},
},
];
return (
<div style={{ position: 'relative' }}>
<>
<Input
placeholder="Search field names"
size="large"
@ -95,13 +120,19 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
onChange={(e): void => setFieldSearchInput(e.target.value)}
/>
<ResizeTable
columns={columns as never}
columns={columns}
tableLayout="fixed"
dataSource={dataSource}
pagination={false}
/>
</div>
</>
);
}
interface DataType {
key: string;
field: string;
value: string;
}
export default TableView;

View File

@ -24,6 +24,19 @@ function LogDetailedView(): JSX.Element {
});
};
const items = [
{
label: 'Table',
key: '1',
children: detailedLog && <TableView logData={detailedLog} />,
},
{
label: 'JSON',
key: '2',
children: detailedLog && <JSONView logData={detailedLog} />,
},
];
return (
<Drawer
width="60%"
@ -35,14 +48,7 @@ function LogDetailedView(): JSX.Element {
style={{ overscrollBehavior: 'contain' }}
destroyOnClose
>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="Table" key="1">
{detailedLog && <TableView logData={detailedLog} />}
</Tabs.TabPane>
<Tabs.TabPane tab="JSON" key="2">
{detailedLog && <JSONView logData={detailedLog} />}
</Tabs.TabPane>
</Tabs>
<Tabs defaultActiveKey="1" items={items} />
</Drawer>
);
}

View File

@ -0,0 +1,47 @@
import { recursiveParseJSON } from './utils';
describe('recursiveParseJSON', () => {
it('should return an empty object if the input is not valid JSON', () => {
const result = recursiveParseJSON('not valid JSON');
expect(result).toEqual({});
});
it('should return the parsed JSON object for valid JSON input', () => {
const jsonString = '{"name": "John", "age": 30}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({ name: 'John', age: 30 });
});
it('should recursively parse nested JSON objects', () => {
const jsonString =
'{"name": "John", "age": 30, "address": {"street": "123 Main St", "city": "Anytown", "state": "CA"}}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA',
},
});
});
it('should recursively parse nested JSON arrays', () => {
const jsonString = '[1, 2, [3, 4], {"foo": "bar"}]';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual([1, 2, [3, 4], { foo: 'bar' }]);
});
it('should recursively parse deeply nested JSON objects', () => {
const jsonString = '{"foo": {"bar": {"baz": {"qux": {"value": 42}}}}}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({ foo: { bar: { baz: { qux: { value: 42 } } } } });
});
it('should handle JSON input that contains escaped characters', () => {
const jsonString = '{"name": "John\\", \\"Doe", "age": 30}';
const result = recursiveParseJSON(jsonString);
expect(result).toEqual({ name: 'John", "Doe', age: 30 });
});
});

View File

@ -0,0 +1,11 @@
export const recursiveParseJSON = (obj: string): Record<string, unknown> => {
try {
const value = JSON.parse(obj);
if (typeof value === 'string') {
return recursiveParseJSON(value);
}
return value;
} catch (e) {
return {};
}
};

View File

@ -27,8 +27,9 @@ function QueryConditionField({
return (
<Select
defaultValue={
(query as any).value &&
(((query as any)?.value as any) as string).toUpperCase()
(query as QueryFields).value &&
(((((query as QueryFields)
?.value as unknown) as QueryFields) as unknown) as string).toUpperCase()
}
onChange={(e): void => {
onUpdate({ ...query, value: e }, queryIndex);

View File

@ -173,6 +173,13 @@ function SearchFilter({
globalTime.minTime,
]);
const onPopOverChange = useCallback(
(isVisible: boolean) => {
onDropDownToggleHandler(isVisible)();
},
[onDropDownToggleHandler],
);
return (
<Container>
<Popover
@ -189,11 +196,9 @@ function SearchFilter({
overlayInnerStyle={{
width: `${searchRef?.current?.input?.offsetWidth || 0}px`,
}}
visible={showDropDown}
open={showDropDown}
destroyTooltipOnHide
onVisibleChange={(value): void => {
onDropDownToggleHandler(value)();
}}
onOpenChange={onPopOverChange}
>
<Input.Search
ref={searchRef}

View File

@ -64,7 +64,7 @@ export function useSearchParser(): {
},
// need to hide this warning as we don't want to update the query string on every change
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch, parsedQuery],
[dispatch, parsedQuery, selectedTime],
);
useEffect(() => {

View File

@ -1,32 +0,0 @@
import { convertMetricKeyToTrace } from 'lib/resourceAttributes';
import React from 'react';
import { QueryChipContainer, QueryChipItem } from './styles';
import { IResourceAttributeQuery } from './types';
interface IQueryChipProps {
queryData: IResourceAttributeQuery;
onClose: (id: string) => void;
disabled: boolean;
}
export default function QueryChip({
queryData,
onClose,
disabled,
}: IQueryChipProps): JSX.Element {
return (
<QueryChipContainer>
<QueryChipItem>{convertMetricKeyToTrace(queryData.tagKey)}</QueryChipItem>
<QueryChipItem>{queryData.operator}</QueryChipItem>
<QueryChipItem
closable={!disabled}
onClose={(): void => {
if (!disabled) onClose(queryData.id);
}}
>
{queryData.tagValue.join(', ')}
</QueryChipItem>
</QueryChipContainer>
);
}

View File

@ -1,61 +0,0 @@
import { createMachine } from 'xstate';
export const ResourceAttributesFilterMachine =
/** @xstate-layout N4IgpgJg5mDOIC5QBECGsAWAjA9qgThAAQDKYBAxhkQIIB2xAYgJYA2ALmPgHQAqqUANJgAngGIAcgFEAGr0SgADjljN2zHHQUgAHogAcAFgAM3AOz6ATAEYAzJdsA2Y4cOWAnABoQIxAFpDR2tuQ319AFYTcKdbFycAX3jvNExcAmIySmp6JjZOHn4hUTFNACFWAFd8bWVVdU1tPQQzY1MXY2tDdzNHM3dHd0NvXwR7biMTa313S0i+63DE5PRsPEJScnwqWgYiFg4uPgFhcQAlKRIpeSQQWrUNLRumx3Czbg8TR0sbS31jfUcw38fW47gBHmm4XCVms3SWIBSq3SGyyO1yBx4AHlFFxUOwcPhJLJrkoVPcGk9ENYFuF3i5YR0wtEHECEAEgiEmV8zH1DLYzHZ4Yi0utMltsrt9vluNjcfjCWVKtUbnd6o9QE1rMYBtxbGFvsZ3NrZj1WdYOfotUZLX0XEFHEKViKMpttjk9nlDrL8HiCWJzpcSbcyWrGoh3NCQj0zK53P1ph1WeFLLqnJZ2s5vmZLA6kginWsXaj3VLDoUAGqoSpgEp0cpVGohh5hhDWDy0sz8zruakzamWVm-Qyg362V5-AZOayO1KFlHitEejFHKCV6v+i5XRt1ZuU1s52zjNOOaZfdOWIY+RDZ0Hc6ZmKEXqyLPPCudit2Sz08ACSEFYNbSHI27kuquiIOEjiONwjJgrM3RWJYZisgEIJgnYPTmuEdi2OaiR5nQOAQHA2hvsiH4Sui0qFCcIGhnuLSmP0YJuJ2xjJsmKELG8XZTK0tjdHG06vgW5GupRS7St6vrKqSO4UhqVL8TBWp8o4eqdl0A5Xmy3G6gK56-B4uERDOSKiuJi6lgUAhrhUYB0buimtrEKZBDYrxaS0OZca8+ltheybOI4hivGZzrzp+VGHH+AGOQp4EIHy+ghNYnawtG4TsbYvk8QKfHGAJfQ9uF76WSW37xWBTSGJ0qXpd0vRZdEKGPqC2YeO2-zfO4+HxEAA */
createMachine({
tsTypes: {} as import('./ResourceAttributesFilter.Machine.typegen').Typegen0,
initial: 'Idle',
states: {
TagKey: {
on: {
NEXT: {
actions: 'onSelectOperator',
target: 'Operator',
},
onBlur: {
actions: 'onBlurPurge',
target: 'Idle',
},
RESET: {
target: 'Idle',
},
},
},
Operator: {
on: {
NEXT: {
actions: 'onSelectTagValue',
target: 'TagValue',
},
onBlur: {
actions: 'onBlurPurge',
target: 'Idle',
},
RESET: {
target: 'Idle',
},
},
},
TagValue: {
on: {
onBlur: {
actions: ['onValidateQuery', 'onBlurPurge'],
target: 'Idle',
},
RESET: {
target: 'Idle',
},
},
},
Idle: {
on: {
NEXT: {
actions: 'onSelectTagKey',
description: 'Select Category',
target: 'TagKey',
},
},
},
},
id: 'Dashboard Search And Filter',
});

View File

@ -1,219 +0,0 @@
import { CloseCircleFilled } from '@ant-design/icons';
import { useMachine } from '@xstate/react';
import { Button, Select, Spin } from 'antd';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import { convertMetricKeyToTrace } from 'lib/resourceAttributes';
import { map } from 'lodash-es';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ResetInitialData } from 'store/actions/metrics/resetInitialData';
import { SetResourceAttributeQueries } from 'store/actions/metrics/setResourceAttributeQueries';
import { AppState } from 'store/reducers';
import MetricReducer from 'types/reducer/metrics';
import { v4 as uuid } from 'uuid';
import QueryChip from './QueryChip';
import { ResourceAttributesFilterMachine } from './ResourceAttributesFilter.Machine';
import { QueryChipItem, SearchContainer } from './styles';
import { IOption, IResourceAttributeQuery } from './types';
import { createQuery, GetTagKeys, GetTagValues, OperatorSchema } from './utils';
function ResourceAttributesFilter(): JSX.Element | null {
const dispatch = useDispatch();
const [disabled, setDisabled] = useState(
!(history.location.pathname === ROUTES.APPLICATION),
);
useEffect(() => {
const unListen = history.listen(({ pathname }) => {
setDisabled(!(pathname === ROUTES.APPLICATION));
});
return (): void => {
if (!history.location.pathname.startsWith(`${ROUTES.APPLICATION}/`)) {
dispatch(ResetInitialData());
}
unListen();
};
}, [dispatch]);
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
(state) => state.metrics,
);
const [loading, setLoading] = useState(true);
const [selectedValues, setSelectedValues] = useState<string[]>([]);
const [staging, setStaging] = useState<string[]>([]);
const [queries, setQueries] = useState<IResourceAttributeQuery[]>([]);
const [optionsData, setOptionsData] = useState<{
mode: undefined | 'tags' | 'multiple';
options: IOption[];
}>({
mode: undefined,
options: [],
});
const dispatchQueries = (updatedQueries: IResourceAttributeQuery[]): void => {
dispatch(SetResourceAttributeQueries(updatedQueries));
};
const handleLoading = (isLoading: boolean): void => {
setLoading(isLoading);
if (isLoading) {
setOptionsData({ mode: undefined, options: [] });
}
};
const [state, send] = useMachine(ResourceAttributesFilterMachine, {
actions: {
onSelectTagKey: () => {
handleLoading(true);
GetTagKeys()
.then((tagKeys) => setOptionsData({ options: tagKeys, mode: undefined }))
.finally(() => {
handleLoading(false);
});
},
onSelectOperator: () => {
setOptionsData({ options: OperatorSchema, mode: undefined });
},
onSelectTagValue: () => {
handleLoading(true);
GetTagValues(staging[0])
.then((tagValuesOptions) =>
setOptionsData({ options: tagValuesOptions, mode: 'multiple' }),
)
.finally(() => {
handleLoading(false);
});
},
onBlurPurge: () => {
setSelectedValues([]);
setStaging([]);
},
onValidateQuery: (): void => {
if (staging.length < 2 || selectedValues.length === 0) {
return;
}
const generatedQuery = createQuery([...staging, selectedValues]);
if (generatedQuery) {
dispatchQueries([...queries, generatedQuery]);
}
},
},
});
useEffect(() => {
setQueries(resourceAttributeQueries);
}, [resourceAttributeQueries]);
const handleFocus = (): void => {
if (state.value === 'Idle') {
send('NEXT');
}
};
const handleBlur = (): void => {
send('onBlur');
};
const handleChange = (value: never): void => {
if (!optionsData.mode) {
setStaging((prevStaging) => [...prevStaging, value]);
setSelectedValues([]);
send('NEXT');
return;
}
setSelectedValues([...value]);
};
const handleClose = (id: string): void => {
dispatchQueries(queries.filter((queryData) => queryData.id !== id));
};
const handleClearAll = (): void => {
send('RESET');
dispatchQueries([]);
setStaging([]);
setSelectedValues([]);
};
const disabledAndEmpty = !!(
!queries.length &&
!staging.length &&
!selectedValues.length &&
disabled
);
const disabledOrEmpty = !!(
queries.length ||
staging.length ||
selectedValues.length ||
disabled
);
if (disabledAndEmpty) {
return null;
}
return (
<SearchContainer disabled={disabled}>
<div
style={{
maxWidth: disabled ? '100%' : '70%',
display: 'flex',
overflowX: 'auto',
}}
>
{map(
queries,
(query): JSX.Element => (
<QueryChip
disabled={disabled}
key={query.id}
queryData={query}
onClose={handleClose}
/>
),
)}
{map(staging, (item, idx) => (
<QueryChipItem key={uuid()}>
{idx === 0 ? convertMetricKeyToTrace(item) : item}
</QueryChipItem>
))}
</div>
{!disabled && (
<Select
placeholder={
disabledOrEmpty ? '' : 'Search and Filter based on resource attributes.'
}
disabled={disabled}
onChange={handleChange}
bordered={false}
value={selectedValues as never}
style={{ flex: 1 }}
options={optionsData.options}
mode={optionsData?.mode}
showArrow={false}
onFocus={handleFocus}
onBlur={handleBlur}
notFoundContent={
loading ? (
<span>
<Spin size="small" /> Loading...{' '}
</span>
) : (
<span>
No resource attributes available to filter. Please refer docs to send
attributes.
</span>
)
}
/>
)}
{(queries.length || staging.length || selectedValues.length) && !disabled ? (
<Button onClick={handleClearAll} icon={<CloseCircleFilled />} type="text" />
) : null}
</SearchContainer>
);
}
export default ResourceAttributesFilter;

View File

@ -1,11 +0,0 @@
export interface IOption {
label: string;
value: string;
}
export interface IResourceAttributeQuery {
id: string;
tagKey: string;
operator: string;
tagValue: string[];
}

View File

@ -1,64 +0,0 @@
import {
getResourceAttributesTagKeys,
getResourceAttributesTagValues,
} from 'api/metrics/getResourceAttributes';
import { OperatorConversions } from 'constants/resourceAttributes';
import { convertMetricKeyToTrace } from 'lib/resourceAttributes';
import { v4 as uuid } from 'uuid';
import { IOption, IResourceAttributeQuery } from './types';
export const OperatorSchema: IOption[] = OperatorConversions.map(
(operator) => ({
label: operator.label,
value: operator.label,
}),
);
export const GetTagKeys = async (): Promise<IOption[]> => {
// if (TagKeysCache) {
// return new Promise((resolve) => {
// resolve(TagKeysCache);
// });
// }
const { payload } = await getResourceAttributesTagKeys({
metricName: 'signoz_calls_total',
match: 'resource_',
});
if (!payload || !payload?.data) {
return [];
}
return payload.data.map((tagKey: string) => ({
label: convertMetricKeyToTrace(tagKey),
value: tagKey,
}));
};
export const GetTagValues = async (tagKey: string): Promise<IOption[]> => {
const { payload } = await getResourceAttributesTagValues({
tagKey,
metricName: 'signoz_calls_total',
});
if (!payload || !payload?.data) {
return [];
}
return payload.data.map((tagValue: string) => ({
label: tagValue,
value: tagValue,
}));
};
export const createQuery = (
selectedItems: Array<string | string[]> = [],
): IResourceAttributeQuery | null => {
if (selectedItems.length === 3) {
return {
id: uuid().slice(0, 8),
tagKey: selectedItems[0] as string,
operator: selectedItems[1] as string,
tagValue: selectedItems[2] as string[],
};
}
return null;
};

View File

@ -4,21 +4,20 @@ import {
databaseCallsAvgDuration,
databaseCallsRPS,
} from 'container/MetricsApplication/MetricsPageQueries/DBCallQueries';
import useResourceAttribute from 'hooks/useResourceAttribute';
import {
convertRawQueriesToTraceSelectedTags,
resourceAttributesToTagFilterItems,
} from 'lib/resourceAttributes';
} from 'hooks/useResourceAttribute/utils';
import React, { useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { AppState } from 'store/reducers';
import { Widgets } from 'types/api/dashboard/getAll';
import MetricReducer from 'types/reducer/metrics';
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
import { Button } from './styles';
import {
dbSystemTags,
handleNonInQueryRange,
onGraphClickHandler,
onViewTracePopupClick,
} from './util';
@ -26,22 +25,22 @@ import {
function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element {
const { servicename } = useParams<{ servicename?: string }>();
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
(state) => state.metrics,
);
const { queries } = useResourceAttribute();
const tagFilterItems = useMemo(
() => resourceAttributesToTagFilterItems(resourceAttributeQueries) || [],
[resourceAttributeQueries],
() =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
[queries],
);
const selectedTraceTags: string = useMemo(
() =>
JSON.stringify(
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries).concat(
...dbSystemTags,
) || [],
convertRawQueriesToTraceSelectedTags(queries).concat(...dbSystemTags) || [],
),
[resourceAttributeQueries],
[queries],
);
const legend = '{{db_system}}';
const databaseCallsRPSWidget = useMemo(

View File

@ -6,33 +6,34 @@ import {
externalCallErrorPercent,
externalCallRpsByAddress,
} from 'container/MetricsApplication/MetricsPageQueries/ExternalQueries';
import useResourceAttribute from 'hooks/useResourceAttribute';
import {
convertRawQueriesToTraceSelectedTags,
resourceAttributesToTagFilterItems,
} from 'lib/resourceAttributes';
} from 'hooks/useResourceAttribute/utils';
import React, { useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { AppState } from 'store/reducers';
import { Widgets } from 'types/api/dashboard/getAll';
import MetricReducer from 'types/reducer/metrics';
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
import { legend } from './constant';
import { Button } from './styles';
import { onGraphClickHandler, onViewTracePopupClick } from './util';
import {
handleNonInQueryRange,
onGraphClickHandler,
onViewTracePopupClick,
} from './util';
function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
const { servicename } = useParams<{ servicename?: string }>();
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
(state) => state.metrics,
);
const { queries } = useResourceAttribute();
const tagFilterItems = useMemo(
() => resourceAttributesToTagFilterItems(resourceAttributeQueries) || [],
[resourceAttributeQueries],
() =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
[queries],
);
const externalCallErrorWidget = useMemo(
@ -51,11 +52,8 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
);
const selectedTraceTags = useMemo(
() =>
JSON.stringify(
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) || [],
),
[resourceAttributeQueries],
() => JSON.stringify(convertRawQueriesToTraceSelectedTags(queries) || []),
[queries],
);
const externalCallDurationWidget = useMemo(

View File

@ -3,13 +3,14 @@ import Graph from 'components/Graph';
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
import ROUTES from 'constants/routes';
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
import { colors } from 'lib/getRandomColor';
import history from 'lib/history';
import useResourceAttribute from 'hooks/useResourceAttribute';
import {
convertRawQueriesToTraceSelectedTags,
resourceAttributesToTagFilterItems,
} from 'lib/resourceAttributes';
} from 'hooks/useResourceAttribute/utils';
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
import { colors } from 'lib/getRandomColor';
import history from 'lib/history';
import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
@ -25,7 +26,11 @@ import {
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
import TopOperationsTable from '../TopOperationsTable';
import { Button } from './styles';
import { onGraphClickHandler, onViewTracePopupClick } from './util';
import {
handleNonInQueryRange,
onGraphClickHandler,
onViewTracePopupClick,
} from './util';
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
const { servicename } = useParams<{ servicename?: string }>();
@ -54,20 +59,21 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
[handleSetTimeStamp],
);
const {
topOperations,
serviceOverview,
resourceAttributeQueries,
topLevelOperations,
} = useSelector<AppState, MetricReducer>((state) => state.metrics);
const { topOperations, serviceOverview, topLevelOperations } = useSelector<
AppState,
MetricReducer
>((state) => state.metrics);
const { queries } = useResourceAttribute();
const selectedTraceTags: string = JSON.stringify(
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) || [],
convertRawQueriesToTraceSelectedTags(queries) || [],
);
const tagFilterItems = useMemo(
() => resourceAttributesToTagFilterItems(resourceAttributeQueries) || [],
[resourceAttributeQueries],
() =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
[queries],
);
const operationPerSecWidget = useMemo(

View File

@ -2,6 +2,7 @@ import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
import { Tags } from 'types/reducer/trace';
export const dbSystemTags: Tags[] = [
@ -60,7 +61,7 @@ export function onGraphClickHandler(
const points = chart.getElementsAtEventForMode(
event.native,
'nearest',
{ intersect: false },
{ intersect: true },
true,
);
const id = `${from}_button`;
@ -84,3 +85,16 @@ export function onGraphClickHandler(
}
};
}
export const handleNonInQueryRange = (
tags: IQueryBuilderTagFilterItems[],
): IQueryBuilderTagFilterItems[] =>
tags.map((tag) => {
if (tag.op === 'Not IN') {
return {
...tag,
op: 'NIN',
};
}
return tag;
});

View File

@ -3,25 +3,23 @@ import { ColumnsType } from 'antd/lib/table';
import { ResizeTable } from 'components/ResizeTable';
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
import ROUTES from 'constants/routes';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import history from 'lib/history';
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
import React from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
import MetricReducer from 'types/reducer/metrics';
function TopOperationsTable(props: TopOperationsTableProps): JSX.Element {
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
(state) => state.metrics,
);
const { queries } = useResourceAttribute();
const selectedTraceTags: string = JSON.stringify(
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) || [],
convertRawQueriesToTraceSelectedTags(queries) || [],
);
const { data } = props;

View File

@ -1,11 +1,11 @@
import RouteTab from 'components/RouteTab';
import ROUTES from 'constants/routes';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import React, { memo } from 'react';
import { generatePath, useParams } from 'react-router-dom';
import { useLocation } from 'react-use';
import { getWidgetQueryBuilder } from './MetricsApplication.factory';
import ResourceAttributesFilter from './ResourceAttributesFilter';
import DBCall from './Tabs/DBCall';
import External from './Tabs/External';
import Overview from './Tabs/Overview';

View File

@ -4,19 +4,13 @@ import React from 'react';
import GeneralDashboardSettings from './General';
import VariablesSetting from './Variables';
const { TabPane } = Tabs;
function DashboardSettingsContent(): JSX.Element {
return (
<Tabs>
<TabPane tab="General" key="general">
<GeneralDashboardSettings />
</TabPane>
<TabPane tab="Variables" key="variables">
<VariablesSetting />
</TabPane>
</Tabs>
);
const items = [
{ label: 'General', key: 'general', children: <GeneralDashboardSettings /> },
{ label: 'Variables', key: 'variables', children: <VariablesSetting /> },
];
return <Tabs items={items} />;
}
export default DashboardSettingsContent;

View File

@ -2,31 +2,31 @@
export interface Typegen0 {
'@@xstate/typegen': true;
eventsCausingActions: {
onSelectOperator: 'NEXT';
onBlurPurge: 'onBlur';
onSelectTagValue: 'NEXT';
onValidateQuery: 'onBlur';
onSelectTagKey: 'NEXT';
};
internalEvents: {
'xstate.init': { type: 'xstate.init' };
};
invokeSrcNameMap: {};
missingImplementations: {
actions:
| 'onSelectOperator'
| 'onBlurPurge'
| 'onSelectOperator'
| 'onSelectTagKey'
| 'onSelectTagValue'
| 'onValidateQuery'
| 'onSelectTagKey';
services: never;
guards: never;
| 'onValidateQuery';
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
onBlurPurge: 'onBlur';
onSelectOperator: 'NEXT';
onSelectTagKey: 'NEXT';
onSelectTagValue: 'NEXT';
onValidateQuery: 'onBlur';
};
eventsCausingServices: {};
eventsCausingGuards: {};
eventsCausingDelays: {};
matchesStates: 'TagKey' | 'Operator' | 'TagValue' | 'Idle';
eventsCausingGuards: {};
eventsCausingServices: {};
matchesStates: 'Idle' | 'Operator' | 'TagKey' | 'TagValue';
tags: never;
}

View File

@ -68,6 +68,8 @@ function MetricsBuilder({
});
}, [metricName]);
// TODO: rewrite to Form component from antd
return (
<QueryHeader
name={queryData.name}

View File

@ -34,7 +34,6 @@ import TabHeader from './TabHeader';
import { getQueryKey } from './utils/getQueryKey';
import { showUnstagedStashConfirmBox } from './utils/userSettings';
const { TabPane } = Tabs;
function QuerySection({
handleUnstagedChanges,
updateQuery,
@ -144,6 +143,80 @@ function QuerySection({
setLocalQueryChanges(cloneDeep(updatedQuery));
};
const items = [
{
key: EQueryType.QUERY_BUILDER.toString(),
label: 'Query Builder',
tab: (
<TabHeader
tabName="Query Builder"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.QUERY_BUILDER,
)}
/>
),
children: (
<QueryBuilderQueryContainer
key={rctTabKey.QUERY_BUILDER}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
metricsBuilderQueries={
localQueryChanges[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME]
}
selectedGraph={selectedGraph}
/>
),
},
{
key: EQueryType.CLICKHOUSE.toString(),
label: 'ClickHouse Query',
tab: (
<TabHeader
tabName="ClickHouse Query"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.CLICKHOUSE,
)}
/>
),
children: (
<ClickHouseQueryContainer
key={rctTabKey.CLICKHOUSE}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
clickHouseQueries={localQueryChanges[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]}
/>
),
},
{
key: EQueryType.PROM.toString(),
label: 'PromQL',
tab: (
<TabHeader
tabName="PromQL"
hasUnstagedChanges={queryDiff(query, localQueryChanges, EQueryType.PROM)}
/>
),
children: (
<PromQLQueryContainer
key={rctTabKey.PROM}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
promQLQueries={localQueryChanges[WIDGET_PROMQL_QUERY_KEY_NAME]}
/>
),
},
];
return (
<>
<div style={{ display: 'flex' }}>
@ -165,77 +238,8 @@ function QuerySection({
</Button>
</span>
}
>
<TabPane
tab={
<TabHeader
tabName="Query Builder"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.QUERY_BUILDER,
)}
/>
}
key={EQueryType.QUERY_BUILDER.toString()}
>
<QueryBuilderQueryContainer
key={rctTabKey.QUERY_BUILDER}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
metricsBuilderQueries={
localQueryChanges[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME]
}
selectedGraph={selectedGraph}
/>
</TabPane>
<TabPane
tab={
<TabHeader
tabName="ClickHouse Query"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.CLICKHOUSE,
)}
/>
}
key={EQueryType.CLICKHOUSE.toString()}
>
<ClickHouseQueryContainer
key={rctTabKey.CLICKHOUSE}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
clickHouseQueries={localQueryChanges[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]}
/>
</TabPane>
<TabPane
tab={
<TabHeader
tabName="PromQL"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.PROM,
)}
/>
}
key={EQueryType.PROM.toString()}
>
<PromQLQueryContainer
key={rctTabKey.PROM}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
promQLQueries={localQueryChanges[WIDGET_PROMQL_QUERY_KEY_NAME]}
/>
</TabPane>
</Tabs>
items={items}
/>
</div>
{/* {localQueryChanges.map((e, index) => (
// <Query

View File

@ -0,0 +1,9 @@
import {
IBuilderFormula,
IBuilderQuery,
} from 'types/api/queryBuilder/queryBuilderData';
export type QueryBuilderProps = {
queryData: IBuilderQuery[];
queryFormula: IBuilderFormula[];
};

View File

@ -0,0 +1,23 @@
// ** Hooks
import { useQueryBuilder } from 'hooks/useQueryBuilder';
import React from 'react';
// ** Types
import { QueryBuilderProps } from './QueryBuilder.interfaces';
// TODO: temporary eslint disable while variable isn't used
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function QueryBuilder(props: QueryBuilderProps): JSX.Element {
// TODO: temporary doesn't use
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { queryBuilderData } = useQueryBuilder();
// Here we can use Form from antd library and fill context data or edit
// Connect form with adding or removing items from the list
// Here will be map of query queryBuilderData.queryData and queryBuilderData.queryFormulas components
// Each component can be part of antd Form list where we can add or remove items
// Also need decide to make a copy of queryData for working with form or not and after it set the full new list with formulas or queries to the context
// With button to add him
return <div>null</div>;
}

View File

@ -0,0 +1,8 @@
export type ListMarkerProps = {
isDisabled: boolean;
labelName: string;
index: number;
className?: string;
isAvailableToDisable: boolean;
toggleDisabled: (index: number) => void;
};

View File

@ -0,0 +1,9 @@
import { Button } from 'antd';
import styled from 'styled-components';
export const StyledButton = styled(Button)`
min-width: 2rem;
height: 2.25rem;
padding: 0.125rem;
border-radius: 0.375rem;
`;

View File

@ -0,0 +1,36 @@
import { EyeFilled, EyeInvisibleFilled } from '@ant-design/icons';
import { ButtonProps } from 'antd';
import React from 'react';
// ** Types
import { ListMarkerProps } from './ListMarker.interfaces';
// ** Styles
import { StyledButton } from './ListMarker.styled';
export function ListMarker({
isDisabled,
labelName,
index,
isAvailableToDisable,
className,
toggleDisabled,
}: ListMarkerProps): JSX.Element {
const buttonProps: Partial<ButtonProps> = isAvailableToDisable
? {
type: isDisabled ? 'default' : 'primary',
icon: isDisabled ? <EyeInvisibleFilled /> : <EyeFilled />,
onClick: (): void => toggleDisabled(index),
}
: { type: 'primary' };
return (
<StyledButton
type={buttonProps.type}
icon={buttonProps.icon}
onClick={buttonProps.onClick}
className={className}
>
{labelName}
</StyledButton>
);
}

View File

@ -0,0 +1 @@
export { ListMarker } from './ListMarker';

View File

@ -0,0 +1,17 @@
import { SelectProps } from 'antd';
import { DataSource } from 'types/common/queryBuilder';
type StaticLabel = { variant: 'static'; dataSource: DataSource };
export type DropdownLabel = {
variant: 'dropdown';
onChange: (value: DataSource) => void;
} & Omit<SelectProps, 'onChange'>;
export type QueryLabelProps = StaticLabel | DropdownLabel;
export function isLabelDropdown(
label: QueryLabelProps,
): label is DropdownLabel {
return label.variant === 'dropdown';
}

View File

@ -0,0 +1,19 @@
import { Select } from 'antd';
import styled, { css } from 'styled-components';
// ** Types
import { DropdownLabel } from './QueryLabel.interfaces';
const LabelStyle = css`
width: fit-content;
min-width: 5.75rem;
`;
export const StyledSingleLabel = styled(Select)`
pointer-events: none;
${LabelStyle}
`;
export const StyledDropdownLabel = styled(Select)<DropdownLabel>`
${LabelStyle}
`;

View File

@ -0,0 +1,49 @@
import { Select } from 'antd';
import React from 'react';
import { DataSource } from 'types/common/queryBuilder';
import { SelectOption } from 'types/common/select';
// ** Types
import { isLabelDropdown, QueryLabelProps } from './QueryLabel.interfaces';
// ** Styles
import { StyledSingleLabel } from './QueryLabel.styled';
const { Option } = Select;
const dataSourceMap = [DataSource.LOGS, DataSource.METRICS, DataSource.TRACES];
export function QueryLabel(props: QueryLabelProps): JSX.Element {
const isDropdown = isLabelDropdown(props);
if (!isDropdown) {
const { dataSource } = props;
return (
<StyledSingleLabel
defaultValue={dataSource}
showArrow={false}
dropdownStyle={{ display: 'none' }}
>
<Option value={dataSource}>{dataSource}</Option>
</StyledSingleLabel>
);
}
const { onChange } = props;
const dataSourceOptions: SelectOption<
DataSource,
string
>[] = dataSourceMap.map((source) => ({
label: source.charAt(0).toUpperCase() + source.slice(1),
value: source,
}));
return (
<Select
defaultValue={dataSourceOptions[0].value}
options={dataSourceOptions}
onChange={onChange}
/>
);
}

View File

@ -0,0 +1 @@
export { QueryLabel } from './QueryLabel';

View File

@ -0,0 +1,2 @@
export { ListMarker } from './ListMarker';
export { QueryLabel } from './QueryLabel';

View File

@ -0,0 +1 @@
export { QueryBuilder } from './QueryBuilder';

View File

@ -0,0 +1,88 @@
import { CloseCircleFilled } from '@ant-design/icons';
import { Button, Select, Spin } from 'antd';
import useResourceAttribute, {
isResourceEmpty,
} from 'hooks/useResourceAttribute';
import { convertMetricKeyToTrace } from 'hooks/useResourceAttribute/utils';
import React, { useMemo } from 'react';
import { v4 as uuid } from 'uuid';
import QueryChip from './components/QueryChip';
import { QueryChipItem, SearchContainer } from './styles';
function ResourceAttributesFilter({
suffixIcon,
}: ResourceAttributesFilterProps): JSX.Element | null {
const {
queries,
staging,
handleClose,
handleBlur,
handleClearAll,
handleFocus,
handleChange,
selectedQuery,
optionsData,
loading,
} = useResourceAttribute();
const isEmpty = useMemo(
() => isResourceEmpty(queries, staging, selectedQuery),
[queries, selectedQuery, staging],
);
return (
<SearchContainer>
<div>
{queries.map((query) => (
<QueryChip key={query.id} queryData={query} onClose={handleClose} />
))}
{staging.map((query, idx) => (
<QueryChipItem key={uuid()}>
{idx === 0 ? convertMetricKeyToTrace(query) : query}
</QueryChipItem>
))}
</div>
<Select
placeholder={!isEmpty && 'Search and Filter based on resource attributes.'}
onChange={handleChange}
bordered={false}
value={selectedQuery as never}
style={{ flex: 1 }}
options={optionsData.options}
mode={optionsData?.mode}
showArrow={!!suffixIcon}
onClick={handleFocus}
onBlur={handleBlur}
onClear={handleClearAll}
suffixIcon={suffixIcon}
notFoundContent={
loading ? (
<span>
<Spin size="small" /> Loading...
</span>
) : (
<span>
No resource attributes available to filter. Please refer docs to send
attributes.
</span>
)
}
/>
{queries.length || staging.length || selectedQuery.length ? (
<Button onClick={handleClearAll} icon={<CloseCircleFilled />} type="text" />
) : null}
</SearchContainer>
);
}
interface ResourceAttributesFilterProps {
suffixIcon?: React.ReactNode;
}
ResourceAttributesFilter.defaultProps = {
suffixIcon: undefined,
};
export default ResourceAttributesFilter;

View File

@ -0,0 +1,23 @@
import { convertMetricKeyToTrace } from 'hooks/useResourceAttribute/utils';
import React from 'react';
import { QueryChipContainer, QueryChipItem } from '../../styles';
import { IQueryChipProps } from './types';
function QueryChip({ queryData, onClose }: IQueryChipProps): JSX.Element {
const onCloseHandler = (): void => {
onClose(queryData.id);
};
return (
<QueryChipContainer>
<QueryChipItem>{convertMetricKeyToTrace(queryData.tagKey)}</QueryChipItem>
<QueryChipItem>{queryData.operator}</QueryChipItem>
<QueryChipItem closable onClose={onCloseHandler}>
{queryData.tagValue.join(', ')}
</QueryChipItem>
</QueryChipContainer>
);
}
export default QueryChip;

View File

@ -0,0 +1,3 @@
import QueryChip from './QueryChip';
export default QueryChip;

View File

@ -0,0 +1,6 @@
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
export interface IQueryChipProps {
queryData: IResourceAttribute;
onClose: (id: string) => void;
}

View File

@ -0,0 +1,3 @@
import ResourceAttributesFilter from './ResourceAttributesFilter';
export default ResourceAttributesFilter;

View File

@ -2,9 +2,7 @@ import { grey } from '@ant-design/colors';
import { Tag } from 'antd';
import styled from 'styled-components';
export const SearchContainer = styled.div<{
disabled: boolean;
}>`
export const SearchContainer = styled.div`
width: 100%;
display: flex;
align-items: center;
@ -12,8 +10,8 @@ export const SearchContainer = styled.div<{
padding: 0.2rem;
margin: 1rem 0;
border: 1px solid #ccc5;
${({ disabled }): string => (disabled ? `cursor: not-allowed;` : '')}
`;
export const QueryChipContainer = styled.span`
display: flex;
align-items: center;

View File

@ -29,6 +29,7 @@ function SideNav(): JSX.Element {
const [collapsed, setCollapsed] = useState<boolean>(
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
);
const { search } = useLocation();
const { currentVersion, latestVersion, isCurrentVersionError } = useSelector<
AppState,
AppReducer
@ -47,11 +48,15 @@ function SideNav(): JSX.Element {
const onClickHandler = useCallback(
(to: string) => {
const queryParams = new URLSearchParams(search);
const url = queryParams.toString();
if (pathname !== to) {
history.push(to);
history.push(`${to}?${url}`);
}
},
[pathname],
[pathname, search],
);
const onClickSlackHandler = (): void => {
@ -98,7 +103,7 @@ function SideNav(): JSX.Element {
);
const items = [
...menus.map(({ to, Icon, name, tags }) => ({
...menus.map(({ to, Icon, name, tags, children }) => ({
key: to,
icon: <Icon />,
onClick: (): void => onClickHandler(to),
@ -113,6 +118,7 @@ function SideNav(): JSX.Element {
))}
</Space>
),
children,
})),
];
@ -129,7 +135,7 @@ function SideNav(): JSX.Element {
theme="dark"
defaultSelectedKeys={[ROUTES.APPLICATION]}
selectedKeys={currentMenu ? [currentMenu?.to] : []}
mode="inline"
mode="vertical"
style={styles}
items={items}
/>

View File

@ -10,6 +10,7 @@ import {
MenuOutlined,
SettingOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import ROUTES from 'constants/routes';
const menus: SidebarMenu[] = [
@ -28,6 +29,12 @@ const menus: SidebarMenu[] = [
to: ROUTES.LOGS,
name: 'Logs',
// tags: ['Beta'],
// children: [
// {
// key: ROUTES.LOGS,
// label: 'Search',
// },
// ],
},
{
Icon: DashboardFilled,
@ -71,6 +78,7 @@ interface SidebarMenu {
name: string;
Icon: typeof ApiOutlined;
tags?: string[];
children?: Required<MenuProps>['items'][number][];
}
export default menus;

View File

@ -210,7 +210,7 @@ function Duration(): JSX.Element {
min={Number(getMs(String(preLocalMinDuration.current || 0)))}
max={Number(getMs(String(preLocalMaxDuration.current || 0)))}
range
tipFormatter={TipComponent}
tooltip={{ formatter: TipComponent }}
onChange={([min, max]): void => {
onRangeSliderHandler([String(min), String(max)]);
}}

View File

@ -15,8 +15,6 @@ import {
} from './styles';
import Tags from './Tags';
const { TabPane } = Tabs;
function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
const { tree, firstSpanStartTime } = props;
@ -44,6 +42,33 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
const { tags, nonChildReferences } = tree;
const items = [
{
label: 'Tags',
key: '1',
children: (
<Tags
onToggleHandler={onToggleHandler}
setText={setText}
tags={tags}
linkedSpans={nonChildReferences}
/>
),
},
{
label: 'Events',
key: '2',
children: (
<Events
events={tree.event}
onToggleHandler={onToggleHandler}
setText={setText}
firstSpanStartTime={firstSpanStartTime}
/>
),
},
];
return (
<CardContainer>
<StyledSpace
@ -81,24 +106,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
)}
</Modal>
<Tabs defaultActiveKey="1">
<TabPane tab="Tags" key="1">
<Tags
onToggleHandler={onToggleHandler}
setText={setText}
tags={tags}
linkedSpans={nonChildReferences}
/>
</TabPane>
<TabPane tab="Events" key="2">
<Events
events={tree.event}
onToggleHandler={onToggleHandler}
setText={setText}
firstSpanStartTime={firstSpanStartTime}
/>
</TabPane>
</Tabs>
<Tabs defaultActiveKey="1" items={items} />
</CardContainer>
);
}

View File

@ -0,0 +1,9 @@
import {
QueryBuilderContext,
QueryBuilderContextType,
} from 'providers/QueryBuilder';
import { useContext } from 'react';
export function useQueryBuilder(): QueryBuilderContextType {
return useContext(QueryBuilderContext);
}

View File

@ -0,0 +1,188 @@
import { useMachine } from '@xstate/react';
import ROUTES from 'constants/routes';
import { encode } from 'js-base64';
import history from 'lib/history';
import React, { useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { whilelistedKeys } from './config';
import { ResourceContext } from './context';
import { ResourceAttributesFilterMachine } from './machine';
import {
IResourceAttribute,
IResourceAttributeProps,
OptionsData,
} from './types';
import {
createQuery,
getResourceAttributeQueriesFromURL,
GetTagKeys,
GetTagValues,
mappingWithRoutesAndKeys,
OperatorSchema,
} from './utils';
function ResourceProvider({ children }: Props): JSX.Element {
const { pathname } = useLocation();
const [loading, setLoading] = useState(true);
const [selectedQuery, setSelectedQueries] = useState<string[]>([]);
const [staging, setStaging] = useState<string[]>([]);
const [queries, setQueries] = useState<IResourceAttribute[]>(
getResourceAttributeQueriesFromURL(),
);
const [optionsData, setOptionsData] = useState<OptionsData>({
mode: undefined,
options: [],
});
const handleLoading = (isLoading: boolean): void => {
setLoading(isLoading);
if (isLoading) {
setOptionsData({ mode: undefined, options: [] });
}
};
const dispatchQueries = useCallback(
(queries: IResourceAttribute[]): void => {
history.replace({
pathname,
search:
queries && queries.length
? `?resourceAttribute=${encode(JSON.stringify(queries))}`
: '',
});
setQueries(queries);
},
[pathname],
);
const [state, send] = useMachine(ResourceAttributesFilterMachine, {
actions: {
onSelectTagKey: () => {
handleLoading(true);
GetTagKeys()
.then((tagKeys) =>
setOptionsData({
options: mappingWithRoutesAndKeys(pathname, tagKeys),
mode: undefined,
}),
)
.finally(() => {
handleLoading(false);
});
},
onSelectOperator: () => {
setOptionsData({ options: OperatorSchema, mode: undefined });
},
onSelectTagValue: () => {
handleLoading(true);
GetTagValues(staging[0])
.then((tagValuesOptions) =>
setOptionsData({ options: tagValuesOptions, mode: 'multiple' }),
)
.finally(() => {
handleLoading(false);
});
},
onBlurPurge: () => {
setSelectedQueries([]);
setStaging([]);
},
onValidateQuery: (): void => {
if (staging.length < 2 || selectedQuery.length === 0) {
return;
}
const generatedQuery = createQuery([...staging, selectedQuery]);
if (generatedQuery) {
dispatchQueries([...queries, generatedQuery]);
}
},
},
});
const handleFocus = useCallback((): void => {
if (state.value === 'Idle') {
send('NEXT');
}
}, [send, state.value]);
const handleBlur = useCallback((): void => {
send('onBlur');
}, [send]);
const handleChange = useCallback(
(value: string): void => {
if (!optionsData.mode) {
setStaging((prevStaging) => [...prevStaging, value]);
setSelectedQueries([]);
send('NEXT');
return;
}
setSelectedQueries([...value]);
},
[optionsData.mode, send],
);
const handleClose = useCallback(
(id: string): void => {
dispatchQueries(queries.filter((queryData) => queryData.id !== id));
},
[dispatchQueries, queries],
);
const handleClearAll = useCallback(() => {
send('RESET');
dispatchQueries([]);
setStaging([]);
setQueries([]);
setOptionsData({ mode: undefined, options: [] });
}, [dispatchQueries, send]);
const getVisibleQueries = useMemo(() => {
if (pathname === ROUTES.SERVICE_MAP) {
return queries.filter((query) => whilelistedKeys.includes(query.tagKey));
}
return queries;
}, [queries, pathname]);
const value: IResourceAttributeProps = useMemo(
() => ({
queries: getVisibleQueries,
staging,
handleClearAll,
handleClose,
handleBlur,
handleFocus,
loading,
handleChange,
selectedQuery,
optionsData,
}),
[
handleBlur,
handleChange,
handleClearAll,
handleClose,
handleFocus,
loading,
staging,
selectedQuery,
optionsData,
getVisibleQueries,
],
);
return (
<ResourceContext.Provider value={value}>{children}</ResourceContext.Provider>
);
}
interface Props {
children: React.ReactNode;
}
export default ResourceProvider;

View File

@ -0,0 +1,5 @@
export const whilelistedKeys = [
'resource_deployment_environment',
'resource_k8s_cluster_name',
'resource_k8s_cluster_namespace',
];

View File

@ -0,0 +1,7 @@
import { createContext } from 'react';
import { IResourceAttributeProps } from './types';
export const ResourceContext = createContext<IResourceAttributeProps>(
{} as IResourceAttributeProps,
);

View File

@ -0,0 +1,7 @@
import ResourceProvider from './ResourceProvider';
import useResourceAttribute from './useResourceAttribute';
import { convertMetricKeyToTrace, isResourceEmpty } from './utils';
export default useResourceAttribute;
export { convertMetricKeyToTrace, isResourceEmpty, ResourceProvider };

View File

@ -0,0 +1,61 @@
import { createMachine } from 'xstate';
export const ResourceAttributesFilterMachine =
/** @xstate-layout N4IgpgJg5mDOIC5QBECGsAWAjA9qgThAAQDKYBAxhkQIIB2xAYgJYA2ALmPgHQAqqUANJgAngGIAcgFEAGrwDaABgC6iUAAccsZu2Y46akAA9EATkUB2bgEYAbBYsBWWwA5HAFkW3F7gDQgRRABaU3duFwsXAGZbWwAmF3co01jTAF80-zRMXAJiMkpqeiY2Th5+IVExfQAhVgBXfCVVJBBNbV19QxMEcys7B2c3T28-AOC4xUduKItrSbiEuNMo6zcMrPRsPEJScnwqWgYiFg4uPgFhcQAlKRIpBRVDdp09A1aevpt7J1cPLx8-kCCCCcUcURmcwWSxWa0cGxA2W2eT2hSOJTOPAA8uouKh2Dh8JJZI8WhotK8uh9EPM4tYZl4IrZHNY1rZrEDgqFwpEoi43HEnMt3NYEUjcrsCgcisdTmVuDi8QSibUGk0nq0Xp13qAerT6VFGRZmayXOzOSDJtNZrT3I44t5bHaLGKthL8vtDsUTqVzor8PjCWJbvdSc8KdrujTFgajSa2RzxpbwZDbfbHc7XTkdh60d65ecKgA1VANMDVOh1RrNcMdN6GYFBayOKw2xZ2h1eZ3+PX2+mxFzWEWmFymBxRLPIyWemUY+XF0v1cshh41zUR+vUhDNuncAdD6wjscWKIW0FTVPt9NdluT92o6Xon2Y7gASQgrHL0jka-JdapuqIPEcTcIoihxHyTh2Pa-JntyETRO4ngig6yTuBkmQgHQOAQHAhjijmD5erKvr4LWlI6sYiDJIo3Aiieh7Gk4UynkmQRRJ44TARYijJC4AJRBOmEESiUrEXOhaXKI5GRluPG0SkI7uIKhr2vaZ7Nq2cxrGByQWKYpiisJbqEWJs7PvK-qBmR67-pReq6aB1g+DEkEcaYcQaS2l7gTCqzrMZ2aiTOT4FuUAglmWMmboB258hCESmNeLgQR4jheVp8y+SlsIBZsQXTnmJEvu+n7RQBVEIEkLh0dYDFjvYjgsRlqY6bxY4GUZGRAA */
createMachine({
tsTypes: {} as import('./machine.typegen').Typegen0,
initial: 'Idle',
states: {
TagKey: {
on: {
NEXT: {
actions: 'onSelectOperator',
target: 'Operator',
},
onBlur: {
actions: 'onBlurPurge',
target: 'Idle',
},
RESET: {
target: 'Idle',
},
},
},
Operator: {
on: {
NEXT: {
actions: 'onSelectTagValue',
target: 'TagValue',
},
onBlur: {
actions: 'onBlurPurge',
target: 'Idle',
},
RESET: {
target: 'Idle',
},
},
},
TagValue: {
on: {
onBlur: {
actions: ['onValidateQuery', 'onBlurPurge'],
target: 'Idle',
},
RESET: {
target: 'Idle',
},
},
},
Idle: {
on: {
NEXT: {
actions: 'onSelectTagKey',
description: 'Select Category',
target: 'TagKey',
},
},
},
},
id: 'ResourceAttributesFilterMachine',
});

View File

@ -2,31 +2,31 @@
export interface Typegen0 {
'@@xstate/typegen': true;
eventsCausingActions: {
onSelectOperator: 'NEXT';
onBlurPurge: 'onBlur';
onSelectTagValue: 'NEXT';
onValidateQuery: 'onBlur';
onSelectTagKey: 'NEXT';
};
internalEvents: {
'xstate.init': { type: 'xstate.init' };
};
invokeSrcNameMap: {};
missingImplementations: {
actions:
| 'onSelectOperator'
| 'onBlurPurge'
| 'onSelectOperator'
| 'onSelectTagKey'
| 'onSelectTagValue'
| 'onValidateQuery'
| 'onSelectTagKey';
services: never;
guards: never;
| 'onValidateQuery';
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
onBlurPurge: 'onBlur';
onSelectOperator: 'NEXT';
onSelectTagKey: 'NEXT';
onSelectTagValue: 'NEXT';
onValidateQuery: 'onBlur';
};
eventsCausingServices: {};
eventsCausingGuards: {};
eventsCausingDelays: {};
matchesStates: 'TagKey' | 'Operator' | 'TagValue' | 'Idle';
eventsCausingGuards: {};
eventsCausingServices: {};
matchesStates: 'Idle' | 'Operator' | 'TagKey' | 'TagValue';
tags: never;
}

View File

@ -0,0 +1,31 @@
export interface IResourceAttribute {
id: string;
tagKey: string;
operator: string;
tagValue: string[];
}
export interface IOption {
label: string;
value: string;
}
type Modes = 'tags' | 'multiple';
export interface OptionsData {
mode?: Modes;
options: IOption[];
}
export interface IResourceAttributeProps {
queries: IResourceAttribute[];
staging: string[];
handleClearAll: VoidFunction;
handleClose: (id: string) => void;
handleBlur: VoidFunction;
handleFocus: VoidFunction;
loading: boolean;
handleChange: (value: string) => void;
selectedQuery: string[];
optionsData: OptionsData;
}

View File

@ -0,0 +1,9 @@
import { useContext } from 'react';
import { ResourceContext } from './context';
import { IResourceAttributeProps } from './types';
const useResourceAttribute = (): IResourceAttributeProps =>
useContext(ResourceContext);
export default useResourceAttribute;

View File

@ -0,0 +1,154 @@
import {
getResourceAttributesTagKeys,
getResourceAttributesTagValues,
} from 'api/metrics/getResourceAttributes';
import { OperatorConversions } from 'constants/resourceAttributes';
import ROUTES from 'constants/routes';
import {
IOption,
IResourceAttribute,
IResourceAttributeProps,
} from 'hooks/useResourceAttribute/types';
import { decode } from 'js-base64';
import history from 'lib/history';
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
import { OperatorValues, Tags } from 'types/reducer/trace';
import { v4 as uuid } from 'uuid';
import { whilelistedKeys } from './config';
/**
* resource_x_y -> x.y
*/
export const convertMetricKeyToTrace = (key: string): string => {
const splittedKey = key.split('_');
if (splittedKey.length <= 1) {
return '';
}
return splittedKey.splice(1).join('.');
};
/**
* x.y -> resource_x_y
*/
export const convertTraceKeyToMetric = (key: string): string => {
const splittedKey = key.split('.');
return `resource_${splittedKey.join('_')}`;
};
export const convertOperatorLabelToMetricOperator = (label: string): string =>
OperatorConversions.find((operator) => operator.label === label)
?.metricValue || '';
export const convertOperatorLabelToTraceOperator = (
label: string,
): OperatorValues =>
OperatorConversions.find((operator) => operator.label === label)
?.traceValue as OperatorValues;
export const convertRawQueriesToTraceSelectedTags = (
queries: IResourceAttribute[],
tagType = 'ResourceAttribute',
): Tags[] =>
queries.map((query) => ({
Key: convertMetricKeyToTrace(query.tagKey),
Operator: convertOperatorLabelToTraceOperator(query.operator),
StringValues: query.tagValue,
NumberValues: [],
BoolValues: [],
TagType: tagType,
}));
/* Convert resource attributes to tagFilter items for queryBuilder */
export const resourceAttributesToTagFilterItems = (
queries: IResourceAttribute[],
): IQueryBuilderTagFilterItems[] =>
queries.map((res) => ({
id: `${res.id}`,
key: `${res.tagKey}`,
op: `${res.operator}`,
value: `${res.tagValue}`.split(','),
}));
export const OperatorSchema: IOption[] = OperatorConversions.map(
(operator) => ({
label: operator.label,
value: operator.label,
}),
);
export const GetTagKeys = async (): Promise<IOption[]> => {
const { payload } = await getResourceAttributesTagKeys({
metricName: 'signoz_calls_total',
match: 'resource_',
});
if (!payload || !payload?.data) {
return [];
}
return payload.data.map((tagKey: string) => ({
label: convertMetricKeyToTrace(tagKey),
value: tagKey,
}));
};
export const GetTagValues = async (tagKey: string): Promise<IOption[]> => {
const { payload } = await getResourceAttributesTagValues({
tagKey,
metricName: 'signoz_calls_total',
});
if (!payload || !payload?.data) {
return [];
}
return payload.data.map((tagValue: string) => ({
label: tagValue,
value: tagValue,
}));
};
export const createQuery = (
selectedItems: Array<string | string[]> = [],
): IResourceAttribute | null => {
if (selectedItems.length === 3) {
return {
id: uuid().slice(0, 8),
tagKey: selectedItems[0] as string,
operator: selectedItems[1] as string,
tagValue: selectedItems[2] as string[],
};
}
return null;
};
export function getResourceAttributeQueriesFromURL(): IResourceAttribute[] {
const resourceAttributeQuery = new URLSearchParams(
history.location.search,
).get('resourceAttribute');
try {
if (resourceAttributeQuery) {
return JSON.parse(decode(resourceAttributeQuery)) as IResourceAttribute[];
}
} catch (error) {
console.error(error);
}
return [];
}
export const isResourceEmpty = (
queries: IResourceAttributeProps['queries'],
staging: IResourceAttributeProps['staging'],
selectedQuery: IResourceAttributeProps['selectedQuery'],
): boolean => !!(queries.length || staging.length || selectedQuery.length);
export const mappingWithRoutesAndKeys = (
pathname: string,
filters: IOption[],
): IOption[] => {
if (ROUTES.SERVICE_MAP === pathname) {
return filters.filter((filter) => whilelistedKeys.includes(filter.value));
}
return filters;
};

View File

@ -1,76 +0,0 @@
import { OperatorConversions } from 'constants/resourceAttributes';
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
import { OperatorValues, Tags } from 'types/reducer/trace';
/**
* resource_x_y -> x.y
*/
export const convertMetricKeyToTrace = (key: string): string => {
const splittedKey = key.split('_');
if (splittedKey.length <= 1) {
return '';
}
return splittedKey.splice(1).join('.');
};
/**
* x.y -> resource_x_y
*/
export const convertTraceKeyToMetric = (key: string): string => {
const splittedKey = key.split('.');
return `resource_${splittedKey.join('_')}`;
};
export const convertOperatorLabelToMetricOperator = (label: string): string =>
OperatorConversions.find((operator) => operator.label === label)
?.metricValue || '';
export const convertOperatorLabelToTraceOperator = (
label: string,
): OperatorValues =>
OperatorConversions.find((operator) => operator.label === label)
?.traceValue as OperatorValues;
export const convertRawQueriesToTraceSelectedTags = (
queries: IResourceAttributeQuery[],
): Tags[] =>
queries.map((query) => ({
Key: convertMetricKeyToTrace(query.tagKey),
Operator: convertOperatorLabelToTraceOperator(query.operator),
StringValues: query.tagValue,
NumberValues: [],
BoolValues: [],
}));
/**
* Converts Resource Attribute Queries to PromQL query string
*/
export const resourceAttributesQueryToPromQL = (
queries: IResourceAttributeQuery[],
): string => {
let parsedQueryString = '';
if (Array.isArray(queries))
queries.forEach((query) => {
parsedQueryString += `, ${
query.tagKey
}${convertOperatorLabelToMetricOperator(
query.operator,
)}"${query.tagValue.join('|')}"`;
});
return parsedQueryString;
};
/* Convert resource attributes to tagFilter items for queryBuilder */
export const resourceAttributesToTagFilterItems = (
queries: IResourceAttributeQuery[],
): IQueryBuilderTagFilterItems[] =>
queries.map((res) => ({
id: `${res.id}`,
key: `${res.tagKey}`,
op: `${res.operator}`,
value: `${res.tagValue}`.split(','),
}));

View File

@ -0,0 +1,56 @@
/* eslint-disable */
//@ts-nocheck
import { useIsDarkMode } from 'hooks/useDarkMode';
import React, { memo } from 'react';
import { ForceGraph2D } from 'react-force-graph';
import { getGraphData, getTooltip, transformLabel } from './utils';
function ServiceMap({ fgRef, serviceMap }: any): JSX.Element {
const isDarkMode = useIsDarkMode();
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
const graphData = { nodes, links };
return (
<ForceGraph2D
ref={fgRef}
cooldownTicks={100}
graphData={graphData}
linkLabel={getTooltip}
linkAutoColorBy={(d) => d.target}
linkDirectionalParticles="value"
linkDirectionalParticleSpeed={(d) => d.value}
nodeCanvasObject={(node, ctx) => {
const label = transformLabel(node.id);
const { fontSize } = node;
ctx.font = `${fontSize}px Roboto`;
const { width } = node;
ctx.fillStyle = node.color;
ctx.beginPath();
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
ctx.fill();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
ctx.fillText(label, node.x, node.y);
}}
onLinkHover={(node) => {
const tooltip = document.querySelector('.graph-tooltip');
if (tooltip && node) {
tooltip.innerHTML = getTooltip(node);
}
}}
nodePointerAreaPaint={(node, color, ctx) => {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
ctx.fill();
}}
/>
);
}
export default memo(ServiceMap);

View File

@ -3,9 +3,12 @@
import { Card } from 'antd';
import Spinner from 'components/Spinner';
import { useIsDarkMode } from 'hooks/useDarkMode';
import TextToolTip from 'components/TextToolTip';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { whilelistedKeys } from 'hooks/useResourceAttribute/config';
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
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, ServiceMapStore } from 'store/actions';
@ -13,7 +16,7 @@ import { AppState } from 'store/reducers';
import styled from 'styled-components';
import { GlobalTime } from 'types/actions/globalTime';
import { getGraphData, getTooltip, getZoomPx, transformLabel } from './utils';
import Map from './Map';
const Container = styled.div`
.force-graph-container {
@ -38,7 +41,10 @@ const Container = styled.div`
interface ServiceMapProps extends RouteComponentProps<any> {
serviceMap: ServiceMapStore;
globalTime: GlobalTime;
getDetailedServiceMapItems: (time: GlobalTime) => void;
getDetailedServiceMapItems: (
time: GlobalTime,
queries: IResourceAttribute[],
) => void;
}
interface graphNode {
id: string;
@ -60,17 +66,17 @@ export interface graphDataType {
function ServiceMap(props: ServiceMapProps): JSX.Element {
const fgRef = useRef();
const isDarkMode = useIsDarkMode();
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
const { queries } = useResourceAttribute();
useEffect(() => {
/*
Call the apis only when the route is loaded.
Check this issue: https://github.com/SigNoz/signoz/issues/110
*/
getDetailedServiceMapItems(globalTime);
}, [globalTime, getDetailedServiceMapItems]);
getDetailedServiceMapItems(globalTime, queries);
}, [globalTime, getDetailedServiceMapItems, queries]);
useEffect(() => {
fgRef.current && fgRef.current.d3Force('charge').strength(-400);
@ -83,51 +89,26 @@ function ServiceMap(props: ServiceMapProps): JSX.Element {
if (!serviceMap.loading && serviceMap.items.length === 0) {
return (
<Container>
<ResourceAttributesFilter />
<Card>No Service Found</Card>
</Container>
);
}
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
const graphData = { nodes, links };
return (
<Container>
<ForceGraph2D
ref={fgRef}
cooldownTicks={100}
graphData={graphData}
linkLabel={getTooltip}
linkAutoColorBy={(d) => d.target}
linkDirectionalParticles="value"
linkDirectionalParticleSpeed={(d) => d.value}
nodeCanvasObject={(node, ctx) => {
const label = transformLabel(node.id);
const { fontSize } = node;
ctx.font = `${fontSize}px Roboto`;
const { width } = node;
ctx.fillStyle = node.color;
ctx.beginPath();
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
ctx.fill();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
ctx.fillText(label, node.x, node.y);
}}
onLinkHover={(node) => {
const tooltip = document.querySelector('.graph-tooltip');
if (tooltip && node) {
tooltip.innerHTML = getTooltip(node);
}
}}
nodePointerAreaPaint={(node, color, ctx) => {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
ctx.fill();
}}
<ResourceAttributesFilter
suffixIcon={
<TextToolTip
{...{
text: `Currently, service map supports filtering of ${whilelistedKeys.join(
', ',
)} only, in resource attributes`,
}}
/>
}
/>
<Map fgRef={fgRef} serviceMap={serviceMap} />
</Container>
);
}

View File

@ -4,31 +4,23 @@ import AllAlertRules from 'container/ListAlertRules';
import TriggeredAlerts from 'container/TriggeredAlerts';
import React from 'react';
const { TabPane } = Tabs;
function AllAlertList(): JSX.Element {
const items = [
{ label: 'Alert Rules', key: 'Alert Rules', children: <AllAlertRules /> },
{
label: 'Triggered Alerts',
key: 'Triggered Alerts',
children: <TriggeredAlerts />,
},
// {
// label: 'Map Alert Channels',
// key = 'Map Alert Channels',
// children: <MapAlertChannels />,
// },
];
return (
<Tabs destroyInactiveTabPane defaultActiveKey="Alert Rules">
<TabPane tabKey="Alert Rules" tab="Alert Rules" key="Alert Rules">
<AllAlertRules />
</TabPane>
<TabPane
tabKey="Triggered Alerts"
key="Triggered Alerts"
tab="Triggered Alerts"
>
<TriggeredAlerts />
</TabPane>
{/* <TabPane
tabKey="Map Alert Channels"
key="Map Alert Channels"
tab="Map Alert Channels"
>
<MapAlertChannels />
</TabPane> */}
</Tabs>
<Tabs destroyInactiveTabPane defaultActiveKey="Alert Rules" items={items} />
);
}

View File

@ -1,6 +1,7 @@
import RouteTab from 'components/RouteTab';
import ROUTES from 'constants/routes';
import AllErrorsContainer from 'container/AllError';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -8,18 +9,21 @@ function AllErrors(): JSX.Element {
const { t } = useTranslation();
return (
<RouteTab
{...{
routes: [
{
Component: AllErrorsContainer,
name: t('routes.all_errors'),
route: ROUTES.ALL_ERROR,
},
],
activeKey: t('routes.all_errors'),
}}
/>
<>
<ResourceAttributesFilter />
<RouteTab
{...{
routes: [
{
Component: AllErrorsContainer,
name: t('routes.all_errors'),
route: ROUTES.ALL_ERROR,
},
],
activeKey: t('routes.all_errors'),
}}
/>
</>
);
}

View File

@ -1,7 +1,8 @@
import { Typography } from 'antd';
import Spinner from 'components/Spinner';
import MetricsApplicationContainer from 'container/MetricsApplication';
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import React, { useEffect, useMemo } from 'react';
import { connect, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
@ -27,16 +28,11 @@ function MetricsApplication({ getInitialData }: MetricsProps): JSX.Element {
>((state) => state.metrics);
const { servicename } = useParams<ServiceProps>();
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
(state) => state.metrics,
);
const { queries } = useResourceAttribute();
const selectedTags = useMemo(
() =>
(convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) as Tags[]) ||
[],
[resourceAttributeQueries],
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
[queries],
);
useEffect(() => {

View File

@ -3,10 +3,11 @@ import getLocalStorageKey from 'api/browser/localstorage/get';
import ReleaseNote from 'components/ReleaseNote';
import Spinner from 'components/Spinner';
import { SKIP_ONBOARDING } from 'constants/onboarding';
import ResourceAttributesFilter from 'container/MetricsApplication/ResourceAttributesFilter';
import MetricTable from 'container/MetricsTable';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import { useNotifications } from 'hooks/useNotifications';
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import React, { useEffect, useMemo } from 'react';
import { connect, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
@ -25,12 +26,9 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
GlobalReducer
>((state) => state.globalTime);
const location = useLocation();
const {
services,
resourceAttributeQueries,
error,
errorMessage,
} = useSelector<AppState, MetricReducer>((state) => state.metrics);
const { services, error, errorMessage } = useSelector<AppState, MetricReducer>(
(state) => state.metrics,
);
const { notifications } = useNotifications();
useEffect(() => {
@ -41,12 +39,13 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
}
}, [error, errorMessage, notifications]);
const { queries } = useResourceAttribute();
const selectedTags = useMemo(
() =>
(convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) as Tags[]) ||
[],
[resourceAttributeQueries],
() => (convertRawQueriesToTraceSelectedTags(queries, '') as Tags[]) || [],
[queries],
);
const isSkipped = getLocalStorageKey(SKIP_ONBOARDING) === 'true';
useEffect(() => {

View File

@ -0,0 +1,93 @@
import React, {
createContext,
PropsWithChildren,
useCallback,
useMemo,
useState,
} from 'react';
// ** Types
// TODO: Rename Types on the Reusable type for any source
import {
IBuilderFormula,
IBuilderQuery,
} from 'types/api/queryBuilder/queryBuilderData';
export type QueryBuilderData = {
queryData: IBuilderQuery[];
queryFormulas: IBuilderFormula[];
};
// ** TODO: temporary types for context, fix it during development
export type QueryBuilderContextType = {
queryBuilderData: QueryBuilderData;
resetQueryBuilderData: () => void;
handleSetQueryData: (index: number, queryData: IBuilderQuery) => void;
handleSetFormulaData: (index: number, formulaData: IBuilderFormula) => void;
};
export const QueryBuilderContext = createContext<QueryBuilderContextType>({
queryBuilderData: { queryData: [], queryFormulas: [] },
resetQueryBuilderData: () => {},
handleSetQueryData: () => {},
handleSetFormulaData: () => {},
});
const initialQueryBuilderData: QueryBuilderData = {
queryData: [],
queryFormulas: [],
};
export function QueryBuilderProvider({
children,
}: PropsWithChildren): JSX.Element {
// ** TODO: get queryId from url for getting data for query builder
// ** TODO: type the params which will be used for request of the data for query builder
const [queryBuilderData, setQueryBuilderData] = useState<QueryBuilderData>({
queryData: [],
queryFormulas: [],
});
// ** TODO: Also in the future need to add AddFormula and AddQuery and remove them.
const resetQueryBuilderData = useCallback((): void => {
setQueryBuilderData(initialQueryBuilderData);
}, []);
const handleSetQueryData = useCallback(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(index: number, queryData: IBuilderQuery): void => {},
[],
);
const handleSetFormulaData = useCallback(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(index: number, formulaData: IBuilderFormula): void => {},
[],
);
// ** TODO: Discuss with Palash how the state of the queryBuilder and queryFormulas
// ** TODO: should be filled from url
// ** TODO: put these values and setter to the context value
const contextValues: QueryBuilderContextType = useMemo(
() => ({
queryBuilderData,
resetQueryBuilderData,
handleSetQueryData,
handleSetFormulaData,
}),
[
queryBuilderData,
resetQueryBuilderData,
handleSetQueryData,
handleSetFormulaData,
],
);
return (
<QueryBuilderContext.Provider value={contextValues}>
{children}
</QueryBuilderContext.Provider>
);
}

View File

@ -1,55 +0,0 @@
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
import { decode, encode } from 'js-base64';
import history from 'lib/history';
import { resourceAttributesQueryToPromQL } from 'lib/resourceAttributes';
import { SET_RESOURCE_ATTRIBUTE_QUERIES } from 'types/actions/metrics';
export function GetResourceAttributeQueriesFromURL():
| IResourceAttributeQuery[]
| null {
const resourceAttributeQuery = new URLSearchParams(
history.location.search,
).get('resourceAttribute');
try {
if (resourceAttributeQuery) {
return JSON.parse(
decode(resourceAttributeQuery),
) as IResourceAttributeQuery[];
}
} catch (error) {
console.error(error);
}
return null;
}
export const SetResourceAttributeQueriesFromURL = (
queries: IResourceAttributeQuery[],
): void => {
history.push({
pathname: history.location.pathname,
search:
queries && queries.length
? `?resourceAttribute=${encode(JSON.stringify(queries))}`
: '',
});
};
export const SetResourceAttributeQueries = (
queries: IResourceAttributeQuery[],
): {
type: typeof SET_RESOURCE_ATTRIBUTE_QUERIES;
payload: {
queries: IResourceAttributeQuery[];
promQLQuery: string;
};
} => {
SetResourceAttributeQueriesFromURL(queries);
return {
type: SET_RESOURCE_ATTRIBUTE_QUERIES,
payload: {
queries,
promQLQuery: resourceAttributesQueryToPromQL(queries),
},
};
};

View File

@ -1,4 +1,6 @@
import api from 'api';
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import { Dispatch } from 'redux';
import { GlobalTime } from 'types/actions/globalTime';
@ -30,16 +32,17 @@ export interface ServiceMapLoading {
};
}
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => async (
dispatch: Dispatch,
): Promise<void> => {
export const getDetailedServiceMapItems = (
globalTime: GlobalTime,
queries: IResourceAttribute[],
) => async (dispatch: Dispatch): Promise<void> => {
const start = `${globalTime.minTime}`;
const end = `${globalTime.maxTime}`;
const serviceMapPayload = {
start,
end,
tags: [],
tags: convertRawQueriesToTraceSelectedTags(queries),
};
const [dependencyGraphResponse] = await Promise.all([
api.post<ServicesMapItem[]>(`/dependency_graph`, serviceMapPayload),

View File

@ -1,5 +1,3 @@
import { resourceAttributesQueryToPromQL } from 'lib/resourceAttributes';
import { GetResourceAttributeQueriesFromURL } from 'store/actions/metrics/setResourceAttributeQueries';
import {
GET_INITIAL_APPLICATION_ERROR,
GET_INITIAL_APPLICATION_LOADING,
@ -9,7 +7,6 @@ import {
GET_SERVICE_LIST_SUCCESS,
MetricsActions,
RESET_INITIAL_APPLICATION_DATA,
SET_RESOURCE_ATTRIBUTE_QUERIES,
} from 'types/actions/metrics';
import InitialValueTypes from 'types/reducer/metrics';
@ -25,10 +22,6 @@ const InitialValue: InitialValueTypes = {
externalAverageDuration: [],
externalError: [],
serviceOverview: [],
resourceAttributeQueries: GetResourceAttributeQueriesFromURL() || [],
resourceAttributePromQLQuery: resourceAttributesQueryToPromQL(
GetResourceAttributeQueriesFromURL() || [],
),
topLevelOperations: [],
};
@ -110,15 +103,6 @@ const metrics = (
};
}
case SET_RESOURCE_ATTRIBUTE_QUERIES: {
const { queries, promQLQuery } = action.payload;
return {
...state,
resourceAttributeQueries: queries,
resourceAttributePromQLQuery: promQLQuery,
};
}
default:
return state;
}

View File

@ -1,8 +1,3 @@
// import { DBOverView } from 'types/api/metrics/getDBOverview';
// import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
// import { ExternalError } from 'types/api/metrics/getExternalError';
// import { ExternalService } from 'types/api/metrics/getExternalService';
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
import { ServicesList } from 'types/api/metrics/getService';
import { ServiceOverview } from 'types/api/metrics/getServiceOverview';
import { TopOperations } from 'types/api/metrics/getTopOperations';
@ -15,7 +10,6 @@ export const GET_INITIAL_APPLICATION_LOADING =
export const GET_INITIAL_APPLICATION_ERROR = 'GET_INITIAL_APPLICATION_ERROR';
export const GET_INTIAL_APPLICATION_DATA = 'GET_INTIAL_APPLICATION_DATA';
export const RESET_INITIAL_APPLICATION_DATA = 'RESET_INITIAL_APPLICATION_DATA';
export const SET_RESOURCE_ATTRIBUTE_QUERIES = 'SET_RESOURCE_ATTRIBUTE_QUERIES';
export interface GetServiceList {
type: typeof GET_SERVICE_LIST_SUCCESS;
@ -52,18 +46,9 @@ export interface ResetInitialApplicationData {
type: typeof RESET_INITIAL_APPLICATION_DATA;
}
export interface SetResourceAttributeQueries {
type: typeof SET_RESOURCE_ATTRIBUTE_QUERIES;
payload: {
queries: IResourceAttributeQuery[];
promQLQuery: string;
};
}
export type MetricsActions =
| GetServiceListError
| GetServiceListLoading
| GetServiceList
| GetInitialApplicationData
| ResetInitialApplicationData
| SetResourceAttributeQueries;
| ResetInitialApplicationData;

View File

@ -1,4 +1,5 @@
import { GlobalTime } from 'types/actions/globalTime';
import { Tags } from 'types/reducer/trace';
export type Order = 'ascending' | 'descending';
export type OrderBy =
@ -17,6 +18,7 @@ export interface Props {
offset?: number;
exceptionType?: string;
serviceName?: string;
tags?: Tags[];
}
export interface Exception {

View File

@ -1,10 +1,12 @@
import { GlobalTime } from 'types/actions/globalTime';
import { Tags } from 'types/reducer/trace';
export type Props = {
start: GlobalTime['minTime'];
end: GlobalTime['minTime'];
exceptionType: string;
serviceName: string;
tags: Tags[];
};
export type PayloadProps = number;

View File

@ -0,0 +1,22 @@
import { EAggregateOperator, EReduceOperator } from 'types/common/dashboard';
import { IQueryBuilderTagFilters } from '../dashboard/getAll';
export interface IBuilderQuery {
// TODO: add another list of operator depended from data source
aggregateOperator: EAggregateOperator;
disabled: boolean;
label: string;
legend: string;
attribute: string;
groupBy: string[];
tagFilters: IQueryBuilderTagFilters;
reduceTo?: EReduceOperator;
}
export interface IBuilderFormula {
expression: string;
disabled: boolean;
label: string;
legend: string;
}

View File

@ -0,0 +1,5 @@
export enum DataSource {
METRICS = 'metrics',
TRACES = 'traces',
LOGS = 'logs',
}

View File

@ -0,0 +1,4 @@
export type SelectOption<Value, Label extends unknown = string> = {
value: Value;
label: Label;
};

View File

@ -1,4 +1,3 @@
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
import { DBOverView } from 'types/api/metrics/getDBOverview';
import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
import { ExternalError } from 'types/api/metrics/getExternalError';
@ -19,8 +18,6 @@ interface MetricReducer {
externalAverageDuration: ExternalAverageDuration[];
externalError: ExternalError[];
serviceOverview: ServiceOverview[];
resourceAttributeQueries: IResourceAttributeQuery[];
resourceAttributePromQLQuery: string;
topLevelOperations: string[];
}

13
go.mod
View File

@ -13,11 +13,13 @@ require (
github.com/gosimple/slug v1.10.0
github.com/jmoiron/sqlx v1.3.4
github.com/json-iterator/go v1.1.12
github.com/knadh/koanf v1.5.0
github.com/mailru/easyjson v0.7.7
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/minio/minio-go/v6 v6.0.57
github.com/mitchellh/mapstructure v1.5.0
github.com/oklog/oklog v0.3.2
github.com/open-telemetry/opamp-go v0.5.0
github.com/pkg/errors v0.9.1
github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f
github.com/prometheus/common v0.39.0
@ -28,8 +30,10 @@ require (
github.com/sethvargo/go-password v0.2.0
github.com/smartystreets/goconvey v1.6.4
github.com/soheilhy/cmux v0.1.5
go.opentelemetry.io/collector/confmap v0.70.0
go.uber.org/zap v1.24.0
gopkg.in/segmentio/analytics-go.v3 v3.1.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.26.0
)
@ -66,17 +70,20 @@ require (
github.com/beevik/etree v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/cpuid v1.2.3 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/minio/md5-simd v1.1.0 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
go.opentelemetry.io/collector/featuregate v0.70.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
@ -116,7 +123,7 @@ require (
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/smartystreets/assertions v1.1.0
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.8.1
github.com/stretchr/testify v1.8.2
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
go.opentelemetry.io/otel v1.11.2 // indirect
go.opentelemetry.io/otel/trace v1.11.2 // indirect
@ -131,7 +138,7 @@ require (
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1 // indirect
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v2 v2.4.0
)

141
go.sum
View File

@ -89,13 +89,27 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8V
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q=
github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/auth0/go-jwt-middleware v1.0.1 h1:/fsQ4vRr4zod1wKReUH+0A3ySRjGiT9G34kypO/EKwI=
github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9vXW72U//IgN0BIM=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.44.159 h1:9odtuHAYQE9tQKyuX6ny1U1MHeH5/yzeCJi96g9H4DU=
github.com/aws/aws-sdk-go v1.44.159/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw=
github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ=
github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8=
github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
@ -104,6 +118,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -130,6 +145,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc h1:PYXxkRUBGUMa5xgMVMDl62vEklZvKpVaxQeN9ie7Hfk=
github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g=
github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -159,12 +176,16 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8=
github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
@ -181,6 +202,7 @@ github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEai
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@ -201,7 +223,9 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@ -239,6 +263,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -304,30 +329,63 @@ github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosimple/slug v1.10.0 h1:3XbiQua1IpCdrvuntWvGBxVm+K99wCSxJjlxkP49GGQ=
github.com/gosimple/slug v1.10.0/go.mod h1:MICb3w495l9KNdZm+Xn5b6T2Hn831f9DMxiJ1r+bAjw=
github.com/gosimple/unidecode v1.0.0 h1:kPdvM+qy0tnk4/BrnkrbdJ82xe88xn7c9hcaipDz4dQ=
github.com/gosimple/unidecode v1.0.0/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww=
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul v1.1.1-0.20180615161029-bed22a81e9fd h1:u6o+bd6FHxDKoCSa8PJ5vrHhAYSKgJtAHQtLO1EYgos=
github.com/hashicorp/consul v1.1.1-0.20180615161029-bed22a81e9fd/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/nomad/api v0.0.0-20221214074818-7dbbf6bc584d h1:kEWrUx7mld3c6HRcO2KhfD1MYBkofuZfEfDwCRQ9aMU=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hetznercloud/hcloud-go v1.38.0 h1:K6Pd/mMdcLfBhvwG39qyAaacp4pCS3dKa8gChmLKxLg=
github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs=
github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
@ -338,6 +396,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -362,11 +422,14 @@ github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexD
github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@ -382,12 +445,22 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
@ -395,10 +468,23 @@ github.com/minio/minio-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/
github.com/minio/minio-go/v6 v6.0.57/go.mod h1:5+R/nM9Pwrh0vqF+HbYYDQ84wdUFPyXHkrdT4AIkifM=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -410,21 +496,29 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g=
github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/ovh/go-ovh v1.3.0 h1:mvZaddk4E4kLcXhzb+cxBsMPYp2pHqiQpWYkInsuZPQ=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/paulmach/orb v0.8.0 h1:W5XAt5yNPNnhaMNEf0xNSkBMJ1LzOzdk2MRlB6EN0Vs=
github.com/paulmach/orb v0.8.0/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@ -434,6 +528,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f h1:h0p1aZ9F5d6IXOygysob3g4B07b+HuVUQC0VJKD8wA4=
github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -441,6 +537,7 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -465,6 +562,7 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@ -477,7 +575,11 @@ github.com/russellhaering/gosaml2 v0.8.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE
github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.10 h1:wsfMs0iv+MJiViM37qh5VEKISi3/ZUq2nNKNdqmumAs=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/segmentio/backo-go v1.0.0 h1:kbOAtGJY2DqOR0jfRkYEorx/b18RgtepGtY3+Cpe6qA=
@ -506,6 +608,7 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@ -518,8 +621,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
@ -533,6 +636,9 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -540,6 +646,10 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/collector/confmap v0.70.0 h1:GJDaM7c3yFyT7Zv6l2/5ahwaqPCvtC92Ii8Bg2AVdjU=
go.opentelemetry.io/collector/confmap v0.70.0/go.mod h1:8//JWR2TMChLH35Az0mGFrCskEIP6POgZJK6iRRhzeM=
go.opentelemetry.io/collector/featuregate v0.70.0 h1:Xr6hrMT/++SjTm06nreex8WlpgFhYJ7S0yRVn1OvVf8=
go.opentelemetry.io/collector/featuregate v0.70.0/go.mod h1:ih+oCwrHW3bLac/qnPUzes28yDCDmh8WzsAKKauwCYI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.37.0 h1:yt2NKzK7Vyo6h0+X8BA4FpreZQTlVEIarnsBP/H5mzs=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.37.0/go.mod h1:+ARmXlUlc51J7sZeCBkBJNdHGySrdOzgzxp6VWRWM1U=
go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0=
@ -549,12 +659,15 @@ go.opentelemetry.io/otel/metric v0.34.0/go.mod h1:ZFuI4yQGNCupurTXCwkeD/zHBt+C2b
go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0=
go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -562,6 +675,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@ -622,6 +736,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -645,6 +760,7 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -697,11 +813,15 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -709,12 +829,19 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -736,10 +863,12 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -776,6 +905,7 @@ golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -804,6 +934,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -902,6 +1033,7 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -979,9 +1111,11 @@ google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@ -1030,6 +1164,7 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -1042,6 +1177,7 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U=
gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -1078,4 +1214,5 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=

View File

@ -0,0 +1,241 @@
package agentConf
import (
"context"
"database/sql"
"fmt"
"math/rand"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"go.signoz.io/signoz/pkg/query-service/agentConf/sqlite"
"go.signoz.io/signoz/pkg/query-service/model"
"go.uber.org/zap"
)
func init() {
rand.Seed(2000)
}
// Repo handles DDL and DML ops on ingestion rules
type Repo struct {
db *sqlx.DB
}
func (r *Repo) initDB(engine string) error {
switch engine {
case "sqlite3", "sqlite":
return sqlite.InitDB(r.db)
default:
return fmt.Errorf("unsupported db")
}
}
func (r *Repo) GetConfigHistory(ctx context.Context, typ ElementTypeDef, limit int) ([]ConfigVersion, error) {
var c []ConfigVersion
err := r.db.SelectContext(ctx, &c, fmt.Sprintf(`SELECT
version,
id,
element_type,
COALESCE(created_by, -1) as created_by,
created_at,
COALESCE((SELECT NAME FROM users
WHERE id = v.created_by), "unknown") created_by_name,
active,
is_valid,
disabled,
deploy_status,
deploy_result,
last_hash,
last_config
FROM agent_config_versions AS v
WHERE element_type = $1
ORDER BY created_at desc, version desc
limit %v`, limit),
typ)
return c, err
}
func (r *Repo) GetConfigVersion(ctx context.Context, typ ElementTypeDef, v int) (*ConfigVersion, error) {
var c ConfigVersion
err := r.db.GetContext(ctx, &c, `SELECT
id,
version,
element_type,
COALESCE(created_by, -1) as created_by,
created_at,
COALESCE((SELECT NAME FROM users
WHERE id = v.created_by), "unknown") created_by_name,
active,
is_valid,
disabled,
deploy_status,
deploy_result,
last_hash,
last_config
FROM agent_config_versions v
WHERE element_type = $1
AND version = $2`, typ, v)
return &c, err
}
func (r *Repo) GetLatestVersion(ctx context.Context, typ ElementTypeDef) (*ConfigVersion, error) {
var c ConfigVersion
err := r.db.GetContext(ctx, &c, `SELECT
id,
version,
element_type,
COALESCE(created_by, -1) as created_by,
created_at,
COALESCE((SELECT NAME FROM users
WHERE id = v.created_by), "unknown") created_by_name,
active,
is_valid,
disabled,
deploy_status,
deploy_result
FROM agent_config_versions AS v
WHERE element_type = $1
AND version = (
SELECT MAX(version)
FROM agent_config_versions
WHERE element_type=$2)`, typ, typ)
if err != nil {
zap.S().Error("failed get latest config version for element:", typ, err)
}
return &c, err
}
func (r *Repo) insertConfig(ctx context.Context, userId string, c *ConfigVersion, elements []string) (fnerr error) {
if string(c.ElementType) == "" {
return fmt.Errorf("element type is required for creating agent config version")
}
if len(elements) == 0 {
zap.S().Error("insert config called with no elements", c.ElementType)
return fmt.Errorf("config must have atleast one element")
}
if c.Version != 0 {
// the version can not be set by the user, we want to auto-assign the versions
// in a monotonically increasing order starting with 1. hence, we reject insert
// requests with version anything other than 0. here, 0 indicates un-assigned
zap.S().Error("invalid version assignment while inserting agent config", c.Version, c.ElementType)
return fmt.Errorf("user defined versions are not supported in the agent config")
}
configVersion, err := r.GetLatestVersion(ctx, c.ElementType)
if err != nil {
if err != sql.ErrNoRows {
zap.S().Error("failed to fetch latest config version", err)
return fmt.Errorf("failed to fetch latest config version")
}
}
c.Version = updateVersion(configVersion.Version)
defer func() {
if fnerr != nil {
// remove all the damage (invalid rows from db)
r.db.Exec("DELETE FROM agent_config_versions WHERE id = $1", c.ID)
r.db.Exec("DELETE FROM agent_config_elements WHERE version_id=$1", c.ID)
}
}()
// insert config
configQuery := `INSERT INTO agent_config_versions(
id,
version,
created_by,
element_type,
active,
is_valid,
disabled,
deploy_status,
deploy_result)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`
_, err = r.db.ExecContext(ctx,
configQuery,
c.ID,
c.Version,
userId,
c.ElementType,
false,
false,
false,
c.DeployStatus,
c.DeployResult)
if err != nil {
zap.S().Error("error in inserting config version: ", zap.Error(err))
return fmt.Errorf("failed to insert ingestion rule")
}
elementsQuery := `INSERT INTO agent_config_elements(
id,
version_id,
element_type,
element_id)
VALUES ($1, $2, $3, $4)`
for _, e := range elements {
_, err = r.db.ExecContext(ctx,
elementsQuery,
uuid.NewString(),
c.ID,
c.ElementType,
e)
if err != nil {
return err
}
}
return nil
}
func (r *Repo) updateDeployStatus(ctx context.Context,
elementType ElementTypeDef,
version int,
status string,
result string,
lastHash string,
lastconf string) error {
updateQuery := `UPDATE agent_config_versions
set deploy_status = $1,
deploy_result = $2,
last_hash = COALESCE($3, last_hash),
last_config = $4
WHERE version=$5
AND element_type = $6`
_, err := r.db.ExecContext(ctx, updateQuery, status, result, lastHash, lastconf, version, string(elementType))
if err != nil {
zap.S().Error("failed to update deploy status", err)
return model.BadRequestStr("failed to update deploy status")
}
return nil
}
func (r *Repo) updateDeployStatusByHash(ctx context.Context, confighash string, status string, result string) error {
updateQuery := `UPDATE agent_config_versions
set deploy_status = $1,
deploy_result = $2
WHERE last_hash=$4`
_, err := r.db.ExecContext(ctx, updateQuery, status, result, confighash)
if err != nil {
zap.S().Error("failed to update deploy status", err)
return model.BadRequestStr("failed to update deploy status")
}
return nil
}

Some files were not shown because too many files have changed in this diff Show More