mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'main' into matyax/monaco-loki-from-hackathon
This commit is contained in:
commit
458bda6eb1
@ -1,5 +1,5 @@
|
||||
// BETTERER RESULTS V2.
|
||||
//
|
||||
//
|
||||
// If this file contains merge conflicts, use `betterer merge` to automatically resolve them:
|
||||
// https://phenomnomnominal.github.io/betterer/docs/results-file/#merge
|
||||
//
|
||||
@ -23,9 +23,6 @@ exports[`no enzyme tests`] = {
|
||||
"packages/grafana-ui/src/slate-plugins/suggestions.test.tsx:2682912140": [
|
||||
[0, 18, 13, "RegExp match", "2409514259"]
|
||||
],
|
||||
"packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/TickLabels.test.js:2931161174": [
|
||||
[14, 19, 13, "RegExp match", "2409514259"]
|
||||
],
|
||||
"packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.test.js:1676554632": [
|
||||
[14, 19, 13, "RegExp match", "2409514259"]
|
||||
],
|
||||
@ -59,7 +56,7 @@ exports[`no enzyme tests`] = {
|
||||
"public/app/features/dimensions/editors/ThresholdsEditor/ThresholdsEditor.test.tsx:145048794": [
|
||||
[0, 17, 13, "RegExp match", "2409514259"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:4057721851": [
|
||||
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:2983010995": [
|
||||
[1, 19, 13, "RegExp match", "2409514259"]
|
||||
]
|
||||
}`
|
||||
@ -860,13 +857,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
|
||||
],
|
||||
"packages/grafana-data/src/utils/logs.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
],
|
||||
"packages/grafana-data/src/utils/logs.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
@ -2778,17 +2768,6 @@ exports[`better eslint`] = {
|
||||
"public/app/core/history/richHistoryLocalStorageUtils.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/core/logsModel.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"]
|
||||
],
|
||||
"public/app/core/logsModel.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
@ -2803,8 +2782,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
],
|
||||
"public/app/core/navigation/RouterDebugger.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@ -2921,34 +2899,17 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
||||
],
|
||||
"public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "8"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
],
|
||||
"public/app/core/services/echo/backends/analytics/RudderstackBackend.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "8"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "10"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "12"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "14"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "15"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
|
||||
],
|
||||
"public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@ -3690,6 +3651,9 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/dashboard/components/HelpWizard/randomizer.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard/components/Inspector/PanelInspectActions.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
@ -3818,7 +3782,7 @@ exports[`better eslint`] = {
|
||||
"public/app/features/dashboard/components/ShareModal/ShareModal.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard/components/ShareModal/SharePublicDashboard.test.tsx:5381": [
|
||||
"public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx:5381": [
|
||||
@ -3834,9 +3798,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/dashboard/components/SupportSnapshot/randomizer.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
@ -3941,11 +3902,9 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/dashboard/services/PublicDashboardDataSource.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"public/app/features/dashboard/services/TimeSrv.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@ -4605,10 +4564,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/library-panels/components/LibraryPanelsSearch/reducer.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/library-panels/components/LibraryPanelsView/actions.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
@ -4691,6 +4646,19 @@ exports[`better eslint`] = {
|
||||
"public/app/features/live/pages/utils.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/logs/components/LogRowContextProvider.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
],
|
||||
"public/app/features/logs/components/LogRows.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/logs/components/logParser.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/manage-dashboards/DashboardImportPage.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
@ -5072,11 +5040,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "8"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "9"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"]
|
||||
],
|
||||
"public/app/features/query/state/runRequest.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@ -5913,14 +5877,6 @@ exports[`better eslint`] = {
|
||||
"public/app/plugins/datasource/cloud-monitoring/types.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/__mocks__/monarch/Monaco.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
@ -5965,54 +5921,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/datasource.d.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/datasource.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "12"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/datasource.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "14"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "15"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "18"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "19"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "20"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "21"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "22"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "23"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "24"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "25"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "26"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "27"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "28"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/guards.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
@ -6036,26 +5946,9 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "15"]
|
||||
"public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/types.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@ -6080,14 +5973,49 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/dashboard/runSharedRequest.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/LanguageProvider.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/QueryBuilder.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/QueryBuilder.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "15"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "18"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/components/AddRemove.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
@ -6217,33 +6145,6 @@ exports[`better eslint`] = {
|
||||
"public/app/plugins/datasource/elasticsearch/hooks/useStatelessReducer.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/language_provider.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/query_builder.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "15"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "18"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/specs/elastic_response.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
@ -6264,16 +6165,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "17"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/specs/query_builder.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
|
||||
],
|
||||
"public/app/plugins/datasource/elasticsearch/test-helpers/render.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
|
449
.drone.yml
449
.drone.yml
@ -2,6 +2,8 @@
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-verify-drone
|
||||
node:
|
||||
@ -54,6 +56,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-test-frontend
|
||||
node:
|
||||
@ -86,17 +90,6 @@ steps:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
- yarn run lint
|
||||
- yarn run i18n:compile
|
||||
- yarn run typecheck
|
||||
depends_on:
|
||||
- yarn-install
|
||||
environment:
|
||||
TEST_MAX_WORKERS: 50%
|
||||
image: grafana/build-container:1.6.2
|
||||
name: lint-frontend
|
||||
- commands:
|
||||
- yarn betterer ci
|
||||
depends_on:
|
||||
@ -133,6 +126,60 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-lint-frontend
|
||||
node:
|
||||
type: no-parallel
|
||||
platform:
|
||||
arch: amd64
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.15.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable
|
||||
depends_on: []
|
||||
image: grafana/build-container:1.6.2
|
||||
name: yarn-install
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
- yarn run lint
|
||||
- yarn run i18n:compile
|
||||
- yarn run typecheck
|
||||
depends_on:
|
||||
- yarn-install
|
||||
environment:
|
||||
TEST_MAX_WORKERS: 50%
|
||||
image: grafana/build-container:1.6.2
|
||||
name: lint-frontend
|
||||
trigger:
|
||||
event:
|
||||
- pull_request
|
||||
paths:
|
||||
exclude:
|
||||
- docs/**
|
||||
- '*.md'
|
||||
- pkg/**
|
||||
- packaging/**
|
||||
- go.sum
|
||||
- go.mod
|
||||
include: []
|
||||
type: docker
|
||||
volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-test-backend
|
||||
node:
|
||||
@ -146,12 +193,6 @@ steps:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.15.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- mkdir -p bin
|
||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.7/grabpl
|
||||
- chmod +x bin/grabpl
|
||||
image: byrnedo/alpine-curl:0.1.8
|
||||
name: grabpl
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
@ -174,14 +215,6 @@ steps:
|
||||
- verify-gen-cue
|
||||
image: grafana/build-container:1.6.2
|
||||
name: wire-install
|
||||
- commands:
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- go test -short -covermode=atomic -timeout=30m ./pkg/...
|
||||
depends_on:
|
||||
@ -219,6 +252,68 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-lint-backend
|
||||
node:
|
||||
type: no-parallel
|
||||
platform:
|
||||
arch: amd64
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.15.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- make gen-go
|
||||
depends_on: []
|
||||
image: grafana/build-container:1.6.2
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: golang:1.19.1
|
||||
name: lint-backend
|
||||
trigger:
|
||||
event:
|
||||
- pull_request
|
||||
paths:
|
||||
exclude:
|
||||
- docs/**
|
||||
- '*.md'
|
||||
include:
|
||||
- pkg/**
|
||||
- packaging/**
|
||||
- conf/**
|
||||
- go.sum
|
||||
- go.mod
|
||||
- public/app/plugins/**/plugin.json
|
||||
- devenv/**
|
||||
type: docker
|
||||
volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-build-e2e
|
||||
node:
|
||||
@ -466,6 +561,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-integration-tests
|
||||
node:
|
||||
@ -589,6 +686,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-docs
|
||||
node:
|
||||
@ -673,6 +772,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: pr-shellcheck
|
||||
node:
|
||||
@ -714,6 +815,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-docs
|
||||
node:
|
||||
@ -799,6 +902,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-test-frontend
|
||||
node:
|
||||
@ -831,17 +936,6 @@ steps:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
- yarn run lint
|
||||
- yarn run i18n:compile
|
||||
- yarn run typecheck
|
||||
depends_on:
|
||||
- yarn-install
|
||||
environment:
|
||||
TEST_MAX_WORKERS: 50%
|
||||
image: grafana/build-container:1.6.2
|
||||
name: lint-frontend
|
||||
- commands:
|
||||
- yarn betterer ci
|
||||
depends_on:
|
||||
@ -875,6 +969,57 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-lint-frontend
|
||||
node:
|
||||
type: no-parallel
|
||||
platform:
|
||||
arch: amd64
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.15.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable
|
||||
depends_on: []
|
||||
image: grafana/build-container:1.6.2
|
||||
name: yarn-install
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
- yarn run lint
|
||||
- yarn run i18n:compile
|
||||
- yarn run typecheck
|
||||
depends_on:
|
||||
- yarn-install
|
||||
environment:
|
||||
TEST_MAX_WORKERS: 50%
|
||||
image: grafana/build-container:1.6.2
|
||||
name: lint-frontend
|
||||
trigger:
|
||||
branch: main
|
||||
event:
|
||||
- push
|
||||
paths:
|
||||
exclude:
|
||||
- '*.md'
|
||||
- docs/**
|
||||
- latest.json
|
||||
type: docker
|
||||
volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-test-backend
|
||||
node:
|
||||
@ -888,12 +1033,6 @@ steps:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.15.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- mkdir -p bin
|
||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.7/grabpl
|
||||
- chmod +x bin/grabpl
|
||||
image: byrnedo/alpine-curl:0.1.8
|
||||
name: grabpl
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
@ -916,14 +1055,6 @@ steps:
|
||||
- verify-gen-cue
|
||||
image: grafana/build-container:1.6.2
|
||||
name: wire-install
|
||||
- commands:
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- go test -short -covermode=atomic -timeout=30m ./pkg/...
|
||||
depends_on:
|
||||
@ -936,6 +1067,60 @@ steps:
|
||||
- wire-install
|
||||
image: grafana/build-container:1.6.2
|
||||
name: test-backend-integration
|
||||
trigger:
|
||||
branch: main
|
||||
event:
|
||||
- push
|
||||
paths:
|
||||
exclude:
|
||||
- '*.md'
|
||||
- docs/**
|
||||
- latest.json
|
||||
type: docker
|
||||
volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-lint-backend
|
||||
node:
|
||||
type: no-parallel
|
||||
platform:
|
||||
arch: amd64
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.15.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- make gen-go
|
||||
depends_on: []
|
||||
image: grafana/build-container:1.6.2
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: golang:1.19.1
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- ./bin/build verify-drone
|
||||
depends_on:
|
||||
@ -960,6 +1145,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-build-e2e-publish
|
||||
node:
|
||||
@ -1035,17 +1222,6 @@ steps:
|
||||
- pkg/build/**
|
||||
repo:
|
||||
- grafana/grafana
|
||||
- image: grafana/drone-downstream
|
||||
name: trigger-enterprise-downstream
|
||||
settings:
|
||||
params:
|
||||
- SOURCE_BUILD_NUMBER=${DRONE_COMMIT}
|
||||
- SOURCE_COMMIT=${DRONE_COMMIT}
|
||||
repositories:
|
||||
- grafana/grafana-enterprise@main
|
||||
server: https://drone.grafana.net
|
||||
token:
|
||||
from_secret: drone_token
|
||||
- commands:
|
||||
- ./bin/build build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
|
||||
depends_on:
|
||||
@ -1378,6 +1554,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-integration-tests
|
||||
node:
|
||||
@ -1499,6 +1677,8 @@ depends_on:
|
||||
- main-test-backend
|
||||
- main-build-e2e-publish
|
||||
- main-integration-tests
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-windows
|
||||
platform:
|
||||
@ -1597,6 +1777,8 @@ depends_on:
|
||||
- main-build-e2e-publish
|
||||
- main-integration-tests
|
||||
- main-windows
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-publish
|
||||
node:
|
||||
@ -1661,6 +1843,47 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on:
|
||||
- main-publish
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: main-trigger-downstream
|
||||
node:
|
||||
type: no-parallel
|
||||
platform:
|
||||
arch: amd64
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- image: grafana/drone-downstream
|
||||
name: trigger-enterprise-downstream
|
||||
settings:
|
||||
params:
|
||||
- SOURCE_BUILD_NUMBER=${DRONE_COMMIT}
|
||||
- SOURCE_COMMIT=${DRONE_COMMIT}
|
||||
repositories:
|
||||
- grafana/grafana-enterprise@main
|
||||
server: https://drone.grafana.net
|
||||
token:
|
||||
from_secret: drone_token
|
||||
trigger:
|
||||
branch: main
|
||||
event:
|
||||
- push
|
||||
paths:
|
||||
exclude:
|
||||
- '*.md'
|
||||
- docs/**
|
||||
- latest.json
|
||||
type: docker
|
||||
volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on:
|
||||
- main-test-frontend
|
||||
- main-test-backend
|
||||
- main-build-e2e-publish
|
||||
@ -1699,6 +1922,8 @@ type: docker
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-oss-build-e2e-publish
|
||||
node:
|
||||
@ -2005,6 +2230,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-oss-test
|
||||
node:
|
||||
@ -2053,12 +2280,13 @@ steps:
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
image: golang:1.19.1
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
@ -2109,6 +2337,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-oss-integration-tests
|
||||
node:
|
||||
@ -2222,6 +2452,8 @@ depends_on:
|
||||
- release-oss-build-e2e-publish
|
||||
- release-oss-test
|
||||
- release-oss-integration-tests
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-oss-windows
|
||||
platform:
|
||||
@ -2706,12 +2938,13 @@ steps:
|
||||
image: grafana/build-container:1.6.2
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
image: golang:1.19.1
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
@ -2745,12 +2978,13 @@ steps:
|
||||
image: grafana/build-container:1.6.2
|
||||
name: test-frontend
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
image: golang:1.19.1
|
||||
name: lint-backend-enterprise2
|
||||
- commands:
|
||||
- go test -tags=pro -covermode=atomic -timeout=30m ./pkg/...
|
||||
@ -3022,6 +3256,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-docker-oss-public
|
||||
node:
|
||||
@ -3038,10 +3274,16 @@ steps:
|
||||
image: byrnedo/alpine-curl:0.1.8
|
||||
name: grabpl
|
||||
- commands:
|
||||
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition oss --base
|
||||
alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition oss
|
||||
depends_on:
|
||||
- grabpl
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
DOCKER_PASSWORD:
|
||||
from_secret: docker_password
|
||||
@ -3102,6 +3344,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-docker-enterprise-public
|
||||
node:
|
||||
@ -3118,10 +3362,16 @@ steps:
|
||||
image: byrnedo/alpine-curl:0.1.8
|
||||
name: grabpl
|
||||
- commands:
|
||||
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition enterprise
|
||||
--base alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition enterprise
|
||||
depends_on:
|
||||
- grabpl
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
DOCKER_PASSWORD:
|
||||
from_secret: docker_password
|
||||
@ -3165,6 +3415,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-docker-oss-security
|
||||
node:
|
||||
@ -3181,10 +3433,16 @@ steps:
|
||||
image: byrnedo/alpine-curl:0.1.8
|
||||
name: grabpl
|
||||
- commands:
|
||||
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition oss --base
|
||||
alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition oss
|
||||
depends_on:
|
||||
- grabpl
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
DOCKER_PASSWORD:
|
||||
from_secret: docker_password
|
||||
@ -3246,6 +3504,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-docker-enterprise-security
|
||||
node:
|
||||
@ -3262,10 +3522,16 @@ steps:
|
||||
image: byrnedo/alpine-curl:0.1.8
|
||||
name: grabpl
|
||||
- commands:
|
||||
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition enterprise
|
||||
--base alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition enterprise
|
||||
depends_on:
|
||||
- grabpl
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
DOCKER_PASSWORD:
|
||||
from_secret: docker_password
|
||||
@ -3310,6 +3576,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-artifacts-security
|
||||
node:
|
||||
@ -3350,6 +3618,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-artifacts-public
|
||||
node:
|
||||
@ -3390,6 +3660,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-npm-packages-public
|
||||
node:
|
||||
@ -3450,6 +3722,8 @@ depends_on:
|
||||
- publish-artifacts-public
|
||||
- publish-docker-oss-public
|
||||
- publish-docker-enterprise-public
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-packages-oss
|
||||
node:
|
||||
@ -3529,6 +3803,8 @@ depends_on:
|
||||
- publish-artifacts-public
|
||||
- publish-docker-oss-public
|
||||
- publish-docker-enterprise-public
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-packages-enterprise
|
||||
node:
|
||||
@ -3606,6 +3882,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: publish-artifacts-page
|
||||
node:
|
||||
@ -3643,6 +3921,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-branch-oss-build-e2e-publish
|
||||
node:
|
||||
@ -3918,6 +4198,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-branch-oss-test
|
||||
node:
|
||||
@ -3966,12 +4248,13 @@ steps:
|
||||
image: golang:1.19.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
image: golang:1.19.1
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
@ -4016,6 +4299,8 @@ volumes:
|
||||
clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-branch-oss-integration-tests
|
||||
node:
|
||||
@ -4123,6 +4408,8 @@ depends_on:
|
||||
- release-branch-oss-build-e2e-publish
|
||||
- release-branch-oss-test
|
||||
- release-branch-oss-integration-tests
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
kind: pipeline
|
||||
name: release-branch-oss-windows
|
||||
platform:
|
||||
@ -4583,12 +4870,13 @@ steps:
|
||||
image: grafana/build-container:1.6.2
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
image: golang:1.19.1
|
||||
name: lint-backend
|
||||
- commands:
|
||||
- yarn run prettier:check
|
||||
@ -4622,12 +4910,13 @@ steps:
|
||||
image: grafana/build-container:1.6.2
|
||||
name: test-frontend
|
||||
- commands:
|
||||
- apt-get update && apt-get install make
|
||||
- make lint-go
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
CGO_ENABLED: "1"
|
||||
image: grafana/build-container:1.6.2
|
||||
image: golang:1.19.1
|
||||
name: lint-backend-enterprise2
|
||||
- commands:
|
||||
- go test -tags=pro -covermode=atomic -timeout=30m ./pkg/...
|
||||
@ -5060,6 +5349,6 @@ kind: secret
|
||||
name: packages_secret_access_key
|
||||
---
|
||||
kind: signature
|
||||
hmac: 1bbfd995ded7c2d1c0330ef2009691577a6613ec98df296fc8ec4388b9898e2d
|
||||
hmac: 51941c93ec6753cf788db7ecefc8ffc63549ec675b23ae98fc27bcd7a22cddbb
|
||||
|
||||
...
|
||||
|
12
.eslintrc
12
.eslintrc
@ -19,6 +19,18 @@
|
||||
"alphabetize": { "order": "asc" }
|
||||
}
|
||||
],
|
||||
"no-restricted-imports": [
|
||||
"warn",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "react-redux",
|
||||
"importNames": ["useDispatch", "useSelector"],
|
||||
"message": "Please import from app/types instead."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
// Use typescript's no-redeclare for compatibility with overrides
|
||||
"no-redeclare": "off",
|
||||
|
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@ -191,3 +191,6 @@ lerna.json @grafana/frontend-ops
|
||||
/pkg/services/oauthtoken @grafana/grafana-authnz-team
|
||||
/pkg/services/teamguardian @grafana/grafana-authnz-team
|
||||
/pkg/services/serviceaccounts @grafana/grafana-authnz-team
|
||||
|
||||
# Grafana Partnerships Team
|
||||
/pkg/infra/httpclient/httpclientprovider/sigv4_middleware.go @grafana/grafana-partnerships-team
|
8
.github/metrics-collector.json
vendored
8
.github/metrics-collector.json
vendored
@ -16,6 +16,10 @@
|
||||
"name": "needs_more_info",
|
||||
"query": "label:\"needs more info\" is:open"
|
||||
},
|
||||
{
|
||||
"name": "triage_needs_confirmation",
|
||||
"query": "label:\"triage/needs-confirmation\" is:open"
|
||||
},
|
||||
{
|
||||
"name": "unlabeled",
|
||||
"query": "is:open is:issue no:label"
|
||||
@ -23,10 +27,6 @@
|
||||
{
|
||||
"name": "open_prs",
|
||||
"query": "is:open is:pr"
|
||||
},
|
||||
{
|
||||
"name": "milestone_7_4_open",
|
||||
"query": "is:open is:issue milestone:7.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ on:
|
||||
|
||||
jobs:
|
||||
workflow-call:
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.10
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.11
|
||||
with:
|
||||
frontend-path-regexp: public\/app\/plugins\/datasource\/(grafana-azure-monitor-datasource|cloud-monitoring|cloudwatch)
|
||||
backend-path-regexp: pkg\/tsdb\/(azuremonitor|cloudmonitoring|cloudwatch)
|
||||
|
17
.github/workflows/ox-code-coverage.yml
vendored
Normal file
17
.github/workflows/ox-code-coverage.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: Observability Experience test code coverage
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'pkg/services/queryhistory/**'
|
||||
- 'public/app/features/explore/**'
|
||||
- 'public/app/features/correlations/**'
|
||||
branches-ignore:
|
||||
- dependabot/**
|
||||
- backport-*
|
||||
|
||||
jobs:
|
||||
workflow-call:
|
||||
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.11
|
||||
with:
|
||||
frontend-path-regexp: public\/app\/features\/(explore|correlations)
|
||||
backend-path-regexp: pkg\/services\/(queryhistory)
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -40,6 +40,8 @@ tsconfig.tsbuildinfo
|
||||
# Enterprise devenv
|
||||
/devenv/docker/blocks/grafana-enterprise
|
||||
/devenv/docker/blocks/saml-enterprise
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
/devenv/docker/blocks/auth/saml-enterprise
|
||||
|
||||
/tmp
|
||||
tools/phantomjs/phantomjs
|
||||
@ -73,6 +75,7 @@ public/css/*.min.css
|
||||
|
||||
# devenv
|
||||
/devenv/docker-compose.yaml
|
||||
/devenv/docker-compose.override.yaml
|
||||
/devenv/.env
|
||||
/devenv/docker/blocks/tempo/tempo-data/
|
||||
|
||||
|
52
CHANGELOG.md
52
CHANGELOG.md
@ -1,3 +1,29 @@
|
||||
<!-- 9.1.6 START -->
|
||||
|
||||
# 9.1.6 (2022-09-20)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Auth:** Trigger auth token cleanup job. (Enterprise)
|
||||
- **DataSource:** Adding possibility to hide queries from the inspector. [#54892](https://github.com/grafana/grafana/pull/54892), [@mckn](https://github.com/mckn)
|
||||
- **Inspect:** Hide Actions tab when it is empty. [#55272](https://github.com/grafana/grafana/pull/55272), [@ryantxu](https://github.com/ryantxu)
|
||||
- **PanelMenu:** Remove hide legend action as it was showing on all panel types. [#54876](https://github.com/grafana/grafana/pull/54876), [@torkelo](https://github.com/torkelo)
|
||||
- **Provisioning Contact points:** Support disableResolveMessage via YAML. [#54122](https://github.com/grafana/grafana/pull/54122), [@mmusenbr](https://github.com/mmusenbr)
|
||||
- **PublicDashboards:** Support subpaths when generating pubdash url. [#55204](https://github.com/grafana/grafana/pull/55204), [@owensmallwood](https://github.com/owensmallwood)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix legacy migration crash when rule name is too long. [#55053](https://github.com/grafana/grafana/pull/55053), [@alexweav](https://github.com/alexweav)
|
||||
- **Alerting:** Fix send resolved notifications. [#54793](https://github.com/grafana/grafana/pull/54793), [@grobinson-grafana](https://github.com/grobinson-grafana)
|
||||
- **Azure Monitor:** Fix migration issue with MetricDefinitionsQuery template variable query types. [#55262](https://github.com/grafana/grafana/pull/55262), [@yaelleC](https://github.com/yaelleC)
|
||||
- **Browse:** Hide dashboard actions if user does not have enough permission. [#55218](https://github.com/grafana/grafana/pull/55218), [@lpskdl](https://github.com/lpskdl)
|
||||
- **ElasticSearch:** Fix dispatching queries at a wrong time. [#55225](https://github.com/grafana/grafana/pull/55225), [@svennergr](https://github.com/svennergr)
|
||||
- **Panel:** Disable legends when showLegend is false prior to schema v37. [#55126](https://github.com/grafana/grafana/pull/55126), [@ivanortegaalba](https://github.com/ivanortegaalba)
|
||||
- **Prometheus:** Fix metadata requests for browser access mode. [#55403](https://github.com/grafana/grafana/pull/55403), [@itsmylife](https://github.com/itsmylife)
|
||||
- **Search:** Avoid requesting all dashboards when in Folder View. [#55169](https://github.com/grafana/grafana/pull/55169), [@JoaoSilvaGrafana](https://github.com/JoaoSilvaGrafana)
|
||||
- **TablePanel/StatPanel:** Fix values not being visible when background transparent. [#55092](https://github.com/grafana/grafana/pull/55092), [@mdvictor](https://github.com/mdvictor)
|
||||
|
||||
<!-- 9.1.6 END -->
|
||||
<!-- 9.1.5 START -->
|
||||
|
||||
# 9.1.5 (2022-09-12)
|
||||
@ -392,6 +418,18 @@ In Prometheus, browser access mode was deprecated in Grafana 7.4.0 and removed i
|
||||
- **Grafana/UI:** Add ColorPickerInput component. [#52222](https://github.com/grafana/grafana/pull/52222), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **Plugins:** Validate root URLs when signing private plugins via grafana-toolkit. [#51968](https://github.com/grafana/grafana/pull/51968), [@wbrowne](https://github.com/wbrowne)
|
||||
|
||||
<!-- 9.0.9 START -->
|
||||
|
||||
# 9.0.9 (2022-09-20)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **AngularPanels:** Fixing changing angular panel options not taking having affect when coming back from panel edit. [#54834](https://github.com/grafana/grafana/pull/54834), [@grafanabot](https://github.com/grafanabot)
|
||||
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55498](https://github.com/grafana/grafana/pull/55498), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **FIX:** RBAC prevents deleting empty snapshots (#54385). [#54509](https://github.com/grafana/grafana/pull/54509), [@gamab](https://github.com/gamab)
|
||||
|
||||
<!-- 9.0.9 END -->
|
||||
|
||||
<!-- 9.0.8 START -->
|
||||
|
||||
# 9.0.8 (2022-08-30)
|
||||
@ -1124,6 +1162,20 @@ In the Loki data source, for consistency and performance reasons, we changed how
|
||||
|
||||
The dependency to [grafana/aws-sdk](https://github.com/grafana/grafana-aws-sdk-react) is moved from [grafana/ui](https://github.com/grafana/grafana/blob/main/packages/grafana-ui/package.json) to the plugin. This means that any plugin that use SIGV4 auth need to pass a SIGV4 editor component as a prop to the `DataSourceHttpSettings` component. Issue [#43559](https://github.com/grafana/grafana/issues/43559)
|
||||
|
||||
<!-- 8.5.13 START -->
|
||||
|
||||
# 8.5.13 (2022-09-20)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Plugins:** Expose @emotion/react to plugins to prevent load failures. [#55297](https://github.com/grafana/grafana/pull/55297), [@jackw](https://github.com/jackw)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55495](https://github.com/grafana/grafana/pull/55495), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
|
||||
<!-- 8.5.13 END -->
|
||||
|
||||
<!-- 8.5.11 START -->
|
||||
|
||||
# 8.5.11 (2022-08-30)
|
||||
|
@ -210,7 +210,10 @@ check_for_updates = true
|
||||
check_for_plugin_updates = true
|
||||
|
||||
# Google Analytics universal tracking code, only enabled if you specify an id here
|
||||
google_analytics_ua_id =
|
||||
google_analytics_ua_id =
|
||||
|
||||
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
||||
google_analytics_4_id =
|
||||
|
||||
# Google Tag Manager ID, only enabled if you specify an id here
|
||||
google_tag_manager_id =
|
||||
@ -1039,6 +1042,11 @@ zipkin_propagation = false
|
||||
# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
|
||||
disable_shared_zipkin_spans = false
|
||||
|
||||
[tracing.opentelemetry]
|
||||
|
||||
# attributes that will always be included in when creating new spans. ex (key1:value1,key2:value2)
|
||||
custom_attributes =
|
||||
|
||||
[tracing.opentelemetry.jaeger]
|
||||
# jaeger destination (ex http://localhost:14268/api/traces)
|
||||
address =
|
||||
@ -1288,3 +1296,19 @@ scheduler_interval =
|
||||
[storage]
|
||||
# Allow uploading SVG files without sanitization.
|
||||
allow_unsanitized_svg_upload = false
|
||||
|
||||
|
||||
#################################### Search ################################################
|
||||
|
||||
[search]
|
||||
# Defines the number of dashboards loaded at once in a batch during a full reindex.
|
||||
# This is a temporary settings that might be removed in the future.
|
||||
dashboard_loading_batch_size = 200
|
||||
|
||||
# Defines the frequency of a full search reindex.
|
||||
# This is a temporary settings that might be removed in the future.
|
||||
full_reindex_interval = 5m
|
||||
|
||||
# Defines the frequency of partial index updates based on recent changes such as dashboard updates.
|
||||
# This is a temporary settings that might be removed in the future.
|
||||
index_update_interval = 10s
|
||||
|
@ -218,6 +218,9 @@
|
||||
# Google Analytics universal tracking code, only enabled if you specify an id here
|
||||
;google_analytics_ua_id =
|
||||
|
||||
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
||||
;google_analytics_4_id =
|
||||
|
||||
# Google Tag Manager ID, only enabled if you specify an id here
|
||||
;google_tag_manager_id =
|
||||
|
||||
@ -1008,6 +1011,10 @@
|
||||
# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
|
||||
;disable_shared_zipkin_spans = false
|
||||
|
||||
[tracing.opentelemetry]
|
||||
# attributes that will always be included in when creating new spans. ex (key1:value1,key2:value2)
|
||||
;custom_attributes = key1:value1,key2:value2
|
||||
|
||||
[tracing.opentelemetry.jaeger]
|
||||
# jaeger destination (ex http://localhost:14268/api/traces)
|
||||
; address = http://localhost:14268/api/traces
|
||||
|
276
contribute/engineering/backend/instrumentation.md
Normal file
276
contribute/engineering/backend/instrumentation.md
Normal file
@ -0,0 +1,276 @@
|
||||
# Instrumenting Grafana
|
||||
|
||||
Guidance, conventions and best practices for instrumenting Grafana using logs, metrics and traces.
|
||||
|
||||
## Logs
|
||||
|
||||
Logs are files that record events, warnings and errors as they occur within a software environment. Most logs include contextual information, such as the time an event occurred and which user or endpoint was associated with it.
|
||||
|
||||
### Usage
|
||||
|
||||
Use the _pkg/infra/log_ package to create a named structured logger. Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
logger := log.New("my-logger")
|
||||
logger.Debug("Debug msg")
|
||||
logger.Info("Info msg")
|
||||
logger.Warning("Warning msg")
|
||||
logger.Error("Error msg", "error", fmt.Errorf("BOOM"))
|
||||
```
|
||||
|
||||
### Naming conventions
|
||||
|
||||
Name the logger using lowercase characters, e.g. `log.New("my-logger")` using snake_case or kebab-case styling.
|
||||
|
||||
Prefix the logger name with an area name when using different loggers across a feature or related packages, e.g. `log.New("plugin.loader")` and `log.New("plugin.client")`.
|
||||
|
||||
Start the log message with a capital letter, e.g. `logger.Info("Hello world")` instead of `logger.Info("hello world")`. The log message should be an identifier for the log entry, avoid parameterization in favor of key-value pairs for additional data.
|
||||
|
||||
Prefer using camelCase style when naming log keys, e.g. _remoteAddr_, to be consistent with Go identifiers.
|
||||
|
||||
Use the key _error_ when logging Go errors, e.g. `logger.Error("Something failed", "error", fmt.Errorf("BOOM"))`.
|
||||
|
||||
### Validate and sanitize input coming from user input
|
||||
|
||||
If log messages or key/value pairs originates from user input they **should** be validated and sanitized.
|
||||
|
||||
Be **careful** to not expose any sensitive information in log messages e.g. secrets, credentials etc. It's especially easy to do by mistake when including a struct as value.
|
||||
|
||||
### Log levels
|
||||
|
||||
When to use which log level?
|
||||
|
||||
- **Debug:** Informational messages of high frequency and/or less-important messages during normal operations.
|
||||
- **Info:** Informational messages of low frequency and/or important messages.
|
||||
- **Warning:** Should in normal cases not be used/needed. If used should be actionable.
|
||||
- **Error:** Error messages indicating some operation failed (with an error) and the program didn't have a way of handle the error.
|
||||
|
||||
### Contextual logging
|
||||
|
||||
Use a contextual logger to include additional key/value pairs attached to `context.Context`, e.g. `traceID`, to allow correlating logs with traces and/or correlate logs with a common identifier.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
var logger = log.New("my-logger")
|
||||
|
||||
func doSomething(ctx context.Context) {
|
||||
ctxLogger := logger.FromContext(ctx)
|
||||
ctxLogger.Debug("Debug msg")
|
||||
ctxLogger.Info("Info msg")
|
||||
ctxLogger.Warning("Warning msg")
|
||||
ctxLogger.Error("Error msg", "error", fmt.Errorf("BOOM"))
|
||||
}
|
||||
```
|
||||
|
||||
### Enable certain log levels for certain loggers
|
||||
|
||||
During development it's convenient to enable certain log level, e.g. debug, for certain loggers to minimize the generated log output and make it easier to find things. See [[log.filters]](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#filters) for information how to configure this.
|
||||
|
||||
It's also possible to configure multiple loggers:
|
||||
|
||||
```ini
|
||||
[log]
|
||||
filters = rendering:debug \
|
||||
; alerting.notifier:debug \
|
||||
oauth.generic_oauth:debug \
|
||||
; oauth.okta:debug \
|
||||
; tsdb.postgres:debug \
|
||||
; tsdb.mssql:debug \
|
||||
; provisioning.plugins:debug \
|
||||
; provisioning:debug \
|
||||
; provisioning.dashboard:debug \
|
||||
; provisioning.datasources:debug \
|
||||
datasources:debug \
|
||||
data-proxy-log:debug
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
Metrics are quantifiable measurements that reflect the health and performance of applications or infrastructure.
|
||||
|
||||
Consider using metrics to provide real-time insight into the state of resources. If you want to know how responsive your application is or identify anomalies that could be early signs of a performance issue, metrics are a key source of visibility.
|
||||
|
||||
### Metric types
|
||||
|
||||
See [Prometheus metric types](https://prometheus.io/docs/concepts/metric_types/) for a list and description of the different metric types you can use and when to use them.
|
||||
|
||||
There are many possible types of metrics that can be tracked. One popular method for defining metrics is the [RED method](https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/).
|
||||
|
||||
### Naming conventions
|
||||
|
||||
Use the namespace _grafana_ as that would prefix any defined metric names with `grafana_`. This will make it clear for operators that any metric named `grafana_*` belongs to Grafana.
|
||||
|
||||
Use snake*case style when naming metrics, e.g. \_http_request_duration_seconds* instead of _httpRequestDurationSeconds_.
|
||||
|
||||
Use snake*case style when naming labels, e.g. \_status_code* instead of _statusCode_.
|
||||
|
||||
If metric type is a _counter_, name it with a `_total` suffix, e.g. _http_requests_total_.
|
||||
|
||||
If metric type is a _histogram_ and you're measuring duration, name it with a `_<unit>` suffix, e.g. _http_request_duration_seconds_.
|
||||
|
||||
If metric type is a _gauge_, name it to denote it's a value that can increase and decrease , e.g. _http_request_in_flight_.
|
||||
|
||||
### Label values and high cardinality
|
||||
|
||||
Be careful with what label values you add/accept. Using/allowing too many label values could result in [high cardinality problems](https://grafana.com/blog/2022/02/15/what-are-cardinality-spikes-and-why-do-they-matter/).
|
||||
|
||||
If label values originates from user input they **should** be validated. Use `metricutil.SanitizeLabelName(<label value>`) from _pkg/infra/metrics/metricutil_ package to sanitize label names. Very **important** to only allow a pre-defined set of labels to minimize the risk of high cardinality problems.
|
||||
|
||||
Be **careful** to not expose any sensitive information in label values, e.g. secrets, credentials etc.
|
||||
|
||||
### Guarantee the existence of metrics
|
||||
|
||||
If you want to guarantee the existence of metrics before any observations has happened there's a couple of helper methods available in the _pkg/infra/metrics/metricutil_ package.
|
||||
|
||||
### How to collect and visualize metrics locally
|
||||
|
||||
1. Start Prometheus
|
||||
|
||||
```bash
|
||||
make devenv sources=prometheus
|
||||
```
|
||||
|
||||
2. Use Grafana Explore or dashboards to query any exported Grafana metrics
|
||||
|
||||
## Traces
|
||||
|
||||
A distributed trace is data that tracks an application request as it flows through the various parts of an application. The trace records how long it takes each application component to process the request and pass the result to the next component. Traces can also identify which parts of the application trigger an error.
|
||||
|
||||
### Usage
|
||||
|
||||
Grafana currently supports two tracing implementations, [OpenTelemetry](https://opentelemetry.io/) and [OpenTracing](https://opentracing.io/). OpenTracing is deprecated, but still supported until we remove it. The two different implementations implements the `Tracer` and `Span` interfaces, defined in the _pkg/infra/tracing_ package, which you can use to create traces and spans. To get a hold of a `Tracer` you would need to get it injected as dependency into your service, see [Services](../../architecture/backend/services.md) for more details.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
type MyService struct {
|
||||
tracer tracing.Tracer
|
||||
}
|
||||
|
||||
func ProvideService(tracer tracing.Tracer) *MyService {
|
||||
return &MyService{
|
||||
tracer: tracer,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MyService) Hello(ctx context.Context, name string) (string, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "MyService.Hello")
|
||||
// this make sure the span is marked as finished when this
|
||||
// method ends to allow the span to be flushed and sent to
|
||||
// storage backend.
|
||||
defer span.End()
|
||||
|
||||
// Add some event to show Events usage
|
||||
span.AddEvents(
|
||||
[]string{"message"},
|
||||
[]tracing.EventValue{
|
||||
{Str: "checking name..."},
|
||||
})
|
||||
|
||||
if name == "" {
|
||||
err := fmt.Errorf("name cannot be empty")
|
||||
|
||||
// record err as an exception span event for this span
|
||||
span.RecordError(err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Add some other event to show Events usage
|
||||
span.AddEvents(
|
||||
[]string{"message"},
|
||||
[]tracing.EventValue{
|
||||
{Str: "name checked"},
|
||||
})
|
||||
|
||||
// Add attribute to show Attributes usage
|
||||
span.SetAttributes("my_service.name", name, attribute.Key("my_service.name").String(name))
|
||||
|
||||
return fmt.Sprintf("Hello %s", name), nil
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Naming conventions
|
||||
|
||||
Span names should follow the [guidelines from OpenTelemetry](https://opentelemetry.io/docs/reference/specification/trace/api/#span).
|
||||
|
||||
| Span Name | Guidance |
|
||||
| ----------------------- | -------------------------------------------------------- |
|
||||
| get | Too general |
|
||||
| get_account/42 | Too specific |
|
||||
| get_account | Good, and account_id=42 would make a nice Span attribute |
|
||||
| get_account/{accountId} | Also good (using the “HTTP route”) |
|
||||
|
||||
Span attribute and span event attributes should follow the [Attribute naming specification from OpenTelemetry](https://opentelemetry.io/docs/reference/specification/common/attribute-naming/). Good attribute key examples:
|
||||
|
||||
- service.version
|
||||
- http.status_code
|
||||
|
||||
See [Trace semantic conventions from OpenTelemetry](https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/) for additional conventions regarding well-known protocols and operations.
|
||||
|
||||
### Span names and high cardinality
|
||||
|
||||
Be careful with what span names you add/accept. Using/allowing too many span names could result in high cardinality problems.
|
||||
|
||||
### Validate and sanitize input coming from user input
|
||||
|
||||
If span names, attribute or event values originates from user input they **should** be validated and sanitized. It's very **important** to only allow a pre-defined set of span names to minimize the risk of high cardinality problems.
|
||||
|
||||
Be **careful** to not expose any sensitive information in span names, attribute or event values, e.g. secrets, credentials etc.
|
||||
|
||||
### How to collect, visualize and query traces (and correlate logs with traces) locally
|
||||
|
||||
1. Start Jaeger
|
||||
|
||||
```bash
|
||||
make devenv sources=jaeger
|
||||
```
|
||||
|
||||
2. Enable tracing in Grafana
|
||||
|
||||
opentelemetry tracing (recommended):
|
||||
|
||||
```ini
|
||||
[tracing.opentelemetry.jaeger]
|
||||
address = http://localhost:14268/api/traces
|
||||
```
|
||||
|
||||
opentracing tracing (deprecated/not recommended):
|
||||
|
||||
```ini
|
||||
[tracing.jaeger]
|
||||
address = localhost:6831
|
||||
```
|
||||
|
||||
3. Search/browse collected logs and traces in Grafana Explore
|
||||
|
||||
You need provisioned gdev-jaeger and gdev-loki datasources, see [developer dashboard and data sources](https://github.com/grafana/grafana/tree/main/devenv#developer-dashboards-and-data-sources) for setup instructions.
|
||||
|
||||
Open Grafana explore and select gdev-loki datasource and use the query `{filename="/var/log/grafana/grafana.log"} | logfmt`.
|
||||
|
||||
You can then inspect any log message that includes a `traceID` and from there click on `gdev-jaeger` to split view and inspect the trace in question.
|
||||
|
||||
4. Search/browse collected traces in Jaeger UI
|
||||
|
||||
You can open http://localhost:16686 to use the Jaeger UI for browsing and searching traces.
|
536
devenv/dev-dashboards/transforms/reuse.json
Normal file
536
devenv/dev-dashboards/transforms/reuse.json
Normal file
@ -0,0 +1,536 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 1394,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"content": "Dashboard queries allow re-using the same results from one panel in another panel context.\n\nThis dashboard shows a single panel that makes a real query and applies transformations. The other panels, all use the same results rather than make their own query requests.",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto",
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 18,
|
||||
"w": 7,
|
||||
"x": 0,
|
||||
"y": 3
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"footer": {
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "flight_info_by_state.csv",
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
},
|
||||
{
|
||||
"csvFileName": "population_by_state.csv",
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Raw data -- with outer join",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "seriesToColumns",
|
||||
"options": {
|
||||
"byField": "State",
|
||||
"mode": "outer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {
|
||||
"1980 population_by_state.csv": true,
|
||||
"2000 population_by_state.csv": true,
|
||||
"DestLocation flight_info_by_state.csv": true,
|
||||
"Lat flight_info_by_state.csv": true,
|
||||
"Lng flight_info_by_state.csv": true,
|
||||
"Price flight_info_by_state.csv": true
|
||||
},
|
||||
"indexByName": {},
|
||||
"renameByName": {
|
||||
"2020 population_by_state.csv": "2020 population",
|
||||
"Count flight_info_by_state.csv": "Flight count",
|
||||
"Price flight_info_by_state.csv": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto",
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 11,
|
||||
"x": 7,
|
||||
"y": 3
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"footer": {
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Reused data (without transform)",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 3
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"name": "Layer 0",
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showMeasure": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"showLegend": true,
|
||||
"style": {
|
||||
"color": {
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"opacity": 0.4,
|
||||
"rotation": {
|
||||
"fixed": 0,
|
||||
"max": 360,
|
||||
"min": -360,
|
||||
"mode": "mod"
|
||||
},
|
||||
"size": {
|
||||
"field": "Flight count",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
},
|
||||
"symbol": {
|
||||
"fixed": "img/icons/marker/circle.svg",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"textConfig": {
|
||||
"fontSize": 12,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"textAlign": "center",
|
||||
"textBaseline": "middle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "lookup"
|
||||
},
|
||||
"name": "Flight count",
|
||||
"tooltip": true,
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "details"
|
||||
},
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 35.70008,
|
||||
"lon": -93.558296,
|
||||
"zoom": 3.09
|
||||
}
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A",
|
||||
"withTransforms": true
|
||||
}
|
||||
],
|
||||
"title": "Reused data (without transform)",
|
||||
"type": "geomap"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto",
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 11,
|
||||
"x": 7,
|
||||
"y": 12
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"footer": {
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A",
|
||||
"withTransforms": true
|
||||
}
|
||||
],
|
||||
"title": "Reused data (with transform)",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 12
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"name": "Layer 0",
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showMeasure": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"showLegend": true,
|
||||
"style": {
|
||||
"color": {
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"opacity": 0.4,
|
||||
"rotation": {
|
||||
"fixed": 0,
|
||||
"max": 360,
|
||||
"min": -360,
|
||||
"mode": "mod"
|
||||
},
|
||||
"size": {
|
||||
"field": "2020 population",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
},
|
||||
"symbol": {
|
||||
"fixed": "img/icons/marker/circle.svg",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"textConfig": {
|
||||
"fontSize": 12,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"textAlign": "center",
|
||||
"textBaseline": "middle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "lookup"
|
||||
},
|
||||
"name": "2022 Population",
|
||||
"tooltip": true,
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "details"
|
||||
},
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 35.70008,
|
||||
"lon": -93.558296,
|
||||
"zoom": 3.09
|
||||
}
|
||||
},
|
||||
"pluginVersion": "9.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 2,
|
||||
"refId": "A",
|
||||
"withTransforms": true
|
||||
}
|
||||
],
|
||||
"title": "Reused data (with transform)",
|
||||
"type": "geomap"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 37,
|
||||
"style": "dark",
|
||||
"tags": ["devenv"],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Reuse dashboard queries",
|
||||
"uid": "fYGWTVW4k"
|
||||
}
|
@ -80,6 +80,9 @@ async function elasticSetupIndexTemplate() {
|
||||
location: {
|
||||
type: 'geo_point',
|
||||
},
|
||||
shapes: {
|
||||
type: 'nested',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -108,6 +111,15 @@ function getRandomLogItem(counter, timestamp) {
|
||||
level: chooseRandomElement(['info', 'info', 'error']),
|
||||
// location: chooseRandomElement(LOCATIONS),
|
||||
location: makeRandomPoint(),
|
||||
shapes: Math.random() < 0.5 ? [
|
||||
{"type": "triangle"},
|
||||
{"type": "square"},
|
||||
] : [
|
||||
{"type": "triangle"},
|
||||
{"type": "triangle"},
|
||||
{"type": "triangle"},
|
||||
{"type": "square"},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
# You need to run 'sysctl -w vm.max_map_count=262144' on the host machine
|
||||
|
||||
elasticsearch7:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
|
||||
command: elasticsearch -E "discovery.type=single-node"
|
||||
ports:
|
||||
- "12200:9200"
|
||||
- "12300:9300"
|
||||
|
||||
fake-elastic7-data:
|
||||
image: grafana/fake-data-gen
|
||||
links:
|
||||
- elasticsearch7
|
||||
environment:
|
||||
FD_SERVER: elasticsearch7
|
||||
FD_DATASOURCE: elasticsearch7
|
||||
FD_PORT: 9200
|
||||
|
||||
filebeat7:
|
||||
image: docker.elastic.co/beats/filebeat-oss:7.17.0
|
||||
command: filebeat -e -strict.perms=false
|
||||
volumes:
|
||||
- ./docker/blocks/elastic7/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
|
||||
- /var/log:/var/log:ro
|
||||
- ../data/log:/var/log/grafana:ro
|
||||
|
||||
metricbeat7:
|
||||
image: docker.elastic.co/beats/metricbeat-oss:7.17.0
|
||||
command: metricbeat -e -strict.perms=false
|
||||
user: root
|
||||
volumes:
|
||||
- ./docker/blocks/elastic7/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
kibana7:
|
||||
image: docker.elastic.co/kibana/kibana-oss:7.0.0
|
||||
ports:
|
||||
- "5601:5601"
|
||||
links:
|
||||
- elasticsearch7
|
||||
environment:
|
||||
ELASTICSEARCH_HOSTS: http://elasticsearch7:9200
|
@ -1,2 +0,0 @@
|
||||
script.inline: on
|
||||
script.indexed: on
|
@ -1,44 +0,0 @@
|
||||
# You need to run 'sysctl -w vm.max_map_count=262144' on the host machine
|
||||
|
||||
elasticsearch77:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.7.1
|
||||
command: elasticsearch
|
||||
environment:
|
||||
- "discovery.type=single-node"
|
||||
ports:
|
||||
- "13200:9200"
|
||||
- "13300:9300"
|
||||
|
||||
fake-elastic77-data:
|
||||
image: grafana/fake-data-gen
|
||||
links:
|
||||
- elasticsearch77
|
||||
environment:
|
||||
FD_SERVER: elasticsearch77
|
||||
FD_DATASOURCE: elasticsearch7
|
||||
FD_PORT: 9200
|
||||
|
||||
filebeat77:
|
||||
image: docker.elastic.co/beats/filebeat:7.7.1
|
||||
command: filebeat -e -strict.perms=false
|
||||
volumes:
|
||||
- ./docker/blocks/elastic77/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
|
||||
- /var/log:/var/log:ro
|
||||
- ../data/log:/var/log/grafana:ro
|
||||
|
||||
metricbeat77:
|
||||
image: docker.elastic.co/beats/metricbeat:7.7.1
|
||||
command: metricbeat -e -strict.perms=false
|
||||
user: root
|
||||
volumes:
|
||||
- ./docker/blocks/elastic77/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
kibana77:
|
||||
image: docker.elastic.co/kibana/kibana:7.7.1
|
||||
ports:
|
||||
- "5601:5601"
|
||||
links:
|
||||
- elasticsearch77
|
||||
environment:
|
||||
ELASTICSEARCH_HOSTS: http://elasticsearch77:9200
|
@ -1,3 +0,0 @@
|
||||
script.inline: on
|
||||
script.indexed: on
|
||||
xpack.license.self_generated.type: basic
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
metricbeat.config:
|
||||
modules:
|
||||
path: ${path.config}/modules.d/*.yml
|
||||
# Reload module configs as they change:
|
||||
reload.enabled: false
|
||||
|
||||
metricbeat.autodiscover:
|
||||
providers:
|
||||
- type: docker
|
||||
hints.enabled: true
|
||||
|
||||
metricbeat.modules:
|
||||
- module: docker
|
||||
metricsets:
|
||||
- "container"
|
||||
- "cpu"
|
||||
- "diskio"
|
||||
- "healthcheck"
|
||||
- "info"
|
||||
#- "image"
|
||||
- "memory"
|
||||
- "network"
|
||||
hosts: ["unix:///var/run/docker.sock"]
|
||||
period: 10s
|
||||
enabled: true
|
||||
|
||||
processors:
|
||||
- add_cloud_metadata: ~
|
||||
|
||||
output.elasticsearch:
|
||||
hosts: ["elasticsearch77:9200"]
|
||||
index: "metricbeat-%{+yyyy.MM.dd}"
|
||||
|
||||
setup.template.name: "metricbeat"
|
||||
setup.template.pattern: "metricbeat-*"
|
||||
setup.template.settings:
|
||||
index.number_of_shards: 1
|
||||
index.number_of_replicas: 1
|
1
devenv/docker/blocks/elasticstack/.env
Normal file
1
devenv/docker/blocks/elasticstack/.env
Normal file
@ -0,0 +1 @@
|
||||
elastic_version=8.4.1
|
32
devenv/docker/blocks/elasticstack/docker-compose.yaml
Normal file
32
devenv/docker/blocks/elasticstack/docker-compose.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
elasticsearch:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:${elastic_version}
|
||||
environment:
|
||||
- "discovery.type=single-node"
|
||||
- "xpack.license.self_generated.type=basic"
|
||||
- "xpack.security.enabled=false"
|
||||
ports:
|
||||
- 9200:9200
|
||||
|
||||
kibana:
|
||||
image: docker.elastic.co/kibana/kibana:${elastic_version}
|
||||
environment:
|
||||
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
|
||||
ports:
|
||||
- 5601:5601
|
||||
|
||||
metricbeat-host:
|
||||
image: docker.elastic.co/beats/metricbeat-oss:${elastic_version}
|
||||
command: metricbeat -e -strict.perms=false
|
||||
user: root
|
||||
volumes:
|
||||
- ./docker/blocks/elasticstack/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
filebeat-host:
|
||||
image: docker.elastic.co/beats/filebeat-oss:${elastic_version}
|
||||
command: filebeat -e -strict.perms=false
|
||||
volumes:
|
||||
- ./docker/blocks/elasticstack/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
|
||||
# - /var/log:/var/log:ro
|
||||
- ../data/log:/var/log/grafana:ro
|
@ -1080,13 +1080,13 @@ filebeat.inputs:
|
||||
#-------------------------- Elasticsearch output -------------------------------
|
||||
output.elasticsearch:
|
||||
# Boolean flag to enable or disable the output module.
|
||||
#enabled: true
|
||||
enabled: true
|
||||
|
||||
# Array of hosts to connect to.
|
||||
# Scheme and port can be left out and will be set to the default (http and 9200)
|
||||
# In case you specify and additional path, the scheme is required: http://localhost:9200/path
|
||||
# IPv6 addresses should always be defined as: https://[2001:db8::1]:9200
|
||||
hosts: ["elasticsearch7:9200"]
|
||||
hosts: ["elasticsearch:9200"]
|
||||
|
||||
# Enabled ilm (beta) to use index lifecycle management instead daily indices.
|
||||
#ilm.enabled: false
|
@ -28,7 +28,7 @@ processors:
|
||||
- add_cloud_metadata: ~
|
||||
|
||||
output.elasticsearch:
|
||||
hosts: ["elasticsearch7:9200"]
|
||||
hosts: ["elasticsearch:9200"]
|
||||
index: "metricbeat-%{+yyyy.MM.dd}"
|
||||
|
||||
setup.template.name: "metricbeat"
|
@ -25,7 +25,7 @@
|
||||
promtail:
|
||||
image: grafana/promtail:master
|
||||
volumes:
|
||||
- ./docker/blocks/loki/config.yaml:/etc/promtail/docker-config.yaml
|
||||
- ./docker/blocks/loki-promtail/promtail-config.yaml:/etc/promtail/docker-config.yaml
|
||||
- /var/log:/var/log
|
||||
- ../data/log:/var/log/grafana
|
||||
command:
|
||||
|
@ -3,7 +3,7 @@ aliases:
|
||||
- /docs/grafana/latest/alerting/fundamentals/
|
||||
- /docs/grafana/latest/alerting/metrics/
|
||||
- /docs/grafana/latest/alerting/unified-alerting/fundamentals/
|
||||
title: Alerting fundamentals
|
||||
title: Explore Grafana Alerting
|
||||
weight: 105
|
||||
---
|
||||
|
||||
|
16
docs/sources/alerting/set-up/_index.md
Normal file
16
docs/sources/alerting/set-up/_index.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
aliases:
|
||||
- /docs/grafana/latest/alerting/set-up/
|
||||
- /docs/grafana/latest/alerting/set-up/
|
||||
- /docs/grafana/latest/alerting/unified-alerting/set-up/
|
||||
title: Set up Grafana Alerting
|
||||
weight: 107
|
||||
---
|
||||
|
||||
# Set up Grafana Alerting
|
||||
|
||||
Configure the features and integrations that you need to create and manage your alerts.
|
||||
|
||||
- [Configure Alertmanager](https://grafana.com/docs/grafana/latest/alerting/set-up/configure-alertmanager/)
|
||||
- [Provision Grafana Alerting resources](https://grafana.com/docs/grafana/latest/alerting/set-up/provision-alerting-resources/)
|
||||
- [Connect Grafana Alerting to Grafana OnCall](https://grafana.com/docs/oncall/latest/integrations/available-integrations/add-grafana-alerting/)
|
36
docs/sources/alerting/set-up/configure-alertmanager/index.md
Normal file
36
docs/sources/alerting/set-up/configure-alertmanager/index.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
aliases:
|
||||
- /docs/grafana/latest/alerting/configure-alertmanager
|
||||
- /docs/grafana/latest/alerting/configure-alertmanager
|
||||
description: Configure Alertmanager
|
||||
keywords:
|
||||
- grafana
|
||||
- alerting
|
||||
- set up
|
||||
- configure
|
||||
- Alertmanager
|
||||
title: Configure Alertmanager
|
||||
weight: 100
|
||||
---
|
||||
|
||||
# Configure Alertmanager
|
||||
|
||||
Configure Alertmanager from Grafana Alerting to group and manage alert rules, adding a layer of orchestration on top of your external alerting engine.
|
||||
|
||||
## Add a new external Alertmanager
|
||||
|
||||
1. In the Grafana menu, click the Alerting (bell) icon to open the Alerting page listing existing alerts.
|
||||
2. Click **Admin** and then scroll down to the External Alertmanager section.
|
||||
3. Click **Add Alertmanager** and a modal opens.
|
||||
4. Add the URL and the port for the external Alertmanager. You do not need to specify the path suffix, for example, `/api/v(1|2)/alerts`. Grafana automatically adds this.
|
||||
|
||||
The external URL is listed in the table with a pending status. Once Grafana verifies that the Alertmanager is discovered, the status changes to active. No requests are made to the external Alertmanager at this point; the verification signals that alerts are ready to be sent.
|
||||
|
||||
### Edit an external Alertmanager
|
||||
|
||||
1. Click the pen symbol to the right of the Alertmanager row in the table.
|
||||
2. When the edit modal opens, you can view all the URLs that were added.
|
||||
|
||||
The edited URL will be pending until Grafana verifies it again.
|
||||
|
||||
{{< figure max-width="40%" src="/static/img/docs/alerting/unified/ext-alertmanager-active.png" max-width="650px" caption="External Alertmanagers" >}}
|
@ -0,0 +1,726 @@
|
||||
---
|
||||
aliases:
|
||||
- /docs/grafana/latest/alerting/provision-alerting-resources
|
||||
- /docs/grafana/latest/alerting/provision-alerting-resources
|
||||
description: Provision alerting resources
|
||||
keywords:
|
||||
- grafana
|
||||
- alerting
|
||||
- set up
|
||||
- configure
|
||||
- provisioning
|
||||
title: Provision Grafana Alerting resources
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Provision Grafana Alerting resources
|
||||
|
||||
Alerting infrastructure is often complex, with many pieces of the pipeline that often live in different places. Scaling this across multiple teams and organizations is an especially challenging task. Grafana Alerting provisioning makes this process easier by enabling you to create, manage, and maintain your alerting data in a way that best suits your organization.
|
||||
|
||||
There are three options to choose from:
|
||||
|
||||
1. Use file provisioning to provision your Grafana Alerting resources, such as alert rules and contact points, through files on disk.
|
||||
|
||||
1. Provision your alerting resources using the Grafana HTTP API.
|
||||
|
||||
For more information on the Grafana Alerting provisioning API, refer to [Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/).
|
||||
|
||||
1. Provision your alerting resources using Terraform.
|
||||
|
||||
**Note:**
|
||||
|
||||
Currently, provisioning for Grafana Alerting supports alert rules, contact points, mute timings, and templates. Provisioned alerting resources can only be edited in the source that created them and not from within Grafana or any other source. For example, if you provision your alerting resources using files from disk, you cannot edit the data in Terraform or from within Grafana.
|
||||
|
||||
**Useful Links:**
|
||||
|
||||
[Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/)
|
||||
|
||||
[Grafana Cloud provisioning](https://grafana.com/docs/grafana-cloud/infrastructure-as-code/terraform/)
|
||||
|
||||
[Grafana Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning)
|
||||
|
||||
## Create and manage alerting resources using file provisioning
|
||||
|
||||
Provision your alerting resources using files from disk. When you start Grafana, the data from these files is created in your Grafana system. Grafana adds any new resources you created, updates any that you changed, and deletes old ones.
|
||||
|
||||
Arrange your files in a directory in a way that best suits your use case. For example, you can choose a team-based layout where every team has its own file, you can have one big file for all your teams; or you can have one file per resource type.
|
||||
|
||||
Details on how to set up the files and which fields are required for each object are listed below depending on which resource you are provisioning.
|
||||
|
||||
**Note:**
|
||||
|
||||
Provisioning takes place during the initial set up of your Grafana system, but you can re-run it at any time using the [Grafana Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/admin/#reload-provisioning-configurations).
|
||||
|
||||
### Provision alert rules
|
||||
|
||||
Create or delete alert rules in your Grafana instance(s).
|
||||
|
||||
1. Create an alert rule in Grafana.
|
||||
1. Use the [Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/admin/#reload-provisioning-configurations) to extract the alert rule.
|
||||
1. Copy the contents into a YAML or JSON configuration file.
|
||||
|
||||
Example configuration files can be found below.
|
||||
|
||||
1. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
|
||||
1. Delete the alert rule in Grafana.
|
||||
|
||||
**Note:**
|
||||
|
||||
If you do not delete the alert rule, it will clash with the provisioned alert rule once uploaded.
|
||||
|
||||
Here is an example of a configuration file for creating alert rules.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of rule groups to import or update
|
||||
groups:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the rule group
|
||||
name: my_rule_group
|
||||
# <string, required> name of the folder the rule group will be stored in
|
||||
folder: my_first_folder
|
||||
# <duration, required> interval that the rule group should evaluated at
|
||||
interval: 60s
|
||||
# <list, required> list of rules that are part of the rule group
|
||||
rules:
|
||||
# <string, required> unique identifier for the rule
|
||||
- uid: my_id_1
|
||||
# <string, required> title of the rule that will be displayed in the UI
|
||||
title: my_first_rule
|
||||
# <string, required> which query should be used for the condition
|
||||
condition: A
|
||||
# <list, required> list of query objects that should be executed on each
|
||||
# evaluation - should be obtained trough the API
|
||||
data:
|
||||
- refId: A
|
||||
datasourceUid: '-100'
|
||||
model:
|
||||
conditions:
|
||||
- evaluator:
|
||||
params:
|
||||
- 3
|
||||
type: gt
|
||||
operator:
|
||||
type: and
|
||||
query:
|
||||
params:
|
||||
- A
|
||||
reducer:
|
||||
type: last
|
||||
type: query
|
||||
datasource:
|
||||
type: __expr__
|
||||
uid: '-100'
|
||||
expression: 1==0
|
||||
intervalMs: 1000
|
||||
maxDataPoints: 43200
|
||||
refId: A
|
||||
type: math
|
||||
# <string> UID of a dashboard that the alert rule should be linked to
|
||||
dashboardUid: my_dashboard
|
||||
# <int> ID of the panel that the alert rule should be linked to
|
||||
panelId: 123
|
||||
# <string> the state the alert rule will have when no data is returned
|
||||
# possible values: "NoData", "Alerting", "OK", default = NoData
|
||||
noDataState: Alerting
|
||||
# <string> the state the alert rule will have when the query execution
|
||||
# failed - possible values: "Error", "Alerting", "OK"
|
||||
# default = Alerting
|
||||
# <duration, required> for how long should the alert fire before alerting
|
||||
for: 60s
|
||||
# <map<string, string>> a map of strings to pass around any data
|
||||
annotations:
|
||||
some_key: some_value
|
||||
# <map<string, string> a map of strings that can be used to filter and
|
||||
# route alerts
|
||||
labels:
|
||||
team: sre_team_1
|
||||
```
|
||||
|
||||
Here is an example of a configuration file for deleting alert rules.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of alert rule UIDs that should be deleted
|
||||
deleteRules:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> unique identifier for the rule
|
||||
uid: my_id_1
|
||||
```
|
||||
|
||||
### Provision contact points
|
||||
|
||||
Create or delete contact points in your Grafana instance(s).
|
||||
|
||||
1. Create a YAML or JSON configuration file.
|
||||
|
||||
Example configuration files can be found below.
|
||||
|
||||
1. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
|
||||
|
||||
Here is an example of a configuration file for creating contact points.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of contact points to import or update
|
||||
contactPoints:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the contact point
|
||||
name: cp_1
|
||||
receivers:
|
||||
# <string, required> unique identifier for the receiver
|
||||
- uid: first_uid
|
||||
# <string, required> type of the receiver
|
||||
type: prometheus-alertmanager
|
||||
# <object, required> settings for the specific receiver type
|
||||
settings:
|
||||
url: http://test:9000
|
||||
```
|
||||
|
||||
Here is an example of a configuration file for deleting contact points.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of receivers that should be deleted
|
||||
deleteContactPoints:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> unique identifier for the receiver
|
||||
uid: first_uid
|
||||
```
|
||||
|
||||
#### Settings
|
||||
|
||||
Here are some examples of settings you can use for the different
|
||||
contact point types.
|
||||
|
||||
##### Alertmanager
|
||||
|
||||
```yaml
|
||||
type: prometheus-alertmanager
|
||||
settings:
|
||||
# <string, required>
|
||||
url: http://localhost:9093
|
||||
# <string>
|
||||
basicAuthUser: abc
|
||||
# <string>
|
||||
basicAuthPassword: abc123
|
||||
```
|
||||
|
||||
##### DingDing
|
||||
|
||||
```yaml
|
||||
type: dingding
|
||||
settings:
|
||||
# <string, required>
|
||||
url: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx
|
||||
# <string> options: link, actionCard
|
||||
msgType: link
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### Discord
|
||||
|
||||
```yaml
|
||||
type: discord
|
||||
settings:
|
||||
# <string, required>
|
||||
url: https://discord/webhook
|
||||
# <string>
|
||||
avatar_url: https://my_avatar
|
||||
# <string>
|
||||
use_discord_username: Grafana
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### E-Mail
|
||||
|
||||
```yaml
|
||||
type: email
|
||||
settings:
|
||||
# <string, required>
|
||||
addresses: me@example.com;you@example.com
|
||||
# <bool>
|
||||
singleEmail: false
|
||||
# <string>
|
||||
message: my optional message to include
|
||||
# <string>
|
||||
subject: |
|
||||
{{ template "default.title" . }}
|
||||
```
|
||||
|
||||
##### Google Hangouts Chat
|
||||
|
||||
```yaml
|
||||
type: googlechat
|
||||
settings:
|
||||
# <string, required>
|
||||
url: https://google/webhook
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### Kafka
|
||||
|
||||
```yaml
|
||||
type: kafka
|
||||
settings:
|
||||
# <string, required>
|
||||
kafkaRestProxy: http://localhost:8082
|
||||
# <string, required>
|
||||
kafkaTopic: topic1
|
||||
```
|
||||
|
||||
##### LINE
|
||||
|
||||
```yaml
|
||||
type: line
|
||||
settings:
|
||||
# <string, required>
|
||||
token: xxx
|
||||
```
|
||||
|
||||
##### Microsoft Teams
|
||||
|
||||
```yaml
|
||||
type: teams
|
||||
settings:
|
||||
# <string, required>
|
||||
url: https://ms_teams_url
|
||||
# <string>
|
||||
title: |
|
||||
{{ template "default.title" . }}
|
||||
# <string>
|
||||
sectiontitle: ''
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### OpsGenie
|
||||
|
||||
```yaml
|
||||
type: opsgenie
|
||||
settings:
|
||||
# <string, required>
|
||||
apiKey: xxx
|
||||
# <string, required>
|
||||
apiUrl: https://api.opsgenie.com/v2/alerts
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.title" . }}
|
||||
# <string>
|
||||
description: some descriptive description
|
||||
# <bool>
|
||||
autoClose: false
|
||||
# <bool>
|
||||
overridePriority: false
|
||||
# <string> options: tags, details, both
|
||||
sendTagsAs: both
|
||||
```
|
||||
|
||||
##### PagerDuty
|
||||
|
||||
```yaml
|
||||
type: pagerduty
|
||||
settings:
|
||||
# <string, required>
|
||||
integrationKey: XXX
|
||||
# <string> options: critical, error, warning, info
|
||||
severity: critical
|
||||
# <string>
|
||||
class: ping failure
|
||||
# <string>
|
||||
component: Grafana
|
||||
# <string>
|
||||
group: app-stack
|
||||
# <string>
|
||||
summary: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### Pushover
|
||||
|
||||
```yaml
|
||||
type: pushover
|
||||
settings:
|
||||
# <string, required>
|
||||
apiToken: XXX
|
||||
# <string, required>
|
||||
userKey: user1,user2
|
||||
# <string>
|
||||
device: device1,device2
|
||||
# <string> options (high to low): 2,1,0,-1,-2
|
||||
priority: '2'
|
||||
# <string>
|
||||
retry: '30'
|
||||
# <string>
|
||||
expire: '120'
|
||||
# <string>
|
||||
sound: siren
|
||||
# <string>
|
||||
okSound: magic
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### Slack
|
||||
|
||||
```yaml
|
||||
type: slack
|
||||
settings:
|
||||
# <string, required>
|
||||
recipient: alerting-dev
|
||||
# <string, required>
|
||||
token: xxx
|
||||
# <string>
|
||||
username: grafana_bot
|
||||
# <string>
|
||||
icon_emoji: heart
|
||||
# <string>
|
||||
icon_url: https://icon_url
|
||||
# <string>
|
||||
mentionUsers: user_1,user_2
|
||||
# <string>
|
||||
mentionGroups: group_1,group_2
|
||||
# <string> options: here, channel
|
||||
mentionChannel: here
|
||||
# <string> Optionally provide a Slack incoming webhook URL for sending messages, in this case the token isn't necessary
|
||||
url: https://some_webhook_url
|
||||
# <string>
|
||||
endpointUrl: https://custom_url/api/chat.postMessage
|
||||
# <string>
|
||||
title: |
|
||||
{{ template "slack.default.title" . }}
|
||||
text: |
|
||||
{{ template "slack.default.text" . }}
|
||||
```
|
||||
|
||||
##### Sensu Go
|
||||
|
||||
```yaml
|
||||
type: sensugo
|
||||
settings:
|
||||
# <string, required>
|
||||
url: http://sensu-api.local:8080
|
||||
# <string, required>
|
||||
apikey: xxx
|
||||
# <string>
|
||||
entity: default
|
||||
# <string>
|
||||
check: default
|
||||
# <string>
|
||||
handler: some_handler
|
||||
# <string>
|
||||
namespace: default
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### Telegram
|
||||
|
||||
```yaml
|
||||
type: telegram
|
||||
settings:
|
||||
# <string, required>
|
||||
bottoken: xxx
|
||||
# <string, required>
|
||||
chatid: some_chat_id
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
```
|
||||
|
||||
##### Threema Gateway
|
||||
|
||||
```yaml
|
||||
type: threema
|
||||
settings:
|
||||
# <string, required>
|
||||
api_secret: xxx
|
||||
# <string, required>
|
||||
gateway_id: A5K94S9
|
||||
# <string, required>
|
||||
recipient_id: A9R4KL4S
|
||||
```
|
||||
|
||||
##### VictorOps
|
||||
|
||||
```yaml
|
||||
type: victorops
|
||||
settings:
|
||||
# <string, required>
|
||||
url: XXX
|
||||
# <string> options: CRITICAL, WARNING
|
||||
messageType: CRITICAL
|
||||
```
|
||||
|
||||
##### Webhook
|
||||
|
||||
```yaml
|
||||
type: webhook
|
||||
settings:
|
||||
# <string, required>
|
||||
url: https://endpoint_url
|
||||
# <string> options: POST, PUT
|
||||
httpMethod: POST
|
||||
# <string>
|
||||
username: abc
|
||||
# <string>
|
||||
password: abc123
|
||||
# <string>
|
||||
authorization_scheme: Bearer
|
||||
# <string>
|
||||
authorization_credentials: abc123
|
||||
# <string>
|
||||
maxAlerts: '10'
|
||||
```
|
||||
|
||||
##### WeCom
|
||||
|
||||
```yaml
|
||||
type: wecom
|
||||
settings:
|
||||
# <string, required>
|
||||
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx
|
||||
# <string>
|
||||
message: |
|
||||
{{ template "default.message" . }}
|
||||
# <string>
|
||||
title: |
|
||||
{{ template "default.title" . }}
|
||||
```
|
||||
|
||||
### Provision notification policies
|
||||
|
||||
Create or reset notification policies in your Grafana instance(s).
|
||||
|
||||
1. Create a YAML or JSON configuration file.
|
||||
|
||||
Example configuration files can be found below.
|
||||
|
||||
2. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
|
||||
|
||||
Here is an example of a configuration file for creating notification policiies.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of notification policies
|
||||
policies:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string> name of the contact point that should be used for this route
|
||||
receiver: grafana-default-email
|
||||
# <list> The labels by which incoming alerts are grouped together. For example,
|
||||
# multiple alerts coming in for cluster=A and alertname=LatencyHigh would
|
||||
# be batched into a single group.
|
||||
#
|
||||
# To aggregate by all possible labels use the special value '...' as
|
||||
# the sole label name, for example:
|
||||
# group_by: ['...']
|
||||
# This effectively disables aggregation entirely, passing through all
|
||||
# alerts as-is. This is unlikely to be what you want, unless you have
|
||||
# a very low alert volume or your upstream notification system performs
|
||||
# its own grouping.
|
||||
group_by: ['...']
|
||||
# <list> a list of matchers that an alert has to fulfill to match the node
|
||||
matchers:
|
||||
- alertname = Watchdog
|
||||
- severity =~ "warning|critical"
|
||||
# <list> Times when the route should be muted. These must match the name of a
|
||||
# mute time interval.
|
||||
# Additionally, the root node cannot have any mute times.
|
||||
# When a route is muted it will not send any notifications, but
|
||||
# otherwise acts normally (including ending the route-matching process
|
||||
# if the `continue` option is not set)
|
||||
mute_time_intervals:
|
||||
- abc
|
||||
# <duration> How long to initially wait to send a notification for a group
|
||||
# of alerts. Allows to collect more initial alerts for the same group.
|
||||
# (Usually ~0s to few minutes), default = 30s
|
||||
group_wait: 30s
|
||||
# <duration> How long to wait before sending a notification about new alerts that
|
||||
# are added to a group of alerts for which an initial notification has
|
||||
# already been sent. (Usually ~5m or more), default = 5m
|
||||
group_internval: 5m
|
||||
# <duration> How long to wait before sending a notification again if it has already
|
||||
# been sent successfully for an alert. (Usually ~3h or more), default = 4h
|
||||
repeat_interval: 4h
|
||||
# <list> Zero or more child routes
|
||||
# routes:
|
||||
# ...
|
||||
```
|
||||
|
||||
Here is an example of a configuration file for resetting notification policies.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of orgIds that should be reset to the default policy
|
||||
resetPolicies:
|
||||
- 1
|
||||
```
|
||||
|
||||
### Provision templates
|
||||
|
||||
Create or delete templates in your Grafana instance(s).
|
||||
|
||||
1. Create a YAML or JSON configuration file.
|
||||
|
||||
Example configuration files can be found below.
|
||||
|
||||
2. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
|
||||
|
||||
Here is an example of a configuration file for creating templates.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of templates to import or update
|
||||
templates:
|
||||
# <int> organization ID, default = 1
|
||||
- orgID: 1
|
||||
# <string, required> name of the template, must be unique
|
||||
name: my_first_template
|
||||
# <string, required> content of the the template
|
||||
template: Alerting with a custom text template
|
||||
```
|
||||
|
||||
Here is an example of a configuration file for deleting templates.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of alert rule UIDs that should be deleted
|
||||
deleteTemplates:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the template, must be unique
|
||||
name: my_first_template
|
||||
```
|
||||
|
||||
### Provision mute timings
|
||||
|
||||
Create or delete mute timings in your Grafana instance(s).
|
||||
|
||||
1. Create a YAML or JSON configuration file.
|
||||
|
||||
Example configuration files can be found below.
|
||||
|
||||
1. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
|
||||
|
||||
Here is an example of a configuration file for creating mute timings.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of mute time intervals to import or update
|
||||
muteTimes:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the mute time interval, must be unique
|
||||
name: mti_1
|
||||
# <list> time intervals that should trigger the muting
|
||||
# refer to https://prometheus.io/docs/alerting/latest/configuration/#time_interval-0
|
||||
time_intervals:
|
||||
- times:
|
||||
- start_time: '06:00'
|
||||
end_time: '23:59'
|
||||
weekdays: ['monday:wednesday', 'saturday', 'sunday']
|
||||
months: ['1:3', 'may:august', 'december']
|
||||
years: ['2020:2022', '2030']
|
||||
days_of_month: ['1:5', '-3:-1']
|
||||
```
|
||||
|
||||
Here is an example of a configuration file for deleting mute timings.
|
||||
|
||||
```yaml
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# List of mute time intervals that should be deleted
|
||||
deleteMuteTimes:
|
||||
# <int> organization ID, default = 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the mute time interval, must be unique
|
||||
name: mti_1
|
||||
```
|
||||
|
||||
### File provisioning using Kubernetes
|
||||
|
||||
If you are a Kubernetes user, you can leverage file provisioning using Kubernetes configuration maps.
|
||||
|
||||
1. Create one or more configuration maps as follows.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: grafana-alerting
|
||||
data:
|
||||
provisioning.yaml: |
|
||||
templates:
|
||||
- name: my_first_template
|
||||
template: the content for my template
|
||||
```
|
||||
|
||||
2. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: grafana
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: grafana
|
||||
template:
|
||||
metadata:
|
||||
name: grafana
|
||||
labels:
|
||||
app: grafana
|
||||
spec:
|
||||
containers:
|
||||
- name: grafana
|
||||
image: grafana/grafana:latest
|
||||
ports:
|
||||
- name: grafana
|
||||
containerPort: 3000
|
||||
volumeMounts:
|
||||
- mountPath: /etc/grafana/provisioning/alerting
|
||||
name: grafana-alerting
|
||||
readOnly: false
|
||||
volumes:
|
||||
- name: grafana-alerting
|
||||
configMap:
|
||||
defaultMode: 420
|
||||
name: grafana-alerting
|
||||
```
|
||||
|
||||
This eliminates the need for a persistent database to use Grafana Alerting in Kubernetes; all your provisioned resources appear after each restart or re-deployment.
|
@ -21,7 +21,7 @@ weight: 200
|
||||
|
||||
Usage insights enables you to have a better understanding of how your Grafana instance is used.
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). Grafana Cloud insights logs include additional fields with their own dashboards. Read more in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/).
|
||||
|
||||
The usage insights feature collects a number of aggregated data and stores them in the database:
|
||||
|
||||
@ -34,6 +34,7 @@ The aggregated data provides you access to several features:
|
||||
- [Dashboard and data source insights]({{< relref "#dashboard-and-data-source-insights" >}})
|
||||
- [Presence indicator]({{< relref "#presence-indicator" >}})
|
||||
- [Sort dashboards by using insights data]({{< relref "#sort-dashboards-by-using-insights-data" >}})
|
||||
- [Visualize usage insight data in a dashboard]({{< relref "#visualize-usage-insights-data" >}})
|
||||
|
||||
This feature also generates detailed logs that can be exported to Loki. Refer to [Export logs of usage insights]({{< relref "../../setup-grafana/configure-security/export-logs/" >}}).
|
||||
|
||||
@ -43,7 +44,7 @@ For every dashboard and data source, you can access usage information.
|
||||
|
||||
### Dashboard insights
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
To see dashboard usage information, click **Dashboard insights** in the top bar.
|
||||
|
||||
@ -58,7 +59,7 @@ Dashboard insights show the following information:
|
||||
|
||||
### Data source insights
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.3 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
Data source insights provides information about how a data source has been used in the past 30 days, such as:
|
||||
|
||||
@ -76,7 +77,7 @@ To find data source insights:
|
||||
|
||||
## Presence indicator
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
When you are signed in and looking at a dashboard, you can know who is looking at the same dashboard as you are via a presence indicator, which displays avatars of users who have recently interacted with the dashboard. The default timeframe is 10 minutes. To see the user's name, hover over the user's avatar. The avatars come from [Gravatar](https://gravatar.com) based on the user's email.
|
||||
|
||||
@ -94,7 +95,7 @@ recent_users_age = 10m
|
||||
|
||||
## Sort dashboards by using insights data
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
In the search view, you can use insights data to help you find most-used, broken, and unused dashbaords.
|
||||
|
||||
@ -106,3 +107,14 @@ You can sort the dashboards by:
|
||||
- Views 30 days
|
||||
|
||||
{{< figure src="/static/img/docs/enterprise/improved-search-7-5.png" max-width="650px" class="docs-image--no-shadow" >}}
|
||||
|
||||
## Visualize usage insights data
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
If you set up your installation to [export logs of usage insights]({{< relref "../../setup-grafana/configure-security/export-logs/" >}}), we've created two dashboards to help you take advantage of this data.
|
||||
|
||||
1. [Usage Insights overview](/grafana/dashboards/13785) provides a top-level perspective of user activity.
|
||||
1. [Data source details](/grafana/dashboards/13786) dashboard provides a view of data source activity and health.
|
||||
|
||||
You can click the previous links to download the respective dashboard JSON, then import into your Grafana installation.
|
||||
|
@ -31,7 +31,9 @@ This topic explains configuring, querying, and other options specific to the Azu
|
||||
|
||||
To access Azure Monitor configuration, hover your mouse over the **Configuration** (gear) icon, click **Data Sources**, and then select the Azure Monitor data source. If you haven't already, you'll need to [add the Azure Monitor data source]({{< relref "../add-a-data-source/" >}}).
|
||||
|
||||
You must create an app registration and service principal in Azure AD to authenticate the data source. See the [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#get-tenant-and-app-id-values-for-signing-in) for configuration details. Alternatively, if you are hosting Grafana in Azure (e.g. App Service, or Azure Virtual Machines) you can configure the Azure Monitor data source to use Managed Identity to securely authenticate without entering credentials into Grafana. Refer to [Configuring using Managed Identity](#configuring-using-managed-identity) for more details.
|
||||
You must create an app registration and service principal in Azure AD to authenticate the data source. For more information, refer to [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#get-tenant-and-app-id-values-for-signing-in) for configuration details. The app registration you create will need to have the `Reader` role assigned on the subscription. For more information, refer to [Azure docs for role assignments](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal?tabs=current).
|
||||
|
||||
Alternatively, if you host Grafana in Azure (such as in App Service or Azure Virtual Machines), you can configure the Azure Monitor data source to use Managed Identity to securely authenticate without entering credentials into Grafana. Refer to [Configuring using Managed Identity](#configuring-using-managed-identity) for details.
|
||||
|
||||
| Name | Description |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
@ -88,7 +88,7 @@ On the left side of the query field, click **Metrics** to open the Metric Explor
|
||||
|
||||
### Query field
|
||||
|
||||
The Query field supports autocomplete for metric names, function and works mostly the same way as the standard Prometheus query editor. Press the enter key to execute a query.
|
||||
The Query field supports autocomplete for metric names and functions, comparable to the standard Prometheus query editor. Press the Enter key to create a new line and Shift+Enter to execute a query.
|
||||
|
||||
The autocomplete menu can be triggered by pressing Ctrl+Space. The Autocomplete menu contains a new History section with a list of recently executed queries.
|
||||
|
||||
|
@ -67,7 +67,7 @@ You can also paste a native emoji in the unit picker and pick it as a custom uni
|
||||
|
||||
#### String units
|
||||
|
||||
Grafana can sometime be too aggressive in parsing strings and displaying them as numbers. To make Grafana show the original string create a field override and add a unit property with the `string` unit.
|
||||
Grafana can sometimes be too aggressive in parsing strings and displaying them as numbers. To configure Grafana to show the original string value, create a field override and add a unit property with the `String` unit.
|
||||
|
||||
### Min
|
||||
|
||||
@ -79,9 +79,9 @@ Lets you set the maximum value used in percentage threshold calculations. Leave
|
||||
|
||||
### Decimals
|
||||
|
||||
Number of decimals to render value with. Leave empty for Grafana to use the number of decimals provided by the data source.
|
||||
Specify the number of decimals Grafana includes in the rendered value. If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. For example 1.1234 will display as 1.12 and 100.456 will display as 100.
|
||||
|
||||
To change this setting, type a number in the field and then click outside the field or press Enter.
|
||||
To display all decimals, set the unit to `String`.
|
||||
|
||||
### Display name
|
||||
|
||||
|
@ -10,6 +10,7 @@ weight: 10000
|
||||
Here you can find detailed release notes that list everything that is included in every release as well as notices
|
||||
about deprecations, breaking changes as well as changes that relate to plugin development.
|
||||
|
||||
- [Release notes for 9.1.6]({{< relref "release-notes-9-1-6" >}})
|
||||
- [Release notes for 9.1.5]({{< relref "release-notes-9-1-5" >}})
|
||||
- [Release notes for 9.1.4]({{< relref "release-notes-9-1-4" >}})
|
||||
- [Release notes for 9.1.3]({{< relref "release-notes-9-1-3" >}})
|
||||
@ -17,6 +18,7 @@ about deprecations, breaking changes as well as changes that relate to plugin de
|
||||
- [Release notes for 9.1.1]({{< relref "release-notes-9-1-1" >}})
|
||||
- [Release notes for 9.1.0]({{< relref "release-notes-9-1-0" >}})
|
||||
- [Release notes for 9.1.0-beta1]({{< relref "release-notes-9-1-0-beta1" >}})
|
||||
- [Release notes for 9.0.9]({{< relref "release-notes-9-0-9" >}})
|
||||
- [Release notes for 9.0.8]({{< relref "release-notes-9-0-8" >}})
|
||||
- [Release notes for 9.0.7]({{< relref "release-notes-9-0-7" >}})
|
||||
- [Release notes for 9.0.6]({{< relref "release-notes-9-0-6" >}})
|
||||
@ -29,6 +31,7 @@ about deprecations, breaking changes as well as changes that relate to plugin de
|
||||
- [Release notes for 9.0.0-beta3]({{< relref "release-notes-9-0-0-beta3/" >}})
|
||||
- [Release notes for 9.0.0-beta2]({{< relref "release-notes-9-0-0-beta2/" >}})
|
||||
- [Release notes for 9.0.0-beta1]({{< relref "release-notes-9-0-0-beta1/" >}})
|
||||
- [Release notes for 8.5.13]({{< relref "release-notes-8-5-13" >}})
|
||||
- [Release notes for 8.5.11]({{< relref "release-notes-8-5-11" >}})
|
||||
- [Release notes for 8.5.10]({{< relref "release-notes-8-5-10" >}})
|
||||
- [Release notes for 8.5.9]({{< relref "release-notes-8-5-9" >}})
|
||||
|
16
docs/sources/release-notes/release-notes-8-5-13.md
Normal file
16
docs/sources/release-notes/release-notes-8-5-13.md
Normal file
@ -0,0 +1,16 @@
|
||||
+++
|
||||
title = "Release notes for Grafana 8.5.13"
|
||||
hide_menu = true
|
||||
+++
|
||||
|
||||
<!-- Auto generated by update changelog github action -->
|
||||
|
||||
# Release notes for Grafana 8.5.13
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Plugins:** Expose @emotion/react to plugins to prevent load failures. [#55297](https://github.com/grafana/grafana/pull/55297), [@jackw](https://github.com/jackw)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55495](https://github.com/grafana/grafana/pull/55495), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
14
docs/sources/release-notes/release-notes-9-0-9.md
Normal file
14
docs/sources/release-notes/release-notes-9-0-9.md
Normal file
@ -0,0 +1,14 @@
|
||||
+++
|
||||
title = "Release notes for Grafana 9.0.9"
|
||||
hide_menu = true
|
||||
+++
|
||||
|
||||
<!-- Auto generated by update changelog github action -->
|
||||
|
||||
# Release notes for Grafana 9.0.9
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **AngularPanels:** Fixing changing angular panel options not taking having affect when coming back from panel edit. [#54834](https://github.com/grafana/grafana/pull/54834), [@grafanabot](https://github.com/grafanabot)
|
||||
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55498](https://github.com/grafana/grafana/pull/55498), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **FIX:** RBAC prevents deleting empty snapshots (#54385). [#54509](https://github.com/grafana/grafana/pull/54509), [@gamab](https://github.com/gamab)
|
29
docs/sources/release-notes/release-notes-9-1-6.md
Normal file
29
docs/sources/release-notes/release-notes-9-1-6.md
Normal file
@ -0,0 +1,29 @@
|
||||
+++
|
||||
title = "Release notes for Grafana 9.1.6"
|
||||
hide_menu = true
|
||||
+++
|
||||
|
||||
<!-- Auto generated by update changelog github action -->
|
||||
|
||||
# Release notes for Grafana 9.1.6
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Auth:** Trigger auth token cleanup job. (Enterprise)
|
||||
- **DataSource:** Adding possibility to hide queries from the inspector. [#54892](https://github.com/grafana/grafana/pull/54892), [@mckn](https://github.com/mckn)
|
||||
- **Inspect:** Hide Actions tab when it is empty. [#55272](https://github.com/grafana/grafana/pull/55272), [@ryantxu](https://github.com/ryantxu)
|
||||
- **PanelMenu:** Remove hide legend action as it was showing on all panel types. [#54876](https://github.com/grafana/grafana/pull/54876), [@torkelo](https://github.com/torkelo)
|
||||
- **Provisioning Contact points:** Support disableResolveMessage via YAML. [#54122](https://github.com/grafana/grafana/pull/54122), [@mmusenbr](https://github.com/mmusenbr)
|
||||
- **PublicDashboards:** Support subpaths when generating pubdash url. [#55204](https://github.com/grafana/grafana/pull/55204), [@owensmallwood](https://github.com/owensmallwood)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Alerting:** Fix legacy migration crash when rule name is too long. [#55053](https://github.com/grafana/grafana/pull/55053), [@alexweav](https://github.com/alexweav)
|
||||
- **Alerting:** Fix send resolved notifications. [#54793](https://github.com/grafana/grafana/pull/54793), [@grobinson-grafana](https://github.com/grobinson-grafana)
|
||||
- **Azure Monitor:** Fix migration issue with MetricDefinitionsQuery template variable query types. [#55262](https://github.com/grafana/grafana/pull/55262), [@yaelleC](https://github.com/yaelleC)
|
||||
- **Browse:** Hide dashboard actions if user does not have enough permission. [#55218](https://github.com/grafana/grafana/pull/55218), [@lpskdl](https://github.com/lpskdl)
|
||||
- **ElasticSearch:** Fix dispatching queries at a wrong time. [#55225](https://github.com/grafana/grafana/pull/55225), [@svennergr](https://github.com/svennergr)
|
||||
- **Panel:** Disable legends when showLegend is false prior to schema v37. [#55126](https://github.com/grafana/grafana/pull/55126), [@ivanortegaalba](https://github.com/ivanortegaalba)
|
||||
- **Prometheus:** Fix metadata requests for browser access mode. [#55403](https://github.com/grafana/grafana/pull/55403), [@itsmylife](https://github.com/itsmylife)
|
||||
- **Search:** Avoid requesting all dashboards when in Folder View. [#55169](https://github.com/grafana/grafana/pull/55169), [@JoaoSilvaGrafana](https://github.com/JoaoSilvaGrafana)
|
||||
- **TablePanel/StatPanel:** Fix values not being visible when background transparent. [#55092](https://github.com/grafana/grafana/pull/55092), [@mdvictor](https://github.com/mdvictor)
|
@ -474,6 +474,10 @@ Set to false disables checking for new versions of installed plugins from https:
|
||||
If you want to track Grafana usage via Google analytics specify _your_ Universal
|
||||
Analytics ID here. By default this feature is disabled.
|
||||
|
||||
### google_analytics_4_id
|
||||
|
||||
If you want to track Grafana usage via Google Analytics 4 specify _your_ GA4 ID here. By default this feature is disabled.
|
||||
|
||||
### google_tag_manager_id
|
||||
|
||||
Google Tag Manager ID, only enabled if you enter an ID here.
|
||||
@ -1571,6 +1575,18 @@ Setting this to `true` turns off shared RPC spans. Leaving this available is the
|
||||
|
||||
<hr>
|
||||
|
||||
## [tracing.opentelemetry]
|
||||
|
||||
Configure general parameters shared between OpenTelemetry providers.
|
||||
|
||||
### custom_attributes
|
||||
|
||||
Comma-separated list of attributes to include in all new spans, such as `key1:value1,key2:value2`.
|
||||
|
||||
Can be set with the environment variable `OTEL_RESOURCE_ATTRIBUTES` (use `=` instead of `:` with the environment variable).
|
||||
|
||||
<hr>
|
||||
|
||||
## [tracing.opentelemetry.jaeger]
|
||||
|
||||
Configure Grafana's Jaeger client for distributed tracing.
|
||||
|
@ -9,7 +9,7 @@ weight: 100
|
||||
|
||||
# Configure Grafana Enterprise
|
||||
|
||||
This page describes Grafana Enterprise-specific configuration options that you can specify in a `.ini` configuration file or using environment variables. Refer to [Configuration]({{< relref "/" >}}) for more information about available configuration options.
|
||||
This page describes Grafana Enterprise-specific configuration options that you can specify in a `.ini` configuration file or using environment variables. Refer to [Configuration]({{< relref "../" >}}) for more information about available configuration options.
|
||||
|
||||
## [enterprise]
|
||||
|
||||
|
@ -202,3 +202,26 @@ the correct teams.
|
||||
You can reference Azure AD groups by group object ID, like `8bab1c86-8fba-33e5-2089-1d1c80ec267d`.
|
||||
|
||||
To learn more, refer to the [Team Sync]({{< relref "../configure-team-sync/" >}}) documentation.
|
||||
|
||||
## Common troubleshooting
|
||||
|
||||
Here are some common issues and particulars you can run into when
|
||||
configuring Azure AD authentication in Grafana.
|
||||
|
||||
### Users with over 200 Group assignments
|
||||
|
||||
> Supported in Grafana v8.5 and later versions.
|
||||
|
||||
To ensure that the token size doesn't exceed HTTP header size limits,
|
||||
Azure AD limits the number of object IDs that it includes in the groups claim.
|
||||
If a user is member of more groups than the
|
||||
overage limit (200), then
|
||||
Azure AD does not emit the groups claim in the token and emits a group overage claim instead.
|
||||
|
||||
> More information in [Groups overage claim](https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens#groups-overage-claim)
|
||||
|
||||
If Grafana receives a token with a group overage claim instead of a groups claim,
|
||||
Grafana attempts to retrieve the user's group membership by calling the included endpoint.
|
||||
|
||||
> Note: The token must include the `GroupMember.Read.All` permission for group overage claim calls to succeed.
|
||||
> Admin consent may be required for this permission.
|
||||
|
@ -383,7 +383,9 @@ To troubleshoot and get more log information, enable SAML debug logging in the c
|
||||
filters = saml.auth:debug
|
||||
```
|
||||
|
||||
## Known issues
|
||||
## Troubleshooting
|
||||
|
||||
Following are common issues found in configuring SAML authentication in Grafana and how to resolve them.
|
||||
|
||||
### SAML authentication fails with error:
|
||||
|
||||
@ -438,3 +440,31 @@ csrf_trusted_origins = https://grafana.example.com
|
||||
csrf_additional_headers = X-Forwarded-Host
|
||||
...
|
||||
```
|
||||
|
||||
### SAML login attempts fail with request response "login session has expired"
|
||||
|
||||
Accessing the Grafana login page from a URL that is not the root URL of the
|
||||
Grafana server can cause the instance to return the following error: "login session has expired".
|
||||
|
||||
If you are accessing grafana through a proxy server, ensure that cookies are correctly
|
||||
rewritten to the root URL of Grafana.
|
||||
Cookies must be set on the same url as the `root_url` of Grafana. This is normally the reverse proxy's domain/address.
|
||||
|
||||
Review the cookie settings in your proxy server configuration to ensure that cookies are
|
||||
not being discarded
|
||||
|
||||
Review the following settings in your grafana config:
|
||||
|
||||
```ini
|
||||
[security]
|
||||
cookie_samesite = none
|
||||
```
|
||||
|
||||
This setting should be set to none to allow grafana session cookies to work correctly with redirects.
|
||||
|
||||
```ini
|
||||
[security]
|
||||
cookie_secure = true
|
||||
```
|
||||
|
||||
Ensure cookie_secure is set to true to ensure that cookies are only sent over HTTPS.
|
||||
|
@ -13,9 +13,9 @@ weight: 900
|
||||
|
||||
# Export logs of usage insights
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../grafana-enterprise" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
|
||||
By exporting usage logs to Loki, you can directly query them and create dashboards of the information that matters to you most, such as dashboard errors, most active organizations, or your top-10 most-used queries.
|
||||
By exporting usage logs to Loki, you can directly query them and create dashboards of the information that matters to you most, such as dashboard errors, most active organizations, or your top-10 most-used queries. This configuration is done for you in Grafana Cloud, with provisioned dashboards. Read about them in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/).
|
||||
|
||||
## Usage insights logs
|
||||
|
||||
|
@ -28,8 +28,11 @@ e2e.scenario({
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox().should('be.enabled').click({ force: true });
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox().should('be.enabled').click({ force: true });
|
||||
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.SaveConfigButton().should('be.disabled');
|
||||
|
||||
// Switch on enabling toggle
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.EnableSwitch().should('be.enabled').click({ force: true });
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.SaveConfigButton().should('be.enabled');
|
||||
|
||||
// Save configuration
|
||||
e2e().intercept('POST', '/api/dashboards/uid/ZqZnVvFZz/public-config').as('save');
|
||||
@ -69,6 +72,8 @@ e2e.scenario({
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.Tab().click();
|
||||
e2e().wait('@query-public-config');
|
||||
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.SaveConfigButton().should('be.enabled');
|
||||
|
||||
// Make a request to public dashboards api endpoint without authentication
|
||||
e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput()
|
||||
.invoke('val')
|
||||
|
21
go.mod
21
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
// Override xorm's outdated go-mssqldb dependency, since we can't upgrade to current xorm (due to breaking changes).
|
||||
// We need a more current go-mssqldb so we get rid of a version of apache/thrift with vulnerabilities.
|
||||
@ -13,6 +13,9 @@ replace k8s.io/client-go => k8s.io/client-go v0.22.1
|
||||
|
||||
replace github.com/russellhaering/goxmldsig@v1.1.0 => github.com/russellhaering/goxmldsig v1.1.1
|
||||
|
||||
// Avoid using v2.0.0+incompatible Redigo used by dependencies as the latest maintained branch of Redigo is v1.
|
||||
replace github.com/gomodule/redigo => github.com/gomodule/redigo v1.8.9
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.21.0
|
||||
cuelang.org/go v0.4.3
|
||||
@ -25,7 +28,7 @@ require (
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/benbjohnson/clock v1.1.0
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
||||
github.com/centrifugal/centrifuge v0.19.0
|
||||
github.com/centrifugal/centrifuge v0.25.0
|
||||
github.com/cortexproject/cortex v1.10.1-0.20211014125347-85c378182d0d
|
||||
github.com/crewjam/saml v0.4.8
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
@ -42,14 +45,14 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-stack/stack v1.8.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/snappy v0.0.4
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/wire v0.5.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/gosimple/slug v1.12.0
|
||||
github.com/grafana/cuetsy v0.0.4-0.20220714174355-ebd987fdab27
|
||||
github.com/grafana/grafana-aws-sdk v0.10.8
|
||||
@ -132,7 +135,7 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/FZambia/eagle v0.0.1 // indirect
|
||||
github.com/FZambia/eagle v0.0.2 // indirect
|
||||
github.com/FZambia/sentinel v1.1.0 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
@ -142,7 +145,7 @@ require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
|
||||
github.com/centrifugal/protocol v0.7.6 // indirect
|
||||
github.com/centrifugal/protocol v0.8.10 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
|
||||
@ -185,7 +188,7 @@ require (
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect
|
||||
github.com/igm/sockjs-go/v3 v3.0.1 // indirect
|
||||
github.com/igm/sockjs-go/v3 v3.0.2 // indirect
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.3.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@ -213,7 +216,7 @@ require (
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b // indirect
|
||||
github.com/rs/cors v1.8.2 // indirect
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
github.com/segmentio/encoding v0.3.2
|
||||
github.com/segmentio/encoding v0.3.5
|
||||
github.com/sercand/kuberesolver v2.4.0+incompatible // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
@ -275,6 +278,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/segmentio/asm v1.1.4 // indirect
|
||||
go.starlark.net v0.0.0-20201118183435-e55f603d8c79 // indirect
|
||||
)
|
||||
|
||||
@ -323,7 +327,6 @@ require (
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.8 // indirect
|
||||
github.com/segmentio/asm v1.1.1 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/wk8/go-ordered-map v1.0.0
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
|
37
go.sum
37
go.sum
@ -212,8 +212,8 @@ github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/FZambia/eagle v0.0.1 h1:FN1yTkPihMb5nE8SrlRjoCf7T9H9bTKJFQOm6ach2YU=
|
||||
github.com/FZambia/eagle v0.0.1/go.mod h1:xq6u/JeNZ5/8mrAQ76MMhzNTodASh9FavQlCgg4j48w=
|
||||
github.com/FZambia/eagle v0.0.2 h1:35qHDuXSQevZ4w9A51k4wU7OE/tPHTEWXoywA93hvkY=
|
||||
github.com/FZambia/eagle v0.0.2/go.mod h1:xq6u/JeNZ5/8mrAQ76MMhzNTodASh9FavQlCgg4j48w=
|
||||
github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8=
|
||||
github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
|
||||
github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8=
|
||||
@ -485,10 +485,10 @@ github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuD
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/centrifugal/centrifuge v0.19.0 h1:YHws0dRpgsBiI73tRl1wwaB13gzuaI1AM4IFcQQQqcw=
|
||||
github.com/centrifugal/centrifuge v0.19.0/go.mod h1:O2elf8q3Qkie3z97wkqVqxB52pnOpPsfFUa7L88Lpy0=
|
||||
github.com/centrifugal/protocol v0.7.6 h1:AfMwTZfwnFwZslIzQL4QtRnWSVO32RPSuk4iNS/V9tg=
|
||||
github.com/centrifugal/protocol v0.7.6/go.mod h1:cJo0/BuXglhPfg0fgSgTXvBZ7y+9rdg4+nPbIDOVmlA=
|
||||
github.com/centrifugal/centrifuge v0.25.0 h1:QivFZRPWcN8w3I/gZ8Zs9rMe/KGZoKIS9Kgo1/bY4JE=
|
||||
github.com/centrifugal/centrifuge v0.25.0/go.mod h1:bFcSFalnROq/wcFeRiTG+wIbHsxEMW66QUnq95RY1d0=
|
||||
github.com/centrifugal/protocol v0.8.10 h1:eezzBIU/4pWyl7a+NUnANYojJBASqbkPZcQh9b8YQRI=
|
||||
github.com/centrifugal/protocol v0.8.10/go.mod h1:dlHBjKakr0r+f1pkfwSMfZ+cnpvidN7pQe1ZrsKfhtE=
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
@ -1227,10 +1227,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 h1:utua3L2IbQJmauC5IXdEA547bcoU5dozgQAfc8Onsg4=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -1363,8 +1361,9 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc=
|
||||
github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
||||
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
||||
@ -1529,8 +1528,8 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK
|
||||
github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
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/igm/sockjs-go/v3 v3.0.1 h1:rmgEkeKqBHCFf7uIAipYrYSX8x9LBB2nOxAac2sooak=
|
||||
github.com/igm/sockjs-go/v3 v3.0.1/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE=
|
||||
github.com/igm/sockjs-go/v3 v3.0.2 h1:2m0k53w0DBiGozeQUIEPR6snZFmpFpYvVsGnfLPNXbE=
|
||||
github.com/igm/sockjs-go/v3 v3.0.2/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE=
|
||||
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
@ -1701,7 +1700,6 @@ github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHU
|
||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knadh/koanf v1.2.0/go.mod h1:xpPTwMhsA/aaQLAilyCCqfpEiY1gpa160AiCuWHJUjY=
|
||||
@ -2287,11 +2285,11 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/securego/gosec v0.0.0-20200203094520-d13bb6d2420c/go.mod h1:gp0gaHj0WlmPh9BdsTmo1aq6C27yIPWdxCKGFGdVKBE=
|
||||
github.com/segmentio/asm v1.1.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4=
|
||||
github.com/segmentio/asm v1.1.1 h1:WzUpP9BRnRgoP+v8qywthpSe9U5KLq1uDGdJBEcSeQo=
|
||||
github.com/segmentio/asm v1.1.1/go.mod h1:VCkA6aQH8usgIAUp4QMLQsAeaMl6iW7+VMLrW9Vkv0Y=
|
||||
github.com/segmentio/encoding v0.3.2 h1:gkXXteOfNaPPlrXTEf/e5tWvaQGVJWnvT3LqMzUeH7U=
|
||||
github.com/segmentio/encoding v0.3.2/go.mod h1:waft2p6XI4z2pk07M0YzZV4wEiqaRvsBSyWNHxVx4gU=
|
||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||
github.com/segmentio/asm v1.1.4 h1:Q/FKBtrgnmDc0YMrurLROqG9mXE6Ndn276EtDnoWtMM=
|
||||
github.com/segmentio/asm v1.1.4/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||
github.com/segmentio/encoding v0.3.5 h1:UZEiaZ55nlXGDL92scoVuw00RmiRCazIEmvPSbSvt8Y=
|
||||
github.com/segmentio/encoding v0.3.5/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM=
|
||||
github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M=
|
||||
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
@ -3097,6 +3095,7 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"stable": "9.1.5",
|
||||
"testing": "9.1.5"
|
||||
"stable": "9.1.6",
|
||||
"testing": "9.1.6"
|
||||
}
|
||||
|
18
package.json
18
package.json
@ -81,7 +81,7 @@
|
||||
"@babel/plugin-proposal-optional-chaining": "7.18.9",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/plugin-transform-react-constant-elements": "7.18.9",
|
||||
"@babel/plugin-transform-runtime": "7.18.9",
|
||||
"@babel/plugin-transform-runtime": "7.19.1",
|
||||
"@babel/plugin-transform-typescript": "7.19.0",
|
||||
"@babel/preset-env": "7.19.0",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
@ -124,6 +124,7 @@
|
||||
"@types/eslint": "8.4.5",
|
||||
"@types/file-saver": "2.0.5",
|
||||
"@types/google.analytics": "^0.0.42",
|
||||
"@types/gtag.js": "^0.0.11",
|
||||
"@types/history": "4.7.11",
|
||||
"@types/hoist-non-react-statics": "3.3.1",
|
||||
"@types/jest": "28.1.6",
|
||||
@ -145,7 +146,6 @@
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react-grid-layout": "1.3.2",
|
||||
"@types/react-highlight-words": "0.16.4",
|
||||
"@types/react-loadable": "5.5.6",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-table": "7.7.12",
|
||||
@ -207,6 +207,7 @@
|
||||
"lerna": "5.2.0",
|
||||
"lint-staged": "13.0.3",
|
||||
"mini-css-extract-plugin": "2.6.1",
|
||||
"msw": "^0.47.3",
|
||||
"mutationobserver-shim": "0.3.7",
|
||||
"ngtemplate-loader": "2.1.0",
|
||||
"node-notifier": "10.0.1",
|
||||
@ -222,6 +223,7 @@
|
||||
"react-test-renderer": "17.0.2",
|
||||
"redux-mock-store": "1.5.4",
|
||||
"rimraf": "3.0.2",
|
||||
"rudder-sdk-js": "^2.13.0",
|
||||
"sass": "1.54.0",
|
||||
"sass-loader": "13.0.2",
|
||||
"sinon": "14.0.0",
|
||||
@ -244,7 +246,7 @@
|
||||
"webpack-merge": "5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@daybrush/utils": "^1.9.1",
|
||||
"@daybrush/utils": "1.10.0",
|
||||
"@emotion/css": "11.9.0",
|
||||
"@emotion/react": "11.9.3",
|
||||
"@grafana/agent-core": "0.4.0",
|
||||
@ -263,12 +265,12 @@
|
||||
"@kusto/monaco-kusto": "5.1.8",
|
||||
"@lezer/common": "1.0.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "1.0.0",
|
||||
"@lezer/lr": "1.2.3",
|
||||
"@lingui/core": "3.14.0",
|
||||
"@lingui/react": "3.14.0",
|
||||
"@opentelemetry/api": "1.2.0",
|
||||
"@opentelemetry/exporter-collector": "0.25.0",
|
||||
"@opentelemetry/semantic-conventions": "1.6.0",
|
||||
"@opentelemetry/semantic-conventions": "1.7.0",
|
||||
"@popperjs/core": "2.11.5",
|
||||
"@prometheus-io/lezer-promql": "^0.37.0-rc.1",
|
||||
"@react-aria/button": "3.6.1",
|
||||
@ -299,11 +301,12 @@
|
||||
"angular-bindonce": "0.3.1",
|
||||
"angular-route": "1.8.3",
|
||||
"angular-sanitize": "1.8.3",
|
||||
"ansicolor": "1.1.100",
|
||||
"app": "link:./public/app",
|
||||
"baron": "3.0.3",
|
||||
"brace": "0.11.1",
|
||||
"calculate-size": "1.1.1",
|
||||
"centrifuge": "2.8.5",
|
||||
"centrifuge": "3.0.1",
|
||||
"classnames": "2.3.1",
|
||||
"comlink": "4.3.1",
|
||||
"common-tags": "1.8.2",
|
||||
@ -362,7 +365,6 @@
|
||||
"react-highlight-words": "0.18.0",
|
||||
"react-hook-form": "7.5.3",
|
||||
"react-inlinesvg": "3.0.0",
|
||||
"react-loadable": "5.5.0",
|
||||
"react-moveable": "0.38.4",
|
||||
"react-popper": "2.3.0",
|
||||
"react-popper-tooltip": "4.4.2",
|
||||
@ -405,7 +407,7 @@
|
||||
"resolutions": {
|
||||
"underscore": "1.13.4",
|
||||
"@types/slate": "0.47.9",
|
||||
"@rushstack/node-core-library": "3.51.1",
|
||||
"@rushstack/node-core-library": "3.52.0",
|
||||
"@rushstack/rig-package": "0.3.13",
|
||||
"@rushstack/ts-command-line": "4.12.1",
|
||||
"@storybook/react/webpack": "5.74.0",
|
||||
|
@ -210,6 +210,7 @@ export interface GrafanaConfig {
|
||||
feedbackLinksEnabled: boolean;
|
||||
secretsManagerPluginEnabled: boolean;
|
||||
googleAnalyticsId: string | undefined;
|
||||
googleAnalytics4Id: string | undefined;
|
||||
rudderstackWriteKey: string | undefined;
|
||||
rudderstackDataPlaneUrl: string | undefined;
|
||||
rudderstackSdkUrl: string | undefined;
|
||||
@ -219,4 +220,5 @@ export interface GrafanaConfig {
|
||||
export interface AuthSettings {
|
||||
OAuthSkipOrgRoleUpdateSync?: boolean;
|
||||
SAMLSkipOrgRoleSync?: boolean;
|
||||
DisableSyncLock?: boolean;
|
||||
}
|
||||
|
@ -21,9 +21,14 @@ export interface ExploreTracePanelState {
|
||||
spanId?: string;
|
||||
}
|
||||
|
||||
export interface SplitOpenOptions<T> {
|
||||
datasourceUid: string;
|
||||
query: T;
|
||||
range?: TimeRange;
|
||||
panelsState?: ExplorePanelsState;
|
||||
}
|
||||
|
||||
/**
|
||||
* SplitOpen type is used in Explore and related components.
|
||||
*/
|
||||
export type SplitOpen = <T extends DataQuery = any>(
|
||||
options?: { datasourceUid: string; query: T; range?: TimeRange; panelsState?: ExplorePanelsState } | undefined
|
||||
) => void;
|
||||
export type SplitOpen = <T extends DataQuery = any>(options?: SplitOpenOptions<T> | undefined) => void;
|
||||
|
@ -46,6 +46,7 @@ export interface FeatureToggles {
|
||||
exploreMixedDatasource?: boolean;
|
||||
tracing?: boolean;
|
||||
commandPalette?: boolean;
|
||||
correlations?: boolean;
|
||||
cloudWatchDynamicLabels?: boolean;
|
||||
datasourceQueryMultiStatus?: boolean;
|
||||
traceToMetrics?: boolean;
|
||||
|
@ -20,9 +20,9 @@ export enum LiveChannelScope {
|
||||
* @alpha
|
||||
*/
|
||||
export enum LiveChannelType {
|
||||
DataStream = 'stream', // each message contains a batch of rows that will be appened to previous values
|
||||
DataStream = 'stream', // each message contains a batch of rows that will be appended to previous values
|
||||
DataFrame = 'frame', // each message is an entire data frame and should *replace* previous content
|
||||
JSON = 'json', // arbitray json message
|
||||
JSON = 'json', // arbitrary json message
|
||||
}
|
||||
|
||||
export enum LiveChannelConnectionState {
|
||||
@ -30,11 +30,13 @@ export enum LiveChannelConnectionState {
|
||||
Pending = 'pending',
|
||||
/** Connected to the channel */
|
||||
Connected = 'connected',
|
||||
/** Connecting to a channel */
|
||||
Connecting = 'connecting',
|
||||
/** Disconnected from the channel. The channel will reconnect when possible */
|
||||
Disconnected = 'disconnected',
|
||||
/** Was at some point connected, and will not try to reconnect */
|
||||
Shutdown = 'shutdown',
|
||||
/** Channel configuraiton was invalid and will not connect */
|
||||
/** Channel configuration was invalid and will not connect */
|
||||
Invalid = 'invalid',
|
||||
}
|
||||
|
||||
@ -139,7 +141,7 @@ export interface LiveChannelAddress {
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* Additional metadata passed to a channel. The backend will propigate this JSON object to
|
||||
* Additional metadata passed to a channel. The backend will propagate this JSON object to
|
||||
* each OnSubscribe and RunStream calls. This value should be constant across multiple requests
|
||||
* to the same channel path
|
||||
*/
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
import { LinkTarget } from './dataLink';
|
||||
import { IconName } from './icon';
|
||||
|
||||
export interface NavLinkDTO {
|
||||
@ -11,7 +12,7 @@ export interface NavLinkDTO {
|
||||
icon?: IconName;
|
||||
img?: string;
|
||||
url?: string;
|
||||
target?: string;
|
||||
target?: LinkTarget;
|
||||
sortWeight?: number;
|
||||
divider?: boolean;
|
||||
hideFromMenu?: boolean;
|
||||
|
@ -69,6 +69,7 @@ export interface PluginMeta<T extends KeyValue = {}> {
|
||||
// Filled in by the backend
|
||||
jsonData?: T;
|
||||
secureJsonData?: KeyValue;
|
||||
secureJsonFields?: KeyValue<boolean>;
|
||||
enabled?: boolean;
|
||||
defaultNavUrl?: string;
|
||||
hasUpdate?: boolean;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { MutableDataFrame } from '../dataframe/MutableDataFrame';
|
||||
import { Labels } from '../types/data';
|
||||
import { LogLevel, LogsModel, LogRowModel, LogsSortOrder } from '../types/logs';
|
||||
|
||||
import {
|
||||
@ -66,11 +67,11 @@ describe('calculateLogsLabelStats()', () => {
|
||||
entry: 'foo 1',
|
||||
labels: {
|
||||
foo: 'bar',
|
||||
},
|
||||
} as Labels,
|
||||
},
|
||||
];
|
||||
] as LogRowModel[];
|
||||
|
||||
expect(calculateLogsLabelStats(rows as any, 'baz')).toEqual([]);
|
||||
expect(calculateLogsLabelStats(rows, 'baz')).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return stats for found labels', () => {
|
||||
@ -79,23 +80,23 @@ describe('calculateLogsLabelStats()', () => {
|
||||
entry: 'foo 1',
|
||||
labels: {
|
||||
foo: 'bar',
|
||||
},
|
||||
} as Labels,
|
||||
},
|
||||
{
|
||||
entry: 'foo 0',
|
||||
labels: {
|
||||
foo: 'xxx',
|
||||
},
|
||||
} as Labels,
|
||||
},
|
||||
{
|
||||
entry: 'foo 2',
|
||||
labels: {
|
||||
foo: 'bar',
|
||||
},
|
||||
} as Labels,
|
||||
},
|
||||
];
|
||||
] as LogRowModel[];
|
||||
|
||||
expect(calculateLogsLabelStats(rows as any, 'foo')).toMatchObject([
|
||||
expect(calculateLogsLabelStats(rows, 'foo')).toMatchObject([
|
||||
{
|
||||
value: 'bar',
|
||||
count: 2,
|
||||
@ -215,9 +216,9 @@ describe('calculateFieldStats()', () => {
|
||||
{
|
||||
entry: 'foo=bar',
|
||||
},
|
||||
];
|
||||
] as LogRowModel[];
|
||||
|
||||
expect(calculateFieldStats(rows as any, /baz=(.*)/)).toEqual([]);
|
||||
expect(calculateFieldStats(rows, /baz=(.*)/)).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return stats for found field', () => {
|
||||
@ -234,9 +235,9 @@ describe('calculateFieldStats()', () => {
|
||||
{
|
||||
entry: 't=2018-12-05T07:44:59+0000 foo=503',
|
||||
},
|
||||
];
|
||||
] as LogRowModel[];
|
||||
|
||||
expect(calculateFieldStats(rows as any, /foo=("[^"]*"|\S+)/)).toMatchObject([
|
||||
expect(calculateFieldStats(rows, /foo=("[^"]*"|\S+)/)).toMatchObject([
|
||||
{
|
||||
value: '"42 + 1"',
|
||||
count: 2,
|
||||
@ -363,8 +364,8 @@ describe('checkLogsError()', () => {
|
||||
labels: {
|
||||
__error__: 'Error Message',
|
||||
foo: 'boo',
|
||||
},
|
||||
} as any as LogRowModel;
|
||||
} as Labels,
|
||||
} as LogRowModel;
|
||||
test('should return correct error if error is present', () => {
|
||||
expect(checkLogsError(log)).toStrictEqual({ hasError: true, errorMessage: 'Error Message' });
|
||||
});
|
||||
|
@ -134,6 +134,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
||||
enabled: true,
|
||||
};
|
||||
googleAnalyticsId: undefined;
|
||||
googleAnalytics4Id: undefined;
|
||||
rudderstackWriteKey: undefined;
|
||||
rudderstackDataPlaneUrl: undefined;
|
||||
rudderstackSdkUrl: undefined;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { DisplayValue, DisplayValueAlignmentFactors, FieldSparkline } from '@grafana/data';
|
||||
import { DisplayValue, DisplayValueAlignmentFactors, FieldSparkline, VizOrientation } from '@grafana/data';
|
||||
import { VizTextDisplayOptions } from '@grafana/schema';
|
||||
|
||||
import { Themeable2 } from '../../types';
|
||||
@ -63,6 +63,8 @@ export interface Props extends Themeable2 {
|
||||
textMode?: BigValueTextMode;
|
||||
/** If true disables the tooltip */
|
||||
hasLinks?: boolean;
|
||||
/** The orientation of the parent container */
|
||||
parentOrientation?: VizOrientation;
|
||||
|
||||
/**
|
||||
* If part of a series of stat panes, this is the total number.
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { CSSProperties } from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import { formattedValueToString, DisplayValue, FieldConfig, FieldType } from '@grafana/data';
|
||||
import { formattedValueToString, DisplayValue, FieldConfig, FieldType, VizOrientation } from '@grafana/data';
|
||||
import { GraphDrawStyle, GraphFieldConfig } from '@grafana/schema';
|
||||
|
||||
import { getTextColorForBackground } from '../../utils';
|
||||
import { getTextColorForAlphaBackground } from '../../utils';
|
||||
import { calculateFontSize } from '../../utils/measureText';
|
||||
import { Sparkline } from '../Sparkline/Sparkline';
|
||||
|
||||
@ -62,8 +62,12 @@ export abstract class BigValueLayout {
|
||||
lineHeight: LINE_HEIGHT,
|
||||
};
|
||||
|
||||
if (this.props.parentOrientation === VizOrientation.Horizontal && this.justifyCenter) {
|
||||
styles.paddingRight = '0.75ch';
|
||||
}
|
||||
|
||||
if (this.props.colorMode === BigValueColorMode.Background) {
|
||||
styles.color = getTextColorForBackground(this.valueColor);
|
||||
styles.color = getTextColorForAlphaBackground(this.valueColor, this.props.theme.isDark);
|
||||
}
|
||||
|
||||
return styles;
|
||||
@ -87,7 +91,7 @@ export abstract class BigValueLayout {
|
||||
styles.color = this.valueColor;
|
||||
break;
|
||||
case BigValueColorMode.Background:
|
||||
styles.color = getTextColorForBackground(this.valueColor);
|
||||
styles.color = getTextColorForAlphaBackground(this.valueColor, this.props.theme.isDark);
|
||||
break;
|
||||
case BigValueColorMode.None:
|
||||
styles.color = this.props.theme.colors.text.primary;
|
||||
|
@ -97,7 +97,7 @@ const Heading = ({ children, className, 'aria-label': ariaLabel }: ChildProps &
|
||||
{children}
|
||||
</a>
|
||||
) : onClick ? (
|
||||
<button onClick={onClick} className={styles.linkHack} aria-label={ariaLabel}>
|
||||
<button onClick={onClick} className={styles.linkHack} aria-label={ariaLabel} type="button">
|
||||
{children}
|
||||
</button>
|
||||
) : (
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { FocusScope } from '@react-aria/focus';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { usePopperTooltip } from 'react-popper-tooltip';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
|
||||
@ -16,6 +16,7 @@ export interface Props {
|
||||
|
||||
export const Dropdown = React.memo(({ children, overlay, placement }: Props) => {
|
||||
const [show, setShow] = useState(false);
|
||||
const transitionRef = useRef(null);
|
||||
|
||||
const { getArrowProps, getTooltipProps, setTooltipRef, setTriggerRef, visible } = usePopperTooltip({
|
||||
visible: show,
|
||||
@ -46,12 +47,13 @@ export const Dropdown = React.memo(({ children, overlay, placement }: Props) =>
|
||||
<div ref={setTooltipRef} {...getTooltipProps()} onClick={onOverlayClicked}>
|
||||
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
|
||||
<CSSTransition
|
||||
nodeRef={transitionRef}
|
||||
appear={true}
|
||||
in={true}
|
||||
timeout={{ appear: animationDuration, exit: 0, enter: 0 }}
|
||||
classNames={animationStyles}
|
||||
>
|
||||
{ReactUtils.renderOrCallToRender(overlay)}
|
||||
<div ref={transitionRef}>{ReactUtils.renderOrCallToRender(overlay)}</div>
|
||||
</CSSTransition>
|
||||
</div>
|
||||
</FocusScope>
|
||||
|
@ -9,10 +9,11 @@ export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'onChange'> {
|
||||
value: string | undefined;
|
||||
width?: number;
|
||||
onChange: (value: string) => void;
|
||||
escapeRegex?: boolean;
|
||||
}
|
||||
|
||||
export const FilterInput = React.forwardRef<HTMLInputElement, Props>(
|
||||
({ value, width, onChange, ...restProps }, ref) => {
|
||||
({ value, width, onChange, escapeRegex = true, ...restProps }, ref) => {
|
||||
const innerRef = React.useRef<HTMLInputElement>(null);
|
||||
const combinedRef = useCombinedRefs(ref, innerRef) as React.Ref<HTMLInputElement>;
|
||||
|
||||
@ -38,8 +39,10 @@ export const FilterInput = React.forwardRef<HTMLInputElement, Props>(
|
||||
suffix={suffix}
|
||||
width={width}
|
||||
type="text"
|
||||
value={value ? unEscapeStringFromRegex(value) : ''}
|
||||
onChange={(event) => onChange(escapeStringForRegex(event.currentTarget.value))}
|
||||
value={escapeRegex ? unEscapeStringFromRegex(value ?? '') : value}
|
||||
onChange={(event) =>
|
||||
onChange(escapeRegex ? escapeStringForRegex(event.currentTarget.value) : event.currentTarget.value)
|
||||
}
|
||||
{...restProps}
|
||||
ref={combinedRef}
|
||||
/>
|
||||
|
@ -22,8 +22,7 @@ import { LogDetailsRow } from './LogDetailsRow';
|
||||
import { getLogRowStyles } from './getLogRowStyles';
|
||||
import { getAllFields } from './logParser';
|
||||
|
||||
//Components
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface Props extends Themeable2 {
|
||||
row: LogRowModel;
|
||||
showDuplicates: boolean;
|
||||
@ -176,5 +175,6 @@ class UnThemedLogDetails extends PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogDetails = withTheme2(UnThemedLogDetails);
|
||||
LogDetails.displayName = 'LogDetails';
|
||||
|
@ -12,8 +12,7 @@ import { IconButton } from '../IconButton/IconButton';
|
||||
import { LogLabelStats } from './LogLabelStats';
|
||||
import { getLogRowStyles } from './getLogRowStyles';
|
||||
|
||||
//Components
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface Props extends Themeable2 {
|
||||
parsedValue: string;
|
||||
parsedKey: string;
|
||||
@ -220,5 +219,6 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogDetailsRow = withTheme2(UnThemedLogDetailsRow);
|
||||
LogDetailsRow.displayName = 'LogDetailsRow';
|
||||
|
@ -96,5 +96,6 @@ class UnThemedLogLabelStats extends PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogLabelStats = withTheme2(UnThemedLogLabelStats);
|
||||
LogLabelStats.displayName = 'LogLabelStats';
|
||||
|
@ -51,6 +51,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
`,
|
||||
});
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface Props {
|
||||
active?: boolean;
|
||||
count: number;
|
||||
@ -58,6 +59,7 @@ export interface Props {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogLabelStatsRow: FunctionComponent<Props> = ({ active, count, proportion, value }) => {
|
||||
const style = useStyles2(getStyles);
|
||||
const percent = `${Math.round(proportion * 100)}%`;
|
||||
|
@ -12,6 +12,7 @@ interface Props {
|
||||
labels: Labels;
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogLabels: FunctionComponent<Props> = ({ labels }) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const displayLabels = Object.keys(labels).filter((label) => !label.startsWith('_') && !HIDDEN_LABELS.includes(label));
|
||||
|
@ -49,6 +49,7 @@ interface State {
|
||||
prevValue: string;
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export class UnThemedLogMessageAnsi extends PureComponent<Props, State> {
|
||||
state: State = {
|
||||
chunks: [],
|
||||
@ -101,5 +102,6 @@ export class UnThemedLogMessageAnsi extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogMessageAnsi = withTheme2(UnThemedLogMessageAnsi);
|
||||
LogMessageAnsi.displayName = 'LogMessageAnsi';
|
||||
|
@ -256,5 +256,6 @@ class UnThemedLogRow extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRow = withTheme2(UnThemedLogRow);
|
||||
LogRow.displayName = 'LogRow';
|
||||
|
@ -123,6 +123,7 @@ const LogRowContextGroupHeader: React.FunctionComponent<LogRowContextGroupHeader
|
||||
);
|
||||
};
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRowContextGroup: React.FunctionComponent<LogRowContextGroupProps> = ({
|
||||
row,
|
||||
rows,
|
||||
@ -184,6 +185,7 @@ export const LogRowContextGroup: React.FunctionComponent<LogRowContextGroupProps
|
||||
);
|
||||
};
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRowContext: React.FunctionComponent<LogRowContextProps> = ({
|
||||
row,
|
||||
context,
|
||||
|
@ -11,20 +11,25 @@ import {
|
||||
DataQueryError,
|
||||
} from '@grafana/data';
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface RowContextOptions {
|
||||
direction?: 'BACKWARD' | 'FORWARD';
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface LogRowContextRows {
|
||||
before?: string[];
|
||||
after?: string[];
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface LogRowContextQueryErrors {
|
||||
before?: string;
|
||||
after?: string;
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface HasMoreContextRows {
|
||||
before: boolean;
|
||||
after: boolean;
|
||||
@ -48,6 +53,7 @@ interface LogRowContextProviderProps {
|
||||
}) => JSX.Element;
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const getRowContexts = async (
|
||||
getRowContext: (row: LogRowModel, options?: RowContextOptions) => Promise<DataQueryResponse>,
|
||||
row: LogRowModel,
|
||||
@ -130,6 +136,7 @@ export const getRowContexts = async (
|
||||
};
|
||||
};
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRowContextProvider: React.FunctionComponent<LogRowContextProviderProps> = ({
|
||||
getRowContext,
|
||||
row,
|
||||
|
@ -16,8 +16,7 @@ import { LogRowContext } from './LogRowContext';
|
||||
import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider';
|
||||
import { getLogRowStyles } from './getLogRowStyles';
|
||||
|
||||
//Components
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const MAX_CHARACTERS = 100000;
|
||||
|
||||
interface Props extends Themeable2 {
|
||||
@ -187,5 +186,6 @@ class UnThemedLogRowMessage extends PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRowMessage = withTheme2(UnThemedLogRowMessage);
|
||||
LogRowMessage.displayName = 'LogRowMessage';
|
||||
|
@ -8,6 +8,7 @@ import { Themeable2 } from '../../types/theme';
|
||||
|
||||
import { getAllFields } from './logParser';
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface Props extends Themeable2 {
|
||||
row: LogRowModel;
|
||||
showDetectedFields: string[];
|
||||
@ -45,5 +46,6 @@ class UnThemedLogRowMessageDetectedFields extends PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRowMessageDetectedFields = withTheme2(UnThemedLogRowMessageDetectedFields);
|
||||
LogRowMessageDetectedFields.displayName = 'LogRowMessageDetectedFields';
|
||||
|
@ -6,13 +6,14 @@ import { TimeZone, LogsDedupStrategy, LogRowModel, Field, LinkModel, LogsSortOrd
|
||||
import { withTheme2 } from '../../themes/index';
|
||||
import { Themeable2 } from '../../types/theme';
|
||||
|
||||
//Components
|
||||
import { LogRow } from './LogRow';
|
||||
import { RowContextOptions } from './LogRowContextProvider';
|
||||
import { getLogRowStyles } from './getLogRowStyles';
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const PREVIEW_LIMIT = 100;
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export interface Props extends Themeable2 {
|
||||
logRows?: LogRowModel[];
|
||||
deduplicatedRows?: LogRowModel[];
|
||||
@ -188,5 +189,6 @@ class UnThemedLogRows extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const LogRows = withTheme2(UnThemedLogRows);
|
||||
LogRows.displayName = 'LogsRows';
|
||||
|
@ -4,6 +4,7 @@ import { GrafanaTheme2, LogLevel } from '@grafana/data';
|
||||
|
||||
import { styleMixins } from '../../themes';
|
||||
|
||||
/** @deprecated will be removed in the next major version */
|
||||
export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => {
|
||||
let logColor = theme.isLight ? theme.v1.palette.gray5 : theme.v1.palette.gray2;
|
||||
const hoverBgColor = styleMixins.hoverColor(theme.colors.background.secondary, theme);
|
||||
|
@ -16,6 +16,8 @@ type FieldDef = {
|
||||
/**
|
||||
* Returns all fields for log row which consists of fields we parse from the message itself and additional fields
|
||||
* found in the dataframe (they may contain links).
|
||||
*
|
||||
* @deprecated will be removed in the next major version
|
||||
*/
|
||||
export const getAllFields = memoizeOne(
|
||||
(row: LogRowModel, getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>) => {
|
||||
|
@ -30,7 +30,7 @@ export interface MenuItemProps<T = any> {
|
||||
/** Url of the menu item */
|
||||
url?: string;
|
||||
/** Handler for the click behaviour */
|
||||
onClick?: (event?: React.SyntheticEvent<HTMLElement>, payload?: T) => void;
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>, payload?: T) => void;
|
||||
/** Custom MenuItem styles*/
|
||||
className?: string;
|
||||
/** Active */
|
||||
@ -115,17 +115,7 @@ export const MenuItem = React.memo(
|
||||
className={itemStyle}
|
||||
rel={target === '_blank' ? 'noopener noreferrer' : undefined}
|
||||
href={url}
|
||||
onClick={
|
||||
onClick
|
||||
? (event) => {
|
||||
if (!(event.ctrlKey || event.metaKey || event.shiftKey) && onClick) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick(event);
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onKeyDown={handleKeys}
|
||||
@ -136,8 +126,11 @@ export const MenuItem = React.memo(
|
||||
aria-checked={ariaChecked}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
{icon && <Icon name={icon} className={styles.icon} aria-hidden />}
|
||||
{label}
|
||||
<>
|
||||
{icon && <Icon name={icon} className={styles.icon} aria-hidden />}
|
||||
{label}
|
||||
</>
|
||||
|
||||
{hasSubMenu && (
|
||||
<SubMenu
|
||||
items={childItems}
|
||||
|
@ -10,7 +10,9 @@ import { getSegmentStyles } from './styles';
|
||||
|
||||
import { useExpandableLabel, SegmentProps } from '.';
|
||||
|
||||
export interface SegmentInputProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
|
||||
export interface SegmentInputProps<T>
|
||||
extends Omit<SegmentProps<T>, 'allowCustomValue' | 'allowEmptyValue'>,
|
||||
Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
|
||||
value: string | number;
|
||||
onChange: (text: string | number) => void;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import tinycolor from 'tinycolor2';
|
||||
|
||||
import { DisplayValue, Field, formattedValueToString } from '@grafana/data';
|
||||
|
||||
import { getTextColorForBackground, getCellLinks } from '../../utils';
|
||||
import { getCellLinks, getTextColorForAlphaBackground } from '../../utils';
|
||||
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
|
||||
|
||||
import { CellActions } from './CellActions';
|
||||
@ -63,7 +63,7 @@ function getCellStyle(
|
||||
|
||||
if (field.config.custom?.displayMode === TableCellDisplayMode.ColorBackgroundSolid) {
|
||||
const bgColor = tinycolor(displayValue.color);
|
||||
const textColor = getTextColorForBackground(displayValue.color!);
|
||||
const textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
|
||||
return tableStyles.buildCellContainerStyle(textColor, bgColor.toRgbString(), !disableOverflowOnHover);
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ function getCellStyle(
|
||||
.spin(5)
|
||||
.toRgbString();
|
||||
|
||||
const textColor = getTextColorForBackground(displayValue.color!);
|
||||
const textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
|
||||
|
||||
return tableStyles.buildCellContainerStyle(
|
||||
textColor,
|
||||
|
@ -23,7 +23,7 @@ export const FooterRow = (props: FooterRowProps) => {
|
||||
const tableStyles = useStyles2(getTableStyles);
|
||||
|
||||
return (
|
||||
<table
|
||||
<div
|
||||
style={{
|
||||
position: isPaginationVisible ? 'relative' : 'absolute',
|
||||
width: totalColumnsWidth ? `${totalColumnsWidth}px` : '100%',
|
||||
@ -33,22 +33,20 @@ export const FooterRow = (props: FooterRowProps) => {
|
||||
{footerGroups.map((footerGroup: HeaderGroup) => {
|
||||
const { key, ...footerGroupProps } = footerGroup.getFooterGroupProps();
|
||||
return (
|
||||
<tfoot
|
||||
<div
|
||||
className={tableStyles.tfoot}
|
||||
{...footerGroupProps}
|
||||
key={key}
|
||||
data-testid={e2eSelectorsTable.footer}
|
||||
style={height ? { height: `${height}px` } : undefined}
|
||||
>
|
||||
<tr>
|
||||
{footerGroup.headers.map((column: ColumnInstance, index: number) =>
|
||||
renderFooterCell(column, tableStyles, height)
|
||||
)}
|
||||
</tr>
|
||||
</tfoot>
|
||||
{footerGroup.headers.map((column: ColumnInstance, index: number) =>
|
||||
renderFooterCell(column, tableStyles, height)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -67,9 +65,9 @@ function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles, heig
|
||||
}
|
||||
|
||||
return (
|
||||
<th className={tableStyles.headerCell} {...footerProps}>
|
||||
<div className={tableStyles.headerCell} {...footerProps}>
|
||||
{column.render('Footer')}
|
||||
</th>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,10 @@ export class UPlotConfigBuilder {
|
||||
const xAxis = axes.find((a) => a.props.scaleKey === 'x');
|
||||
const axesWithoutGridSet = axes.filter((a) => a.props.grid?.show === undefined);
|
||||
const firstValueAxisIdx = axesWithoutGridSet.findIndex(
|
||||
(a) => a.props.placement === AxisPlacement.Left || (a.props.placement === AxisPlacement.Bottom && a !== xAxis)
|
||||
(a) =>
|
||||
a.props.placement === AxisPlacement.Left ||
|
||||
a.props.placement === AxisPlacement.Right ||
|
||||
(a.props.placement === AxisPlacement.Bottom && a !== xAxis)
|
||||
);
|
||||
|
||||
// For all axes with no grid set, set the grid automatically (grid only for first left axis )
|
||||
|
@ -143,6 +143,8 @@ export function SuggestionsPlugin({
|
||||
|
||||
const preserveSuffix = suggestion.kind === 'function';
|
||||
const move = suggestion.move || 0;
|
||||
const moveForward = move > 0 ? move : 0;
|
||||
const moveBackward = move < 0 ? -move : 0;
|
||||
|
||||
const { typeaheadPrefix, typeaheadText, typeaheadContext } = state;
|
||||
|
||||
@ -180,7 +182,8 @@ export function SuggestionsPlugin({
|
||||
.deleteBackward(backward)
|
||||
.deleteForward(forward)
|
||||
.insertText(suggestionText)
|
||||
.moveForward(move)
|
||||
.moveForward(moveForward)
|
||||
.moveBackward(moveBackward)
|
||||
.focus();
|
||||
|
||||
return editor;
|
||||
|
@ -114,6 +114,19 @@ function hslToHex(color: tinycolor.ColorFormats.HSLA) {
|
||||
|
||||
export function getTextColorForBackground(color: string) {
|
||||
const b = tinycolor(color).getBrightness();
|
||||
|
||||
return b > 180 ? 'rgb(32, 34, 38)' : 'rgb(247, 248, 250)';
|
||||
}
|
||||
|
||||
export function getTextColorForAlphaBackground(color: string, themeIsDark: boolean) {
|
||||
const tcolor = tinycolor(color);
|
||||
const b = tcolor.getBrightness();
|
||||
const a = tcolor.getAlpha();
|
||||
|
||||
if (a < 0.3) {
|
||||
return themeIsDark ? 'rgb(247, 248, 250)' : 'rgb(32, 34, 38)';
|
||||
}
|
||||
|
||||
return b > 180 ? 'rgb(32, 34, 38)' : 'rgb(247, 248, 250)';
|
||||
}
|
||||
|
||||
|
@ -16,20 +16,19 @@ const ThemeableStory: React.FunctionComponent<{ handleSassThemeChange: SassTheme
|
||||
|
||||
handleSassThemeChange(theme);
|
||||
|
||||
const css = `#root {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
min-height: 100%;
|
||||
background: ${theme.colors.background.primary};
|
||||
}`;
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '20px',
|
||||
display: 'flex',
|
||||
minHeight: '100%',
|
||||
background: `${theme.colors.background.primary}`,
|
||||
}}
|
||||
>
|
||||
<GlobalStyles />
|
||||
{children}
|
||||
</div>
|
||||
<GlobalStyles />
|
||||
<style>{css}</style>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -11,8 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import TickLabels from './TickLabels';
|
||||
@ -23,37 +22,36 @@ describe('<TickLabels>', () => {
|
||||
duration: 5000,
|
||||
};
|
||||
|
||||
let wrapper;
|
||||
let ticks;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<TickLabels {...defaultProps} />);
|
||||
ticks = wrapper.find('[data-test="tick"]');
|
||||
render(<TickLabels {...defaultProps} />);
|
||||
ticks = screen.getAllByTestId('tick');
|
||||
});
|
||||
|
||||
it('renders the right number of ticks', () => {
|
||||
expect(ticks.length).toBe(defaultProps.numTicks + 1);
|
||||
expect(ticks).toHaveLength(defaultProps.numTicks + 1);
|
||||
});
|
||||
|
||||
it('places the first tick on the left', () => {
|
||||
const firstTick = ticks.first();
|
||||
expect(firstTick.prop('style')).toEqual({ left: '0%' });
|
||||
const firstTick = ticks[0];
|
||||
expect(firstTick).toHaveStyle(`left: 0%;`);
|
||||
});
|
||||
|
||||
it('places the last tick on the right', () => {
|
||||
const lastTick = ticks.last();
|
||||
expect(lastTick.prop('style')).toEqual({ right: '0%' });
|
||||
const lastTick = ticks[ticks.length - 1];
|
||||
expect(lastTick).toHaveStyle(`right: 0%;`);
|
||||
});
|
||||
|
||||
it('places middle ticks at proper intervals', () => {
|
||||
const positions = ['25%', '50%', '75%'];
|
||||
positions.forEach((pos, i) => {
|
||||
const tick = ticks.at(i + 1);
|
||||
expect(tick.prop('style')).toEqual({ left: pos });
|
||||
expect(tick).toHaveStyle(`left: ${pos};`);
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't explode if no trace is present", () => {
|
||||
expect(() => shallow(<TickLabels {...defaultProps} trace={null} />)).not.toThrow();
|
||||
expect(() => render(<TickLabels {...defaultProps} trace={null} />)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
@ -50,7 +50,7 @@ export default function TickLabels(props: TickLabelsProps) {
|
||||
const portion = i / numTicks;
|
||||
const style = portion === 1 ? { right: '0%' } : { left: `${portion * 100}%` };
|
||||
ticks.push(
|
||||
<div key={portion} className={styles.TickLabelsLabel} style={style} data-test="tick">
|
||||
<div key={portion} className={styles.TickLabelsLabel} style={style} data-testid="tick">
|
||||
{formatDuration(duration * portion)}
|
||||
</div>
|
||||
);
|
||||
|
@ -63,9 +63,7 @@ func (hs *HTTPServer) GetAnnotations(c *models.ReqContext) response.Response {
|
||||
}
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
items, err := repo.Find(c.Req.Context(), query)
|
||||
items, err := hs.annotationsRepo.Find(c.Req.Context(), query)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to get annotations", err)
|
||||
}
|
||||
@ -135,8 +133,6 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
|
||||
return dashboardGuardianResponse(err)
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
if cmd.Text == "" {
|
||||
err := &AnnotationError{"text field should not be empty"}
|
||||
return response.Error(400, "Failed to save annotation", err)
|
||||
@ -154,7 +150,7 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
|
||||
Tags: cmd.Tags,
|
||||
}
|
||||
|
||||
if err := repo.Save(&item); err != nil {
|
||||
if err := hs.annotationsRepo.Save(c.Req.Context(), &item); err != nil {
|
||||
if errors.Is(err, annotations.ErrTimerangeMissing) {
|
||||
return response.Error(400, "Failed to save annotation", err)
|
||||
}
|
||||
@ -194,8 +190,6 @@ func (hs *HTTPServer) PostGraphiteAnnotation(c *models.ReqContext) response.Resp
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
if cmd.What == "" {
|
||||
err := &AnnotationError{"what field should not be empty"}
|
||||
return response.Error(400, "Failed to save Graphite annotation", err)
|
||||
@ -234,7 +228,7 @@ func (hs *HTTPServer) PostGraphiteAnnotation(c *models.ReqContext) response.Resp
|
||||
Tags: tagsArray,
|
||||
}
|
||||
|
||||
if err := repo.Save(&item); err != nil {
|
||||
if err := hs.annotationsRepo.Save(c.Req.Context(), &item); err != nil {
|
||||
return response.Error(500, "Failed to save Graphite annotation", err)
|
||||
}
|
||||
|
||||
@ -267,9 +261,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
|
||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||
if resp != nil {
|
||||
return resp
|
||||
}
|
||||
@ -288,7 +280,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
|
||||
Tags: cmd.Tags,
|
||||
}
|
||||
|
||||
if err := repo.Update(c.Req.Context(), &item); err != nil {
|
||||
if err := hs.annotationsRepo.Update(c.Req.Context(), &item); err != nil {
|
||||
return response.Error(500, "Failed to update annotation", err)
|
||||
}
|
||||
|
||||
@ -319,9 +311,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
|
||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||
if resp != nil {
|
||||
return resp
|
||||
}
|
||||
@ -356,7 +346,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
|
||||
existing.EpochEnd = cmd.TimeEnd
|
||||
}
|
||||
|
||||
if err := repo.Update(c.Req.Context(), &existing); err != nil {
|
||||
if err := hs.annotationsRepo.Update(c.Req.Context(), &existing); err != nil {
|
||||
return response.Error(500, "Failed to update annotation", err)
|
||||
}
|
||||
|
||||
@ -391,7 +381,6 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
var deleteParams *annotations.DeleteParams
|
||||
|
||||
// validations only for RBAC. A user can mass delete all annotations in a (dashboard + panel) or a specific annotation
|
||||
@ -400,7 +389,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
||||
var dashboardId int64
|
||||
|
||||
if cmd.AnnotationId != 0 {
|
||||
annotation, respErr := findAnnotationByID(c.Req.Context(), repo, cmd.AnnotationId, c.SignedInUser)
|
||||
annotation, respErr := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, cmd.AnnotationId, c.SignedInUser)
|
||||
if respErr != nil {
|
||||
return respErr
|
||||
}
|
||||
@ -431,7 +420,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
||||
}
|
||||
}
|
||||
|
||||
err = repo.Delete(c.Req.Context(), deleteParams)
|
||||
err = hs.annotationsRepo.Delete(c.Req.Context(), deleteParams)
|
||||
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to delete annotations", err)
|
||||
@ -454,9 +443,7 @@ func (hs *HTTPServer) GetAnnotationByID(c *models.ReqContext) response.Response
|
||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||
if resp != nil {
|
||||
return resp
|
||||
}
|
||||
@ -485,9 +472,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
|
||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
||||
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||
if resp != nil {
|
||||
return resp
|
||||
}
|
||||
@ -496,7 +481,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
|
||||
return dashboardGuardianResponse(err)
|
||||
}
|
||||
|
||||
err = repo.Delete(c.Req.Context(), &annotations.DeleteParams{
|
||||
err = hs.annotationsRepo.Delete(c.Req.Context(), &annotations.DeleteParams{
|
||||
OrgId: c.OrgID,
|
||||
Id: annotationID,
|
||||
})
|
||||
@ -563,8 +548,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *models.ReqContext) response.Response
|
||||
Limit: c.QueryInt64("limit"),
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
result, err := repo.FindTags(c.Req.Context(), query)
|
||||
result, err := hs.annotationsRepo.FindTags(c.Req.Context(), query)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to find annotation tags", err)
|
||||
}
|
||||
@ -575,7 +559,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *models.ReqContext) response.Response
|
||||
// AnnotationTypeScopeResolver provides an ScopeAttributeResolver able to
|
||||
// resolve annotation types. Scope "annotations:id:<id>" will be translated to "annotations:type:<type>,
|
||||
// where <type> is the type of annotation with id <id>.
|
||||
func AnnotationTypeScopeResolver() (string, accesscontrol.ScopeAttributeResolver) {
|
||||
func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository) (string, accesscontrol.ScopeAttributeResolver) {
|
||||
prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("")
|
||||
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
|
||||
scopeParts := strings.Split(initialScope, ":")
|
||||
@ -601,7 +585,7 @@ func AnnotationTypeScopeResolver() (string, accesscontrol.ScopeAttributeResolver
|
||||
},
|
||||
}
|
||||
|
||||
annotation, resp := findAnnotationByID(ctx, annotations.GetRepository(), int64(annotationId), tempUser)
|
||||
annotation, resp := findAnnotationByID(ctx, annotationsRepo, int64(annotationId), tempUser)
|
||||
if resp != nil {
|
||||
return nil, errors.New("could not resolve annotation type")
|
||||
}
|
||||
|
@ -17,11 +17,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||
)
|
||||
|
||||
func TestAnnotationsAPIEndpoint(t *testing.T) {
|
||||
@ -73,8 +75,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
||||
mock := mockstore.NewSQLStoreMock()
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
@ -103,8 +103,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
||||
mock := mockstore.NewSQLStoreMock()
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
@ -178,8 +176,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||
setUpACL()
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
@ -211,8 +207,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||
setUpACL()
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
@ -286,59 +280,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
type fakeAnnotationsRepo struct {
|
||||
annotations map[int64]annotations.Item
|
||||
}
|
||||
|
||||
func NewFakeAnnotationsRepo() *fakeAnnotationsRepo {
|
||||
return &fakeAnnotationsRepo{
|
||||
annotations: map[int64]annotations.Item{},
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *fakeAnnotationsRepo) Delete(_ context.Context, params *annotations.DeleteParams) error {
|
||||
if params.Id != 0 {
|
||||
delete(repo.annotations, params.Id)
|
||||
} else {
|
||||
for _, v := range repo.annotations {
|
||||
if params.DashboardId == v.DashboardId && params.PanelId == v.PanelId {
|
||||
delete(repo.annotations, v.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (repo *fakeAnnotationsRepo) Save(item *annotations.Item) error {
|
||||
if item.Id == 0 {
|
||||
item.Id = int64(len(repo.annotations) + 1)
|
||||
}
|
||||
repo.annotations[item.Id] = *item
|
||||
return nil
|
||||
}
|
||||
func (repo *fakeAnnotationsRepo) Update(_ context.Context, item *annotations.Item) error {
|
||||
return nil
|
||||
}
|
||||
func (repo *fakeAnnotationsRepo) Find(_ context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
||||
if annotation, has := repo.annotations[query.AnnotationId]; has {
|
||||
return []*annotations.ItemDTO{{Id: annotation.Id, DashboardId: annotation.DashboardId}}, nil
|
||||
}
|
||||
annotations := []*annotations.ItemDTO{{Id: 1, DashboardId: 0}}
|
||||
return annotations, nil
|
||||
}
|
||||
func (repo *fakeAnnotationsRepo) FindTags(_ context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
||||
result := annotations.FindTagsResult{
|
||||
Tags: []*annotations.TagsDTO{},
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (repo *fakeAnnotationsRepo) LoadItems() {
|
||||
|
||||
}
|
||||
|
||||
var fakeAnnoRepo *fakeAnnotationsRepo
|
||||
|
||||
func postAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role org.RoleType,
|
||||
cmd dtos.PostAnnotationsCmd, store sqlstore.Store, dashSvc dashboards.DashboardService, fn scenarioFunc) {
|
||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||
@ -358,9 +299,6 @@ func postAnnotationScenario(t *testing.T, desc string, url string, routePattern
|
||||
return hs.PostAnnotation(c)
|
||||
})
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
|
||||
sc.m.Post(routePattern, sc.defaultHandler)
|
||||
|
||||
fn(sc)
|
||||
@ -387,9 +325,6 @@ func putAnnotationScenario(t *testing.T, desc string, url string, routePattern s
|
||||
return hs.UpdateAnnotation(c)
|
||||
})
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
|
||||
sc.m.Put(routePattern, sc.defaultHandler)
|
||||
|
||||
fn(sc)
|
||||
@ -415,9 +350,6 @@ func patchAnnotationScenario(t *testing.T, desc string, url string, routePattern
|
||||
return hs.PatchAnnotation(c)
|
||||
})
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
|
||||
sc.m.Patch(routePattern, sc.defaultHandler)
|
||||
|
||||
fn(sc)
|
||||
@ -443,9 +375,6 @@ func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePatte
|
||||
return hs.MassDeleteAnnotations(c)
|
||||
})
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
|
||||
sc.m.Post(routePattern, sc.defaultHandler)
|
||||
|
||||
fn(sc)
|
||||
@ -461,11 +390,8 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
|
||||
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
|
||||
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
_ = fakeAnnoRepo.Save(dashboardAnnotation)
|
||||
_ = fakeAnnoRepo.Save(organizationAnnotation)
|
||||
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
_ = sc.hs.annotationsRepo.Save(context.Background(), dashboardAnnotation)
|
||||
_ = sc.hs.annotationsRepo.Save(context.Background(), organizationAnnotation)
|
||||
|
||||
postOrganizationCmd := dtos.PostAnnotationsCmd{
|
||||
Time: 1000,
|
||||
@ -788,7 +714,7 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
setUpRBACGuardian(t)
|
||||
sc.acmock.
|
||||
RegisterScopeAttributeResolver(AnnotationTypeScopeResolver())
|
||||
RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(sc.hs.annotationsRepo))
|
||||
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgID)
|
||||
|
||||
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
||||
@ -835,13 +761,11 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
|
||||
dashboardAnnotation := annotations.Item{Id: 1, DashboardId: 1}
|
||||
organizationAnnotation := annotations.Item{Id: 2}
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
_ = fakeAnnoRepo.Save(&dashboardAnnotation)
|
||||
_ = fakeAnnoRepo.Save(&organizationAnnotation)
|
||||
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||
_ = fakeAnnoRepo.Save(context.Background(), &dashboardAnnotation)
|
||||
_ = fakeAnnoRepo.Save(context.Background(), &organizationAnnotation)
|
||||
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
|
||||
prefix, resolver := AnnotationTypeScopeResolver()
|
||||
prefix, resolver := AnnotationTypeScopeResolver(fakeAnnoRepo)
|
||||
require.Equal(t, "annotations:id:", prefix)
|
||||
|
||||
for _, tc := range testCases {
|
||||
@ -986,11 +910,8 @@ func TestAPI_MassDeleteAnnotations_AccessControl(t *testing.T) {
|
||||
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
|
||||
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
|
||||
|
||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
||||
_ = fakeAnnoRepo.Save(dashboardAnnotation)
|
||||
_ = fakeAnnoRepo.Save(organizationAnnotation)
|
||||
|
||||
annotations.SetRepository(fakeAnnoRepo)
|
||||
_ = sc.hs.annotationsRepo.Save(context.Background(), dashboardAnnotation)
|
||||
_ = sc.hs.annotationsRepo.Save(context.Background(), organizationAnnotation)
|
||||
|
||||
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
||||
assert.Equalf(t, tt.want, r.Code, "Annotations API(%v)", tt.args.url)
|
||||
@ -1002,7 +923,7 @@ func setUpACL() {
|
||||
viewerRole := org.RoleViewer
|
||||
editorRole := org.RoleEditor
|
||||
store := mockstore.NewSQLStoreMock()
|
||||
store.ExpectedTeamsByUser = []*models.TeamDTO{}
|
||||
teamSvc := &teamtest.FakeService{}
|
||||
dashSvc := &dashboards.FakeDashboardService{}
|
||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
||||
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
||||
@ -1012,7 +933,7 @@ func setUpACL() {
|
||||
}
|
||||
}).Return(nil)
|
||||
|
||||
guardian.InitLegacyGuardian(store, dashSvc)
|
||||
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
|
||||
}
|
||||
|
||||
func setUpRBACGuardian(t *testing.T) {
|
||||
|
@ -58,7 +58,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
reqGrafanaAdmin := middleware.ReqGrafanaAdmin
|
||||
reqEditorRole := middleware.ReqEditorRole
|
||||
reqOrgAdmin := middleware.ReqOrgAdmin
|
||||
reqOrgAdminDashOrFolderAdminOrTeamAdmin := middleware.OrgAdminDashOrFolderAdminOrTeamAdmin(hs.SQLStore, hs.DashboardService)
|
||||
reqOrgAdminDashOrFolderAdminOrTeamAdmin := middleware.OrgAdminDashOrFolderAdminOrTeamAdmin(hs.SQLStore, hs.DashboardService, hs.teamService)
|
||||
reqCanAccessTeams := middleware.AdminOrEditorAndFeatureEnabled(hs.Cfg.EditorsCanAdmin)
|
||||
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
|
||||
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
|
||||
@ -41,6 +42,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/login/loginservice"
|
||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||
"github.com/grafana/grafana/pkg/services/preference/preftest"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
@ -49,6 +51,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/searchusers/filters"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@ -295,6 +301,7 @@ type accessControlScenarioContext struct {
|
||||
cfg *setting.Cfg
|
||||
|
||||
dashboardsStore dashboards.Store
|
||||
teamService team.Service
|
||||
}
|
||||
|
||||
func setAccessControlPermissions(acmock *accesscontrolmock.Mock, perms []accesscontrol.Permission, org int64) {
|
||||
@ -337,10 +344,11 @@ func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer {
|
||||
cfg.IsFeatureToggleEnabled = features.IsEnabled
|
||||
|
||||
return &HTTPServer{
|
||||
Cfg: cfg,
|
||||
Features: features,
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AccessControl: accesscontrolmock.New().WithDisabled(),
|
||||
Cfg: cfg,
|
||||
Features: features,
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AccessControl: accesscontrolmock.New().WithDisabled(),
|
||||
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,7 +371,8 @@ func setupHTTPServerWithCfgDb(
|
||||
|
||||
license := &licensing.OSSLicensingService{}
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
dashboardsStore := dashboardsstore.ProvideDashboardStore(db, featuremgmt.WithFeatures())
|
||||
teamService := teamimpl.ProvideService(db)
|
||||
dashboardsStore := dashboardsstore.ProvideDashboardStore(db, featuremgmt.WithFeatures(), tagimpl.ProvideService(db))
|
||||
|
||||
var acmock *accesscontrolmock.Mock
|
||||
var ac accesscontrol.AccessControl
|
||||
@ -408,6 +417,9 @@ func setupHTTPServerWithCfgDb(
|
||||
),
|
||||
preferenceService: preftest.NewPreferenceServiceFake(),
|
||||
userService: userMock,
|
||||
orgService: orgtest.NewOrgServiceFake(),
|
||||
teamService: teamService,
|
||||
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
@ -442,6 +454,7 @@ func setupHTTPServerWithCfgDb(
|
||||
db: db,
|
||||
cfg: cfg,
|
||||
dashboardsStore: dashboardsStore,
|
||||
teamService: teamService,
|
||||
usermock: userMock,
|
||||
}
|
||||
}
|
||||
@ -519,11 +532,12 @@ func setUp(confs ...setUpConf) *HTTPServer {
|
||||
}
|
||||
}
|
||||
store.ExpectedTeamsByUser = []*models.TeamDTO{}
|
||||
teamSvc := &teamtest.FakeService{}
|
||||
dashSvc := &dashboards.FakeDashboardService{}
|
||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
||||
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
||||
q.Result = aclMockResp
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(store, dashSvc)
|
||||
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
|
||||
return hs
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
|
||||
meta.FolderUrl = query.Result.GetUrl()
|
||||
}
|
||||
|
||||
provisioningData, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
||||
provisioningData, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(c.Req.Context(), dash.Id)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
||||
}
|
||||
@ -407,13 +407,13 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
||||
|
||||
var provisioningData *models.DashboardProvisioning
|
||||
if dash.Id != 0 {
|
||||
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
||||
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(c.Req.Context(), dash.Id)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while checking if dashboard is provisioned using ID", err)
|
||||
}
|
||||
provisioningData = data
|
||||
} else if dash.Uid != "" {
|
||||
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(dash.OrgId, dash.Uid)
|
||||
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(c.Req.Context(), dash.OrgId, dash.Uid)
|
||||
if err != nil && !errors.Is(err, dashboards.ErrProvisionedDashboardNotFound) && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||
)
|
||||
|
||||
func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
|
||||
@ -70,10 +71,11 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
|
||||
hs := &HTTPServer{dashboardsnapshotsService: setUpSnapshotTest(t, 0, "")}
|
||||
sc.handlerFunc = hs.DeleteDashboardSnapshot
|
||||
|
||||
teamSvc := &teamtest.FakeService{}
|
||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Return(nil).Maybe()
|
||||
|
||||
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc)
|
||||
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc)
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
|
||||
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
@ -107,6 +109,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("When user is editor and dashboard has default ACL", func(t *testing.T) {
|
||||
teamSvc := &teamtest.FakeService{}
|
||||
dashSvc := &dashboards.FakeDashboardService{}
|
||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
||||
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
||||
@ -118,7 +121,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
|
||||
|
||||
loggedInUserScenarioWithRole(t, "Should be able to delete a snapshot when calling DELETE on", "DELETE",
|
||||
"/api/snapshots/12345", "/api/snapshots/:key", org.RoleEditor, func(sc *scenarioContext) {
|
||||
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc)
|
||||
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc)
|
||||
var externalRequest *http.Request
|
||||
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(200)
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
@ -38,6 +39,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@ -109,7 +112,7 @@ func newTestLive(t *testing.T, store *sqlstore.SQLStore) *live.GrafanaLive {
|
||||
nil,
|
||||
&usagestats.UsageStatsMock{T: t},
|
||||
nil,
|
||||
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{})
|
||||
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{}, annotationstest.NewFakeAnnotationsRepo(), nil)
|
||||
require.NoError(t, err)
|
||||
return gLive
|
||||
}
|
||||
@ -128,6 +131,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
fakeDash.HasACL = false
|
||||
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
||||
fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersion{}
|
||||
teamService := &teamtest.FakeService{}
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
||||
q := args.Get(1).(*models.GetDashboardQuery)
|
||||
@ -156,7 +160,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
|
||||
}
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
}
|
||||
|
||||
// This tests two scenarios:
|
||||
@ -234,6 +238,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
fakeDash.HasACL = true
|
||||
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
||||
fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersion{}
|
||||
teamService := &teamtest.FakeService{}
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
||||
q := args.Get(1).(*models.GetDashboardQuery)
|
||||
@ -273,7 +278,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
setting.ViewersCanEdit = origCanEdit
|
||||
})
|
||||
setting.ViewersCanEdit = false
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
}
|
||||
|
||||
// This tests six scenarios:
|
||||
@ -377,7 +382,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_EDIT},
|
||||
}
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
}
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
|
||||
@ -439,7 +444,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
|
||||
}
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
}
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||
@ -479,7 +484,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_ADMIN},
|
||||
}
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
}
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||
@ -532,7 +537,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
|
||||
}
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
}
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||
@ -742,9 +747,10 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
}
|
||||
sqlmock := mockstore.SQLStoreMock{}
|
||||
setUp := func() {
|
||||
teamSvc := &teamtest.FakeService{}
|
||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Return(nil)
|
||||
guardian.InitLegacyGuardian(&sqlmock, dashSvc)
|
||||
guardian.InitLegacyGuardian(&sqlmock, dashSvc, teamSvc)
|
||||
}
|
||||
|
||||
cmd := dtos.CalculateDiffOptions{
|
||||
@ -860,6 +866,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
dashboardStore := dashboards.NewFakeDashboardStore(t)
|
||||
dashboardStore.On("GetProvisionedDataByDashboardID", mock.Anything).Return(&models.DashboardProvisioning{ExternalId: "/dashboard1.json"}, nil).Once()
|
||||
|
||||
teamService := &teamtest.FakeService{}
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
|
||||
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
|
||||
@ -872,7 +879,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
||||
q.Result = []*models.DashboardACLInfoDTO{{OrgId: testOrgID, DashboardId: 1, UserId: testUserID, Permission: models.PERMISSION_EDIT}}
|
||||
}).Return(nil)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
|
||||
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
||||
fakeProvisioningService := provisioning.NewProvisioningServiceMock(context.Background())
|
||||
@ -929,7 +936,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
||||
|
||||
if dashboardStore == nil {
|
||||
sql := sqlstore.InitTestDB(t)
|
||||
dashboardStore = database.ProvideDashboardStore(sql, featuremgmt.WithFeatures())
|
||||
dashboardStore = database.ProvideDashboardStore(sql, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql))
|
||||
}
|
||||
|
||||
libraryPanelsService := mockLibraryPanelService{}
|
||||
@ -1134,7 +1141,7 @@ type mockDashboardProvisioningService struct {
|
||||
dashboards.DashboardProvisioningService
|
||||
}
|
||||
|
||||
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(dashboardID int64) (
|
||||
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(ctx context.Context, dashboardID int64) (
|
||||
*models.DashboardProvisioning, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -8,18 +8,21 @@ import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/datasource"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@ -326,6 +329,26 @@ func validateURL(cmdType string, url string) response.Response {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateJSONData prevents the user from adding a custom header with name that matches the auth proxy header name.
|
||||
// This is done to prevent data source proxy from being used to circumvent auth proxy.
|
||||
// For more context take a look at CVE-2022-35957
|
||||
func validateJSONData(jsonData *simplejson.Json, cfg *setting.Cfg) error {
|
||||
if jsonData == nil || !cfg.AuthProxyEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
for key, value := range jsonData.MustMap() {
|
||||
if strings.HasPrefix(key, "httpHeaderName") {
|
||||
header := fmt.Sprint(value)
|
||||
if http.CanonicalHeaderKey(header) == http.CanonicalHeaderKey(cfg.AuthProxyHeaderName) {
|
||||
datasourcesLogger.Error("Forbidden to add a data source header with a name equal to auth proxy header name", "headerName", key)
|
||||
return errors.New("validation error, invalid header name specified")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// swagger:route POST /datasources datasources addDataSource
|
||||
//
|
||||
// Create a data source.
|
||||
@ -357,6 +380,9 @@ func (hs *HTTPServer) AddDataSource(c *models.ReqContext) response.Response {
|
||||
return resp
|
||||
}
|
||||
}
|
||||
if err := validateJSONData(cmd.JsonData, hs.Cfg); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Failed to add datasource", err)
|
||||
}
|
||||
|
||||
if err := hs.DataSourcesService.AddDataSource(c.Req.Context(), &cmd); err != nil {
|
||||
if errors.Is(err, datasources.ErrDataSourceNameExists) || errors.Is(err, datasources.ErrDataSourceUidExists) {
|
||||
@ -414,6 +440,9 @@ func (hs *HTTPServer) UpdateDataSourceByID(c *models.ReqContext) response.Respon
|
||||
if resp := validateURL(cmd.Type, cmd.Url); resp != nil {
|
||||
return resp
|
||||
}
|
||||
if err := validateJSONData(cmd.JsonData, hs.Cfg); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Failed to update datasource", err)
|
||||
}
|
||||
|
||||
ds, err := hs.getRawDataSourceById(c.Req.Context(), cmd.Id, cmd.OrgId)
|
||||
if err != nil {
|
||||
@ -451,6 +480,9 @@ func (hs *HTTPServer) UpdateDataSourceByUID(c *models.ReqContext) response.Respo
|
||||
if resp := validateURL(cmd.Type, cmd.Url); resp != nil {
|
||||
return resp
|
||||
}
|
||||
if err := validateJSONData(cmd.JsonData, hs.Cfg); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Failed to update datasource", err)
|
||||
}
|
||||
|
||||
ds, err := hs.getRawDataSourceByUID(c.Req.Context(), web.Params(c.Req)[":uid"], c.OrgID)
|
||||
if err != nil {
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
@ -83,6 +84,7 @@ func TestAddDataSource_InvalidURL(t *testing.T) {
|
||||
sc := setupScenarioContext(t, "/api/datasources")
|
||||
hs := &HTTPServer{
|
||||
DataSourcesService: &dataSourcesServiceMock{},
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
|
||||
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
@ -109,6 +111,7 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
DataSourcesService: &dataSourcesServiceMock{
|
||||
expectedDatasource: &datasources.DataSource{},
|
||||
},
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, "/api/datasources")
|
||||
@ -128,10 +131,42 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
}
|
||||
|
||||
// Using a custom header whose name matches the name specified for auth proxy header should fail
|
||||
func TestAddDataSource_InvalidJSONData(t *testing.T) {
|
||||
hs := &HTTPServer{
|
||||
DataSourcesService: &dataSourcesServiceMock{},
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, "/api/datasources")
|
||||
|
||||
hs.Cfg = setting.NewCfg()
|
||||
hs.Cfg.AuthProxyEnabled = true
|
||||
hs.Cfg.AuthProxyHeaderName = "X-AUTH-PROXY-HEADER"
|
||||
jsonData := simplejson.New()
|
||||
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxyHeaderName)
|
||||
|
||||
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
|
||||
Name: "Test",
|
||||
Url: "localhost:5432",
|
||||
Access: "direct",
|
||||
Type: "test",
|
||||
JsonData: jsonData,
|
||||
})
|
||||
return hs.AddDataSource(c)
|
||||
}))
|
||||
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
}
|
||||
|
||||
// Updating data sources with invalid URLs should lead to an error.
|
||||
func TestUpdateDataSource_InvalidURL(t *testing.T) {
|
||||
hs := &HTTPServer{
|
||||
DataSourcesService: &dataSourcesServiceMock{},
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
sc := setupScenarioContext(t, "/api/datasources/1234")
|
||||
|
||||
@ -150,6 +185,35 @@ func TestUpdateDataSource_InvalidURL(t *testing.T) {
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
}
|
||||
|
||||
// Using a custom header whose name matches the name specified for auth proxy header should fail
|
||||
func TestUpdateDataSource_InvalidJSONData(t *testing.T) {
|
||||
hs := &HTTPServer{
|
||||
DataSourcesService: &dataSourcesServiceMock{},
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
sc := setupScenarioContext(t, "/api/datasources/1234")
|
||||
|
||||
hs.Cfg.AuthProxyEnabled = true
|
||||
hs.Cfg.AuthProxyHeaderName = "X-AUTH-PROXY-HEADER"
|
||||
jsonData := simplejson.New()
|
||||
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxyHeaderName)
|
||||
|
||||
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
|
||||
Name: "Test",
|
||||
Url: "localhost:5432",
|
||||
Access: "direct",
|
||||
Type: "test",
|
||||
JsonData: jsonData,
|
||||
})
|
||||
return hs.AddDataSource(c)
|
||||
}))
|
||||
|
||||
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
|
||||
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
}
|
||||
|
||||
// Updating data sources with URLs not specifying protocol should work.
|
||||
func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
const name = "Test"
|
||||
@ -159,6 +223,7 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
DataSourcesService: &dataSourcesServiceMock{
|
||||
expectedDatasource: &datasources.DataSource{},
|
||||
},
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, "/api/datasources/1234")
|
||||
|
@ -12,6 +12,7 @@ type IndexViewData struct {
|
||||
AppUrl string
|
||||
AppSubUrl string
|
||||
GoogleAnalyticsId string
|
||||
GoogleAnalytics4Id string
|
||||
GoogleTagManagerId string
|
||||
NavTree []*NavLink
|
||||
BuildVersion string
|
||||
|
@ -6,18 +6,19 @@ import (
|
||||
)
|
||||
|
||||
type PluginSetting struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Module string `json:"module"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
Info plugins.Info `json:"info"`
|
||||
Includes []*plugins.Includes `json:"includes"`
|
||||
Dependencies plugins.Dependencies `json:"dependencies"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
DefaultNavUrl string `json:"defaultNavUrl"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Module string `json:"module"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
Info plugins.Info `json:"info"`
|
||||
Includes []*plugins.Includes `json:"includes"`
|
||||
Dependencies plugins.Dependencies `json:"dependencies"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
SecureJsonFields map[string]bool `json:"secureJsonFields"`
|
||||
DefaultNavUrl string `json:"defaultNavUrl"`
|
||||
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
HasUpdate bool `json:"hasUpdate"`
|
||||
|
@ -86,10 +86,16 @@ type fakePluginSettings struct {
|
||||
}
|
||||
|
||||
// GetPluginSettings returns all Plugin Settings for the provided Org
|
||||
func (ps *fakePluginSettings) GetPluginSettings(ctx context.Context, args *pluginsettings.GetArgs) ([]*pluginsettings.DTO, error) {
|
||||
res := []*pluginsettings.DTO{}
|
||||
func (ps *fakePluginSettings) GetPluginSettings(_ context.Context, _ *pluginsettings.GetArgs) ([]*pluginsettings.InfoDTO, error) {
|
||||
res := []*pluginsettings.InfoDTO{}
|
||||
for _, dto := range ps.plugins {
|
||||
res = append(res, dto)
|
||||
res = append(res, &pluginsettings.InfoDTO{
|
||||
PluginID: dto.PluginID,
|
||||
OrgID: dto.OrgID,
|
||||
Enabled: dto.Enabled,
|
||||
Pinned: dto.Pinned,
|
||||
PluginVersion: dto.PluginVersion,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
@ -237,13 +238,14 @@ func createFolderScenario(t *testing.T, desc string, url string, routePattern st
|
||||
setUpRBACGuardian(t)
|
||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||
aclMockResp := []*models.DashboardACLInfoDTO{}
|
||||
teamSvc := &teamtest.FakeService{}
|
||||
dashSvc := &dashboards.FakeDashboardService{}
|
||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
|
||||
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
|
||||
q.Result = aclMockResp
|
||||
}).Return(nil)
|
||||
store := mockstore.NewSQLStoreMock()
|
||||
guardian.InitLegacyGuardian(store, dashSvc)
|
||||
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
|
||||
hs := HTTPServer{
|
||||
AccessControl: acmock.New(),
|
||||
folderService: folderService,
|
||||
|
@ -117,6 +117,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
"profileEnabled": setting.ProfileEnabled,
|
||||
"queryHistoryEnabled": hs.Cfg.QueryHistoryEnabled,
|
||||
"googleAnalyticsId": setting.GoogleAnalyticsId,
|
||||
"googleAnalytics4Id": setting.GoogleAnalytics4Id,
|
||||
"rudderstackWriteKey": setting.RudderstackWriteKey,
|
||||
"rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl,
|
||||
"rudderstackSdkUrl": setting.RudderstackSdkUrl,
|
||||
@ -139,6 +140,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
"auth": map[string]interface{}{
|
||||
"OAuthSkipOrgRoleUpdateSync": hs.Cfg.OAuthSkipOrgRoleUpdateSync,
|
||||
"SAMLSkipOrgRoleSync": hs.Cfg.SectionWithEnvOverrides("auth.saml").Key("skip_org_role_sync").MustBool(false),
|
||||
"DisableSyncLock": hs.Cfg.DisableSyncLock,
|
||||
},
|
||||
"buildInfo": map[string]interface{}{
|
||||
"hideVersion": hideVersion,
|
||||
@ -429,8 +431,8 @@ func (hs *HTTPServer) enabledPlugins(ctx context.Context, orgID int64) (EnabledP
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[string]*pluginsettings.DTO, error) {
|
||||
pluginSettings := make(map[string]*pluginsettings.DTO)
|
||||
func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[string]*pluginsettings.InfoDTO, error) {
|
||||
pluginSettings := make(map[string]*pluginsettings.InfoDTO)
|
||||
|
||||
// fill settings from database
|
||||
if pss, err := hs.PluginSettings.GetPluginSettings(ctx, &pluginsettings.GetArgs{OrgID: orgID}); err != nil {
|
||||
@ -449,11 +451,12 @@ func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[stri
|
||||
}
|
||||
|
||||
// add new setting which is enabled depending on if AutoEnabled: true
|
||||
pluginSetting := &pluginsettings.DTO{
|
||||
PluginID: plugin.ID,
|
||||
OrgID: orgID,
|
||||
Enabled: plugin.AutoEnabled,
|
||||
Pinned: plugin.AutoEnabled,
|
||||
pluginSetting := &pluginsettings.InfoDTO{
|
||||
PluginID: plugin.ID,
|
||||
OrgID: orgID,
|
||||
Enabled: plugin.AutoEnabled,
|
||||
Pinned: plugin.AutoEnabled,
|
||||
PluginVersion: plugin.Info.Version,
|
||||
}
|
||||
|
||||
pluginSettings[plugin.ID] = pluginSetting
|
||||
@ -467,10 +470,12 @@ func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[stri
|
||||
}
|
||||
|
||||
// add new setting which is enabled by default
|
||||
pluginSetting := &pluginsettings.DTO{
|
||||
PluginID: plugin.ID,
|
||||
OrgID: orgID,
|
||||
Enabled: true,
|
||||
pluginSetting := &pluginsettings.InfoDTO{
|
||||
PluginID: plugin.ID,
|
||||
OrgID: orgID,
|
||||
Enabled: true,
|
||||
Pinned: false,
|
||||
PluginVersion: plugin.Info.Version,
|
||||
}
|
||||
|
||||
// if plugin is included in an app, check app settings
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/cleanup"
|
||||
"github.com/grafana/grafana/pkg/services/comments"
|
||||
@ -83,6 +84,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/star"
|
||||
"github.com/grafana/grafana/pkg/services/store"
|
||||
"github.com/grafana/grafana/pkg/services/tag"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
||||
tempUser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/services/thumbs"
|
||||
@ -188,7 +191,10 @@ type HTTPServer struct {
|
||||
dashboardThumbsService dashboardThumbs.Service
|
||||
loginAttemptService loginAttempt.Service
|
||||
orgService org.Service
|
||||
teamService team.Service
|
||||
accesscontrolService accesscontrol.Service
|
||||
annotationsRepo annotations.Repository
|
||||
tagService tag.Service
|
||||
}
|
||||
|
||||
type ServerOptions struct {
|
||||
@ -226,8 +232,9 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore,
|
||||
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
|
||||
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
|
||||
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service, loginAttemptService loginAttempt.Service, orgService org.Service,
|
||||
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service,
|
||||
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service,
|
||||
loginAttemptService loginAttempt.Service, orgService org.Service, teamService team.Service,
|
||||
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service, annotationRepo annotations.Repository, tagService tag.Service,
|
||||
) (*HTTPServer, error) {
|
||||
web.Env = cfg.Env
|
||||
m := web.New()
|
||||
@ -322,7 +329,10 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dashboardThumbsService: dashboardThumbsService,
|
||||
loginAttemptService: loginAttemptService,
|
||||
orgService: orgService,
|
||||
teamService: teamService,
|
||||
accesscontrolService: accesscontrolService,
|
||||
annotationsRepo: annotationRepo,
|
||||
tagService: tagService,
|
||||
}
|
||||
if hs.Listener != nil {
|
||||
hs.log.Debug("Using provided listener")
|
||||
@ -330,7 +340,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
hs.registerRoutes()
|
||||
|
||||
// Register access control scope resolver for annotations
|
||||
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver())
|
||||
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo))
|
||||
|
||||
if err := hs.declareFixedRoles(); err != nil {
|
||||
return nil, err
|
||||
|
@ -105,7 +105,7 @@ func (hs *HTTPServer) getAppLinks(c *models.ReqContext) ([]*dtos.NavLink, error)
|
||||
}
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagTopnav) {
|
||||
appLink.Url = path.Join(hs.Cfg.AppSubURL, "a", plugin.ID)
|
||||
appLink.Url = hs.Cfg.AppSubURL + "/a/" + plugin.ID
|
||||
} else {
|
||||
appLink.Url = path.Join(hs.Cfg.AppSubURL, plugin.DefaultNavURL)
|
||||
}
|
||||
@ -356,7 +356,7 @@ func (hs *HTTPServer) setupConfigNodes(c *models.ReqContext) ([]*dtos.NavLink, e
|
||||
})
|
||||
}
|
||||
|
||||
if hasAccess(ac.ReqOrgAdmin, correlations.ConfigurationPageAccess) {
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagCorrelations) && hasAccess(ac.ReqOrgAdmin, correlations.ConfigurationPageAccess) {
|
||||
configNodes = append(configNodes, &dtos.NavLink{
|
||||
Text: "Correlations",
|
||||
Icon: "gf-glue",
|
||||
@ -521,6 +521,7 @@ func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm b
|
||||
Text: "Browse", Id: "dashboards/browse", Url: hs.Cfg.AppSubURL + "/dashboards", Icon: "sitemap",
|
||||
})
|
||||
}
|
||||
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Playlists", Id: "dashboards/playlists", Url: hs.Cfg.AppSubURL + "/playlists", Icon: "presentation-play",
|
||||
})
|
||||
@ -840,6 +841,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
AppUrl: appURL,
|
||||
AppSubUrl: appSubURL,
|
||||
GoogleAnalyticsId: setting.GoogleAnalyticsId,
|
||||
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
|
||||
GoogleTagManagerId: setting.GoogleTagManagerId,
|
||||
BuildVersion: setting.BuildVersion,
|
||||
BuildCommit: setting.BuildCommit,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user