diff --git a/.betterer.results b/.betterer.results
index fcb25c5f876..43127440a4e 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -523,7 +523,8 @@ exports[`better eslint`] = {
],
"packages/grafana-data/src/types/logs.ts: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.", "1"],
+ [0, 0, 0, "Do not use any type assertions.", "2"]
],
"packages/grafana-data/src/types/logsVolume.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@@ -697,13 +698,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"]
],
- "packages/grafana-data/src/utils/dataLinks.test.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
- ],
"packages/grafana-data/src/utils/dataLinks.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.", "0"]
],
"packages/grafana-data/src/utils/datasource.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -1103,8 +1099,7 @@ exports[`better eslint`] = {
"packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx: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, "Do not use any type assertions.", "3"]
+ [0, 0, 0, "Do not use any type assertions.", "2"]
],
"packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
@@ -1622,8 +1617,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"packages/grafana-ui/src/slate-plugins/braces.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, "Do not use any type assertions.", "0"]
],
"packages/grafana-ui/src/slate-plugins/slate-prism/index.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
@@ -1695,101 +1689,6 @@ exports[`better eslint`] = {
"packages/grafana-ui/src/utils/useAsyncDependency.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
- "packages/jaeger-ui-components/src/ScrollManager.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, "Unexpected any. Specify a different type.", "2"]
- ],
- "packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/Scrubber.tsx: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"]
- ],
- "packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/render-into-canvas.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
- "packages/jaeger-ui-components/src/TraceTimelineViewer/ListView/index.tsx: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"]
- ],
- "packages/jaeger-ui-components/src/TraceTimelineViewer/SpanBar.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/TraceTimelineViewer/SpanDetail/index.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
- ],
- "packages/jaeger-ui-components/src/TraceTimelineViewer/TimelineHeaderRow/TimelineViewingLayer.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
- "packages/jaeger-ui-components/src/TraceTimelineViewer/utils.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/common/BreakableText.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/common/UiFindInput.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
- ],
- "packages/jaeger-ui-components/src/constants/index.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, "Do not use any type assertions.", "4"]
- ],
- "packages/jaeger-ui-components/src/constants/tag-keys.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"]
- ],
- "packages/jaeger-ui-components/src/keyboard-shortcuts.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
- ],
- "packages/jaeger-ui-components/src/model/link-patterns.tsx: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, "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"]
- ],
- "packages/jaeger-ui-components/src/model/transform-trace-data.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"]
- ],
- "packages/jaeger-ui-components/src/types/api-error.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/types/links.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/types/trace.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/utils/DraggableManager/types.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "packages/jaeger-ui-components/src/utils/date.tsx:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
"plugins-bundled/internal/input-datasource/src/InputDatasource.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
@@ -2476,6 +2375,24 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
+ "public/app/core/components/TimelineChart/TimelineChart.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
+ ],
+ "public/app/core/components/TimelineChart/timeline.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, "Do not use any type assertions.", "3"],
+ [0, 0, 0, "Do not use any type assertions.", "4"]
+ ],
+ "public/app/core/components/TimelineChart/utils.test.ts:5381": [
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
+ ],
+ "public/app/core/components/TimelineChart/utils.ts:5381": [
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
+ ],
"public/app/core/components/connectWithCleanUp.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
@@ -2983,8 +2900,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/alerting/unified/components/rules/RulesFilter.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.", "0"]
],
"public/app/features/alerting/unified/components/silences/SilencesEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@@ -3597,15 +3513,14 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "25"],
[0, 0, 0, "Unexpected any. Specify a different type.", "26"],
[0, 0, 0, "Unexpected any. Specify a different type.", "27"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "28"],
- [0, 0, 0, "Do not use any type assertions.", "29"],
+ [0, 0, 0, "Do not use any type assertions.", "28"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "29"],
[0, 0, 0, "Unexpected any. Specify a different type.", "30"],
[0, 0, 0, "Unexpected any. Specify a different type.", "31"],
[0, 0, 0, "Unexpected any. Specify a different type.", "32"],
[0, 0, 0, "Unexpected any. Specify a different type.", "33"],
[0, 0, 0, "Unexpected any. Specify a different type.", "34"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "35"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "36"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "35"]
],
"public/app/features/dashboard/state/PanelModel.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -3877,16 +3792,66 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Do not use any type assertions.", "5"]
],
- "public/app/features/explore/TraceView/createSpanLink.tsx:5381": [
+ "public/app/features/explore/TraceView/components/ScrollManager.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, "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, "Do not use any type assertions.", "7"],
- [0, 0, 0, "Do not use any type assertions.", "8"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
+ ],
+ "public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/Scrubber.tsx: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"]
+ ],
+ "public/app/features/explore/TraceView/components/TracePageHeader/SpanGraph/render-into-canvas.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/TraceTimelineViewer/ListView/index.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanBar.tsx:5381": [
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/TraceTimelineViewer/utils.tsx:5381": [
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/common/BreakableText.tsx:5381": [
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/constants/index.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/demo/trace-generators.ts:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/model/link-patterns.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/explore/TraceView/components/model/link-patterns.tsx: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, "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"]
+ ],
+ "public/app/features/explore/TraceView/components/model/transform-trace-data.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
+ "public/app/features/explore/TraceView/components/types/trace.ts:5381": [
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
+ ],
+ "public/app/features/explore/TraceView/createSpanLink.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"],
+ [0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/features/explore/spec/helper/setup.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
@@ -4587,8 +4552,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/search/utils.ts: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.", "0"]
],
"public/app/features/serviceaccounts/ServiceAccountPage.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6100,9 +6064,10 @@ 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, "Do not use any type assertions.", "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.", "6"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "7"]
],
"public/app/plugins/datasource/loki/getDerivedFields.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6695,12 +6660,9 @@ exports[`better eslint`] = {
[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"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "5"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "6"],
- [0, 0, 0, "Do not use any type assertions.", "7"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "8"]
+ [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"]
],
"public/app/plugins/datasource/testdata/components/PredictablePulseEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@@ -6735,10 +6697,7 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/testdata/datasource.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, "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.", "2"]
],
"public/app/plugins/datasource/testdata/module.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@@ -6758,9 +6717,6 @@ 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/testdata/types.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
"public/app/plugins/datasource/zipkin/QueryField.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@@ -7510,10 +7466,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"]
],
- "public/app/plugins/panel/state-timeline/TimelineChart.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
- ],
"public/app/plugins/panel/state-timeline/migrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@@ -7524,22 +7476,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Do not use any type assertions.", "7"]
],
- "public/app/plugins/panel/state-timeline/timeline.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, "Do not use any type assertions.", "3"],
- [0, 0, 0, "Do not use any type assertions.", "4"]
- ],
- "public/app/plugins/panel/state-timeline/types.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "public/app/plugins/panel/state-timeline/utils.test.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
- "public/app/plugins/panel/state-timeline/utils.ts:5381": [
- [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
- ],
"public/app/plugins/panel/table-old/column_options.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
diff --git a/.dockerignore b/.dockerignore
index 7ed8d01615e..1df915d2461 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -2,7 +2,6 @@
.dockerignore
.git
.gitignore
-.github
.vscode
bin
data*
diff --git a/.drone.yml b/.drone.yml
index bbc16269349..e787f4007f8 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -84,13 +84,13 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn betterer ci
depends_on:
- yarn-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: betterer-frontend
- commands:
- yarn run ci:test-frontend
@@ -98,7 +98,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-frontend
trigger:
event:
@@ -141,7 +141,7 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn run prettier:check
@@ -152,7 +152,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: lint-frontend
trigger:
event:
@@ -206,7 +206,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -214,25 +214,25 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- go test -short -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend
- commands:
- go test -run Integration -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend-integration
trigger:
event:
@@ -286,7 +286,7 @@ steps:
- commands:
- make gen-go
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update && apt-get install make
@@ -359,7 +359,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -367,18 +367,18 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
@@ -403,7 +403,7 @@ steps:
from_secret: github_token_pr
TEST_TAG: v0.0.0-test
failure: ignore
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: trigger-test-release
when:
paths:
@@ -430,7 +430,7 @@ steps:
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend
- commands:
- ./bin/build build-frontend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
@@ -439,7 +439,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
@@ -448,7 +448,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition oss
@@ -456,7 +456,7 @@ steps:
- compile-build-cmd
- yarn-install
environment: null
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- . scripts/build/gpg-test-vars.sh && ./bin/build package --jobs 8 --edition oss
@@ -467,7 +467,7 @@ steps:
- build-frontend
- build-frontend-packages
environment: null
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package
- commands:
- ./scripts/grafana-server/start-server
@@ -480,7 +480,7 @@ steps:
environment:
ARCH: linux-amd64
PORT: 3001
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: grafana-server
- commands:
- apt-get install -y netcat
@@ -583,7 +583,7 @@ steps:
- build-frontend-packages
environment:
NODE_OPTIONS: --max_old_space_size=4096
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-storybook
when:
paths:
@@ -594,7 +594,7 @@ steps:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- yarn wait-on http://$HOST:$PORT
@@ -696,7 +696,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -704,13 +704,13 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update
@@ -726,7 +726,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: postgres-integration-tests
- commands:
- apt-get update
@@ -742,7 +742,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: mysql-integration-tests
trigger:
event:
@@ -800,7 +800,7 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- |-
@@ -812,7 +812,7 @@ steps:
wan" > words_to_ignore.txt
- codespell -I words_to_ignore.txt docs/
- rm words_to_ignore.txt
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: codespell
- commands:
- yarn run prettier:checkDocs
@@ -820,7 +820,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: lint-docs
- commands:
- mkdir -p /hugo/content/docs/grafana
@@ -870,7 +870,7 @@ steps:
- ./bin/build shellcheck
depends_on:
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: shellcheck
trigger:
event:
@@ -917,7 +917,7 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- |-
@@ -929,7 +929,7 @@ steps:
wan" > words_to_ignore.txt
- codespell -I words_to_ignore.txt docs/
- rm words_to_ignore.txt
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: codespell
- commands:
- yarn run prettier:checkDocs
@@ -937,7 +937,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: lint-docs
- commands:
- mkdir -p /hugo/content/docs/grafana
@@ -990,13 +990,13 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn betterer ci
depends_on:
- yarn-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: betterer-frontend
- commands:
- yarn run ci:test-frontend
@@ -1004,7 +1004,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-frontend
trigger:
branch: main
@@ -1044,7 +1044,7 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn run prettier:check
@@ -1055,7 +1055,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: lint-frontend
trigger:
branch: main
@@ -1106,7 +1106,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -1114,25 +1114,25 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- go test -short -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend
- commands:
- go test -run Integration -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend-integration
trigger:
branch: main
@@ -1179,7 +1179,7 @@ steps:
- commands:
- make gen-go
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update && apt-get install make
@@ -1251,7 +1251,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -1259,25 +1259,25 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- ./bin/build build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend
- commands:
- ./bin/build build-frontend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
@@ -1286,7 +1286,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
@@ -1295,7 +1295,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition oss
@@ -1305,7 +1305,7 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --sign
@@ -1323,7 +1323,7 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package
- commands:
- ./scripts/grafana-server/start-server
@@ -1336,7 +1336,7 @@ steps:
environment:
ARCH: linux-amd64
PORT: 3001
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: grafana-server
- commands:
- apt-get install -y netcat
@@ -1439,7 +1439,7 @@ steps:
- build-frontend-packages
environment:
NODE_OPTIONS: --max_old_space_size=4096
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-storybook
when:
paths:
@@ -1450,7 +1450,7 @@ steps:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- yarn wait-on http://$HOST:$PORT
@@ -1494,7 +1494,7 @@ steps:
GRAFANA_MISC_STATS_API_KEY:
from_secret: grafana_misc_stats_api_key
failure: ignore
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: publish-frontend-metrics
when:
repo:
@@ -1575,7 +1575,7 @@ steps:
environment:
NPM_TOKEN:
from_secret: npm_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: release-canary-npm-packages
when:
repo:
@@ -1686,7 +1686,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -1694,13 +1694,13 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update
@@ -1716,7 +1716,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: postgres-integration-tests
- commands:
- apt-get update
@@ -1732,7 +1732,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: mysql-integration-tests
trigger:
branch: main
@@ -1970,18 +1970,18 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -1995,7 +1995,7 @@ steps:
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend
- commands:
- ./bin/build build-frontend --jobs 8 --edition oss ${DRONE_TAG}
@@ -2004,7 +2004,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition oss ${DRONE_TAG}
@@ -2013,7 +2013,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition oss
@@ -2023,7 +2023,7 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build package --jobs 8 --edition oss --sign ${DRONE_TAG}
@@ -2041,14 +2041,14 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package
- commands:
- ls dist/*.tar.gz*
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition oss --shouldSave
@@ -2087,7 +2087,7 @@ steps:
environment:
ARCH: linux-amd64
PORT: 3001
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: grafana-server
- commands:
- apt-get install -y netcat
@@ -2164,7 +2164,7 @@ steps:
- build-frontend-packages
environment:
NODE_OPTIONS: --max_old_space_size=4096
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-storybook
when:
event:
@@ -2223,7 +2223,7 @@ steps:
from_secret: gcp_key
PRERELEASE_BUCKET:
from_secret: prerelease_bucket
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: store-npm-packages
trigger:
event:
@@ -2272,13 +2272,13 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn betterer ci
depends_on:
- yarn-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: betterer-frontend
- commands:
- yarn run ci:test-frontend
@@ -2286,7 +2286,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-frontend
trigger:
event:
@@ -2334,7 +2334,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -2342,25 +2342,25 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- go test -short -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend
- commands:
- go test -run Integration -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend-integration
trigger:
event:
@@ -2427,7 +2427,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -2435,13 +2435,13 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update
@@ -2457,7 +2457,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: postgres-integration-tests
- commands:
- apt-get update
@@ -2473,7 +2473,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: mysql-integration-tests
trigger:
event:
@@ -2590,7 +2590,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -2606,7 +2606,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -2620,13 +2620,13 @@ steps:
- make gen-go
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -2636,7 +2636,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -2645,14 +2645,14 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- ./bin/build build-backend --jobs 8 --edition enterprise ${DRONE_TAG}
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend
- commands:
- ./bin/build build-frontend --jobs 8 --edition enterprise ${DRONE_TAG}
@@ -2661,7 +2661,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition enterprise ${DRONE_TAG}
@@ -2670,7 +2670,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition enterprise
@@ -2680,7 +2680,7 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build package --jobs 8 --edition enterprise --sign ${DRONE_TAG}
@@ -2698,14 +2698,14 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package
- commands:
- ls dist/*.tar.gz*
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition enterprise --shouldSave
@@ -2745,7 +2745,7 @@ steps:
ARCH: linux-amd64
PORT: 3001
RUNDIR: scripts/grafana-server/tmp-grafana-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: grafana-server
- commands:
- apt-get install -y netcat
@@ -2877,7 +2877,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -2893,7 +2893,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- echo $DRONE_RUNNER_NAME
@@ -2909,14 +2909,14 @@ steps:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn betterer ci
depends_on:
- init-enterprise
- yarn-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: betterer-frontend
- commands:
- yarn run ci:test-frontend
@@ -2925,7 +2925,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-frontend
trigger:
event:
@@ -2962,7 +2962,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mkdir -p bin
@@ -2984,7 +2984,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- echo $DRONE_RUNNER_NAME
@@ -3006,7 +3006,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -3015,25 +3015,25 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- go test -short -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend
- commands:
- go test -run Integration -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend-integration
trigger:
event:
@@ -3106,7 +3106,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -3122,7 +3122,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -3132,7 +3132,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -3141,13 +3141,13 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update
@@ -3163,7 +3163,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: postgres-integration-tests
- commands:
- apt-get update
@@ -3179,7 +3179,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: mysql-integration-tests
- commands:
- dockerize -wait tcp://redis:6379/0 -timeout 120s
@@ -3190,7 +3190,7 @@ steps:
- wire-install
environment:
REDIS_URL: redis://redis:6379/0
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: redis-integration-tests
- commands:
- dockerize -wait tcp://memcached:11211 -timeout 120s
@@ -3201,7 +3201,7 @@ steps:
- wire-install
environment:
MEMCACHED_HOSTS: memcached:11211
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: memcached-integration-tests
trigger:
event:
@@ -3338,7 +3338,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -3354,7 +3354,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -3368,13 +3368,13 @@ steps:
- make gen-go
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -3384,7 +3384,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- ./bin/build build-frontend --jobs 8 --edition enterprise ${DRONE_TAG}
@@ -3393,7 +3393,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition enterprise ${DRONE_TAG}
@@ -3402,7 +3402,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition enterprise
@@ -3412,14 +3412,14 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build build-backend --jobs 8 --edition enterprise2 ${DRONE_TAG}
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend-enterprise2
- commands:
- ./bin/build package --jobs 8 --edition enterprise2 --sign ${DRONE_TAG}
@@ -3437,7 +3437,7 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package-enterprise2
- commands:
- ./bin/build upload-cdn --edition enterprise2
@@ -3457,7 +3457,7 @@ steps:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package-enterprise2
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition enterprise2 --shouldSave
@@ -3588,7 +3588,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -3604,7 +3604,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -3618,13 +3618,13 @@ steps:
- make gen-go
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -3634,7 +3634,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- ./bin/build build-frontend --jobs 8 --edition enterprise ${DRONE_TAG}
@@ -3643,7 +3643,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition enterprise ${DRONE_TAG}
@@ -3652,7 +3652,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition enterprise
@@ -3662,14 +3662,14 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build build-backend --jobs 8 --edition enterprise2 ${DRONE_TAG}
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend-enterprise2
- commands:
- ./bin/build package --jobs 8 --edition enterprise2 --sign ${DRONE_TAG}
@@ -3687,7 +3687,7 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package-enterprise2
- commands:
- ./bin/build upload-cdn --edition enterprise2
@@ -3707,7 +3707,7 @@ steps:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package-enterprise2
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition enterprise2 --shouldSave
@@ -4366,7 +4366,7 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- ./bin/build artifacts npm retrieve --tag ${DRONE_TAG}
@@ -4390,7 +4390,7 @@ steps:
NPM_TOKEN:
from_secret: npm_token
failure: ignore
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: release-npm-packages
trigger:
event:
@@ -4626,7 +4626,7 @@ steps:
environment:
GCP_KEY:
from_secret: gcp_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: artifacts-page
trigger:
event:
@@ -4671,18 +4671,18 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -4696,7 +4696,7 @@ steps:
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend
- commands:
- ./bin/build build-frontend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
@@ -4705,7 +4705,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
@@ -4714,7 +4714,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition oss
@@ -4724,7 +4724,7 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --sign
@@ -4742,14 +4742,14 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package
- commands:
- ls dist/*.tar.gz*
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition oss --shouldSave
@@ -4788,7 +4788,7 @@ steps:
environment:
ARCH: linux-amd64
PORT: 3001
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: grafana-server
- commands:
- apt-get install -y netcat
@@ -4865,7 +4865,7 @@ steps:
- build-frontend-packages
environment:
NODE_OPTIONS: --max_old_space_size=4096
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-storybook
when:
paths:
@@ -4946,13 +4946,13 @@ steps:
- commands:
- yarn install --immutable
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn betterer ci
depends_on:
- yarn-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: betterer-frontend
- commands:
- yarn run ci:test-frontend
@@ -4960,7 +4960,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-frontend
trigger:
ref:
@@ -5005,7 +5005,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -5013,25 +5013,25 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- go test -short -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend
- commands:
- go test -run Integration -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend-integration
trigger:
ref:
@@ -5095,7 +5095,7 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -5103,13 +5103,13 @@ steps:
in output.'
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update
@@ -5125,7 +5125,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: postgres-integration-tests
- commands:
- apt-get update
@@ -5141,7 +5141,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: mysql-integration-tests
trigger:
ref:
@@ -5248,7 +5248,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -5263,7 +5263,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -5277,13 +5277,13 @@ steps:
- make gen-go
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -5293,7 +5293,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -5302,14 +5302,14 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- ./bin/build build-backend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER}
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend
- commands:
- ./bin/build build-frontend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER}
@@ -5318,7 +5318,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER}
@@ -5327,7 +5327,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition enterprise
@@ -5337,7 +5337,7 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build package --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER}
@@ -5356,14 +5356,14 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package
- commands:
- ls dist/*.tar.gz*
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition enterprise --shouldSave
@@ -5403,7 +5403,7 @@ steps:
ARCH: linux-amd64
PORT: 3001
RUNDIR: scripts/grafana-server/tmp-grafana-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: grafana-server
- commands:
- apt-get install -y netcat
@@ -5538,7 +5538,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -5553,7 +5553,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- echo $DRONE_RUNNER_NAME
@@ -5569,14 +5569,14 @@ steps:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- yarn betterer ci
depends_on:
- init-enterprise
- yarn-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: betterer-frontend
- commands:
- yarn run ci:test-frontend
@@ -5585,7 +5585,7 @@ steps:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-frontend
trigger:
ref:
@@ -5619,7 +5619,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mkdir -p bin
@@ -5640,7 +5640,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- echo $DRONE_RUNNER_NAME
@@ -5662,7 +5662,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -5671,25 +5671,25 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- go test -short -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend
- commands:
- go test -run Integration -covermode=atomic -timeout=5m ./pkg/...
depends_on:
- wire-install
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: test-backend-integration
trigger:
ref:
@@ -5759,7 +5759,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -5774,7 +5774,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -5784,7 +5784,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -5793,13 +5793,13 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-jsonnet
- commands:
- make gen-go
depends_on:
- verify-gen-cue
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- apt-get update
@@ -5815,7 +5815,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: postgres-integration-tests
- commands:
- apt-get update
@@ -5831,7 +5831,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: mysql-integration-tests
- commands:
- dockerize -wait tcp://redis:6379/0 -timeout 120s
@@ -5842,7 +5842,7 @@ steps:
- wire-install
environment:
REDIS_URL: redis://redis:6379/0
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: redis-integration-tests
- commands:
- dockerize -wait tcp://memcached:11211 -timeout 120s
@@ -5853,7 +5853,7 @@ steps:
- wire-install
environment:
MEMCACHED_HOSTS: memcached:11211
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: memcached-integration-tests
trigger:
ref:
@@ -5980,7 +5980,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: clone-enterprise
- commands:
- mv bin/grabpl /tmp/
@@ -5995,7 +5995,7 @@ steps:
environment:
GITHUB_TOKEN:
from_secret: github_token
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: init-enterprise
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -6009,13 +6009,13 @@ steps:
- make gen-go
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: wire-install
- commands:
- yarn install --immutable
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: yarn-install
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -6025,7 +6025,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- init-enterprise
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: verify-gen-cue
- commands:
- ./bin/build build-frontend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER}
@@ -6034,7 +6034,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend
- commands:
- ./bin/build build-frontend-packages --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER}
@@ -6043,7 +6043,7 @@ steps:
- yarn-install
environment:
NODE_OPTIONS: --max_old_space_size=8192
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-frontend-packages
- commands:
- ./bin/build build-plugins --jobs 8 --edition enterprise
@@ -6053,7 +6053,7 @@ steps:
environment:
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-plugins
- commands:
- ./bin/build build-backend --jobs 8 --edition enterprise2 --build-id ${DRONE_BUILD_NUMBER}
@@ -6061,7 +6061,7 @@ steps:
depends_on:
- wire-install
- compile-build-cmd
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: build-backend-enterprise2
- commands:
- ./bin/build package --jobs 8 --edition enterprise2 --build-id ${DRONE_BUILD_NUMBER}
@@ -6080,7 +6080,7 @@ steps:
from_secret: packages_gpg_public_key
GRAFANA_API_KEY:
from_secret: grafana_api_key
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: package-enterprise2
- commands:
- ./bin/build upload-cdn --edition enterprise2
@@ -6100,7 +6100,7 @@ steps:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
- package-enterprise2
- image: grafana/build-container:1.6.6
+ image: grafana/build-container:1.6.7
name: copy-packages-for-docker
- commands:
- ./bin/build build-docker --edition enterprise2 --shouldSave
@@ -6451,6 +6451,6 @@ kind: secret
name: aws_secret_access_key
---
kind: signature
-hmac: 5b2831b92627887ebe7ee9963796449981a12a96ea775a331fb84536598536ce
+hmac: 86222a86386ae1d2afce23b4a15f09e97aaeb873275ed34ac73d733335d63e4b
...
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 4544d4f52c9..84dec49745d 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -12,26 +12,26 @@
# This should make it easy to add new rules without breaking existing ones.
# Documentation
-/docs/ @grafana/docs-squad
-/contribute/ @grafana/docs-squad
-/docs/sources/developers/plugins/ @grafana/docs-squad @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
-/docs/sources/developers/plugins/backend/ @grafana/docs-squad @grafana/plugins-platform-backend
-/.changelog-archive @grafana/docs-squad
-CHANGELOG.md @grafana/docs-squad
-CODE_OF_CONDUCT.md @grafana/docs-squad
-CONTRIBUTING.md @grafana/docs-squad
+/docs/ @grafana/docs-grafana
+/contribute/ @grafana/docs-grafana
+/docs/sources/developers/plugins/ @grafana/docs-grafana @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
+/docs/sources/developers/plugins/backend/ @grafana/docs-grafana @grafana/plugins-platform-backend
+/.changelog-archive @grafana/docs-grafana
+CHANGELOG.md @grafana/docs-grafana
+CODE_OF_CONDUCT.md @grafana/docs-grafana
+CONTRIBUTING.md @grafana/docs-grafana
GOVERNANCE.md @RichiH
-HALL_OF_FAME.md @grafana/docs-squad
+HALL_OF_FAME.md @grafana/docs-grafana
ISSUE_TRIAGE.md @grafana/grafana-community-support
LICENSE @torkelo
LICENSING.md @torkelo
MAINTAINERS.md @RichiH
NOTICE.md @torkelo
-README.md @grafana/docs-squad
+README.md @grafana/docs-grafana
ROADMAP.md @torkelo
SECURITY.md @grafana/security-team
SUPPORT.md @torkelo
-UPGRADING_DEPENDENCIES.md @grafana/docs-squad
+UPGRADING_DEPENDENCIES.md @grafana/docs-grafana
WORKFLOW.md @torkelo
@@ -112,7 +112,7 @@ WORKFLOW.md @torkelo
/pkg/services/pluginsettings/ @grafana/backend-platform
/pkg/services/preference/ @grafana/backend-platform
/pkg/services/provisioning/ @grafana/backend-platform
-/pkg/services/publicdashboards/ @grafana/backend-platform
+/pkg/services/publicdashboards/ @grafana/dashboards-squad
/pkg/services/query/ @grafana/backend-platform
/pkg/services/queryhistory/ @grafana/backend-platform
/pkg/services/quota/ @grafana/backend-platform
@@ -259,7 +259,6 @@ WORKFLOW.md @torkelo
/pkg/services/querylibrary/ @grafana/multitenancy-squad
/pkg/services/export/ @grafana/multitenancy-squad
/pkg/infra/filestorage/ @grafana/multitenancy-squad
-/pkg/tsdb/testdatasource/ @grafana/multitenancy-squad
/pkg/util/converter/ @grafana/multitenancy-squad
# Alerting
@@ -281,6 +280,7 @@ WORKFLOW.md @torkelo
/pkg/services/datasources/ @grafana/plugins-platform-backend
/pkg/services/pluginsintegration/ @grafana/plugins-platform-backend
/pkg/plugins/pfs/ @grafana/plugins-platform-backend @grafana/grafana-as-code
+/pkg/tsdb/testdatasource/ @grafana/plugins-platform-backend
# Dashboard previews / crawler (behind feature flag)
/pkg/services/thumbs/ @grafana/multitenancy-squad
@@ -306,7 +306,7 @@ WORKFLOW.md @torkelo
/packages/grafana-ui/src/components/TimeSeries/ @grafana/grafana-bi-squad
/packages/grafana-ui/src/components/uPlot/ @grafana/grafana-bi-squad
/packages/grafana-ui/src/utils/storybook/ @grafana/plugins-platform-frontend
-/packages/jaeger-ui-components// @grafana/observability-traces-and-profiling
+/packages/grafana-data/src/**/*logs* @grafana/observability-logs
/plugins-bundled/ @grafana/plugins-platform-frontend
@@ -432,6 +432,10 @@ lerna.json @grafana/frontend-ops
/public/testdata/ @grafana/user-essentials
/public/views/ @grafana/user-essentials
+/public/app/features/explore/Logs.tsx @grafana/observability-logs
+/public/app/features/explore/LogsContainer.tsx @grafana/observability-logs
+
+/public/app/features/explore/TraceView/ @grafana/observability-traces-and-profiling
/public/api-merged.json @grafana/backend-platform
/public/api-spec.json @grafana/backend-platform
@@ -486,7 +490,7 @@ lerna.json @grafana/frontend-ops
/public/app/plugins/datasource/cloudwatch/ @grafana/aws-plugins
/public/app/plugins/datasource/elasticsearch/ @grafana/observability-logs
/public/app/plugins/datasource/grafana/ @grafana/user-essentials
-/public/app/plugins/datasource/testdata/ @grafana/backend-platform
+/public/app/plugins/datasource/testdata/ @grafana/plugins-platform-frontend
/public/app/plugins/datasource/grafana-azure-monitor-datasource/ @grafana/partner-plugins
/public/app/plugins/datasource/graphite/ @grafana/observability-metrics
/public/app/plugins/datasource/influxdb/ @grafana/observability-metrics
@@ -535,9 +539,12 @@ lerna.json @grafana/frontend-ops
/pkg/infra/httpclient/httpclientprovider/sigv4_middleware.go @grafana/grafana-partnerships-team
/pkg/infra/httpclient/httpclientprovider/sigv4_middleware_test.go @grafana/grafana-partnerships-team
+# Kind definitions
+/kinds/dashboard @grafana/dashboards-squad
+/kinds/ @grafana/grafana-as-code
+
# Kind system and code generation
embed.go @grafana/grafana-as-code
-/kinds/ @grafana/grafana-as-code
/pkg/kinds/ @grafana/grafana-as-code
/pkg/cuectx/ @grafana/grafana-as-code
/pkg/registry/ @grafana/grafana-as-code
@@ -564,12 +571,12 @@ embed.go @grafana/grafana-as-code
/.github/workflows/backport.yml @grafana/grafana-release-eng
/.github/workflows/bump-version.yml @grafana/grafana-release-eng
/.github/workflows/close-milestone.yml @grafana/grafana-release-eng
-/.github/workflows/cloud-data-sources-code-coverage.yml @grafana/partner-plugins
+/.github/workflows/cloud-data-sources-code-coverage.yml @grafana/partner-plugins @grafana/aws-plugins
/.github/workflows/codeowners-validator.yml @tolzhabayev
/.github/workflows/codeql-analysis.yml @DanCech
/.github/workflows/commands.yml @torkelo
/.github/workflows/detect-breaking-changes-* @grafana/plugins-platform-frontend
-/.github/workflows/doc-validator.yml @grafana/docs-squad
+/.github/workflows/doc-validator.yml @grafana/docs-grafana
/.github/workflows/enterprise-pr-check.yml @grafana/grafana-release-eng
/.github/workflows/epic-add-to-platform-ux-parent-project.yml @meanmina
/.github/workflows/github-release.yml @torkelo
@@ -583,8 +590,8 @@ embed.go @grafana/grafana-as-code
/.github/workflows/pr-codeql-analysis-python.yml @DanCech
/.github/workflows/pr-commands-closed.yml @tolzhabayev
/.github/workflows/pr-commands.yml @marefr
-/.github/workflows/publish-technical-documentation-next.yml @grafana/docs-squad
-/.github/workflows/publish-technical-documentation-release.yml @grafana/docs-squad
+/.github/workflows/publish-technical-documentation-next.yml @grafana/docs-grafana
+/.github/workflows/publish-technical-documentation-release.yml @grafana/docs-grafana
/.github/workflows/remove-milestone.yml @grafana/user-essentials
/.github/workflows/sbom-report.yml @grafana/security-team
/.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend
@@ -604,6 +611,3 @@ embed.go @grafana/grafana-as-code
/conf/provisioning/datasources/ @grafana/plugins-platform-backend
/conf/provisioning/notifiers/ @bergquist
/conf/provisioning/plugins/ @grafana/plugins-platform-backend
-
-
-
diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 1ed4455944f..a828336afbd 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -5,7 +5,6 @@
"enabledManagers": ["npm"],
"ignoreDeps": [
"@types/systemjs",
- "@types/react-icons", // jaeger-ui-components is being refactored to use @grafana/ui icons instead
"commander", // we are planning to remove this, so no need to update it
"execa", // we should bump this once we move to esm modules
"history", // we should bump this together with react-router-dom
@@ -13,7 +12,6 @@
"monaco-editor", // due to us exposing this via @grafana/ui/CodeEditor's props bumping can break plugins
"node-fetch", // we should bump this once we move to esm modules
"react-hook-form", // due to us exposing these hooks via @grafana/ui form components bumping can break plugins
- "react-icons", // jaeger-ui-components is being refactored to use @grafana/ui icons instead
"react-redux", // react-beautiful-dnd depends on react-redux 7.x, we need to update that one first
"react-router-dom", // we should bump this together with history
"systemjs",
diff --git a/.github/workflows/cloud-data-sources-code-coverage.yml b/.github/workflows/cloud-data-sources-code-coverage.yml
index c95c666d869..bb27cb086de 100644
--- a/.github/workflows/cloud-data-sources-code-coverage.yml
+++ b/.github/workflows/cloud-data-sources-code-coverage.yml
@@ -14,7 +14,7 @@ on:
jobs:
workflow-call:
- uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.14
+ uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.17
with:
frontend-path-regexp: public\/app\/plugins\/datasource\/(grafana-azure-monitor-datasource|cloud-monitoring|cloudwatch)
backend-path-regexp: pkg\/tsdb\/(azuremonitor|cloudmonitoring|cloudwatch)
diff --git a/.github/workflows/doc-validator.yml b/.github/workflows/doc-validator.yml
index 695b07ef338..5444eef33a0 100644
--- a/.github/workflows/doc-validator.yml
+++ b/.github/workflows/doc-validator.yml
@@ -7,10 +7,10 @@ jobs:
doc-validator:
runs-on: "ubuntu-latest"
container:
- image: "grafana/doc-validator:v1.5.0"
+ image: "grafana/doc-validator:v1.9.0"
steps:
- name: "Checkout code"
uses: "actions/checkout@v3"
- name: "Run doc-validator tool"
# Ensure that the CI always passes until all errors are resolved.
- run: "doc-validator ./docs/sources || true"
+ run: "doc-validator --skip-image-validation ./docs/sources /docs/grafana/latest || true"
diff --git a/.github/workflows/ox-code-coverage.yml b/.github/workflows/ox-code-coverage.yml
index 460e0070133..7413350f646 100644
--- a/.github/workflows/ox-code-coverage.yml
+++ b/.github/workflows/ox-code-coverage.yml
@@ -15,7 +15,7 @@ on:
jobs:
workflow-call:
- uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.14
+ uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.17
with:
frontend-path-regexp: public\/app\/features\/(explore|correlations)|public\/app\/plugins\/datasource\/(loki|elasticsearch)
backend-path-regexp: pkg\/services\/(queryhistory)|pkg\/tsdb\/(loki|elasticsearch)
diff --git a/.gitignore b/.gitignore
index a4c02bfa4ca..7c44ef5bdd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -157,6 +157,8 @@ compilation-stats.json
/e2e/benchmarks/**/results/*
/e2e/benchmarks/**/results
/e2e/build_results.zip
+/e2e/extensions
+/e2e/extensions-suite
# grafana server
/scripts/grafana-server/server.log
diff --git a/.nvmrc b/.nvmrc
index bf79505bb85..9dfef472196 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v16.14.0
+v18.12.0
diff --git a/.prettierignore b/.prettierignore
index 62700e93ea4..0f07251b065 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -36,3 +36,5 @@ kinds/report.json
# Generated schema docs
docs/sources/developers/kinds/
+
+scripts/cli/bettererIssueTemplate.md
diff --git a/.yarn/sdks/eslint/package.json b/.yarn/sdks/eslint/package.json
index 379e652a6e4..c0e941b7605 100644
--- a/.yarn/sdks/eslint/package.json
+++ b/.yarn/sdks/eslint/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint",
- "version": "8.30.0-sdk",
+ "version": "8.32.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c30477ca0a3..97e79f113de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,42 @@
+
+
+# 9.3.6 (2023-01-26)
+
+### Bug fixes
+
+- **QueryEditorRow:** Fixes issue loading query editor when data source variable selected. [#61927](https://github.com/grafana/grafana/pull/61927), [@torkelo](https://github.com/torkelo)
+
+
+
+
+# 9.3.4 (2023-01-25)
+
+### Features and enhancements
+
+- **Prometheus:** Add default editor configuration. [#61510](https://github.com/grafana/grafana/pull/61510), [@itsmylife](https://github.com/itsmylife)
+- **TextPanel:** Refactor to functional component (#60885). [#61937](https://github.com/grafana/grafana/pull/61937), [@ryantxu](https://github.com/ryantxu)
+
+### Bug fixes
+
+- **Alerting:** Fix webhook to use correct key for decrypting token. [#61717](https://github.com/grafana/grafana/pull/61717), [@yuri-tceretian](https://github.com/yuri-tceretian)
+- **Alerting:** Set error annotation on EvaluationError regardless of underlying error type. [#61506](https://github.com/grafana/grafana/pull/61506), [@alexweav](https://github.com/alexweav)
+- **Datasources:** Fix Proxy by UID Failing for UIDs with a Hyphen. [#61723](https://github.com/grafana/grafana/pull/61723), [@csmarchbanks](https://github.com/csmarchbanks)
+- **Elasticsearch:** Fix creating of span link with no tags. [#61753](https://github.com/grafana/grafana/pull/61753), [@ivanahuckova](https://github.com/ivanahuckova)
+- **Elasticsearch:** Fix failing requests when using SigV4. [#61923](https://github.com/grafana/grafana/pull/61923), [@svennergr](https://github.com/svennergr)
+- **Elasticsearch:** Fix toggle-settings are not shown correctly. [#61751](https://github.com/grafana/grafana/pull/61751), [@svennergr](https://github.com/svennergr)
+- **Explore:** Be sure time range key bindings are mounted after clear. [#61892](https://github.com/grafana/grafana/pull/61892), [@gelicia](https://github.com/gelicia)
+- **Explore:** Unsync time ranges when a pane is closed. [#61369](https://github.com/grafana/grafana/pull/61369), [@Elfo404](https://github.com/Elfo404)
+- **Logs:** Lines with long words do not break properly. [#61707](https://github.com/grafana/grafana/pull/61707), [@svennergr](https://github.com/svennergr)
+- **Loki:** Fix misaligned derived fields settings. [#61475](https://github.com/grafana/grafana/pull/61475), [@svennergr](https://github.com/svennergr)
+- **Query Builder:** Fix max width of input component to prevent overflows. [#61798](https://github.com/grafana/grafana/pull/61798), [@matyax](https://github.com/matyax)
+- **Search:** Auto focus input elements. [#61443](https://github.com/grafana/grafana/pull/61443), [@ryantxu](https://github.com/ryantxu)
+- **Search:** Fix empty folder message showing when by starred dashboards. [#61610](https://github.com/grafana/grafana/pull/61610), [@eledobleefe](https://github.com/eledobleefe)
+- **Table Panel:** Fix image of image cell overflowing table cell and cells ignoring text alignment setting when a data link is added. [#59392](https://github.com/grafana/grafana/pull/59392), [@oscarkilhed](https://github.com/oscarkilhed)
+
+
-# 9.3.2 (unreleased)
+# 9.3.2 (2023-12-13)
### Features and enhancements
@@ -288,6 +324,23 @@ The following functions and classes related to logs are deprecated in the `grafa
- **Toolkit:** Deprecate `component:create` command. [#56086](https://github.com/grafana/grafana/pull/56086), [@academo](https://github.com/academo)
- **Toolkit:** Remove changelog command. [#56073](https://github.com/grafana/grafana/pull/56073), [@gitstart](https://github.com/gitstart)
+
+
+# 9.2.10 (2023-01-24)
+
+### Features and enhancements
+
+- **TextPanel:** Refactor to functional component (#60885). [#61940](https://github.com/grafana/grafana/pull/61940), [@ryantxu](https://github.com/ryantxu)
+- **[v9.2.x] Chore:** Upgrade Go to 1.19.4. [#60826](https://github.com/grafana/grafana/pull/60826), [@sakjur](https://github.com/sakjur)
+
+### Bug fixes
+
+- **Live:** Fix `Subscription to the channel already exists` live streaming error. [#61420](https://github.com/grafana/grafana/pull/61420), [@grafanabot](https://github.com/grafanabot)
+- **Live:** Fix `Subscription to the channel already exists` live streaming error. [#61419](https://github.com/grafana/grafana/pull/61419), [@grafanabot](https://github.com/grafanabot)
+- **Live:** Fix `Subscription to the channel already exists` live streaming error. [#61406](https://github.com/grafana/grafana/pull/61406), [@ArturWierzbicki](https://github.com/ArturWierzbicki)
+
+
+
# 9.2.7 (2022-11-29)
@@ -1953,6 +2006,16 @@ 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.20 (2023-01-25)
+
+### Features and enhancements
+
+- **Chore:** Upgrade Go to 1.19.4 [v8.5.x]. [#60824](https://github.com/grafana/grafana/pull/60824), [@sakjur](https://github.com/sakjur)
+
+
+
# 8.5.15 (2022-11-08)
diff --git a/Dockerfile b/Dockerfile
index e9bbd124e01..f5d3881f5b2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=alpine:3.15
-ARG JS_IMAGE=node:16-alpine3.15
+ARG JS_IMAGE=node:18-alpine3.15
ARG GO_IMAGE=golang:1.19.4-alpine3.17
ARG GO_SRC=go-builder
@@ -53,6 +53,7 @@ COPY public/api-spec.json public/api-spec.json
COPY pkg pkg
COPY scripts scripts
COPY conf conf
+COPY .github .github
RUN make build-go
diff --git a/LICENSING.md b/LICENSING.md
index 7b64973e012..696cf188f9b 100644
--- a/LICENSING.md
+++ b/LICENSING.md
@@ -15,7 +15,6 @@ packages/grafana-e2e-selectors/
packages/grafana-runtime/
packages/grafana-toolkit/
packages/grafana-ui/
-packages/jaeger-ui-components/
packaging/
kinds/
pkg/kinds/
@@ -23,6 +22,7 @@ pkg/kindsys/
pkg/registry/corekind/
grafana-mixin/
public/app/plugins/datasource/tempo
+public/app/features/explore/TraceView/components
public/img/icons/solid/
public/img/icons/unicons/
```
diff --git a/conf/defaults.ini b/conf/defaults.ini
index 2a5884efc9e..d67385d1db5 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -366,6 +366,9 @@ data_keys_cache_cleanup_interval = 1m
#################################### Snapshots ###########################
[snapshots]
+# set to false to remove snapshot functionality
+enabled = true
+
# snapshot sharing options
external_enabled = true
external_snapshot_url = https://snapshots.raintank.io
@@ -546,6 +549,7 @@ allowed_groups =
role_attribute_path =
role_attribute_strict = false
allow_assign_grafana_admin = false
+skip_org_role_sync = false
#################################### Google Auth #########################
[auth.google]
diff --git a/conf/sample.ini b/conf/sample.ini
index f509496244d..546f828ac45 100644
--- a/conf/sample.ini
+++ b/conf/sample.ini
@@ -372,6 +372,9 @@
#################################### Snapshots ###########################
[snapshots]
+# set to false to remove snapshot functionality
+;enabled = true
+
# snapshot sharing options
;external_enabled = true
;external_snapshot_url = https://snapshots.raintank.io
@@ -550,6 +553,7 @@
;role_attribute_path =
;role_attribute_strict = false
;allow_assign_grafana_admin = false
+;skip_org_role_sync = false
#################################### Google Auth ##########################
[auth.google]
diff --git a/contribute/developer-guide.md b/contribute/developer-guide.md
index 202d9e192b2..fb20192b939 100644
--- a/contribute/developer-guide.md
+++ b/contribute/developer-guide.md
@@ -18,7 +18,7 @@ We recommend using [Homebrew](https://brew.sh/) for installing any missing depen
```
brew install git
brew install go
-brew install node@16
+brew install node@18
npm install -g yarn
```
diff --git a/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json b/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json
new file mode 100644
index 00000000000..be7a3567bfa
--- /dev/null
+++ b/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json
@@ -0,0 +1,761 @@
+{
+ "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": 1263,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 0,
+ "y": 0
+ },
+ "id": 11,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "1,20,90,30,5,0"
+ }
+ ],
+ "title": "default",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 10
+ },
+ {
+ "color": "#EAB839",
+ "value": 20
+ },
+ {
+ "color": "#6ED0E0",
+ "value": 30
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 6,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "5,10,20,30,40"
+ }
+ ],
+ "title": "default absolute thresholds",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "percentage",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 10
+ },
+ {
+ "color": "#EAB839",
+ "value": 20
+ },
+ {
+ "color": "#6ED0E0",
+ "value": 30
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 12,
+ "y": 0
+ },
+ "id": 8,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "5,10,20,30,40"
+ }
+ ],
+ "title": "default percentage thresholds",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "A-series"
+ },
+ "properties": [
+ {
+ "id": "thresholds",
+ "value": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 10
+ },
+ {
+ "color": "#EAB839",
+ "value": 20
+ },
+ {
+ "color": "#6ED0E0",
+ "value": 30
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 18,
+ "y": 0
+ },
+ "id": 3,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "5,10,20,30,40"
+ }
+ ],
+ "title": "override thresholds",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [
+ {
+ "options": {
+ "from": 0,
+ "result": {
+ "color": "green",
+ "index": 0
+ },
+ "to": 9.9999
+ },
+ "type": "range"
+ },
+ {
+ "options": {
+ "from": 10,
+ "result": {
+ "color": "yellow",
+ "index": 1
+ },
+ "to": 14.9999
+ },
+ "type": "range"
+ },
+ {
+ "options": {
+ "from": 15,
+ "result": {
+ "color": "red",
+ "index": 2
+ },
+ "to": 24.9999
+ },
+ "type": "range"
+ },
+ {
+ "options": {
+ "from": 25,
+ "result": {
+ "color": "blue",
+ "index": 3
+ },
+ "to": 100000
+ },
+ "type": "range"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 0,
+ "y": 9
+ },
+ "id": 6,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "5,10,20,30,40"
+ }
+ ],
+ "title": "default value mappings",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "A-series"
+ },
+ "properties": [
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "from": 0,
+ "result": {
+ "color": "green",
+ "index": 0
+ },
+ "to": 9.9999
+ },
+ "type": "range"
+ },
+ {
+ "options": {
+ "from": 10,
+ "result": {
+ "color": "yellow",
+ "index": 1
+ },
+ "to": 14.9999
+ },
+ "type": "range"
+ },
+ {
+ "options": {
+ "from": 15,
+ "result": {
+ "color": "red",
+ "index": 2
+ },
+ "to": 24.9999
+ },
+ "type": "range"
+ },
+ {
+ "options": {
+ "from": 25,
+ "result": {
+ "color": "blue",
+ "index": 3
+ },
+ "to": 100000
+ },
+ "type": "range"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 6,
+ "y": 9
+ },
+ "id": 7,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "5,10,20,30,40"
+ }
+ ],
+ "title": "override value mappings",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 12,
+ "y": 9
+ },
+ "id": 5,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n },\n {\n \"color\": \"#EAB839\",\n \"value\": 10\n },\n {\n \"color\": \"red\",\n \"value\": 15\n },\n {\n \"color\": \"#6ED0E0\",\n \"value\": 25\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000\n ],\n [\n 5,\n 10,\n 20,\n 30\n ]\n ]\n }\n }\n]",
+ "refId": "A",
+ "scenarioId": "raw_frame"
+ }
+ ],
+ "title": "field thresholds from data",
+ "type": "state-timeline"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 70,
+ "lineWidth": 0,
+ "spanNulls": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 18,
+ "y": 9
+ },
+ "id": 9,
+ "options": {
+ "alignValue": "left",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "mergeValues": true,
+ "rowHeight": 0.9,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "5,10,20,30,40"
+ },
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "hide": false,
+ "max": 30,
+ "min": 0.01,
+ "noise": 30,
+ "refId": "B",
+ "scenarioId": "random_walk",
+ "startValue": 1
+ }
+ ],
+ "title": "threshold from random walk",
+ "transformations": [
+ {
+ "id": "configFromData",
+ "options": {
+ "configRefId": "B",
+ "mappings": [
+ {
+ "fieldName": "B-series",
+ "handlerKey": "threshold1"
+ }
+ ]
+ }
+ }
+ ],
+ "type": "state-timeline"
+ }
+ ],
+ "refresh": false,
+ "revision": 1,
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [
+ "gdev",
+ "panel-tests",
+ "state-timeline",
+ "graph-ng"
+ ],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "2023-01-26T11:33:55.000Z",
+ "to": "2023-01-26T14:33:55.000Z"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "StateTimeline - Thresholds & Mappings",
+ "uid": "Kce7z9TVz",
+ "version": 14,
+ "weekStart": ""
+}
diff --git a/devenv/docker/blocks/auth/oauth/readme.md b/devenv/docker/blocks/auth/oauth/readme.md
index 60c87d8d508..a935f2c8682 100644
--- a/devenv/docker/blocks/auth/oauth/readme.md
+++ b/devenv/docker/blocks/auth/oauth/readme.md
@@ -59,7 +59,7 @@ You can obtain a jwt token by using the following command for oauth-admin:
```sh
curl --request POST \
- --url http://localhost:8087/auth/realms/grafana/protocol/openid-connect/token \
+ --url http://localhost:8087/realms/grafana/protocol/openid-connect/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data client_id=grafana-oauth \
--data grant_type=password \
diff --git a/devenv/docker/blocks/loki/data/data.js b/devenv/docker/blocks/loki/data/data.js
index fed60c9e70b..1dcc4ccb053 100644
--- a/devenv/docker/blocks/loki/data/data.js
+++ b/devenv/docker/blocks/loki/data/data.js
@@ -96,7 +96,7 @@ function escapeLogFmtKey(key) {
function escapeLogFmtValue(value) {
if (logFmtProblemRe.test(value)) {
- throw new Error(`invalid logfmt-value: ${key}`)
+ throw new Error(`invalid logfmt-value: ${value}`)
}
// we must handle the space-character because we have values with spaces :-(
diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet
index 60a5faa1694..a8ec282659e 100644
--- a/devenv/jsonnet/dev-dashboards.libsonnet
+++ b/devenv/jsonnet/dev-dashboards.libsonnet
@@ -632,6 +632,13 @@ local dashboard = grafana.dashboard;
id: 0,
}
},
+ dashboard.new('timeline-thresholds-mappings', import '../dev-dashboards/panel-timeline/timeline-thresholds-mappings.json') +
+ resource.addMetadata('folder', 'dev-dashboards') +
+ {
+ spec+: {
+ id: 0,
+ }
+ },
dashboard.new('timeseries', import '../dev-dashboards/panel-timeseries/timeseries.json') +
resource.addMetadata('folder', 'dev-dashboards') +
{
diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md
index c6b07ec078c..c243fc68f12 100644
--- a/docs/sources/administration/provisioning/index.md
+++ b/docs/sources/administration/provisioning/index.md
@@ -215,6 +215,7 @@ Common settings in the [built-in core data sources]({{< relref "../../datasource
| prometheusVersion | string | Prometheus | The version of the Prometheus data source, such as `2.37.0`, `2.24.0` |
| prometheusType | string | Prometheus | The type of the Prometheus data sources. such as `Prometheus`, `Cortex`, `Thanos`, `Mimir` |
| implementation | string | AlertManager | The implementation of the AlertManager data source, such as `prometheus`, `cortex` or `mimir` |
+| handleGrafanaManagedAlerts | boolean | AlertManager | When enabled, Grafana-managed alerts are sent to this Alertmanager |
For examples of specific data sources' JSON data, refer to that [data source's documentation]({{< relref "../../datasources" >}}).
diff --git a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md
index 6bbc25f90f6..4c9d6ee7e46 100644
--- a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md
+++ b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md
@@ -36,19 +36,17 @@ To create a Grafana Mimir or Loki managed recording rule
1. In the Grafana menu, click the **Alerting** (bell) icon to open the Alerting page listing existing alerts.
1. Click **New alert rule**. The new alerting rule page opens where the **Grafana managed alert** option is selected by default.
-1. In Step 1, add the rule name.
- - In **Rule name**, add a descriptive name. This name is displayed in the alert rule list. It is also the `alertname` label for every alert instance that is created from this rule.
+1. In Step 1, add the rule name. The recording name must be a Prometheus metric name and contain no whitespace.
+ - In **Rule name**, add a descriptive name.
1. In Step 2, select **Mimir or Loki recording rule** option.
- - Select your Loki or Prometheus data source, add the query to evaluate, and then select the alert condition.
- - Enter a PromQL or LogQL expression. The rule fires if the evaluation result has at least one series with a value that is greater than 0. An alert is created for each series.
-1. In Step 3, add the namespace, rule group, as well as additional metadata associated with the rule.
+ - Select your Loki or Prometheus data source.
+ - Enter a PromQL or LogQL query.
+1. In Step 3, add the namespace and the group.
- From the **Namespace** drop-down, select an existing rule namespace. Otherwise, click Add new and enter a name to create a new one. Namespaces can contain one or more rule groups and only have an organizational purpose. For more information, see [Grafana Mimir or Loki rule groups and namespaces]({{< relref "edit-mimir-loki-namespace-group/" >}}).
- - From the **Group** drop-down, select an existing group within the selected namespace. Otherwise, click **Add new** and enter a name to create a new one. Newly created rules are appended to the end of the group. Rules within a group are run sequentially at a regular interval, with the same evaluation time.
- - Add a description and summary to customize alert messages. Use the guidelines in [Annotations and labels for alerting]({{< relref "../fundamentals/annotation-label/" >}}).
- - Add Runbook URL, panel, dashboard, and alert IDs.
- - Add custom labels.
-1. Click **Save** to save the rule or **Save and exit** to save the rule and go back to the Alerting page.
-1. Next, create a notification for the rule.
+ - From the **Group** drop-down, select an existing group within the selected namespace. Otherwise, click **Add new** and enter a name to create a new one.
+1. In Step 4, add the custom labels.
+ - Add custom labels selecting existing key-value pairs from the drop down, or add new labels by entering the new key or value.
+1. Click **Save** to save the recording rule or **Save and exit** to save the recording rule and go back to the Alerting page.
1. In the Grafana menu, click the **Alerting** (bell) icon to open the Alerting page listing existing alerts.
1. Click **New alert rule**.
@@ -57,13 +55,12 @@ To create a Grafana Mimir or Loki managed recording rule
1. In Step 2, add the type, and storage location.
- From the **Rule type** drop-down, select **Mimir / Loki managed alert**.
- From the **Select data source** drop-down, select an external Prometheus, an external Loki, or a Grafana Cloud data source.
- - From the **Namespace** drop-down, select an existing rule namespace. Otherwise, click **Add new** and enter a name to create a new one. Namespaces can contain one or more rule groups and only have an organizational purpose.
- - From the **Group** drop-down, select an existing group within the selected namespace. Otherwise, click **Add new** and enter a name to create a new one. Newly created rules are appended to the end of the group. Rules within a group are run sequentially at a regular interval, with the same evaluation time.
- {{< figure src="/static/img/docs/alerting/unified/rule-edit-mimir-alert-type-8-0.png" max-width="550px" caption="Alert details" >}}
-1. In Step 3, add the query to evaluate.
- Enter a PromQL or LogQL expression. The rule fires if the evaluation result has at least one series with a value that is greater than 0. An alert is created for each series.
- {{< figure src="/static/img/docs/alerting/unified/rule-edit-mimir-query-8-0.png" max-width="550px" caption="Alert details" >}}
+1. In Step 3, add evaluation behavior.
+ - Enter a valid **For** duration. The expression has to be true for this long for the alert to be fired.
1. In Step 4, add additional metadata associated with the rule.
+ - From the **Namespace** drop-down, select an existing rule namespace. Otherwise, click Add new and enter a name to create a new one. Namespaces can contain one or more rule groups and only have an organizational purpose. For more information, see [Grafana Mimir or Loki rule groups and namespaces]({{< relref "edit-mimir-loki-namespace-group/" >}}).
+ - From the **Group** drop-down, select an existing group within the selected namespace. Otherwise, click **Add new** and enter a name to create a new one. Newly created rules are appended to the end of the group. Rules within a group are run sequentially at a regular interval, with the same evaluation time.
- Add a description and summary to customize alert messages. Use the guidelines in [Annotations and labels for alerting]({{< relref "../fundamentals/annotation-label/" >}}).
- Add Runbook URL, panel, dashboard, and alert IDs.
1. In Step 5, add custom labels.
diff --git a/docs/sources/alerting/fundamentals/alert-rules/message-templating.md b/docs/sources/alerting/fundamentals/alert-rules/message-templating.md
index 0d43a2d948c..cd563f1e4c0 100644
--- a/docs/sources/alerting/fundamentals/alert-rules/message-templating.md
+++ b/docs/sources/alerting/fundamentals/alert-rules/message-templating.md
@@ -18,7 +18,7 @@ weight: 415
Notifications sent via contact points are built using notification templates. Grafana's default templates are based on the [Go templating system](https://golang.org/pkg/text/template) where some fields are evaluated as text, while others are evaluated as HTML (which can affect escaping). The default template, defined in [default_template.go](https://github.com/grafana/alerting/blob/main/alerting/notifier/channels/default_template.go), is a useful reference for custom templates.
-Since most of the contact point fields can be templated, you can create reusable custom templates and use them in multiple contact points. The default template is defined in [default_template.go](https://github.com/grafana/alerting/blob/main/alerting/notifier/channels/default_template.go) which can serve as a useful reference or starting point for custom templates.
+Since most of the contact point fields can be templated, you can create reusable custom templates and use them in multiple contact points.
### Using templates
diff --git a/docs/sources/alerting/manage-notifications/alertmanager.md b/docs/sources/alerting/manage-notifications/alertmanager.md
index 1bc4e62fca3..336ed301c3e 100644
--- a/docs/sources/alerting/manage-notifications/alertmanager.md
+++ b/docs/sources/alerting/manage-notifications/alertmanager.md
@@ -37,6 +37,8 @@ Here are two examples of when you may want to configure your own external alertm
Alertmanagers are visible from the drop-down menu on the Alerting Contact Points, Notification Policies, and Silences pages.
+If you are provisioning your data source, set the flag `handleGrafanaManagedAlerts` in the `jsonData` field to `true` to send Grafana-managed alerts to this Alertmanager.
+
**Useful links**
[Prometheus Alertmanager documentation](https://prometheus.io/docs/alerting/latest/alertmanager/)
diff --git a/docs/sources/alerting/performance-limitations/index.md b/docs/sources/alerting/performance-limitations/index.md
index e108dd1ce0c..f8e17c8715d 100644
--- a/docs/sources/alerting/performance-limitations/index.md
+++ b/docs/sources/alerting/performance-limitations/index.md
@@ -23,7 +23,7 @@ The following section provides a list of alerting performance considerations.
- Cardinality of the rule's result set. For example, suppose you are monitoring API response errors for every API path, on every VM in your fleet. This set has a cardinality of _n_ number of paths multiplied by _v_ number of VMs. You can reduce the cardinality of a result set - perhaps by monitoring errors-per-VM instead of for each path per VM.
- Complexity of the alerting query consideration. Queries that data sources can process and respond to quickly consume fewer resources. Although this consideration is less important than the other considerations listed above, if you have reduced those as much as possible, looking at individual query performance could make a difference.
-Each evaluation of an alert rule generates a set of alert instances; one for each member of the result set. The state of all the instances is written to the `alert_instance` table in Grafana's SQL database.
+Each evaluation of an alert rule generates a set of alert instances; one for each member of the result set. The state of all the instances is written to the `alert_instance` table in Grafana's SQL database. This number of write-heavy operations can cause issues when using SQLite.
Grafana Alerting exposes a metric, `grafana_alerting_rule_evaluations_total` that counts the number of alert rule evaluations. To get a feel for the influence of rule evaluations on your Grafana instance, you can observe the rate of evaluations and compare it with resource consumption. In a Prometheus-compatible database, you can use the query `rate(grafana_alerting_rule_evaluations_total[5m])` to compute the rate over 5 minute windows of time. It's important to remember that this isn't the full picture of rule evaluation. For example, the load will be unevenly distributed if you have some rules that evaluate every 10 seconds, and others every 30 minutes.
diff --git a/docs/sources/alerting/set-up/configure-alertmanager/index.md b/docs/sources/alerting/set-up/configure-alertmanager/index.md
index 166fd3013d9..f0eceb9ae56 100644
--- a/docs/sources/alerting/set-up/configure-alertmanager/index.md
+++ b/docs/sources/alerting/set-up/configure-alertmanager/index.md
@@ -29,6 +29,8 @@ To add an external Alertmanager, complete the following steps.
2. Search for Alertmanager.
3. Choose your Implementation and fill out the fields on the page, as required.
+If you are provisioning your data source, set the flag `handleGrafanaManagedAlerts` in the `jsonData` field to `true` to send Grafana-managed alerts to this Alertmanager.
+
**Note:**
Prometheus, Grafana Mimir, and Cortex implementations of Alertmanager are supported. For Prometheus, contact points and notification policies are read-only in the Grafana Alerting UI.
diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md
index 1aec13bc0e4..82b1c05ae20 100644
--- a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md
+++ b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md
@@ -22,20 +22,20 @@ Details on how to set up the files and which fields are required for each object
**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).
+Provisioning takes place during the initial set up of your Grafana system, but you can re-run it at any time using the [Grafana Admin 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/alerting_provisioning/#route-get-alert-rule) to extract the alert rule.
+1. Create alert rules in Grafana.
+1. Use the [Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/#route-get-alert-rule-export) export endpoints to download a provisioning file for your alert rules.
1. Copy the contents into a YAML or JSON configuration file in the default provisioning directory or in your configured directory.
Example configuration files can be found below.
1. Ensure that your files are in the right directory on the node running the Grafana server, so that they deploy alongside your Grafana instance(s).
-1. Delete the alert rule in Grafana.
+1. Delete the alert rules in Grafana that will be provisioned.
**Note:**
diff --git a/docs/sources/dashboards/create-reports/index.md b/docs/sources/dashboards/create-reports/index.md
index 2ddd5e6673e..7def573355e 100644
--- a/docs/sources/dashboards/create-reports/index.md
+++ b/docs/sources/dashboards/create-reports/index.md
@@ -181,7 +181,7 @@ You can send a report email with an image of the dashboard embedded in the email
You can generate and save PDF files of any dashboard.
-> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}).
+> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 6.7 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
1. In the upper-right corner of the dashboard that you want to export as PDF, click the **Share dashboard** icon.
1. On the PDF tab, select a layout option for the exported dashboard: **Portrait** or **Landscape**.
diff --git a/docs/sources/datasources/azure-monitor/_index.md b/docs/sources/datasources/azure-monitor/_index.md
index 227b4414e27..175fba96596 100644
--- a/docs/sources/datasources/azure-monitor/_index.md
+++ b/docs/sources/datasources/azure-monitor/_index.md
@@ -155,4 +155,4 @@ Until Grafana v8.0, you could query the same Azure Application Insights data usi
These queries were deprecated in Grafana v7.5. In Grafana v8.0, Application Insights and Insights Analytics were made read-only in favor of querying this data through Metrics and Logs. These query methods were completely removed in Grafana v9.0.
-If you're upgrading from a Grafana version prior to v9.0 and relied on Application Insights and Analytics queries, refer to the [Grafana v9.0 documentation](/v9.0/datasources/azuremonitor/deprecated-application-insights/) for help migrating these queries to Metrics and Logs queries.
+If you're upgrading from a Grafana version prior to v9.0 and relied on Application Insights and Analytics queries, refer to the [Grafana v9.0 documentation](/docs/grafana/v9.0/datasources/azuremonitor/deprecated-application-insights/) for help migrating these queries to Metrics and Logs queries.
diff --git a/docs/sources/datasources/jaeger/_index.md b/docs/sources/datasources/jaeger/_index.md
index 4441dc5fc61..0d2bcb14636 100644
--- a/docs/sources/datasources/jaeger/_index.md
+++ b/docs/sources/datasources/jaeger/_index.md
@@ -154,7 +154,7 @@ datasources:
tags: [{ key: 'service.name', value: 'service' }, { key: 'job' }]
queries:
- name: 'Sample query'
- query: 'sum(rate(tempo_spanmetrics_latency_bucket{$__tags}[5m]))'
+ query: 'sum(rate(traces_spanmetrics_latency_bucket{$__tags}[5m]))'
secureJsonData:
basicAuthPassword: my_password
```
diff --git a/docs/sources/datasources/tempo/_index.md b/docs/sources/datasources/tempo/_index.md
index 124e273f6c3..419f4f78e1c 100644
--- a/docs/sources/datasources/tempo/_index.md
+++ b/docs/sources/datasources/tempo/_index.md
@@ -175,7 +175,7 @@ datasources:
tags: [{ key: 'service.name', value: 'service' }, { key: 'job' }]
queries:
- name: 'Sample query'
- query: 'sum(rate(tempo_spanmetrics_latency_bucket{$__tags}[5m]))'
+ query: 'sum(rate(traces_spanmetrics_latency_bucket{$__tags}[5m]))'
serviceMap:
datasourceUid: 'prometheus'
search:
diff --git a/docs/sources/developers/http_api/alerting_provisioning.md b/docs/sources/developers/http_api/alerting_provisioning.md
index 1198c17eead..98ff5686667 100644
--- a/docs/sources/developers/http_api/alerting_provisioning.md
+++ b/docs/sources/developers/http_api/alerting_provisioning.md
@@ -18,7 +18,7 @@ title: 'Alerting Provisioning HTTP API '
### Version
-1.0.0
+1.1.0
## Content negotiation
@@ -29,53 +29,61 @@ title: 'Alerting Provisioning HTTP API '
### Produces
- application/json
+- text/yaml
+- application/yaml
## All endpoints
### Alert rules
-| Method | URI | Name | Summary |
-| ------ | ----------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------ |
-| GET | /api/v1/provisioning/alert-rules/{UID} | [route get alert rule](#route-get-alert-rule) | Get a specific alert rule by UID. |
-| POST | /api/v1/provisioning/alert-rules | [route post alert rule](#route-post-alert-rule) | Create a new alert rule. |
-| PUT | /api/v1/provisioning/alert-rules/{UID} | [route put alert rule](#route-put-alert-rule) | Update an existing alert rule. |
-| PUT | /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} | [route put alert rule group](#route-put-alert-rule-group) | Update the interval of a rule group. |
-| DELETE | /api/v1/provisioning/alert-rules/{UID} | [route delete alert rule](#route-delete-alert-rule) | Delete a specific alert rule by UID. |
+| Method | URI | Name | Summary |
+| ------ | ------------------------------------------------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------- |
+| DELETE | /api/v1/provisioning/alert-rules/{UID} | [route delete alert rule](#route-delete-alert-rule) | Delete a specific alert rule by UID. |
+| GET | /api/v1/provisioning/alert-rules/{UID} | [route get alert rule](#route-get-alert-rule) | Get a specific alert rule by UID. |
+| GET | /api/v1/provisioning/alert-rules/{UID}/export | [route get alert rule export](#route-get-alert-rule-export) | Export an alert rule in provisioning file format. |
+| GET | /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} | [route get alert rule group](#route-get-alert-rule-group) | Get a rule group. |
+| GET | /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}/export | [route get alert rule group export](#route-get-alert-rule-group-export) | Export an alert rule group in provisioning file format. |
+| GET | /api/v1/provisioning/alert-rules | [route get alert rules](#route-get-alert-rules) | Get all the alert rules. |
+| GET | /api/v1/provisioning/alert-rules/export | [route get alert rules export](#route-get-alert-rules-export) | Export all alert rules in provisioning file format. |
+| POST | /api/v1/provisioning/alert-rules | [route post alert rule](#route-post-alert-rule) | Create a new alert rule. |
+| PUT | /api/v1/provisioning/alert-rules/{UID} | [route put alert rule](#route-put-alert-rule) | Update an existing alert rule. |
+| PUT | /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} | [route put alert rule group](#route-put-alert-rule-group) | Update the interval of a rule group. |
### Contact points
| Method | URI | Name | Summary |
| ------ | ----------------------------------------- | --------------------------------------------------------- | --------------------------------- |
+| DELETE | /api/v1/provisioning/contact-points/{UID} | [route delete contactpoints](#route-delete-contactpoints) | Delete a contact point. |
| GET | /api/v1/provisioning/contact-points | [route get contactpoints](#route-get-contactpoints) | Get all the contact points. |
| POST | /api/v1/provisioning/contact-points | [route post contactpoints](#route-post-contactpoints) | Create a contact point. |
| PUT | /api/v1/provisioning/contact-points/{UID} | [route put contactpoint](#route-put-contactpoint) | Update an existing contact point. |
-| DELETE | /api/v1/provisioning/contact-points/{UID} | [route delete contactpoints](#route-delete-contactpoints) | Delete a contact point. |
### Notification policies
-| Method | URI | Name | Summary |
-| ------ | ----------------------------- | ----------------------------------------------- | ---------------------------------- |
-| GET | /api/v1/provisioning/policies | [route get policy tree](#route-get-policy-tree) | Get the notification policy tree. |
-| PUT | /api/v1/provisioning/policies | [route put policy tree](#route-put-policy-tree) | Sets the notification policy tree. |
+| Method | URI | Name | Summary |
+| ------ | ----------------------------- | --------------------------------------------------- | ------------------------------------ |
+| DELETE | /api/v1/provisioning/policies | [route reset policy tree](#route-reset-policy-tree) | Clears the notification policy tree. |
+| GET | /api/v1/provisioning/policies | [route get policy tree](#route-get-policy-tree) | Get the notification policy tree. |
+| PUT | /api/v1/provisioning/policies | [route put policy tree](#route-put-policy-tree) | Sets the notification policy tree. |
### Mute timings
| Method | URI | Name | Summary |
| ------ | ---------------------------------------- | ----------------------------------------------------- | -------------------------------- |
-| GET | /api/v1/provisioning/mute-timings | [route get mute timings](#route-get-mute-timings) | Get all the mute timings. |
+| DELETE | /api/v1/provisioning/mute-timings/{name} | [route delete mute timing](#route-delete-mute-timing) | Delete a mute timing. |
| GET | /api/v1/provisioning/mute-timings/{name} | [route get mute timing](#route-get-mute-timing) | Get a mute timing. |
+| GET | /api/v1/provisioning/mute-timings | [route get mute timings](#route-get-mute-timings) | Get all the mute timings. |
| POST | /api/v1/provisioning/mute-timings | [route post mute timing](#route-post-mute-timing) | Create a new mute timing. |
| PUT | /api/v1/provisioning/mute-timings/{name} | [route put mute timing](#route-put-mute-timing) | Replace an existing mute timing. |
-| DELETE | /api/v1/provisioning/mute-timings/{name} | [route delete mute timing](#route-delete-mute-timing) | Delete a mute timing. |
### Templates
-| Method | URI | Name | Summary |
-| ------ | ------------------------------------- | ----------------------------------------------- | ------------------------------- |
-| GET | /api/v1/provisioning/templates | [route get templates](#route-get-templates) | Get all notification templates. |
-| GET | /api/v1/provisioning/templates/{name} | [route get template](#route-get-template) | Get a notification template. |
-| PUT | /api/v1/provisioning/templates/{name} | [route put template](#route-put-template) | Creates or updates a template. |
-| DELETE | /api/v1/provisioning/templates/{name} | [route delete template](#route-delete-template) | Delete a template. |
+| Method | URI | Name | Summary |
+| ------ | ------------------------------------- | ----------------------------------------------- | ------------------------------------------ |
+| DELETE | /api/v1/provisioning/templates/{name} | [route delete template](#route-delete-template) | Delete a template. |
+| GET | /api/v1/provisioning/templates/{name} | [route get template](#route-get-template) | Get a notification template. |
+| GET | /api/v1/provisioning/templates | [route get templates](#route-get-templates) | Get all notification templates. |
+| PUT | /api/v1/provisioning/templates/{name} | [route put template](#route-put-template) | Updates an existing notification template. |
## Paths
@@ -87,16 +95,15 @@ DELETE /api/v1/provisioning/alert-rules/{UID}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ----------- |
-| UID | `path` | string | `string` | | ✓ | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ------ | -------- | --------- | :------: | ------- | -------------- |
+| UID | `path` | string | `string` | | ✓ | | Alert rule UID |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ----------------------------------- | ----------- | ---------------------------------------- | :---------: | --------------------------------------------- |
-| [204](#route-delete-alert-rule-204) | No Content | The alert rule was deleted successfully. | | [schema](#route-delete-alert-rule-204-schema) |
-| [400](#route-delete-alert-rule-400) | Bad Request | ValidationError | | [schema](#route-delete-alert-rule-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ----------------------------------- | ---------- | ---------------------------------------- | :---------: | --------------------------------------------- |
+| [204](#route-delete-alert-rule-204) | No Content | The alert rule was deleted successfully. | | [schema](#route-delete-alert-rule-204-schema) |
#### Responses
@@ -106,14 +113,6 @@ Status: No Content
###### Schema
-##### 400 - ValidationError
-
-Status: Bad Request
-
-###### Schema
-
-[ValidationError](#validation-error)
-
### Delete a contact point. (_RouteDeleteContactpoints_)
```
@@ -126,34 +125,23 @@ DELETE /api/v1/provisioning/contact-points/{UID}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ------------------------------------------------- |
-| UID | `path` | string | `string` | | ✓ | | UID should be the contact point unique identifier |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ------------------------------------------ |
+| UID | `path` | string | `string` | | ✓ | | UID is the contact point unique identifier |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| -------------------------------------- | ----------- | --------------- | :---------: | ------------------------------------------------ |
-| [202](#route-delete-contactpoints-202) | Accepted | Ack | | [schema](#route-delete-contactpoints-202-schema) |
-| [400](#route-delete-contactpoints-400) | Bad Request | ValidationError | | [schema](#route-delete-contactpoints-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| -------------------------------------- | ---------- | ------------------------------------------- | :---------: | ------------------------------------------------ |
+| [204](#route-delete-contactpoints-204) | No Content | The contact point was deleted successfully. | | [schema](#route-delete-contactpoints-204-schema) |
#### Responses
-##### 202 - Ack
+##### 204 - The contact point was deleted successfully.
-Status: Accepted
+Status: No Content
-###### Schema
-
-[Ack](#ack)
-
-##### 400 - ValidationError
-
-Status: Bad Request
-
-###### Schema
-
-[ValidationError](#validation-error)
+###### Schema
### Delete a mute timing. (_RouteDeleteMuteTiming_)
@@ -163,26 +151,24 @@ DELETE /api/v1/provisioning/mute-timings/{name}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ------------- |
-| name | `path` | string | `string` | | ✓ | | Template Name |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ---------------- |
+| name | `path` | string | `string` | | ✓ | | Mute timing name |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ------------------------------------ | ---------- | ----------- | :---------: | ---------------------------------------------- |
-| [204](#route-delete-mute-timing-204) | No Content | Ack | | [schema](#route-delete-mute-timing-204-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ------------------------------------ | ---------- | ----------------------------------------- | :---------: | ---------------------------------------------- |
+| [204](#route-delete-mute-timing-204) | No Content | The mute timing was deleted successfully. | | [schema](#route-delete-mute-timing-204-schema) |
#### Responses
-##### 204 - Ack
+##### 204 - The mute timing was deleted successfully.
Status: No Content
###### Schema
-[Ack](#ack)
-
### Delete a template. (_RouteDeleteTemplate_)
```
@@ -197,20 +183,18 @@ DELETE /api/v1/provisioning/templates/{name}
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| --------------------------------- | ---------- | ----------- | :---------: | ------------------------------------------- |
-| [204](#route-delete-template-204) | No Content | Ack | | [schema](#route-delete-template-204-schema) |
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------- | ---------- | -------------------------------------- | :---------: | ------------------------------------------- |
+| [204](#route-delete-template-204) | No Content | The template was deleted successfully. | | [schema](#route-delete-template-204-schema) |
#### Responses
-##### 204 - Ack
+##### 204 - The template was deleted successfully.
Status: No Content
###### Schema
-[Ack](#ack)
-
### Get a specific alert rule by UID. (_RouteGetAlertRule_)
```
@@ -219,34 +203,210 @@ GET /api/v1/provisioning/alert-rules/{UID}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ----------- |
-| UID | `path` | string | `string` | | ✓ | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ------ | -------- | --------- | :------: | ------- | -------------- |
+| UID | `path` | string | `string` | | ✓ | | Alert rule UID |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| -------------------------------- | ----------- | --------------- | :---------: | ------------------------------------------ |
-| [200](#route-get-alert-rule-200) | OK | AlertRule | | [schema](#route-get-alert-rule-200-schema) |
-| [400](#route-get-alert-rule-400) | Bad Request | ValidationError | | [schema](#route-get-alert-rule-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| -------------------------------- | --------- | -------------------- | :---------: | ------------------------------------------ |
+| [200](#route-get-alert-rule-200) | OK | ProvisionedAlertRule | | [schema](#route-get-alert-rule-200-schema) |
+| [404](#route-get-alert-rule-404) | Not Found | Not found. | | [schema](#route-get-alert-rule-404-schema) |
#### Responses
-##### 200 - AlertRule
+##### 200 - ProvisionedAlertRule
Status: OK
###### Schema
-[AlertRule](#alert-rule)
+[ProvisionedAlertRule](#provisioned-alert-rule)
-##### 400 - ValidationError
+##### 404 - Not found.
-Status: Bad Request
+Status: Not Found
-###### Schema
+###### Schema
-[ValidationError](#validation-error)
+### Export an alert rule in provisioning file format. (_RouteGetAlertRuleExport_)
+
+```
+GET /api/v1/provisioning/alert-rules/{UID}/export
+```
+
+#### Produces
+
+- application/json
+- application/yaml
+- text/yaml
+
+#### Parameters
+
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| -------- | ------- | ------- | -------- | --------- | :------: | ------- | -------------------------------------------------- |
+| UID | `path` | string | `string` | | ✓ | | Alert rule UID |
+| download | `query` | boolean | `bool` | | | | Whether to initiate a download of the file or not. |
+
+#### All responses
+
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------------- | --------- | ------------------ | :---------: | ------------------------------------------------- |
+| [200](#route-get-alert-rule-export-200) | OK | AlertingFileExport | | [schema](#route-get-alert-rule-export-200-schema) |
+| [404](#route-get-alert-rule-export-404) | Not Found | Not found. | | [schema](#route-get-alert-rule-export-404-schema) |
+
+#### Responses
+
+##### 200 - AlertingFileExport
+
+Status: OK
+
+###### Schema
+
+[AlertingFileExport](#alerting-file-export)
+
+##### 404 - Not found.
+
+Status: Not Found
+
+###### Schema
+
+### Get a rule group. (_RouteGetAlertRuleGroup_)
+
+```
+GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}
+```
+
+#### Parameters
+
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| --------- | ------ | ------ | -------- | --------- | :------: | ------- | ----------- |
+| FolderUID | `path` | string | `string` | | ✓ | | |
+| Group | `path` | string | `string` | | ✓ | | |
+
+#### All responses
+
+| Code | Status | Description | Has headers | Schema |
+| -------------------------------------- | --------- | -------------- | :---------: | ------------------------------------------------ |
+| [200](#route-get-alert-rule-group-200) | OK | AlertRuleGroup | | [schema](#route-get-alert-rule-group-200-schema) |
+| [404](#route-get-alert-rule-group-404) | Not Found | Not found. | | [schema](#route-get-alert-rule-group-404-schema) |
+
+#### Responses
+
+##### 200 - AlertRuleGroup
+
+Status: OK
+
+###### Schema
+
+[AlertRuleGroup](#alert-rule-group)
+
+##### 404 - Not found.
+
+Status: Not Found
+
+###### Schema
+
+### Export an alert rule group in provisioning file format. (_RouteGetAlertRuleGroupExport_)
+
+```
+GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}/export
+```
+
+#### Produces
+
+- application/json
+- application/yaml
+- text/yaml
+
+#### Parameters
+
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| --------- | ------- | ------- | -------- | --------- | :------: | ------- | -------------------------------------------------- |
+| FolderUID | `path` | string | `string` | | ✓ | | |
+| Group | `path` | string | `string` | | ✓ | | |
+| download | `query` | boolean | `bool` | | | | Whether to initiate a download of the file or not. |
+
+#### All responses
+
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------------------- | --------- | ------------------ | :---------: | ------------------------------------------------------- |
+| [200](#route-get-alert-rule-group-export-200) | OK | AlertingFileExport | | [schema](#route-get-alert-rule-group-export-200-schema) |
+| [404](#route-get-alert-rule-group-export-404) | Not Found | Not found. | | [schema](#route-get-alert-rule-group-export-404-schema) |
+
+#### Responses
+
+##### 200 - AlertingFileExport
+
+Status: OK
+
+###### Schema
+
+[AlertingFileExport](#alerting-file-export)
+
+##### 404 - Not found.
+
+Status: Not Found
+
+###### Schema
+
+### Get all the alert rules. (_RouteGetAlertRules_)
+
+```
+GET /api/v1/provisioning/alert-rules
+```
+
+#### All responses
+
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------- | ------ | --------------------- | :---------: | ------------------------------------------- |
+| [200](#route-get-alert-rules-200) | OK | ProvisionedAlertRules | | [schema](#route-get-alert-rules-200-schema) |
+
+#### Responses
+
+##### 200 - ProvisionedAlertRules
+
+Status: OK
+
+###### Schema
+
+[ProvisionedAlertRules](#provisioned-alert-rules)
+
+### Export all alert rules in provisioning file format. (_RouteGetAlertRulesExport_)
+
+```
+GET /api/v1/provisioning/alert-rules/export
+```
+
+#### Parameters
+
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| -------- | ------- | ------- | ------- | --------- | :------: | ------- | -------------------------------------------------- |
+| download | `query` | boolean | `bool` | | | | Whether to initiate a download of the file or not. |
+
+#### All responses
+
+| Code | Status | Description | Has headers | Schema |
+| ---------------------------------------- | --------- | ------------------ | :---------: | -------------------------------------------------- |
+| [200](#route-get-alert-rules-export-200) | OK | AlertingFileExport | | [schema](#route-get-alert-rules-export-200-schema) |
+| [404](#route-get-alert-rules-export-404) | Not Found | Not found. | | [schema](#route-get-alert-rules-export-404-schema) |
+
+#### Responses
+
+##### 200 - AlertingFileExport
+
+Status: OK
+
+###### Schema
+
+[AlertingFileExport](#alerting-file-export)
+
+##### 404 - Not found.
+
+Status: Not Found
+
+###### Schema
### Get all the contact points. (_RouteGetContactpoints_)
@@ -254,30 +414,27 @@ Status: Bad Request
GET /api/v1/provisioning/contact-points
```
+#### Parameters
+
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------- | ------ | -------- | --------- | :------: | ------- | -------------- |
+| name | `query` | string | `string` | | | | Filter by name |
+
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ----------------------------------- | ----------- | --------------- | :---------: | --------------------------------------------- |
-| [200](#route-get-contactpoints-200) | OK | Route | | [schema](#route-get-contactpoints-200-schema) |
-| [400](#route-get-contactpoints-400) | Bad Request | ValidationError | | [schema](#route-get-contactpoints-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ----------------------------------- | ------ | ------------- | :---------: | --------------------------------------------- |
+| [200](#route-get-contactpoints-200) | OK | ContactPoints | | [schema](#route-get-contactpoints-200-schema) |
#### Responses
-##### 200 - Route
+##### 200 - ContactPoints
Status: OK
###### Schema
-[Route](#route)
-
-##### 400 - ValidationError
-
-Status: Bad Request
-
-###### Schema
-
-[ValidationError](#validation-error)
+[ContactPoints](#contact-points)
### Get a mute timing. (_RouteGetMuteTiming_)
@@ -287,16 +444,16 @@ GET /api/v1/provisioning/mute-timings/{name}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ------------- |
-| name | `path` | string | `string` | | ✓ | | Template Name |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ---------------- |
+| name | `path` | string | `string` | | ✓ | | Mute timing name |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| --------------------------------- | ----------- | ---------------- | :---------: | ------------------------------------------- |
-| [200](#route-get-mute-timing-200) | OK | MuteTimeInterval | | [schema](#route-get-mute-timing-200-schema) |
-| [400](#route-get-mute-timing-400) | Bad Request | ValidationError | | [schema](#route-get-mute-timing-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------- | --------- | ---------------- | :---------: | ------------------------------------------- |
+| [200](#route-get-mute-timing-200) | OK | MuteTimeInterval | | [schema](#route-get-mute-timing-200-schema) |
+| [404](#route-get-mute-timing-404) | Not Found | Not found. | | [schema](#route-get-mute-timing-404-schema) |
#### Responses
@@ -308,13 +465,11 @@ Status: OK
[MuteTimeInterval](#mute-time-interval)
-##### 400 - ValidationError
+##### 404 - Not found.
-Status: Bad Request
+Status: Not Found
-###### Schema
-
-[ValidationError](#validation-error)
+###### Schema
### Get all the mute timings. (_RouteGetMuteTimings_)
@@ -324,10 +479,9 @@ GET /api/v1/provisioning/mute-timings
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ---------------------------------- | ----------- | --------------- | :---------: | -------------------------------------------- |
-| [200](#route-get-mute-timings-200) | OK | MuteTimings | | [schema](#route-get-mute-timings-200-schema) |
-| [400](#route-get-mute-timings-400) | Bad Request | ValidationError | | [schema](#route-get-mute-timings-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ---------------------------------- | ------ | ----------- | :---------: | -------------------------------------------- |
+| [200](#route-get-mute-timings-200) | OK | MuteTimings | | [schema](#route-get-mute-timings-200-schema) |
#### Responses
@@ -339,14 +493,6 @@ Status: OK
[MuteTimings](#mute-timings)
-##### 400 - ValidationError
-
-Status: Bad Request
-
-###### Schema
-
-[ValidationError](#validation-error)
-
### Get the notification policy tree. (_RouteGetPolicyTree_)
```
@@ -355,10 +501,9 @@ GET /api/v1/provisioning/policies
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| --------------------------------- | ----------- | --------------- | :---------: | ------------------------------------------- |
-| [200](#route-get-policy-tree-200) | OK | Route | | [schema](#route-get-policy-tree-200-schema) |
-| [400](#route-get-policy-tree-400) | Bad Request | ValidationError | | [schema](#route-get-policy-tree-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------- | ------ | ----------- | :---------: | ------------------------------------------- |
+| [200](#route-get-policy-tree-200) | OK | Route | | [schema](#route-get-policy-tree-200-schema) |
#### Responses
@@ -370,14 +515,6 @@ Status: OK
[Route](#route)
-##### 400 - ValidationError
-
-Status: Bad Request
-
-###### Schema
-
-[ValidationError](#validation-error)
-
### Get a notification template. (_RouteGetTemplate_)
```
@@ -395,7 +532,7 @@ GET /api/v1/provisioning/templates/{name}
| Code | Status | Description | Has headers | Schema |
| ------------------------------ | --------- | -------------------- | :---------: | ---------------------------------------- |
| [200](#route-get-template-200) | OK | NotificationTemplate | | [schema](#route-get-template-200-schema) |
-| [404](#route-get-template-404) | Not Found | NotFound | | [schema](#route-get-template-404-schema) |
+| [404](#route-get-template-404) | Not Found | Not found. | | [schema](#route-get-template-404-schema) |
#### Responses
@@ -405,16 +542,14 @@ Status: OK
###### Schema
-[NotificationTemplate](#message-template)
+[NotificationTemplate](#notification-template)
-##### 404 - NotFound
+##### 404 - Not found.
Status: Not Found
###### Schema
-[NotFound](#not-found)
-
### Get all notification templates. (_RouteGetTemplates_)
```
@@ -423,28 +558,26 @@ GET /api/v1/provisioning/templates
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ------------------------------- | ----------- | -------------------- | :---------: | ----------------------------------------- |
-| [200](#route-get-templates-200) | OK | NotificationTemplate | | [schema](#route-get-templates-200-schema) |
-| [400](#route-get-templates-400) | Bad Request | ValidationError | | [schema](#route-get-templates-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ------------------------------- | --------- | --------------------- | :---------: | ----------------------------------------- |
+| [200](#route-get-templates-200) | OK | NotificationTemplates | | [schema](#route-get-templates-200-schema) |
+| [404](#route-get-templates-404) | Not Found | Not found. | | [schema](#route-get-templates-404-schema) |
#### Responses
-##### 200 - NotificationTemplate
+##### 200 - NotificationTemplates
Status: OK
###### Schema
-[NotificationTemplate](#message-template)
+[NotificationTemplates](#notification-templates)
-##### 400 - ValidationError
+##### 404 - Not found.
-Status: Bad Request
+Status: Not Found
-###### Schema
-
-[ValidationError](#validation-error)
+###### Schema
### Create a new alert rule. (_RoutePostAlertRule_)
@@ -452,28 +585,33 @@ Status: Bad Request
POST /api/v1/provisioning/alert-rules
```
+#### Consumes
+
+- application/json
+
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------------------------ | ------------------ | --------- | :------: | ------- | ----------- |
-| Body | `body` | [AlertRule](#alert-rule) | `models.AlertRule` | | | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| -------------------- | -------- | ----------------------------------------------- | ----------------------------- | --------- | :------: | ------- | ----------- |
+| X-Disable-Provenance | `header` | string | `string` | | | | |
+| Body | `body` | [ProvisionedAlertRule](#provisioned-alert-rule) | `models.ProvisionedAlertRule` | | | | |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| --------------------------------- | ----------- | --------------- | :---------: | ------------------------------------------- |
-| [201](#route-post-alert-rule-201) | Created | AlertRule | | [schema](#route-post-alert-rule-201-schema) |
-| [400](#route-post-alert-rule-400) | Bad Request | ValidationError | | [schema](#route-post-alert-rule-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| --------------------------------- | ----------- | -------------------- | :---------: | ------------------------------------------- |
+| [201](#route-post-alert-rule-201) | Created | ProvisionedAlertRule | | [schema](#route-post-alert-rule-201-schema) |
+| [400](#route-post-alert-rule-400) | Bad Request | ValidationError | | [schema](#route-post-alert-rule-400-schema) |
#### Responses
-##### 201 - AlertRule
+##### 201 - ProvisionedAlertRule
Status: Created
###### Schema
-[AlertRule](#alert-rule)
+[ProvisionedAlertRule](#provisioned-alert-rule)
##### 400 - ValidationError
@@ -501,20 +639,20 @@ POST /api/v1/provisioning/contact-points
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ------------------------------------ | ----------- | --------------- | :---------: | ---------------------------------------------- |
-| [202](#route-post-contactpoints-202) | Accepted | Ack | | [schema](#route-post-contactpoints-202-schema) |
-| [400](#route-post-contactpoints-400) | Bad Request | ValidationError | | [schema](#route-post-contactpoints-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ------------------------------------ | ----------- | -------------------- | :---------: | ---------------------------------------------- |
+| [202](#route-post-contactpoints-202) | Accepted | EmbeddedContactPoint | | [schema](#route-post-contactpoints-202-schema) |
+| [400](#route-post-contactpoints-400) | Bad Request | ValidationError | | [schema](#route-post-contactpoints-400-schema) |
#### Responses
-##### 202 - Ack
+##### 202 - EmbeddedContactPoint
Status: Accepted
###### Schema
-[Ack](#ack)
+[EmbeddedContactPoint](#embedded-contact-point)
##### 400 - ValidationError
@@ -577,27 +715,28 @@ PUT /api/v1/provisioning/alert-rules/{UID}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ------------------------ | ------------------ | --------- | :------: | ------- | ----------- |
-| UID | `path` | string | `string` | | ✓ | | |
-| Body | `body` | [AlertRule](#alert-rule) | `models.AlertRule` | | | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| -------------------- | -------- | ----------------------------------------------- | ----------------------------- | --------- | :------: | ------- | -------------- |
+| UID | `path` | string | `string` | | ✓ | | Alert rule UID |
+| X-Disable-Provenance | `header` | string | `string` | | | | |
+| Body | `body` | [ProvisionedAlertRule](#provisioned-alert-rule) | `models.ProvisionedAlertRule` | | | | |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| -------------------------------- | ----------- | --------------- | :---------: | ------------------------------------------ |
-| [200](#route-put-alert-rule-200) | OK | AlertRule | | [schema](#route-put-alert-rule-200-schema) |
-| [400](#route-put-alert-rule-400) | Bad Request | ValidationError | | [schema](#route-put-alert-rule-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| -------------------------------- | ----------- | -------------------- | :---------: | ------------------------------------------ |
+| [200](#route-put-alert-rule-200) | OK | ProvisionedAlertRule | | [schema](#route-put-alert-rule-200-schema) |
+| [400](#route-put-alert-rule-400) | Bad Request | ValidationError | | [schema](#route-put-alert-rule-400-schema) |
#### Responses
-##### 200 - AlertRule
+##### 200 - ProvisionedAlertRule
Status: OK
###### Schema
-[AlertRule](#alert-rule)
+[ProvisionedAlertRule](#provisioned-alert-rule)
##### 400 - ValidationError
@@ -662,10 +801,10 @@ PUT /api/v1/provisioning/contact-points/{UID}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | ----------------------------------------------- | ----------------------------- | --------- | :------: | ------- | ------------------------------------------------- |
-| UID | `path` | string | `string` | | ✓ | | UID should be the contact point unique identifier |
-| Body | `body` | [EmbeddedContactPoint](#embedded-contact-point) | `models.EmbeddedContactPoint` | | | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ----------------------------------------------- | ----------------------------- | --------- | :------: | ------- | ------------------------------------------ |
+| UID | `path` | string | `string` | | ✓ | | UID is the contact point unique identifier |
+| Body | `body` | [EmbeddedContactPoint](#embedded-contact-point) | `models.EmbeddedContactPoint` | | | | |
#### All responses
@@ -704,10 +843,10 @@ PUT /api/v1/provisioning/mute-timings/{name}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | --------------------------------------- | ------------------------- | --------- | :------: | ------- | ------------- |
-| name | `path` | string | `string` | | ✓ | | Template Name |
-| Body | `body` | [MuteTimeInterval](#mute-time-interval) | `models.MuteTimeInterval` | | | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | --------------------------------------- | ------------------------- | --------- | :------: | ------- | ---------------- |
+| name | `path` | string | `string` | | ✓ | | Mute timing name |
+| Body | `body` | [MuteTimeInterval](#mute-time-interval) | `models.MuteTimeInterval` | | | | |
#### All responses
@@ -746,9 +885,9 @@ PUT /api/v1/provisioning/policies
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | --------------- | -------------- | --------- | :------: | ------- | ----------- |
-| Body | `body` | [Route](#route) | `models.Route` | | | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | --------------- | -------------- | --------- | :------: | ------- | ---------------------------------------- |
+| Body | `body` | [Route](#route) | `models.Route` | | | | The new notification routing tree to use |
#### All responses
@@ -775,7 +914,7 @@ Status: Bad Request
[ValidationError](#validation-error)
-### Updates an existing template. (_RoutePutTemplate_)
+### Updates an existing notification template. (_RoutePutTemplate_)
```
PUT /api/v1/provisioning/templates/{name}
@@ -787,27 +926,27 @@ PUT /api/v1/provisioning/templates/{name}
#### Parameters
-| Name | Source | Type | Go type | Separator | Required | Default | Description |
-| ---- | ------ | -------------------------------------------------------- | ------------------------------------ | --------- | :------: | ------- | ------------- |
-| name | `path` | string | `string` | | ✓ | | Template Name |
-| Body | `body` | [NotificationTemplateContent](#message-template-content) | `models.NotificationTemplateContent` | | | | |
+| Name | Source | Type | Go type | Separator | Required | Default | Description |
+| ---- | ------ | ------------------------------------------------------------- | ------------------------------------ | --------- | :------: | ------- | ------------- |
+| name | `path` | string | `string` | | ✓ | | Template Name |
+| Body | `body` | [NotificationTemplateContent](#notification-template-content) | `models.NotificationTemplateContent` | | | | |
#### All responses
-| Code | Status | Description | Has headers | Schema |
-| ------------------------------ | ----------- | --------------- | :---------: | ---------------------------------------- |
-| [202](#route-put-template-202) | Accepted | Ack | | [schema](#route-put-template-202-schema) |
-| [400](#route-put-template-400) | Bad Request | ValidationError | | [schema](#route-put-template-400-schema) |
+| Code | Status | Description | Has headers | Schema |
+| ------------------------------ | ----------- | -------------------- | :---------: | ---------------------------------------- |
+| [202](#route-put-template-202) | Accepted | NotificationTemplate | | [schema](#route-put-template-202-schema) |
+| [400](#route-put-template-400) | Bad Request | ValidationError | | [schema](#route-put-template-400-schema) |
#### Responses
-##### 202 - Ack
+##### 202 - NotificationTemplate
Status: Accepted
###### Schema
-[Ack](#ack)
+[NotificationTemplate](#notification-template)
##### 400 - ValidationError
@@ -817,57 +956,116 @@ Status: Bad Request
[ValidationError](#validation-error)
+### Clears the notification policy tree. (_RouteResetPolicyTree_)
+
+```
+DELETE /api/v1/provisioning/policies
+```
+
+#### Consumes
+
+- application/json
+
+#### All responses
+
+| Code | Status | Description | Has headers | Schema |
+| ----------------------------------- | -------- | ----------- | :---------: | --------------------------------------------- |
+| [202](#route-reset-policy-tree-202) | Accepted | Ack | | [schema](#route-reset-policy-tree-202-schema) |
+
+#### Responses
+
+##### 202 - Ack
+
+Status: Accepted
+
+###### Schema
+
+[Ack](#ack)
+
+## Models
+
+### Ack
+
+[interface{}](#interface)
+
### AlertQuery
**Properties**
| Name | Type | Go type | Required | Default | Description | Example |
| --------------------------------------------------------- | ----------------------------------------- | ------------------- | :------: | ------- | -------------------------------------------------------------------------------------------------- | ------- |
-| DatasourceUID | string | `string` | | | Grafana data source unique identifier; it should be '-100' for a Server Side Expression operation. | |
-| Model | object | `interface{}` | | | JSON is the raw JSON query and includes the above properties as well as custom properties. | |
-| QueryType | string | `string` | | | QueryType is an optional identifier for the type of query. |
+| datasourceUid | string | `string` | | | Grafana data source unique identifier; it should be '-100' for a Server Side Expression operation. | |
+| model | [interface{}](#interface) | `interface{}` | | | JSON is the raw JSON query and includes the above properties as well as custom properties. | |
+| queryType | string | `string` | | | QueryType is an optional identifier for the type of query. |
| It can be used to distinguish different types of queries. | |
-| RefID | string | `string` | | | RefID is the unique identifier of the query, set by the frontend call. | |
+| refId | string | `string` | | | RefID is the unique identifier of the query, set by the frontend call. | |
| relativeTimeRange | [RelativeTimeRange](#relative-time-range) | `RelativeTimeRange` | | | | |
-### AlertRule
+### AlertQueryExport
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| ------------ | ---------------------------- | ------------------- | :------: | ------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Annotations | map of string | `map[string]string` | | | | `{"runbook_url":"https://supercoolrunbook.com/page/13"}` |
-| Condition | string | `string` | ✓ | | | `A` |
-| Data | [][alertquery](#alert-query) | `[]*AlertQuery` | ✓ | | | `[{"datasourceUid":"-100","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}]` |
-| ExecErrState | string | `string` | ✓ | | Allowed values: "OK", "Alerting", "Error" | |
-| FolderUID | string | `string` | ✓ | | | `project_x` |
-| ID | int64 (formatted integer) | `int64` | | | | |
-| Labels | map of string | `map[string]string` | | | | `{"team":"sre-team-1"}` |
-| NoDataState | string | `string` | ✓ | | Allowed values: "OK", "NoData", "Error" | |
-| OrgID | int64 (formatted integer) | `int64` | ✓ | | | |
-| RuleGroup | string | `string` | ✓ | | | `eval_group_1` |
-| Title | string | `string` | ✓ | | | `Always firing` |
-| UID | string | `string` | | | | |
-| Updated | date-time (formatted string) | `strfmt.DateTime` | | | | |
-| for | [Duration](#duration) | `Duration` | ✓ | | | |
-| provenance | string | `Provenance` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| ----------------- | ----------------------------------------- | ------------------- | :------: | ------- | ----------- | ------- |
+| datasourceUid | string | `string` | | | | |
+| model | [interface{}](#interface) | `interface{}` | | | | |
+| queryType | string | `string` | | | | |
+| refId | string | `string` | | | | |
+| relativeTimeRange | [RelativeTimeRange](#relative-time-range) | `RelativeTimeRange` | | | | |
+
+### AlertRuleExport
+
+**Properties**
+
+| Name | Type | Go type | Required | Default | Description | Example |
+| ------------ | ----------------------------------------- | --------------------- | :------: | ------- | ----------- | ------- |
+| annotations | map of string | `map[string]string` | | | | |
+| condition | string | `string` | | | | |
+| dasboardUid | string | `string` | | | | |
+| data | [][alertqueryexport](#alert-query-export) | `[]*AlertQueryExport` | | | | |
+| execErrState | string | `string` | | | | |
+| for | [Duration](#duration) | `Duration` | | | | |
+| labels | map of string | `map[string]string` | | | | |
+| noDataState | string | `string` | | | | |
+| panelId | int64 (formatted integer) | `int64` | | | | |
+| title | string | `string` | | | | |
+| uid | string | `string` | | | | |
### AlertRuleGroup
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| -------- | ------------------------- | ------- | :------: | ------- | ----------- | ------- |
-| Interval | int64 (formatted integer) | `int64` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| --------- | ------------------------------------------------- | ------------------------- | :------: | ------- | ----------- | ------- |
+| folderUid | string | `string` | | | | |
+| interval | int64 (formatted integer) | `int64` | | | | |
+| rules | [][provisionedalertrule](#provisioned-alert-rule) | `[]*ProvisionedAlertRule` | | | | |
+| title | string | `string` | | | | |
-### DayOfMonthRange
+### AlertRuleGroupExport
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| ----- | ------------------------- | ------- | :------: | ------- | ----------- | ------- |
-| Begin | int64 (formatted integer) | `int64` | | | | |
-| End | int64 (formatted integer) | `int64` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| -------- | --------------------------------------- | -------------------- | :------: | ------- | ----------- | ------- |
+| folder | string | `string` | | | | |
+| interval | [Duration](#duration) | `Duration` | | | | |
+| name | string | `string` | | | | |
+| orgId | int64 (formatted integer) | `int64` | | | | |
+| rules | [][alertruleexport](#alert-rule-export) | `[]*AlertRuleExport` | | | | |
+
+### AlertingFileExport
+
+**Properties**
+
+| Name | Type | Go type | Required | Default | Description | Example |
+| ---------- | -------------------------------------------------- | ------------------------- | :------: | ------- | ----------- | ------- |
+| apiVersion | int64 (formatted integer) | `int64` | | | | |
+| groups | [][alertrulegroupexport](#alert-rule-group-export) | `[]*AlertRuleGroupExport` | | | | |
+
+### ContactPoints
+
+[][embeddedcontactpoint](#embedded-contact-point)
### Duration
@@ -877,25 +1075,35 @@ Status: Bad Request
### EmbeddedContactPoint
-> EmbeddedContactPoint is the contact point integration that is used
+> EmbeddedContactPoint is the contact point type that is used
> by grafanas embedded alertmanager implementation.
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| --------------------- | ------- | -------- | :------: | ------- | ---------------------------------------------------------------------------------------------------- | ----------------------- |
-| DisableResolveMessage | boolean | `bool` | | | | `false` |
-| Name | string | `string` | ✓ | | Name is used as grouping key in the UI. Contact points with the same name will be grouped in the UI. | `webhook_1` |
-| Provenance | string | `string` | | | | |
-| Type | string | `string` | ✓ | | | `webhook` |
-| UID | string | `string` | | | UID is the unique identifier of the contact point. The UID can be set by the user. | `my_external_reference` |
-| settings | object | `JSON` | ✓ | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| ------------------------------------ | ----------------------- | -------- | :------: | ------- | ----------------------------------------------------------------- | --------- |
+| disableResolveMessage | boolean | `bool` | | | | `false` |
+| name | string | `string` | | | Name is used as grouping key in the UI. Contact points with the |
+| same name will be grouped in the UI. | `webhook_1` |
+| provenance | string | `string` | | | | |
+| settings | [JSON](#json) | `JSON` | ✓ | | | |
+| type | string | `string` | ✓ | | | `webhook` |
+| uid | string | `string` | | | UID is the unique identifier of the contact point. The UID can be |
+| set by the user. | `my_external_reference` |
+
+### Json
+
+[interface{}](#interface)
+
+### MatchRegexps
+
+[MatchRegexps](#match-regexps)
### MatchType
-| Name | Type | Go type | Default | Description | Example |
-| --------- | ------------------------- | ------- | ------- | ---------------------------------------------------------------------- | ------- |
-| MatchType | int64 (formatted integer) | int64 | | 0 = MatchEqual, 1 = MatchNotEqual, 2 = MatchRegexp, 3 = MatchNotRegexp | |
+| Name | Type | Go type | Default | Description | Example |
+| --------- | ------------------------- | ------- | ------- | ----------- | ------- |
+| MatchType | int64 (formatted integer) | int64 | | | |
### Matcher
@@ -915,49 +1123,40 @@ Status: Bad Request
[][matcher](#matcher)
-### NotificationTemplate
-
-**Properties**
-
-| Name | Type | Go type | Required | Default | Description | Example |
-| ---------- | ------ | ------------ | :------: | ------- | ----------- | ------- |
-| Name | string | `string` | | | | |
-| Template | string | `string` | | | | |
-| provenance | string | `Provenance` | | | | |
-
-### NotificationTemplateContent
-
-**Properties**
-
-| Name | Type | Go type | Required | Default | Description | Example |
-| -------- | ------ | -------- | :------: | ------- | ----------- | ------- |
-| Template | string | `string` | | | | |
-
-### MonthRange
-
-**Properties**
-
-| Name | Type | Go type | Required | Default | Description | Example |
-| ----- | ------------------------- | ------- | :------: | ------- | ----------- | ------- |
-| Begin | int64 (formatted integer) | `int64` | | | | |
-| End | int64 (formatted integer) | `int64` | | | | |
-
### MuteTimeInterval
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| ------------- | -------------------------------- | ----------------- | :------: | ------- | ----------- | ------- |
-| Name | string | `string` | | | | |
-| TimeIntervals | [][timeinterval](#time-interval) | `[]*TimeInterval` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| -------------- | -------------------------------- | ----------------- | :------: | ------- | ----------- | ------- |
+| name | string | `string` | | | | |
+| time_intervals | [][timeinterval](#time-interval) | `[]*TimeInterval` | | | | |
### MuteTimings
[][mutetimeinterval](#mute-time-interval)
-### NotFound
+### NotificationTemplate
-[interface{}](#interface)
+**Properties**
+
+| Name | Type | Go type | Required | Default | Description | Example |
+| ---------- | ------------------------- | ------------ | :------: | ------- | ----------- | ------- |
+| name | string | `string` | | | | |
+| provenance | [Provenance](#provenance) | `Provenance` | | | | |
+| template | string | `string` | | | | |
+
+### NotificationTemplateContent
+
+**Properties**
+
+| Name | Type | Go type | Required | Default | Description | Example |
+| -------- | ------ | -------- | :------: | ------- | ----------- | ------- |
+| template | string | `string` | | | | |
+
+### NotificationTemplates
+
+[][notificationtemplate](#notification-template)
### ObjectMatchers
@@ -965,6 +1164,45 @@ Status: Bad Request
#### Inlined models
+### Provenance
+
+| Name | Type | Go type | Default | Description | Example |
+| ---------- | ------ | ------- | ------- | ----------- | ------- |
+| Provenance | string | string | | | |
+
+### ProvisionedAlertRule
+
+**Properties**
+
+| Name | Type | Go type | Required | Default | Description | Example |
+| ------------ | ---------------------------- | ------------------- | :------: | ------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| annotations | map of string | `map[string]string` | | | | `{"runbook_url":"https://supercoolrunbook.com/page/13"}` |
+| condition | string | `string` | ✓ | | | `A` |
+| data | [][alertquery](#alert-query) | `[]*AlertQuery` | ✓ | | | `[{"datasourceUid":"-100","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}]` |
+| execErrState | string | `string` | ✓ | | | |
+| folderUID | string | `string` | ✓ | | | `project_x` |
+| for | [Duration](#duration) | `Duration` | ✓ | | | |
+| id | int64 (formatted integer) | `int64` | | | | |
+| labels | map of string | `map[string]string` | | | | `{"team":"sre-team-1"}` |
+| noDataState | string | `string` | ✓ | | | |
+| orgID | int64 (formatted integer) | `int64` | ✓ | | | |
+| provenance | [Provenance](#provenance) | `Provenance` | | | | |
+| ruleGroup | string | `string` | ✓ | | | `eval_group_1` |
+| title | string | `string` | ✓ | | | `Always firing` |
+| uid | string | `string` | | | | |
+| updated | date-time (formatted string) | `strfmt.DateTime` | | | | |
+
+### ProvisionedAlertRules
+
+[][provisionedalertrule](#provisioned-alert-rule)
+
+### Regexp
+
+> A Regexp is safe for concurrent use by multiple goroutines,
+> except for configuration methods, such as Longest.
+
+[interface{}](#interface)
+
### RelativeTimeRange
> RelativeTimeRange is the per query start and end time
@@ -979,22 +1217,26 @@ Status: Bad Request
### Route
-> A Route is a node that contains definitions of how to handle alerts.
+> A Route is a node that contains definitions of how to handle alerts. This is modified
+> from the upstream alertmanager in that it adds the ObjectMatchers property.
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| ----------------- | ---------------------------------- | ---------------- | :------: | ------- | ----------- | ------- |
-| Continue | boolean | `bool` | | | | |
-| GroupByStr | []string | `[]string` | | | | |
-| MuteTimeIntervals | []string | `[]string` | | | | |
-| Receiver | string | `string` | | | | |
-| Routes | [][route](#route) | `[]*Route` | | | | |
-| group_interval | [Duration](#duration) | `Duration` | | | | |
-| group_wait | [Duration](#duration) | `Duration` | | | | |
-| object_matchers | [ObjectMatchers](#object-matchers) | `ObjectMatchers` | | | | |
-| provenance | string | `Provenance` | | | | |
-| repeat_interval | [Duration](#duration) | `Duration` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| ------------------- | ---------------------------------- | ------------------- | :------: | ------- | --------------------------------------- | ------- |
+| continue | boolean | `bool` | | | | |
+| group_by | []string | `[]string` | | | | |
+| group_interval | string | `string` | | | | |
+| group_wait | string | `string` | | | | |
+| match | map of string | `map[string]string` | | | Deprecated. Remove before v1.0 release. | |
+| match_re | [MatchRegexps](#match-regexps) | `MatchRegexps` | | | | |
+| matchers | [Matchers](#matchers) | `Matchers` | | | | |
+| mute_time_intervals | []string | `[]string` | | | | |
+| object_matchers | [ObjectMatchers](#object-matchers) | `ObjectMatchers` | | | | |
+| provenance | [Provenance](#provenance) | `Provenance` | | | | |
+| receiver | string | `string` | | | | |
+| repeat_interval | string | `string` | | | | |
+| routes | [][route](#route) | `[]*Route` | | | | |
### TimeInterval
@@ -1003,13 +1245,14 @@ Status: Bad Request
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| ----------- | ---------------------------------------- | -------------------- | :------: | ------- | ----------- | ------- |
-| DaysOfMonth | [][dayofmonthrange](#day-of-month-range) | `[]*DayOfMonthRange` | | | | |
-| Months | [][monthrange](#month-range) | `[]*MonthRange` | | | | |
-| Times | [][timerange](#time-range) | `[]*TimeRange` | | | | |
-| Weekdays | [][weekdayrange](#weekday-range) | `[]*WeekdayRange` | | | | |
-| Years | [][yearrange](#year-range) | `[]*YearRange` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| ------------- | -------------------------- | -------------- | :------: | ------- | ----------- | ------- |
+| days_of_month | []string | `[]string` | | | | |
+| location | string | `string` | | | | |
+| months | []string | `[]string` | | | | |
+| times | [][timerange](#time-range) | `[]*TimeRange` | | | | |
+| weekdays | []string | `[]string` | | | | |
+| years | []string | `[]string` | | | | |
### TimeRange
@@ -1026,24 +1269,6 @@ Status: Bad Request
**Properties**
-| Name | Type | Go type | Required | Default | Description | Example |
-| ---- | ------ | -------- | :------: | ------- | ----------- | ------- |
-| Msg | string | `string` | | | | |
-
-### WeekdayRange
-
-**Properties**
-
-| Name | Type | Go type | Required | Default | Description | Example |
-| ----- | ------------------------- | ------- | :------: | ------- | ----------- | ------- |
-| Begin | int64 (formatted integer) | `int64` | | | | |
-| End | int64 (formatted integer) | `int64` | | | | |
-
-### YearRange
-
-**Properties**
-
-| Name | Type | Go type | Required | Default | Description | Example |
-| ----- | ------------------------- | ------- | :------: | ------- | ----------- | ------- |
-| Begin | int64 (formatted integer) | `int64` | | | | |
-| End | int64 (formatted integer) | `int64` | | | | |
+| Name | Type | Go type | Required | Default | Description | Example |
+| ---- | ------ | -------- | :------: | ------- | ----------- | --------------- |
+| msg | string | `string` | | | | `error message` |
diff --git a/docs/sources/developers/kinds/composable/nodegraphpanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/nodegraphpanelcfg/schema-reference.md
new file mode 100644
index 00000000000..e0b84e4862e
--- /dev/null
+++ b/docs/sources/developers/kinds/composable/nodegraphpanelcfg/schema-reference.md
@@ -0,0 +1,97 @@
+---
+keywords:
+ - grafana
+ - schema
+title: NodeGraphPanelCfg kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# NodeGraphPanelCfg kind
+
+## Maturity: experimental
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|----------------|-------------------------|----------|-------------|
+| `ArcOption` | [object](#arcoption) | **Yes** | |
+| `EdgeOptions` | [object](#edgeoptions) | **Yes** | |
+| `NodeOptions` | [object](#nodeoptions) | **Yes** | |
+| `PanelOptions` | [object](#paneloptions) | **Yes** | |
+
+## ArcOption
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|-----------------------------------------------------------------------------------------------------|
+| `color` | string | No | The color of the arc. |
+| `field` | string | No | Field from which to get the value. Values should be less than 1, representing fraction of a circle. |
+
+## EdgeOptions
+
+### Properties
+
+| Property | Type | Required | Description |
+|---------------------|--------|----------|-----------------------------------------------------------------------------|
+| `mainStatUnit` | string | No | Unit for the main stat to override what ever is set in the data frame. |
+| `secondaryStatUnit` | string | No | Unit for the secondary stat to override what ever is set in the data frame. |
+
+## NodeOptions
+
+### Properties
+
+| Property | Type | Required | Description |
+|---------------------|---------------------------|----------|-----------------------------------------------------------------------------------------|
+| `arcs` | [ArcOption](#arcoption)[] | No | Define which fields are shown as part of the node arc (colored circle around the node). |
+| `mainStatUnit` | string | No | Unit for the main stat to override what ever is set in the data frame. |
+| `secondaryStatUnit` | string | No | Unit for the secondary stat to override what ever is set in the data frame. |
+
+### ArcOption
+
+#### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|-----------------------------------------------------------------------------------------------------|
+| `color` | string | No | The color of the arc. |
+| `field` | string | No | Field from which to get the value. Values should be less than 1, representing fraction of a circle. |
+
+## PanelOptions
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|-----------------------------|----------|-------------|
+| `edges` | [EdgeOptions](#edgeoptions) | No | |
+| `nodes` | [NodeOptions](#nodeoptions) | No | |
+
+### EdgeOptions
+
+#### Properties
+
+| Property | Type | Required | Description |
+|---------------------|--------|----------|-----------------------------------------------------------------------------|
+| `mainStatUnit` | string | No | Unit for the main stat to override what ever is set in the data frame. |
+| `secondaryStatUnit` | string | No | Unit for the secondary stat to override what ever is set in the data frame. |
+
+### NodeOptions
+
+#### Properties
+
+| Property | Type | Required | Description |
+|---------------------|---------------------------|----------|-----------------------------------------------------------------------------------------|
+| `arcs` | [ArcOption](#arcoption)[] | No | Define which fields are shown as part of the node arc (colored circle around the node). |
+| `mainStatUnit` | string | No | Unit for the main stat to override what ever is set in the data frame. |
+| `secondaryStatUnit` | string | No | Unit for the secondary stat to override what ever is set in the data frame. |
+
+#### ArcOption
+
+##### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|-----------------------------------------------------------------------------------------------------|
+| `color` | string | No | The color of the arc. |
+| `field` | string | No | Field from which to get the value. Values should be less than 1, representing fraction of a circle. |
+
+
diff --git a/docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md
new file mode 100644
index 00000000000..326d99d4a84
--- /dev/null
+++ b/docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md
@@ -0,0 +1,41 @@
+---
+keywords:
+ - grafana
+ - schema
+title: StateTimelinePanelCfg kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# StateTimelinePanelCfg kind
+
+## Maturity: experimental
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|--------------------|-----------------------------|----------|-------------|
+| `PanelFieldConfig` | [object](#panelfieldconfig) | **Yes** | |
+| `PanelOptions` | [object](#paneloptions) | **Yes** | |
+
+## PanelFieldConfig
+
+### Properties
+
+| Property | Type | Required | Description |
+|---------------|---------|----------|----------------|
+| `fillOpacity` | integer | No | Default: `70`. |
+| `lineWidth` | integer | No | Default: `0`. |
+
+## PanelOptions
+
+### Properties
+
+| Property | Type | Required | Description |
+|---------------|---------|----------|-------------------------------------------------------------------------------------------------------------|
+| `alignValue` | string | No | Controls the value alignment in the TimelineChart component Possible values are: `center`, `left`, `right`. |
+| `mergeValues` | boolean | No | Merge equal consecutive values Default: `true`. |
+| `rowHeight` | number | No | Controls the row height Default: `0.9`. |
+| `showValue` | string | No | TODO docs Possible values are: `auto`, `never`, `always`. |
+
+
diff --git a/docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md
new file mode 100644
index 00000000000..46a8a21a125
--- /dev/null
+++ b/docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md
@@ -0,0 +1,40 @@
+---
+keywords:
+ - grafana
+ - schema
+title: StatusHistoryPanelCfg kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# StatusHistoryPanelCfg kind
+
+## Maturity: experimental
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|--------------------|-----------------------------|----------|-------------|
+| `PanelFieldConfig` | [object](#panelfieldconfig) | **Yes** | |
+| `PanelOptions` | [object](#paneloptions) | **Yes** | |
+
+## PanelFieldConfig
+
+### Properties
+
+| Property | Type | Required | Description |
+|---------------|---------|----------|----------------|
+| `fillOpacity` | integer | No | Default: `70`. |
+| `lineWidth` | integer | No | Default: `1`. |
+
+## PanelOptions
+
+### Properties
+
+| Property | Type | Required | Description |
+|-------------|--------|----------|-----------------------------------------------------------|
+| `colWidth` | number | No | Controls the column width Default: `0.9`. |
+| `rowHeight` | number | No | Set the height of the rows Default: `0.9`. |
+| `showValue` | string | No | TODO docs Possible values are: `auto`, `never`, `always`. |
+
+
diff --git a/docs/sources/developers/kinds/composable/testdatadataquery/schema-reference.md b/docs/sources/developers/kinds/composable/testdatadataquery/schema-reference.md
new file mode 100644
index 00000000000..561d2037195
--- /dev/null
+++ b/docs/sources/developers/kinds/composable/testdatadataquery/schema-reference.md
@@ -0,0 +1,121 @@
+---
+keywords:
+ - grafana
+ - schema
+title: TestDataDataQuery kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# TestDataDataQuery kind
+
+## Maturity: experimental
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|-------------------|-------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `alias` | string | No | |
+| `channel` | string | No | |
+| `csvContent` | string | No | |
+| `csvFileName` | string | No | |
+| `csvWave` | [CSVWave](#csvwave)[] | No | |
+| `errorType` | string | No | Possible values are: `server_panic`, `frontend_exception`, `frontend_observable`. |
+| `labels` | string | No | |
+| `levelColumn` | boolean | No | |
+| `lines` | integer | No | |
+| `nodes` | [NodesQuery](#nodesquery) | No | |
+| `points` | array[] | No | |
+| `pulseWave` | [PulseWaveQuery](#pulsewavequery) | No | |
+| `rawFrameContent` | string | No | |
+| `scenarioId` | string | No | Possible values are: `random_walk`, `slow_query`, `random_walk_with_error`, `random_walk_table`, `exponential_heatmap_bucket_data`, `linear_heatmap_bucket_data`, `no_data_points`, `datapoints_outside_range`, `csv_metric_values`, `predictable_pulse`, `predictable_csv_wave`, `streaming_client`, `simulation`, `usa`, `live`, `grafana_api`, `arrow`, `annotations`, `table_static`, `server_error_500`, `logs`, `node_graph`, `flame_graph`, `raw_frame`, `csv_file`, `csv_content`, `trace`, `manual_entry`, `variables-query`. |
+| `seriesCount` | integer | No | |
+| `sim` | [SimulationQuery](#simulationquery) | No | |
+| `spanCount` | integer | No | |
+| `stream` | [StreamingQuery](#streamingquery) | No | |
+| `stringInput` | string | No | |
+| `usa` | [USAQuery](#usaquery) | No | |
+
+## CSVWave
+
+### Properties
+
+| Property | Type | Required | Description |
+|-------------|---------|----------|-------------|
+| `labels` | string | No | |
+| `name` | string | No | |
+| `timeStep` | integer | No | |
+| `valuesCSV` | string | No | |
+
+## NodesQuery
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|---------|----------|------------------------------------------------------------|
+| `count` | integer | No | |
+| `type` | string | No | Possible values are: `random`, `response`, `random edges`. |
+
+## PulseWaveQuery
+
+### Properties
+
+| Property | Type | Required | Description |
+|------------|---------|----------|-------------|
+| `offCount` | integer | No | |
+| `offValue` | number | No | |
+| `onCount` | integer | No | |
+| `onValue` | number | No | |
+| `timeStep` | integer | No | |
+
+## SimulationQuery
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|-------------------|----------|-------------|
+| `key` | [object](#key) | **Yes** | |
+| `config` | [object](#config) | No | |
+| `last` | boolean | No | |
+| `stream` | boolean | No | |
+
+### config
+
+| Property | Type | Required | Description |
+|----------|------|----------|-------------|
+
+### key
+
+#### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|-------------|
+| `tick` | number | **Yes** | |
+| `type` | string | **Yes** | |
+| `uid` | string | No | |
+
+## StreamingQuery
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|---------|----------|-------------------------------------------------|
+| `noise` | integer | **Yes** | |
+| `speed` | integer | **Yes** | |
+| `spread` | integer | **Yes** | |
+| `type` | string | **Yes** | Possible values are: `signal`, `logs`, `fetch`. |
+| `bands` | integer | No | |
+| `url` | string | No | |
+
+## USAQuery
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|----------|----------|-------------|
+| `fields` | string[] | No | |
+| `mode` | string | No | |
+| `period` | string | No | |
+| `states` | string[] | No | |
+
+
diff --git a/docs/sources/developers/kinds/composable/xychartpanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/xychartpanelcfg/schema-reference.md
new file mode 100644
index 00000000000..19e2bfed332
--- /dev/null
+++ b/docs/sources/developers/kinds/composable/xychartpanelcfg/schema-reference.md
@@ -0,0 +1,132 @@
+---
+keywords:
+ - grafana
+ - schema
+title: XYChartPanelCfg kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# XYChartPanelCfg kind
+
+## Maturity: experimental
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|-----------------------|--------------------------------|----------|---------------------------------------------------------|
+| `PanelOptions` | [object](#paneloptions) | **Yes** | |
+| `ScatterFieldConfig` | [object](#scatterfieldconfig) | **Yes** | |
+| `ScatterSeriesConfig` | [object](#scatterseriesconfig) | **Yes** | |
+| `ScatterShow` | string | **Yes** | Possible values are: `points`, `lines`, `points+lines`. |
+| `SeriesMapping` | string | **Yes** | Possible values are: `auto`, `manual`. |
+| `XYDimensionConfig` | [object](#xydimensionconfig) | **Yes** | |
+
+## PanelOptions
+
+### Properties
+
+| Property | Type | Required | Description |
+|-----------------|-----------------------------------------------|----------|----------------------------------------|
+| `dims` | [XYDimensionConfig](#xydimensionconfig) | No | |
+| `seriesMapping` | string | No | Possible values are: `auto`, `manual`. |
+| `series` | [ScatterSeriesConfig](#scatterseriesconfig)[] | No | |
+
+### ScatterSeriesConfig
+
+#### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|-------------|
+| `name` | string | No | |
+| `x` | string | No | |
+| `y` | string | No | |
+
+### XYDimensionConfig
+
+#### Properties
+
+| Property | Type | Required | Description |
+|-----------|----------|----------|-------------|
+| `frame` | integer | **Yes** | |
+| `exclude` | string[] | No | |
+| `x` | string | No | |
+
+## ScatterFieldConfig
+
+### Properties
+
+| Property | Type | Required | Description |
+|--------------|-----------------------------------------------|----------|-------------------------------------------------------------|
+| `labelValue` | [TextDimensionConfig](#textdimensionconfig) | No | |
+| `label` | string | No | TODO docs Possible values are: `auto`, `never`, `always`. |
+| `lineColor` | [ColorDimensionConfig](#colordimensionconfig) | No | This is actually an empty interface used mainly for naming? |
+| `lineStyle` | [LineStyle](#linestyle) | No | TODO docs |
+| `lineWidth` | integer | No | |
+| `pointColor` | [ColorDimensionConfig](#colordimensionconfig) | No | This is actually an empty interface used mainly for naming? |
+| `pointSize` | [ScaleDimensionConfig](#scaledimensionconfig) | No | |
+| `show` | string | No | Possible values are: `points`, `lines`, `points+lines`. |
+
+### ColorDimensionConfig
+
+This is actually an empty interface used mainly for naming?
+
+| Property | Type | Required | Description |
+|----------|------|----------|-------------|
+
+### ColorDimensionConfig
+
+This is actually an empty interface used mainly for naming?
+
+| Property | Type | Required | Description |
+|----------|------|----------|-------------|
+
+### LineStyle
+
+TODO docs
+
+#### Properties
+
+| Property | Type | Required | Description |
+|----------|----------|----------|--------------------------------------------------------|
+| `dash` | number[] | No | |
+| `fill` | string | No | Possible values are: `solid`, `dash`, `dot`, `square`. |
+
+### ScaleDimensionConfig
+
+#### Properties
+
+| Property | Type | Required | Description |
+|----------|---------|----------|-------------|
+| `max` | integer | No | |
+| `min` | integer | No | |
+
+### TextDimensionConfig
+
+#### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|----------------------------------------------------|
+| `mode` | string | No | Possible values are: `fixed`, `field`, `template`. |
+
+## ScatterSeriesConfig
+
+### Properties
+
+| Property | Type | Required | Description |
+|----------|--------|----------|-------------|
+| `name` | string | No | |
+| `x` | string | No | |
+| `y` | string | No | |
+
+## XYDimensionConfig
+
+### Properties
+
+| Property | Type | Required | Description |
+|-----------|----------|----------|-------------|
+| `frame` | integer | **Yes** | |
+| `exclude` | string[] | No | |
+| `x` | string | No | |
+
+
diff --git a/docs/sources/developers/kinds/core/dashboard/schema-reference.md b/docs/sources/developers/kinds/core/dashboard/schema-reference.md
index 695d39fcc96..3672213e9d0 100644
--- a/docs/sources/developers/kinds/core/dashboard/schema-reference.md
+++ b/docs/sources/developers/kinds/core/dashboard/schema-reference.md
@@ -22,19 +22,19 @@ title: Dashboard kind
| `style` | string | **Yes** | Theme of dashboard. Possible values are: `dark`, `light`. Default: `dark`. |
| `annotations` | [object](#annotations) | No | TODO docs |
| `description` | string | No | Description of dashboard. |
-| `fiscalYearStartMonth` | integer | No | TODO docs |
+| `fiscalYearStartMonth` | integer | No | The month that the fiscal year starts on. 0 = January, 11 = December Default: `0`. |
| `gnetId` | string | No | |
| `id` | integer | No | Unique numeric identifier for the dashboard. TODO must isolate or remove identifiers local to a Grafana instance...? |
| `links` | [DashboardLink](#dashboardlink)[] | No | TODO docs |
| `liveNow` | boolean | No | TODO docs |
| `panels` | [object](#panels)[] | No | |
-| `refresh` | | No | TODO docs |
+| `refresh` | | No | Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". |
| `snapshot` | [Snapshot](#snapshot) | No | TODO docs |
| `tags` | string[] | No | Tags associated with dashboard. |
| `templating` | [object](#templating) | No | TODO docs |
| `time` | [object](#time) | No | Time range for dashboard, e.g. last 6 hours, last 7 days, etc |
| `timepicker` | [object](#timepicker) | No | TODO docs TODO this appears to be spread all over in the frontend. Concepts will likely need tidying in tandem with schema changes |
-| `timezone` | string | No | Timezone of dashboard, Possible values are: `browser`, `utc`, ``. Default: `browser`. |
+| `timezone` | string | No | Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc". Default: `browser`. |
| `title` | string | No | Title of dashboard. |
| `uid` | string | No | Unique dashboard identifier that can be generated by anyone. string (8-40) |
| `version` | integer | No | Version of the dashboard, incremented each time the dashboard is updated. |
diff --git a/docs/sources/developers/kinds/core/preferences/schema-reference.md b/docs/sources/developers/kinds/core/preferences/schema-reference.md
new file mode 100644
index 00000000000..fc5bf9ec796
--- /dev/null
+++ b/docs/sources/developers/kinds/core/preferences/schema-reference.md
@@ -0,0 +1,33 @@
+---
+keywords:
+ - grafana
+ - schema
+title: Preferences kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# Preferences kind
+
+## Maturity: merged
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|--------------------|---------------------------------------------------|----------|---------------------------------------------------------------------------------|
+| `homeDashboardUID` | string | No | UID for the home dashboard |
+| `language` | string | No | Selected language (beta) |
+| `queryHistory` | [QueryHistoryPreference](#queryhistorypreference) | No | |
+| `theme` | string | No | light, dark, empty is default |
+| `timezone` | string | No | The timezone selection TODO: this should use the timezone defined in common |
+| `weekStart` | string | No | day of the week (sunday, monday, etc) |
+
+## QueryHistoryPreference
+
+### Properties
+
+| Property | Type | Required | Description |
+|-----------|--------|----------|---------------------------------------------|
+| `homeTab` | string | No | one of: '' | 'query' | 'starred'; |
+
+
diff --git a/docs/sources/developers/kinds/core/publicdashboard/schema-reference.md b/docs/sources/developers/kinds/core/publicdashboard/schema-reference.md
new file mode 100644
index 00000000000..afc5180618b
--- /dev/null
+++ b/docs/sources/developers/kinds/core/publicdashboard/schema-reference.md
@@ -0,0 +1,25 @@
+---
+keywords:
+ - grafana
+ - schema
+title: PublicDashboard kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# PublicDashboard kind
+
+## Maturity: merged
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|------------------------|---------|----------|-----------------------------------------------------------------|
+| `annotationsEnabled` | boolean | **Yes** | Flag that indicates if annotations are enabled |
+| `dashboardUid` | string | **Yes** | Dashboard unique identifier referenced by this public dashboard |
+| `isEnabled` | boolean | **Yes** | Flag that indicates if the public dashboard is enabled |
+| `timeSelectionEnabled` | boolean | **Yes** | Flag that indicates if the time range picker is enabled |
+| `uid` | string | **Yes** | Unique public dashboard identifier |
+| `accessToken` | string | No | Unique public access token |
+
+
diff --git a/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md b/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md
new file mode 100644
index 00000000000..7a9b487e6d1
--- /dev/null
+++ b/docs/sources/developers/kinds/core/serviceaccount/schema-reference.md
@@ -0,0 +1,38 @@
+---
+keywords:
+ - grafana
+ - schema
+title: ServiceAccount kind
+---
+> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
+
+# ServiceAccount kind
+
+## Maturity: merged
+## Version: 0.0
+
+## Properties
+
+| Property | Type | Required | Description |
+|-----------------|--------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------|
+| `avatarUrl` | string | **Yes** | AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front of the service account. |
+| `id` | integer | **Yes** | ID is the unique identifier of the service account in the database. |
+| `isDisabled` | boolean | **Yes** | IsDisabled indicates if the service account is disabled. |
+| `login` | string | **Yes** | Login of the service account. |
+| `name` | string | **Yes** | Name of the service account. |
+| `orgId` | integer | **Yes** | OrgId is the ID of an organisation the service account belongs to. |
+| `role` | string | **Yes** | OrgRole is a Grafana Organization Role which can be 'Viewer', 'Editor', 'Admin'. Possible values are: `Admin`, `Editor`, `Viewer`. |
+| `tokens` | integer | **Yes** | Tokens is the number of active tokens for the service account. Tokens are used to authenticate the service account against Grafana. |
+| `accessControl` | [object](#accesscontrol) | No | AccessControl metadata associated with a given resource. |
+| `created` | integer | No | Created indicates when the service account was created. |
+| `teams` | string[] | No | Teams is a list of teams the service account belongs to. |
+| `updated` | integer | No | Updated indicates when the service account was updated. |
+
+## accessControl
+
+AccessControl metadata associated with a given resource.
+
+| Property | Type | Required | Description |
+|----------|------|----------|-------------|
+
+
diff --git a/docs/sources/developers/plugins/publish-a-plugin.md b/docs/sources/developers/plugins/publish-a-plugin.md
index 3e3b51f3191..2896896df02 100644
--- a/docs/sources/developers/plugins/publish-a-plugin.md
+++ b/docs/sources/developers/plugins/publish-a-plugin.md
@@ -105,8 +105,28 @@ To speed up the time it takes to review your plugin:
- No. We cannot guarantee specific publishing dates, as plugins are immediately published after a review based on our internal prioritization.
+**Can I see metrics of my plugin installs, downloads or usage?**
+
+- No. We don't offer this information at the moment to plugin authors.
+
+**How can I update my plugin's catalog page?**
+
+- The plugin's catalog page content is extracted from the plugin README file. To update the plugin's catalog page, it is necessary to submit an updated plugin with the new content included in the README file.
+
+**Can I unlist my plugin from the Grafana Plugin's Catalog in case of a bug?**
+
+- In the event of a bug, unlisting the plugin from the Grafana Plugin's Catalog may be possible in exceptional cases, such as security concerns. However, we do not have control over the specific instances where the plugin is installed.
+
+**Can I distribute my plugin somewhere else than the Grafana Catalog?**
+
+- The official method for distributing Grafana plugins is through the Grafana Catalog. Alternative methods, such as installing private or development plugins on local Grafana instances, are available as per the guidelines provided in [this guide](https://grafana.com/docs/grafana/latest/administration/plugin-management/#install-plugin-on-local-grafana).
+
## Publishing your plugin for the first time
+**Do plugin signatures expire?**
+
+- Plugin signatures do not currently expire.
+
{{< figure src="/static/img/docs/plugins/plugins-submission-create2.png" class="docs-image--no-shadow" max-width="650px" >}}
1. [Sign in](https://grafana.com/auth/sign-in) to your Grafana Cloud account.
diff --git a/docs/sources/panels-visualizations/query-transform-data/expression-queries/index.md b/docs/sources/panels-visualizations/query-transform-data/expression-queries/index.md
index 6e5af762599..e2724f6818c 100644
--- a/docs/sources/panels-visualizations/query-transform-data/expression-queries/index.md
+++ b/docs/sources/panels-visualizations/query-transform-data/expression-queries/index.md
@@ -219,3 +219,8 @@ For more information about expressions, refer to [About expressions]({{< relref
1. Write the expression.
1. Click **Apply**.
+
+## Special cases
+
+When any queried data source returns no series or numbers, the expression engine returns `NoData`. For example, if a request contains two data source queries that are merged by an expression, if `NoData` is returned by at least one of the data source queries, then the returned result for the entire query is `NoData`.
+For more information about how [Grafana Alerting]({{< relref "../../../alerting/" >}}) processes `NoData` results, refer to [No data and error handling]({{< relref "../../../alerting/alerting-rules/create-grafana-managed-rule/#no-data-and-error-handling" >}}).
diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md
index dc936faa815..8e3415c9af5 100644
--- a/docs/sources/setup-grafana/configure-grafana/_index.md
+++ b/docs/sources/setup-grafana/configure-grafana/_index.md
@@ -675,6 +675,10 @@ List of allowed headers to be set by the user. Suggested to use for if authentic
## [snapshots]
+### enabled
+
+Set to `false` to disable the snapshot feature (default `true`).
+
### external_enabled
Set to `false` to disable external snapshot publish endpoint (default `true`).
@@ -904,6 +908,36 @@ The following table shows the OAuth provider's setting with the default value an
| Google | false | true | User organization roles are set with `defaultRole` and the org role can be changed for Google synced users. |
| Google | true | true | User organization roles are set with `defaultRole` for Google. For other providers, the synchronization will be skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
+### [auth.github] skip_org_role_sync
+
+When a user logs in the first time, Grafana sets the organization role based on the value specified in `AutoAssignOrgRole`. If you want to manage organization roles, set the `skip_org_role_sync` option to `true`. GitHub syncs organization roles and sets Grafana Admins.
+This also impacts `allow_assign_grafana_admin` setting, by not syncing the grafana admin role from GitHub.
+
+> **Note:** There is a separate setting called `oauth_skip_org_role_update_sync` which has a different scope. While `skip_org_role_sync` only applies to the specific OAuth provider, `oauth_skip_org_role_update_sync` is a generic setting that affects all configured OAuth providers.
+
+The following table shows the OAuth provider's setting with the default value and the skip org role sync setting.
+| OAuth Provider | `oauth_skip_org_role_sync_update` | `skip_org_role_sync` | Behavior |
+| --- | --- | --- | --- |
+| GitHub | false | false | User organization roles are set with `defaultRole` and cannot be changed |
+| Github | true | false | User organization roles are set with `defaultRole` for GitHub, and Grafana Admins are set. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
+| GitHub | false | true | User organization roles are set with `defaultRole`, and the organization role can be changed for GitHub synced users. |
+| GitHub | true | true | User organization roles are set with `defaultRole` for Google. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
+
+### [auth.gitlab] skip_org_role_sync
+
+When a user logs in the first time, Grafana sets the organization role based on the value specified in `AutoAssignOrgRole`. If you want to manage organization roles, set the `skip_org_role_sync` option to `true`. GitLab syncs organization roles and sets Grafana Admins.
+This also impacts `allow_assign_grafana_admin` setting, by not syncing the grafana admin role from GitLab.
+
+> **Note:** There is a separate setting called `oauth_skip_org_role_update_sync` which has a different scope. While `skip_org_role_sync` only applies to the specific OAuth provider, `oauth_skip_org_role_update_sync` is a generic setting that affects all configured OAuth providers.
+
+The following table shows the OAuth provider's setting with the default value and the skip org role sync setting.
+| OAuth Provider | `oauth_skip_org_role_sync_update` | `skip_org_role_sync` | Behavior |
+| --- | --- | --- | --- |
+| GitLab | false | false | User organization roles are set with `defaultRole` and cannot be changed |
+| Github | true | false | User organization roles are set with `defaultRole` for GitLab, and Grafana Admins are set. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
+| GitLab | false | true | User organization roles are set with `defaultRole`, and the organization role can be changed for GitLab synced users. |
+| GitLab | true | true | User organization roles are set with `defaultRole` for GitLab. For other providers, the synchronization is skipped, and the org role can be changed, along with other OAuth provider users' org roles. |
+
### api_key_max_seconds_to_live
Limit of API key seconds to live before expiration. Default is -1 (unlimited).
diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
index 6dccb953693..bcec59423e3 100644
--- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
+++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
@@ -30,6 +30,7 @@ Some stable features are enabled by default. You can disable a stable feature by
| `internationalization` | Enables internationalization | Yes |
| `accessTokenExpirationCheck` | Enable OAuth access_token expiration check and token refresh using the refresh_token | |
| `disablePrometheusExemplarSampling` | Disable Prometheus examplar sampling | |
+| `logsSampleInExplore` | Enables access to the logs sample feature in Explore | Yes |
## Beta feature toggles
@@ -98,6 +99,7 @@ Alpha features might be changed or removed without prior notice.
| `alertingBacktesting` | Rule backtesting API for alerting |
| `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files |
| `azureMultipleResourcePicker` | Azure multiple resource picker |
+| `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view |
## Development feature toggles
@@ -109,6 +111,7 @@ The following toggles require explicitly setting Grafana's [app mode]({{< relref
| `showFeatureFlagsInUI` | Show feature flags in the settings UI |
| `publicDashboardsEmailSharing` | Allows public dashboard sharing to be restricted to only allowed emails |
| `k8s` | Explore native k8s integrations |
+| `k8sDashboards` | Save dashboards via k8s |
| `dashboardsFromStorage` | Load dashboards from the generic storage interface |
| `export` | Export grafana instance (to git, etc) |
| `azureMonitorResourcePickerForMetrics` | New UI for Azure Monitor Metrics Query |
diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md
index 9146e224623..81703a7c0fe 100644
--- a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md
+++ b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md
@@ -203,3 +203,16 @@ Your GitHub teams can be referenced in two ways:
Example: `@grafana/developers`
[Learn more about Team Sync]({{< relref "../../configure-team-sync/" >}})
+
+## Skip organization role sync
+
+To prevent the sync of organization roles from GitHub, set `skip_org_role_sync` to `true`. This is useful if you want to manage the organization roles for your users from within Grafana.
+This also impacts the `allow_assign_grafana_admin` setting by not syncing the Grafana admin role from GitHub.
+
+```ini
+[auth.github]
+# ..
+# prevents the sync of org roles from Github
+skip_org_role_sync = true
+``
+```
diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md
index 333e190083f..2fe7302ab1d 100644
--- a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md
+++ b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md
@@ -223,3 +223,16 @@ the correct teams.
Your GitLab groups can be referenced in the same way as `allowed_groups`, like `example` or `foo/bar`.
[Learn more about Team Sync]({{< relref "../../configure-team-sync/" >}})
+
+## Skip organization role sync
+
+To prevent the sync of organization roles from GitLab, set `skip_org_role_sync` to `true`. This is useful if you want to manage the organization roles for your users from within Grafana.
+This also impacts the `allow_assign_grafana_admin` setting by not syncing the Grafana admin role from GitLab.
+
+```ini
+[auth.gitlab]
+# ..
+# prevents the sync of org roles from Github
+skip_org_role_sync = true
+``
+```
diff --git a/docs/sources/shared/tutorials/create-plugin.md b/docs/sources/shared/tutorials/create-plugin.md
new file mode 100755
index 00000000000..656bce3cf9e
--- /dev/null
+++ b/docs/sources/shared/tutorials/create-plugin.md
@@ -0,0 +1,40 @@
+---
+title: Create Plugin
+---
+
+Tooling for modern web development can be tricky to wrap your head around. While you certainly can write your own webpack configuration, for this guide, you'll be using grafana create-plugin tool
+
+Grafana [create-plugin tool](https://www.npmjs.com/package/@grafana/create-plugin) is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The tool scaffolds a starter plugin and all the required configuration for you.
+
+1. In the plugin directory, create a plugin from template using create-plugin:
+
+ ```
+ npx @grafana/create-plugin
+ ```
+
+1. Change directory to your newly created plugin:
+
+ ```
+ cd my-plugin
+ ```
+
+1. Install the dependencies:
+
+ ```
+ yarn install
+ ```
+
+1. Build the plugin:
+
+ ```
+ yarn dev
+ ```
+
+1. Restart the Grafana server for Grafana to discover your plugin.
+1. Open Grafana and go to **Configuration** -> **Plugins**. Make sure that your plugin is there.
+
+By default, Grafana logs whenever it discovers a plugin:
+
+```
+INFO[01-01|12:00:00] Registering plugin logger=plugins name=my-plugin
+```
diff --git a/docs/sources/shared/tutorials/plugin-anatomy.md b/docs/sources/shared/tutorials/plugin-anatomy.md
new file mode 100644
index 00000000000..4d6e2c4ead0
--- /dev/null
+++ b/docs/sources/shared/tutorials/plugin-anatomy.md
@@ -0,0 +1,29 @@
+---
+title: Plugin Anatomy
+---
+
+Plugins come in different shapes and sizes. Before we dive deeper, let's look at some of the properties that are shared by all of them.
+
+Every plugin you create will require at least two files: `plugin.json` and `module.ts`.
+
+### plugin.json
+
+When Grafana starts, it scans the plugin directory for any subdirectory that contains a `plugin.json` file. The `plugin.json` file contains information about your plugin, and tells Grafana about what capabilities and dependencies your plugin needs.
+
+While certain plugin types can have specific configuration options, let's look at the mandatory ones:
+
+- `type` tells Grafana what type of plugin to expect. Grafana supports three types of plugins: `panel`, `datasource`, and `app`.
+- `name` is what users will see in the list of plugins. If you're creating a data source, this is typically the name of the database it connects to, such as Prometheus, PostgreSQL, or Stackdriver.
+- `id` uniquely identifies your plugin, and should start with your Grafana username, to avoid clashing with other plugins. [Sign up for a Grafana account](/signup/) to claim your username.
+
+To see all the available configuration settings for the `plugin.json`, refer to the [plugin.json Schema](/docs/grafana/latest/plugins/developing/plugin.json/).
+
+### module.ts
+
+After discovering your plugin, Grafana loads the `module.ts` file, the entrypoint for your plugin. `module.ts` exposes the implementation of your plugin, which depends on the type of plugin you're building.
+
+Specifically, `module.ts` needs to expose an object that extends [GrafanaPlugin](https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/plugin.ts#L124), and can be any of the following:
+
+- [PanelPlugin](https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/panel.ts#L73)
+- [DataSourcePlugin](https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/datasource.ts#L33)
+- [AppPlugin](https://github.com/grafana/grafana/blob/45b7de1910819ad0faa7a8aeac2481e675870ad9/packages/grafana-data/src/types/app.ts#L27)
diff --git a/docs/sources/shared/tutorials/publish-your-plugin.md b/docs/sources/shared/tutorials/publish-your-plugin.md
new file mode 100644
index 00000000000..097f2417608
--- /dev/null
+++ b/docs/sources/shared/tutorials/publish-your-plugin.md
@@ -0,0 +1,77 @@
+---
+title: Package your plugin
+---
+
+Once you're happy with your plugin, it's time to package it, and submit to the plugin repository.
+
+For users to be able to use the plugin without building it themselves, you need to make a production build of the plugin, and commit to a release branch in your repository.
+
+To submit a plugin to the plugin repository, you need to create a release of your plugin. While we recommend following the branching strategy outlined below, you're free to use one that makes more sense to you.
+
+#### Create a plugin release
+
+Let's create version 0.1.0 of our plugin.
+
+1. Create a branch called `release-0.1.x`.
+
+ ```
+ git checkout -b release-0.1.x
+ ```
+
+1. Do a production build.
+
+ ```
+ yarn build
+ ```
+
+1. Add the `dist` directory.
+
+ ```
+ git add -f dist
+ ```
+
+1. Create the release commit.
+
+ ```
+ git commit -m "Release v0.1.0"
+ ```
+
+1. Create a release tag.
+
+ ```
+ git tag -a v0.1.0 -m "Create release tag v0.1.0"
+ ```
+
+1. Push to GitHub. `follow-tags` tells Git to push the release tag along with our release branch.
+ ```
+ git push --set-upstream origin release-0.1.x --follow-tags
+ ```
+
+#### Submit the plugin
+
+For a plugin to be published on [Grafana Plugins](/grafana/plugins/), it needs to be added to the [grafana-plugin-repository](https://github.com/grafana/grafana-plugin-repository).
+
+1. Fork the [grafana-plugin-repository](https://github.com/grafana/grafana-plugin-repository)
+
+1. Add your plugin to the `repo.json` file in the project root directory:
+
+ ```json
+ {
+ "id": "",
+ "type": "",
+ "url": "https://github.com//my-plugin",
+ "versions": [
+ {
+ "version": "",
+ "commit": "",
+ "url": "https://github.com//my-plugin"
+ }
+ ]
+ }
+ ```
+
+1. [Create a pull request](https://github.com/grafana/grafana-plugin-repository/pull/new/master).
+
+Once your plugin has been accepted, it'll be published on [Grafana Plugin](/grafana/plugins/), available for anyone to [install](/docs/grafana/latest/plugins/installation/)!
+
+> We're auditing every plugin that's added to make sure it's ready to be published. This means that it might take some time before your plugin is accepted. We're working on adding more automated tests to improve this process.
diff --git a/docs/sources/shared/tutorials/set-up-environment.md b/docs/sources/shared/tutorials/set-up-environment.md
new file mode 100644
index 00000000000..07eb372727a
--- /dev/null
+++ b/docs/sources/shared/tutorials/set-up-environment.md
@@ -0,0 +1,34 @@
+---
+title: Set up Environment
+---
+
+Before you can get started building plugins, you need to set up your environment for plugin development.
+
+To discover plugins, Grafana scans a _plugin directory_, the location of which depends on your operating system.
+
+1. Create a directory called `grafana-plugins` in your preferred workspace.
+
+1. Find the `plugins` property in the Grafana configuration file and set the `plugins` property to the path of your `grafana-plugins` directory. Refer to the [Grafana configuration documentation](/docs/grafana/latest/installation/configuration/#plugins) for more information.
+
+ ```ini
+ [paths]
+ plugins = "/path/to/grafana-plugins"
+ ```
+
+1. Restart Grafana if it's already running, to load the new configuration.
+
+### Alternative method: Docker
+
+If you don't want to install Grafana on your local machine, you can use [Docker](https://www.docker.com).
+
+To set up Grafana for plugin development using Docker, run the following command:
+
+```
+docker run -d -p 3000:3000 -v "$(pwd)"/grafana-plugins:/var/lib/grafana/plugins --name=grafana grafana/grafana:7.0.0
+```
+
+Since Grafana only loads plugins on start-up, you need to restart the container whenever you add or remove a plugin.
+
+```
+docker restart grafana
+```
diff --git a/docs/sources/tutorials/_index.md b/docs/sources/tutorials/_index.md
new file mode 100644
index 00000000000..cfe6c7633df
--- /dev/null
+++ b/docs/sources/tutorials/_index.md
@@ -0,0 +1,9 @@
+---
+title: 'Tutorials'
+menuTitle: 'Tutorials'
+description: 'Grafana tutorials'
+---
+
+# Tutorials
+
+{{< section >}}
diff --git a/docs/sources/tutorials/build-a-data-source-backend-plugin/index.md b/docs/sources/tutorials/build-a-data-source-backend-plugin/index.md
new file mode 100644
index 00000000000..ebc6c160926
--- /dev/null
+++ b/docs/sources/tutorials/build-a-data-source-backend-plugin/index.md
@@ -0,0 +1,180 @@
+---
+title: Build a data source backend plugin
+summary: Create a backend for your data source plugin.
+description: Create a backend for your data source plugin.
+id: build-a-data-source-backend-plugin
+categories: ['plugins']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 75
+---
+
+## Introduction
+
+Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There's a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a support for your data source.
+
+For more information about backend plugins, refer to the documentation on [Backend plugins](/docs/grafana/latest/developers/plugins/backend/).
+
+In this tutorial, you'll:
+
+- Build a backend for your data source
+- Implement a health check for your data source
+- Enable Grafana Alerting for your data source
+
+{{% class "prerequisite-section" %}}
+
+#### Prerequisites
+
+- Knowledge about how data sources are implemented in the frontend.
+- Grafana 7.0
+- Go ([Version](https://github.com/grafana/plugin-tools/blob/main/packages/create-plugin/templates/backend/go.mod#L3))
+- [Mage](https://magefile.org/)
+- NodeJS ([Version](https://github.com/grafana/plugin-tools/blob/main/packages/create-plugin/templates/common/package.json#L66))
+- yarn
+ {{% /class %}}
+
+## Set up your environment
+
+{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
+
+## Create a new plugin
+
+To build a backend for your data source plugin, Grafana requires a binary that it can execute when it loads the plugin during start-up. In this guide, we will build a binary using the [Grafana plugin SDK for Go](/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/).
+
+The easiest way to get started is to use the Grafana [create-plugin tool](https://www.npmjs.com/package/@grafana/create-plugin). Navigate to the plugin folder that you configured in step 1 and type:
+
+```
+npx @grafana/create-plugin
+```
+
+Follow the steps and select **datasource** as your plugin type and answer **yes** when prompted to create a backend for your plugin.
+
+```bash
+cd my-plugin
+```
+
+Install frontend dependencies and build frontend parts of the plugin to _dist_ directory:
+
+```bash
+yarn install
+yarn build
+```
+
+Run the following to update [Grafana plugin SDK for Go](/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/) dependency to the latest minor version:
+
+```bash
+go get -u github.com/grafana/grafana-plugin-sdk-go
+go mod tidy
+```
+
+Build backend plugin binaries for Linux, Windows and Darwin to _dist_ directory:
+
+```bash
+mage -v
+```
+
+Now, let's verify that the plugin you've built so far can be used in Grafana when creating a new data source:
+
+1. Restart your Grafana instance.
+1. Open Grafana in your web browser.
+1. Navigate via the side-menu to **Configuration** -> **Data Sources**.
+1. Click **Add data source**.
+1. Find your newly created plugin and select it.
+1. Enter a name and then click **Save & Test** (ignore any errors reported for now).
+
+You now have a new data source instance of your plugin that is ready to use in a dashboard:
+
+1. Navigate via the side-menu to **Create** -> **Dashboard**.
+1. Click **Add new panel**.
+1. In the query tab, select the data source you just created.
+1. A line graph is rendered with one series consisting of two data points.
+1. Save the dashboard.
+
+### Troubleshooting
+
+#### Grafana doesn't load my plugin
+
+By default, Grafana requires backend plugins to be signed. To load unsigned backend plugins, you need to
+configure Grafana to [allow unsigned plugins](/docs/grafana/latest/plugins/plugin-signature-verification/#allow-unsigned-plugins).
+For more information, refer to [Plugin signature verification](/docs/grafana/latest/plugins/plugin-signature-verification/#backend-plugins).
+
+## Anatomy of a backend plugin
+
+The folders and files used to build the backend for the data source are:
+
+| file/folder | description |
+| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `Magefile.go` | It’s not a requirement to use mage build files, but we strongly recommend using it so that you can use the build targets provided by the plugin SDK. |
+| `/go.mod ` | Go modules dependencies, [reference](https://golang.org/cmd/go/#hdr-The_go_mod_file) |
+| `/src/plugin.json` | A JSON file describing the backend plugin |
+| `/pkg/main.go` | Starting point of the plugin binary. |
+
+#### plugin.json
+
+The [plugin.json](/docs/grafana/latest/developers/plugins/metadata/) file is required for all plugins. When building a backend plugin these properties are important:
+
+| property | description |
+| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| backend | Should be set to `true` for backend plugins. This tells Grafana that it should start a binary when loading the plugin. |
+| executable | This is the name of the executable that Grafana expects to start, see [plugin.json reference](/docs/grafana/latest/developers/plugins/metadata/) for details. |
+| alerting | Should be set to `true` if your backend datasource supports alerting. |
+
+In the next step we will look at the query endpoint!
+
+## Implement data queries
+
+We begin by opening the file `/pkg/plugin/plugin.go`. In this file you will see the `SampleDatasource` struct which implements the [backend.QueryDataHandler](https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend?tab=doc#QueryDataHandler) interface. The `QueryData` method on this struct is where the data fetching happens for a data source plugin.
+
+Each request contains multiple queries to reduce traffic between Grafana and plugins. So you need to loop over the slice of queries, process each query, and then return the results of all queries.
+
+In the tutorial we have extracted a method named `query` to take care of each query model. Since each plugin has their own unique query model, Grafana sends it to the backend plugin as JSON. Therefore the plugin needs to `Unmarshal` the query model into something easier to work with.
+
+As you can see the sample only returns static numbers. Try to extend the plugin to return other types of data.
+
+You can read more about how to [build data frames in our docs](/docs/grafana/latest/developers/plugins/data-frames/).
+
+## Add support for health checks
+
+Implementing the health check handler allows Grafana to verify that a data source has been configured correctly.
+
+When editing a data source in Grafana's UI, you can **Save & Test** to verify that it works as expected.
+
+In this sample data source, there is a 50% chance that the health check will be successful. Make sure to return appropriate error messages to the users, informing them about what is misconfigured in the data source.
+
+Open `/pkg/plugin/plugin.go`. In this file you'll see that the `SampleDatasource` struct also implements the [backend.CheckHealthHandler](https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend?tab=doc#CheckHealthHandler) interface. Navigate to the `CheckHealth` method to see how the health check for this sample plugin is implemented.
+
+## Enable Grafana Alerting
+
+1. Open _src/plugin.json_.
+1. Add the top level `backend` property with a value of `true` to specify that your plugin supports Grafana Alerting, e.g.
+ ```json
+ {
+ ...
+ "backend": true,
+ "executable": "gpx_simple_datasource_backend",
+ "alerting": true,
+ "info": {
+ ...
+ }
+ ```
+1. Rebuild frontend parts of the plugin to _dist_ directory:
+
+```bash
+yarn build
+```
+
+1. Restart your Grafana instance.
+1. Open Grafana in your web browser.
+1. Open the dashboard you created earlier in the _Create a new plugin_ step.
+1. Edit the existing panel.
+1. Click on the _Alert_ tab.
+1. Click on _Create Alert_ button.
+1. Edit condition and specify _IS ABOVE 10_. Change _Evaluate every_ to _10s_ and clear the _For_ field to make the alert rule evaluate quickly.
+1. Save the dashboard.
+1. After some time the alert rule evaluates and transitions into _Alerting_ state.
+
+## Summary
+
+In this tutorial you created a backend for your data source plugin.
diff --git a/docs/sources/tutorials/build-a-data-source-plugin/index.md b/docs/sources/tutorials/build-a-data-source-plugin/index.md
new file mode 100644
index 00000000000..8cb9af04281
--- /dev/null
+++ b/docs/sources/tutorials/build-a-data-source-plugin/index.md
@@ -0,0 +1,372 @@
+---
+title: Build a data source plugin
+summary: Create a plugin to add support for your own data sources.
+description: Create a plugin to add support for your own data sources.
+id: build-a-data-source-plugin
+categories: ['plugins']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 70
+---
+
+## Introduction
+
+Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There's a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a support for your data source.
+
+In this tutorial, you'll:
+
+- Build a data source to visualize a sine wave
+- Construct queries using the query editor
+- Configure your data source using the config editor
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Grafana >=7.0
+- NodeJS >=14
+- yarn
+ {{% /class %}}
+
+## Set up your environment
+
+{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
+
+## Create a new plugin
+
+{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
+
+## Anatomy of a plugin
+
+{{< docs/shared lookup="tutorials/plugin-anatomy.md" source="grafana" version="latest" >}}
+
+## Data source plugins
+
+A data source in Grafana must extend the `DataSourceApi` interface, which requires you to defines two methods: `query` and `testDatasource`.
+
+### The `query` method
+
+The `query` method is the heart of any data source plugin. It accepts a query from the user, retrieves the data from an external database, and returns the data in a format that Grafana recognizes.
+
+```
+async query(options: DataQueryRequest): Promise
+```
+
+The `options` object contains the queries, or _targets_, that the user made, along with context information, like the current time interval. Use this information to query an external database.
+
+> The term _target_ originates from Graphite, and the earlier days of Grafana when Graphite was the only supported data source. As Grafana gained support for more data sources, the term "target" became synonymous with any type of query.
+
+### Test your data source
+
+`testDatasource` implements a health check for your data source. For example, Grafana calls this method whenever the user clicks the **Save & Test** button, after changing the connection settings.
+
+```
+async testDatasource()
+```
+
+## Data frames
+
+Nowadays there are countless of different databases, each with their own ways of querying data. To be able to support all the different data formats, Grafana consolidates the data into a unified data structure called _data frames_.
+
+Let's see how to create and return a data frame from the `query` method. In this step, you'll change the code in the starter plugin to return a [sine wave](https://en.wikipedia.org/wiki/Sine_wave).
+
+1. In the current `query` method, remove the code inside the `map` function.
+
+ The `query` method now look like this:
+
+ ```ts
+ async query(options: DataQueryRequest): Promise {
+ const { range } = options;
+ const from = range!.from.valueOf();
+ const to = range!.to.valueOf();
+
+ const data = options.targets.map(target => {
+ // Your code goes here.
+ });
+
+ return { data };
+ }
+ ```
+
+1. In the `map` function, use the `lodash/defaults` package to set default values for query properties that haven't been set:
+
+ ```ts
+ const query = defaults(target, defaultQuery);
+ ```
+
+1. Create a data frame with a time field and a number field:
+
+ ```ts
+ const frame = new MutableDataFrame({
+ refId: query.refId,
+ fields: [
+ { name: 'time', type: FieldType.time },
+ { name: 'value', type: FieldType.number },
+ ],
+ });
+ ```
+
+ `refId` needs to be set to tell Grafana which query that generated this date frame.
+
+Next, we'll add the actual values to the data frame. Don't worry about the math used to calculate the values.
+
+1. Create a couple of helper variables:
+
+ ```ts
+ // duration of the time range, in milliseconds.
+ const duration = to - from;
+
+ // step determines how close in time (ms) the points will be to each other.
+ const step = duration / 1000;
+ ```
+
+1. Add the values to the data frame:
+
+ ```ts
+ for (let t = 0; t < duration; t += step) {
+ frame.add({ time: from + t, value: Math.sin((2 * Math.PI * t) / duration) });
+ }
+ ```
+
+ The `frame.add()` accepts an object where the keys corresponds to the name of each field in the data frame.
+
+1. Return the data frame:
+
+ ```ts
+ return frame;
+ ```
+
+1. Rebuild the plugin and try it out.
+
+Your data source is now sending data frames that Grafana can visualize. Next, we'll look at how you can control the frequency of the sine wave by defining a _query_.
+
+> In this example, we're generating timestamps from the current time range. This means that you'll get the same graph no matter what time range you're using. In practice, you'd instead use the timestamps returned by your database.
+
+## Define a query
+
+Most data sources offer a way to query specific data. MySQL and PostgreSQL use SQL, while Prometheus has its own query language, called _PromQL_. No matter what query language your databases are using, Grafana lets you build support for it.
+
+Add support for custom queries to your data source, by implementing your own _query editor_, a React component that enables users to build their own queries, through a user-friendly graphical interface.
+
+A query editor can be as simple as a text field where the user edits the raw query text, or it can provide a more user-friendly form with drop-down menus and switches, that later gets converted into the raw query text before it gets sent off to the database.
+
+### Define the query model
+
+The first step in designing your query editor is to define its _query model_. The query model defines the user input to your data source.
+
+We want to be able to control the frequency of the sine wave, so let's add another property.
+
+1. Add a new number property called `frequency` to the query model:
+
+ **src/types.ts**
+
+ ```ts
+ export interface MyQuery extends DataQuery {
+ queryText?: string;
+ constant: number;
+ frequency: number;
+ }
+ ```
+
+1. Set a default value to the new `frequency` property:
+
+ ```ts
+ export const defaultQuery: Partial = {
+ constant: 6.5,
+ frequency: 1.0,
+ };
+ ```
+
+### Bind the model to a form
+
+Now that you've defined the query model you wish to support, the next step is to bind the model to a form. The `FormField` is a text field component from `grafana/ui` that lets you register a listener which will be invoked whenever the form field value changes.
+
+1. Add a new form field to the query editor to control the new frequency property.
+
+ **QueryEditor.tsx**
+
+ ```ts
+ const { queryText, constant, frequency } = query;
+ ```
+
+ ```ts
+
+ ```
+
+1. Add a event listener for the new property.
+
+ ```ts
+ onFrequencyChange = (event: ChangeEvent) => {
+ const { onChange, query, onRunQuery } = this.props;
+ onChange({ ...query, frequency: parseFloat(event.target.value) });
+ // executes the query
+ onRunQuery();
+ };
+ ```
+
+ The registered listener, `onFrequencyChange`, calls `onChange` to update the current query with the value from the form field.
+
+ `onRunQuery();` tells Grafana to run the query after each change. For fast queries, this is recommended to provide a more responsive experience.
+
+### Use the property
+
+The new query model is now ready to use in our `query` method.
+
+1. In the `query` method, use the `frequency` property to adjust our equation.
+
+ ```ts
+ frame.add({ time: from + t, value: Math.sin((2 * Math.PI * query.frequency * t) / duration) });
+ ```
+
+## Configure your data source
+
+To access a specific data source, you often need to configure things like hostname, credentials, or authentication method. A _config editor_ lets your users configure your data source plugin to fit their needs.
+
+The config editor looks similar to the query editor, in that it defines a model and binds it to a form.
+
+Since we're not actually connecting to an external database in our sine wave example, we don't really need many options. To show you how you can add an option however, we're going to add the _wave resolution_ as an option.
+
+The resolution controls how close in time the data points are to each other. A higher resolution means more points closer together, at the cost of more data being processed.
+
+### Define the options model
+
+1. Add a new number property called `resolution` to the options model.
+
+ **types.ts**
+
+ ```ts
+ export interface MyDataSourceOptions extends DataSourceJsonData {
+ path?: string;
+ resolution?: number;
+ }
+ ```
+
+### Bind the model to a form
+
+Just like query editor, the form field in the config editor calls the registered listener whenever the value changes.
+
+1. Add a new form field to the query editor to control the new resolution option.
+
+ **ConfigEditor.tsx**
+
+ ```ts
+
+
+
+ ```
+
+1. Add a event listener for the new option.
+
+ ```ts
+ onResolutionChange = (event: ChangeEvent) => {
+ const { onOptionsChange, options } = this.props;
+ const jsonData = {
+ ...options.jsonData,
+ resolution: parseFloat(event.target.value),
+ };
+ onOptionsChange({ ...options, jsonData });
+ };
+ ```
+
+ The `onResolutionChange` listener calls `onOptionsChange` to update the current options with the value from the form field.
+
+### Use the option
+
+1. Create a property called `resolution` to the `DataSource` class.
+
+ ```ts
+ export class DataSource extends DataSourceApi {
+ resolution: number;
+
+ constructor(instanceSettings: DataSourceInstanceSettings) {
+ super(instanceSettings);
+
+ this.resolution = instanceSettings.jsonData.resolution || 1000.0;
+ }
+
+ // ...
+ ```
+
+1. In the `query` method, use the `resolution` property to calculate the step size.
+
+ **src/DataSource.ts**
+
+ ```ts
+ const step = duration / this.resolution;
+ ```
+
+## Get data from an external API
+
+So far, you've generated the data returned by the data source. A more realistic use case would be to fetch data from an external API.
+
+While you can use something like [axios](https://github.com/axios/axios) or the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make requests, we recommend using the [`getBackendSrv`](/docs/grafana/latest/packages_api/runtime/getbackendsrv/) function from the [grafana/runtime](/docs/grafana/latest/packages_api/runtime/) package.
+
+The main advantage of `getBackendSrv` is that it proxies requests through the Grafana server rather making the request from the browser. This is strongly recommended when making authenticated requests to an external API. For more information on authenticating external requests, refer to [Add authentication for data source plugins](/docs/grafana/latest/developers/plugins/add-authentication-for-data-source-plugins/).
+
+1. Import `getBackendSrv`.
+
+ **src/DataSource.ts**
+
+ ```ts
+ import { getBackendSrv } from '@grafana/runtime';
+ ```
+
+1. Create a helper method `doRequest` and use the `datasourceRequest` method to make a request to your API. Replace `https://api.example.com/metrics` to point to your own API endpoint.
+
+ ```ts
+ async doRequest(query: MyQuery) {
+ const result = await getBackendSrv().datasourceRequest({
+ method: "GET",
+ url: "https://api.example.com/metrics",
+ params: query,
+ })
+
+ return result;
+ }
+ ```
+
+1. Make a request for each query. `Promises.all` waits for all requests to finish before returning the data.
+
+ ```ts
+ async query(options: DataQueryRequest): Promise {
+ const promises = options.targets.map((query) =>
+ this.doRequest(query).then((response) => {
+ const frame = new MutableDataFrame({
+ refId: query.refId,
+ fields: [
+ { name: "Time", type: FieldType.time },
+ { name: "Value", type: FieldType.number },
+ ],
+ });
+
+ response.data.forEach((point: any) => {
+ frame.appendRow([point.time, point.value]);
+ });
+
+ return frame;
+ })
+ );
+
+ return Promise.all(promises).then((data) => ({ data }));
+ }
+ ```
+
+## Summary
+
+In this tutorial you built a complete data source plugin for Grafana that uses a query editor to control what data to visualize. You've added a data source option, commonly used to set connection options and more.
+
+### Learn more
+
+Learn how you can improve your plugin even further, by reading our advanced guides:
+
+- [Add support for variables](/docs/grafana/latest/developers/plugins/add-support-for-variables/)
+- [Add support for annotations](/docs/grafana/latest/developers/plugins/add-support-for-annotations/)
+- [Add support for Explore queries](/docs/grafana/latest/developers/plugins/add-support-for-explore-queries/)
+- [Build a logs data source](/docs/grafana/latest/developers/plugins/build-a-logs-data-source-plugin/)
diff --git a/docs/sources/tutorials/build-a-panel-plugin-with-d3/index.md b/docs/sources/tutorials/build-a-panel-plugin-with-d3/index.md
new file mode 100644
index 00000000000..3638df56e06
--- /dev/null
+++ b/docs/sources/tutorials/build-a-panel-plugin-with-d3/index.md
@@ -0,0 +1,235 @@
+---
+title: Build a panel plugin with D3.js
+summary: Learn how to use D3.js in your panel plugins.
+description: how to use D3.js in your panel plugins.
+id: build-a-panel-plugin-with-d3
+categories: ['plugins']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 60
+---
+
+## Introduction
+
+Panels are the building blocks of Grafana, and allow you to visualize data in different ways. This tutorial gives you a hands-on walkthrough of creating your own panel using [D3.js](https://d3js.org/).
+
+For more information about panels, refer to the documentation on [Panels](/docs/grafana/latest/features/panels/panels/).
+
+In this tutorial, you'll:
+
+- Build a simple panel plugin to visualize a bar chart.
+- Learn how to use D3.js to build a panel using data-driven transformations.
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Grafana 7.0
+- NodeJS 12.x
+- yarn
+ {{% /class %}}
+
+## Set up your environment
+
+{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
+
+## Create a new plugin
+
+{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
+
+## Data-driven documents
+
+[D3.js](https://d3js.org/) is a JavaScript library for manipulating documents based on data. It lets you transform arbitrary data into HTML, and is commonly used for creating visualizations.
+
+Wait a minute. Manipulating documents based on data? That's sounds an awful lot like React. In fact, much of what you can accomplish with D3 you can already do with React. So before we start looking at D3, let's see how you can create an SVG from data, using only React.
+
+In **SimplePanel.tsx**, change `SimplePanel` to return an `svg` with a `rect` element.
+
+```ts
+export const SimplePanel: React.FC = ({ options, data, width, height }) => {
+ const theme = useTheme();
+
+ return (
+
+ );
+};
+```
+
+One single rectangle might not be very exciting, so let's see how you can create rectangles from data.
+
+1. Create some data that we can visualize.
+
+ ```ts
+ const values = [4, 8, 15, 16, 23, 42];
+ ```
+
+1. Calculate the height of each bar based on the height of the panel.
+
+ ```ts
+ const barHeight = height / values.length;
+ ```
+
+1. Inside a SVG group, `g`, create a `rect` element for every value in the dataset. Each rectangle uses the value as its width.
+
+ ```ts
+ return (
+
+ );
+ ```
+
+1. Rebuild the plugin and reload your browser to see the changes you've made.
+
+As you can see, React is perfectly capable of dynamically creating HTML elements. In fact, creating elements using React is often faster than creating them using D3.
+
+So why would you use even use D3? In the next step, we'll see how you can take advantage of D3's data transformations.
+
+## Transform data using D3.js
+
+In this step, you'll see how you can transform data using D3 before rendering it using React.
+
+D3 is already bundled with Grafana, and you can access it by importing the `d3` package. However, we're going to need the type definitions while developing.
+
+1. Install the D3 type definitions:
+
+ ```bash
+ yarn add --dev @types/d3
+ ```
+
+1. Import `d3` in **SimplePanel.tsx**.
+
+ ```ts
+ import * as d3 from 'd3';
+ ```
+
+In the previous step, we had to define the width of each bar in pixels. Instead, let's use _scales_ from the D3 library to make the width of each bar depend on the width of the panel.
+
+Scales are functions that map a range of values to another range of values. In this case, we want to map the values in our datasets to a position within our panel.
+
+1. Create a scale to map a value between 0 and the maximum value in the dataset, to a value between 0 and the width of the panel. We'll be using this to calculate the width of the bar.
+
+ ```ts
+ const scale = d3
+ .scaleLinear()
+ .domain([0, d3.max(values) || 0.0])
+ .range([0, width]);
+ ```
+
+1. Pass the value to the scale function to calculate the width of the bar in pixels.
+
+ ```ts
+ return (
+
+ );
+ ```
+
+As you can see, even if we're using React to render the actual elements, the D3 library contains useful tools that you can use to transform your data before rendering it.
+
+## Add an axis
+
+Another useful tool in the D3 toolbox is the ability to generate _axes_. Adding axes to our chart makes it easier for the user to understand the differences between each bar.
+
+Let's see how you can use D3 to add a horizontal axis to your bar chart.
+
+1. Create a D3 axis. Notice that by using the same scale as before, we make sure that the bar width aligns with the ticks on the axis.
+
+ ```ts
+ const axis = d3.axisBottom(scale);
+ ```
+
+1. Generate the axis. While D3 needs to generate the elements for the axis, we can encapsulate it by generating them within an anonymous function which we pass as a `ref` to a group element `g`.
+
+ ```ts
+ {
+ d3.select(node).call(axis as any);
+ }}
+ />
+ ```
+
+By default, the axis renders at the top of the SVG element. We'd like to move it to the bottom, but to do that, we first need to make room for it by decreasing the height of each bar.
+
+1. Calculate the new bar height based on the padded height.
+
+ ```ts
+ const padding = 20;
+ const chartHeight = height - padding;
+ const barHeight = chartHeight / values.length;
+ ```
+
+1. Translate the axis by adding a transform to the `g` element.
+
+ ```ts
+ {
+ d3.select(node).call(axis as any);
+ }}
+ />
+ ```
+
+Congrats! You've created a simple and responsive bar chart.
+
+## Complete example
+
+```ts
+import React from 'react';
+import { PanelProps } from '@grafana/data';
+import { SimpleOptions } from 'types';
+import { useTheme } from '@grafana/ui';
+import * as d3 from 'd3';
+
+interface Props extends PanelProps {}
+
+export const SimplePanel: React.FC = ({ options, data, width, height }) => {
+ const theme = useTheme();
+
+ const values = [4, 8, 15, 16, 23, 42];
+
+ const scale = d3
+ .scaleLinear()
+ .domain([0, d3.max(values) || 0.0])
+ .range([0, width]);
+
+ const axis = d3.axisBottom(scale);
+
+ const padding = 20;
+ const chartHeight = height - padding;
+ const barHeight = chartHeight / values.length;
+
+ return (
+
+ );
+};
+```
+
+## Summary
+
+In this tutorial you built a panel plugin with D3.js.
diff --git a/docs/sources/tutorials/build-a-panel-plugin/index.md b/docs/sources/tutorials/build-a-panel-plugin/index.md
new file mode 100644
index 00000000000..4f5f101d000
--- /dev/null
+++ b/docs/sources/tutorials/build-a-panel-plugin/index.md
@@ -0,0 +1,259 @@
+---
+title: Build a panel plugin
+summary: Learn how to create a custom visualization for your dashboards.
+description: Learn how to create a custom visualization for your dashboards.
+id: build-a-panel-plugin
+categories: ['plugins']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 50
+---
+
+## Introduction
+
+Panels are the building blocks of Grafana. They allow you to visualize data in different ways. While Grafana has several types of panels already built-in, you can also build your own panel, to add support for other visualizations.
+
+For more information about panels, refer to the documentation on [Panels](/docs/grafana/latest/panels/).
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Grafana >=7.0
+- NodeJS >=14
+- yarn
+ {{% /class %}}
+
+## Set up your environment
+
+{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
+
+## Create a new plugin
+
+{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
+
+## Anatomy of a plugin
+
+{{< docs/shared lookup="tutorials/plugin-anatomy.md" source="grafana" version="latest" >}}
+
+## Panel plugins
+
+Since Grafana 6.x, panels are [ReactJS components](https://reactjs.org/docs/components-and-props.html).
+
+Prior to Grafana 6.0, plugins were written in [AngularJS](https://angular.io/). Even though we still support plugins written in AngularJS, we highly recommend that you write new plugins using ReactJS.
+
+### Panel properties
+
+The [PanelProps](https://github.com/grafana/grafana/blob/747b546c260f9a448e2cb56319f796d0301f4bb9/packages/grafana-data/src/types/panel.ts#L27-L40) interface exposes runtime information about the panel, such as panel dimensions, and the current time range.
+
+You can access the panel properties through `props`, as seen in your plugin.
+
+**src/SimplePanel.tsx**
+
+```js
+const { options, data, width, height } = props;
+```
+
+### Development workflow
+
+Next, you'll learn the basic workflow of making a change to your panel, building it, and reloading Grafana to reflect the changes you made.
+
+First, you need to add your panel to a dashboard:
+
+1. Open Grafana in your browser.
+1. Create a new dashboard, and add a new panel.
+1. Select your panel from the list of visualization types.
+1. Save the dashboard.
+
+Now that you can view your panel, try making a change to the panel plugin:
+
+1. In `SimplePanel.tsx`, change the fill color of the circle.
+1. Run `yarn dev` to build the plugin.
+1. In the browser, reload Grafana with the new changes.
+
+## Add panel options
+
+Sometimes you want to offer the users of your panel an option to configure the behavior of your plugin. By configuring _panel options_ for your plugin, your panel will be able to accept user input.
+
+In the previous step, you changed the fill color of the circle in the code. Let's change the code so that the plugin user can configure the color from the panel editor.
+
+#### Add an option
+
+Panel options are defined in a _panel options object_. `SimpleOptions` is an interface that describes the options object.
+
+1. In `types.ts`, add a `CircleColor` type to hold the colors the users can choose from:
+
+ ```
+ type CircleColor = 'red' | 'green' | 'blue';
+ ```
+
+1. In the `SimpleOptions` interface, add a new option called `color`:
+
+ ```
+ color: CircleColor;
+ ```
+
+Here's the updated options definition:
+
+**src/types.ts**
+
+```ts
+type SeriesSize = 'sm' | 'md' | 'lg';
+type CircleColor = 'red' | 'green' | 'blue';
+
+// interface defining panel options type
+export interface SimpleOptions {
+ text: string;
+ showSeriesCount: boolean;
+ seriesCountSize: SeriesSize;
+ color: CircleColor;
+}
+```
+
+#### Add an option control
+
+To change the option from the panel editor, you need to bind the `color` option to an _option control_.
+
+Grafana supports a range of option controls, such as text inputs, switches, and radio groups.
+
+Let's create a radio control and bind it to the `color` option.
+
+1. In `src/module.ts`, add the control at the end of the builder:
+
+ ```ts
+ .addRadio({
+ path: 'color',
+ name: 'Circle color',
+ defaultValue: 'red',
+ settings: {
+ options: [
+ {
+ value: 'red',
+ label: 'Red',
+ },
+ {
+ value: 'green',
+ label: 'Green',
+ },
+ {
+ value: 'blue',
+ label: 'Blue',
+ },
+ ],
+ }
+ });
+ ```
+
+ The `path` is used to bind the control to an option. You can bind a control to nested option by specifying the full path within a options object, for example `colors.background`.
+
+Grafana builds an options editor for you and displays it in the panel editor sidebar in the **Display** section.
+
+#### Use the new option
+
+You're almost done. You've added a new option and a corresponding control to change the value. But the plugin isn't using the option yet. Let's change that.
+
+1. To convert option value to the colors used by the current theme, add a `switch` statement right before the `return` statement in `SimplePanel.tsx`.
+
+ **src/SimplePanel.tsx**
+
+ ```ts
+ let color: string;
+ switch (options.color) {
+ case 'red':
+ color = theme.palette.redBase;
+ break;
+ case 'green':
+ color = theme.palette.greenBase;
+ break;
+ case 'blue':
+ color = theme.palette.blue95;
+ break;
+ }
+ ```
+
+1. Configure the circle to use the color.
+
+ ```ts
+
+
+
+ ```
+
+Now, when you change the color in the panel editor, the fill color of the circle changes as well.
+
+## Create dynamic panels using data frames
+
+Most panels visualize dynamic data from a Grafana data source. In this step, you'll create one circle per series, each with a radius equal to the last value in the series.
+
+> To use data from queries in your panel, you need to set up a data source. If you don't have one available, you can use the [TestData DB](/docs/grafana/latest/features/datasources/testdata) data source while developing.
+
+The results from a data source query within your panel are available in the `data` property inside your panel component.
+
+```ts
+const { data } = props;
+```
+
+`data.series` contains the series returned from a data source query. Each series is represented as a data structure called _data frame_. A data frame resembles a table, where data is stored by columns, or _fields_, instead of rows. Every value in a field share the same data type, such as string, number, or time.
+
+Here's an example of a data frame with a time field, `Time`, and a number field, `Value`:
+
+| Time | Value |
+| ------------- | ----- |
+| 1589189388597 | 32.4 |
+| 1589189406480 | 27.2 |
+| 1589189513721 | 15.0 |
+
+Let's see how you can retrieve data from a data frame and use it in your visualization.
+
+1. Get the last value of each field of type `number`, by adding the following to `SimplePanel.tsx`, before the `return` statement:
+
+ ```ts
+ const radii = data.series
+ .map((series) => series.fields.find((field) => field.type === 'number'))
+ .map((field) => field?.values.get(field.values.length - 1));
+ ```
+
+ `radii` will contain the last values in each of the series that are returned from a data source query. You'll use these to set the radius for each circle.
+
+1. Change the `svg` element to the following:
+
+ ```ts
+
+ ```
+
+ Note how we're creating a `` element for each value in `radii`:
+
+ ```ts
+ {
+ radii.map((radius, index) => {
+ const step = width / radii.length;
+ return ;
+ });
+ }
+ ```
+
+ We use the `transform` here to distribute the circle horizontally within the panel.
+
+1. Rebuild your plugin and try it out by adding multiple queries to the panel. Refresh the dashboard.
+
+If you want to know more about data frames, check out our introduction to [Data frames](/docs/grafana/latest/developers/plugins/data-frames/).
+
+## Summary
+
+In this tutorial you learned how to create a custom visualization for your dashboards.
diff --git a/docs/sources/tutorials/build-a-streaming-data-source-plugin/index.md b/docs/sources/tutorials/build-a-streaming-data-source-plugin/index.md
new file mode 100644
index 00000000000..7978f6e3629
--- /dev/null
+++ b/docs/sources/tutorials/build-a-streaming-data-source-plugin/index.md
@@ -0,0 +1,164 @@
+---
+title: Build a streaming data source backend plugin
+summary: Create a backend for your data source plugin with streaming capabilities.
+description: Create a backend for your data source plugin with streaming capabilities.
+id: build-a-streaming-data-source-backend-plugin
+categories: ['plugins']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 75
+---
+
+## Introduction
+
+Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. In previous tutorials we have shown how to extend Grafana capabilities to query custom data sources by [building a backend datasource plugin](/tutorials/build-a-data-source-backend-plugin/). In this tutorial we take a step further and add streaming capabilities to the backend datasource plugin. Streaming allows plugins to push data to Grafana panels as soon as data appears (without periodic polling from UI side).
+
+For more information about backend plugins, refer to the documentation on [Backend plugins](/docs/grafana/latest/developers/plugins/backend/).
+
+In this tutorial, you'll:
+
+- Extend a backend plugin with streaming capabilities
+
+{{% class "prerequisite-section" %}}
+
+#### Prerequisites
+
+- Knowledge about how data sources are implemented in the frontend.
+- Knowledge about [backend datasource anatomy](/tutorials/build-a-data-source-backend-plugin/)
+- Grafana 8.0+
+- Go ([Version](https://github.com/grafana/plugin-tools/blob/main/packages/create-plugin/templates/backend/go.mod#L3))
+- [Mage](https://magefile.org/)
+- NodeJS ([Version](https://github.com/grafana/plugin-tools/blob/main/packages/create-plugin/templates/common/package.json#L66))
+- yarn
+ {{% /class %}}
+
+## Set up your environment
+
+{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
+
+## Create a new plugin
+
+To build a backend for your data source plugin, Grafana requires a binary that it can execute when it loads the plugin during start-up. In this guide, we will build a binary using the [Grafana plugin SDK for Go](/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/).
+
+The easiest way to get started is to use the Grafana [create-plugin tool](https://www.npmjs.com/package/@grafana/create-plugin). Navigate to the plugin folder that you configured in step 1 and type:
+
+```
+npx @grafana/create-plugin
+```
+
+Follow the steps and select **datasource** as your plugin type and answer **yes** when prompted to create a backend for your plugin.
+
+```bash
+cd my-plugin
+```
+
+Install frontend dependencies and build frontend parts of the plugin to _dist_ directory:
+
+```bash
+yarn install
+yarn build
+```
+
+Run the following to update [Grafana plugin SDK for Go](/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/) dependency to the latest minor version:
+
+```bash
+go get -u github.com/grafana/grafana-plugin-sdk-go
+go mod tidy
+```
+
+Build backend plugin binaries for Linux, Windows and Darwin to _dist_ directory:
+
+```bash
+mage -v
+```
+
+Now, let's verify that the plugin you've built can be used in Grafana when creating a new data source:
+
+1. Restart your Grafana instance.
+1. Open Grafana in your web browser.
+1. Navigate via the side-menu to **Configuration** -> **Data Sources**.
+1. Click **Add data source**.
+1. Find your newly created plugin and select it.
+1. Enter a name and then click **Save & Test** (ignore any errors reported for now).
+
+You now have a new data source instance of your plugin that is ready to use in a dashboard. To confirm, follow these steps:
+
+1. Navigate via the side-menu to **Create** -> **Dashboard**.
+1. Click **Add new panel**.
+1. In the query tab, select the data source you just created.
+1. A line graph is rendered with one series consisting of two data points.
+1. Save the dashboard.
+
+### Troubleshooting
+
+#### Grafana doesn't load my plugin
+
+By default, Grafana requires backend plugins to be signed. To load unsigned backend plugins, you need to
+configure Grafana to [allow unsigned plugins](/docs/grafana/latest/plugins/plugin-signature-verification/#allow-unsigned-plugins).
+For more information, refer to [Plugin signature verification](/docs/grafana/latest/plugins/plugin-signature-verification/#backend-plugins).
+
+## Anatomy of a backend plugin
+
+As you may notice till this moment we did the same steps described in [build a backend datasource plugin tutorial](/tutorials/build-a-data-source-backend-plugin/). At this point, you should be familiar with backend plugin structure and a way how data querying and health check capabilities could be implemented. Let's take the next step and discuss how datasource plugin can handle data streaming.
+
+## Add streaming capabilities
+
+What we want to achieve here is to issue a query to load initial data from a datasource plugin and then switching to data streaming mode where the plugin will push data frames to Grafana time-series panel.
+
+In short – implementing a streaming plugin means implementing a `backend.StreamHandler` interface which contains `SubscribeStream`, `RunStream`, and `PublishStream` methods.
+
+`SubscribeStream` is a method where the plugin has a chance to authorize user subscription requests to a channel. Users on the frontend side subscribe to different channels to consume real-time data.
+
+When returning a `data.Frame` with initial data we can return a special field `Channel` to let the frontend know that we are going to stream data frames after initial data load. When the frontend receives a frame with a `Channel` set it automatically issues a subscription request to that channel.
+
+Channel is a string identifier of topic to which clients can subscribe in Grafana Live. See a documentation of Grafana Live for [details about channel structure](/docs/grafana/latest/live/live-channel/).
+
+As said in docs in Grafana Live channel consists of 3 parts delimited by `/`:
+
+- Scope
+- Namespace
+- Path
+
+For datasource plugin channels Grafana uses `ds` scope. Namespace in the case of datasource channels is a datasource unique ID (UID) which is issued by Grafana at the moment of datasource creation. The path is a custom string that plugin authors free to choose themselves (just make sure it consists of allowed symbols). I.e. datasource channel looks like `ds//`.
+
+So to let the frontend know that we are going to stream data we set a `Channel` field into frame metadata inside `QueryData` implementation. In our tutorial it's a `ds//stream`. The frontend will issue a subscription request to this channel.
+
+Inside `SubscribeStream` implementation we check whether a user allowed to subscribe on a channel path. If yes – we return an OK status code to tell Grafana user can join a channel:
+
+```go
+status := backend.SubscribeStreamStatusPermissionDenied
+if req.Path == "stream" {
+ // Allow subscribing only on expected path.
+ status = backend.SubscribeStreamStatusOK
+}
+return &backend.SubscribeStreamResponse{
+ Status: status,
+}, nil
+```
+
+As soon as the first subscriber joins a channel Grafana opens a unidirectional stream to consume streaming frames from a plugin. To handle this and to push data towards clients we implement a `RunStream` method which provides a way to push JSON data into a channel. So we can push data frame like this (error handling skipped):
+
+```go
+// Send frame to stream including both frame schema and data frame parts.
+_ = sender.SendFrame(frame, data.IncludeAll)
+```
+
+Open example datasource query editor and make sure `With Streaming` toggle is on. After doing this you should see data displayed and then periodically updated by streaming frames coming periodically from `RunStream` method.
+
+The important thing to note is that Grafana opens a unidirectional stream only once per channel upon the first subscriber joined. Every other subscription request will be still authorized by `SubscribeStream` method but the new `RunStream` won't be issued. I.e. you can have many active subscribers but only one running stream. At this moment this guarantee works for a single Grafana instance, we are planning to support this for highly-available Grafana setup (many Grafana instances behind load-balancer) in future releases.
+
+The stream will be automatically closed as soon as all subscriber users left.
+
+For the tutorial use case, we only need to properly implement `SubscribeStream` and `RunStream` - we don't need to handle publications to a channel from users. But we still need to write `PublishStream` method to fully implement `backend.StreamHandler` interface. Inside `PublishStream` we just do not allow any publications from users since we are pushing data from a backend:
+
+```go
+return &backend.PublishStreamResponse{
+ Status: backend.PublishStreamStatusPermissionDenied,
+}, nil
+```
+
+## Summary
+
+In this tutorial you created a backend for your data source plugin with streaming capabilities.
diff --git a/docs/sources/tutorials/build-an-app-plugin/index.md b/docs/sources/tutorials/build-an-app-plugin/index.md
new file mode 100644
index 00000000000..0649b0fd596
--- /dev/null
+++ b/docs/sources/tutorials/build-an-app-plugin/index.md
@@ -0,0 +1,208 @@
+---
+title: Build an app plugin
+summary: Learn at how to create an app for Grafana.
+description: Learn at how to create an app for Grafana.
+id: build-an-app-plugin
+categories: ['plugins']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 50
+draft: true
+---
+
+## Introduction
+
+App plugins are Grafana plugins that can bundle data source and panel plugins within one package. They also let you create _custom pages_ within Grafana. Custom pages enable the plugin author to include things like documentation, sign-up forms, or to control other services over HTTP.
+
+Data source and panel plugins will show up like normal plugins. The app pages will be available in the main menu.
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Grafana 7.0
+- NodeJS 12.x
+- yarn
+ {{% /class %}}
+
+## Set up your environment
+
+{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
+
+## Create a new plugin
+
+{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
+
+## Anatomy of a plugin
+
+{{< docs/shared lookup="tutorials/plugin-anatomy.md" source="grafana" version="latest" >}}
+
+## App plugins
+
+App plugins let you bundle resources such as dashboards, panels, and data sources into a single plugin.
+
+Any resource you want to include needs to be added to the `includes` property in the `plugin.json` file. To add a resource to your app plugin, you need to include it to the `plugin.json`.
+
+Plugins that are included in an app plugin are available like any other plugin.
+
+Dashboards and pages can be added to the app menu by setting `addToNav` to `true`.
+
+By setting `"defaultNav": true`, users can navigate to the dashboard by clicking the app icon in the side menu.
+
+## Add a custom page
+
+App plugins let you extend the Grafana user interface through the use of _custom pages_.
+
+Any requests sent to `/a/`, e.g. `/a/myorgid-simple-app/`, are routed to the _root page_ of the app plugin. The root page is a React component that returns the content for a given route.
+
+While you're free to implement your own routing, in this tutorial you'll use a tab-based navigation page that you can use by calling `onNavChange`.
+
+Let's add a tab for managing server instances.
+
+1. In the `src/pages` directory, add a new file called `Instances.tsx`. This component contains the content for the new tab.
+
+ ```ts
+ import { AppRootProps } from '@grafana/data';
+ import React, { FC } from 'react';
+
+ export const Instances: FC = ({ query, path, meta }) => {
+ return
Hello
;
+ };
+ ```
+
+1. Register the page by adding it to the `pages` array in `src/pages/index.ts`.
+
+ **index.ts**
+
+ ```ts
+ import { Instances } from './Instances';
+ ```
+
+ ```ts
+ {
+ component: Instances,
+ icon: 'file-alt',
+ id: 'instances',
+ text: 'Instances',
+ }
+ ```
+
+1. Add the page to the app menu, by including it in `plugin.json`. This will be the main view of the app, so we'll set `defaultNav` to let users quickly get to it by clicking the app icon in the side menu.
+
+ **plugin.json**
+
+ ```json
+ "includes": [
+ {
+ "type": "page",
+ "name": "Instances",
+ "path": "/a/myorgid-simple-app?tab=instances",
+ "role": "Viewer",
+ "addToNav": true,
+ "defaultNav": true
+ }
+ ]
+ ```
+
+> **Note:** While `page` includes typically reference pages created by the app, you can set `path` to any URL, internal or external. Try setting `path` to `https://grafana.com`.
+
+## Configure the app
+
+Let's add a new configuration page where users are able to configure default zone and regions for any instances they create.
+
+1. In `module.ts`, add new configuration page using the `addConfigPage` method. `body` is the React component that renders the page content.
+
+ **module.ts**
+
+ ```ts
+ .addConfigPage({
+ title: 'Defaults',
+ icon: 'fa fa-info',
+ body: DefaultsConfigPage,
+ id: 'defaults',
+ })
+ ```
+
+## Add a dashboard
+
+#### Include a dashboard in your app
+
+1. In `src/`, create a new directory called `dashboards`.
+1. Create a file called `overview.json` in the `dashboards` directory.
+1. Copy the JSON definition for the dashboard you want to include and paste it into `overview.json`. If you don't have one available, you can find a sample dashboard at the end of this step.
+1. In `plugin.json`, add the following object to the `includes` property.
+
+ - The `name` of the dashboard needs to be the same as the `title` in the dashboard JSON model.
+ - `path` points out the file that contains the dashboard definition, relative to the `plugin.json` file.
+
+ ```json
+ "includes": [
+ {
+ "type": "dashboard",
+ "name": "System overview",
+ "path": "dashboards/overview.json",
+ "addToNav": true
+ }
+ ]
+ ```
+
+1. Save and restart Grafana to load the new changes.
+
+## Bundle a plugin
+
+An app plugin can contain panel and data source plugins that get installed along with the app plugin.
+
+In this step, you'll add a data source to your app plugin. You can add panel plugins the same way by changing `datasource` to `panel`.
+
+1. In `src/`, create a new directory called `datasources`.
+1. Create a new data source using Grafana create-plugin tool in a temporary directory.
+
+ ```bash
+ mkdir tmp
+ cd tmp
+ npx @grafana/create-plugin
+ ```
+
+1. Move the `src` directory in the data source plugin to `src/datasources`, and rename it to `my-datasource`.
+
+ ```bash
+ mv ./my-datasource/src ../src/datasources/my-datasource
+ ```
+
+Any bundled plugins are built along with the app plugin. Grafana looks for any subdirectory containing a `plugin.json` file and attempts to load a plugin in that directory.
+
+To let users know that your plugin bundles other plugins, you can optionally display it on the plugin configuration page. This is not done automatically, so you need to add it to the `plugin.json`.
+
+1. Include the data source in the `plugin.json`. The `name` property is only used for displaying in the Grafana UI.
+
+ ```json
+ "includes": [
+ {
+ "type": "datasource",
+ "name": "My data source"
+ }
+ ]
+ ```
+
+#### Include external plugins
+
+If you want to let users know that your app requires an existing plugin, you can add it as a dependency in `plugin.json`. Note that they'll still need to install it themselves.
+
+```json
+"dependencies": {
+ "plugins": [
+ {
+ "type": "panel",
+ "name": "Worldmap Panel",
+ "id": "grafana-worldmap-panel",
+ "version": "^0.3.2"
+ }
+ ]
+}
+```
+
+## Summary
+
+In this tutorial you learned how to create an app plugin.
diff --git a/docs/sources/tutorials/create-alerts-from-flux-queries/index.md b/docs/sources/tutorials/create-alerts-from-flux-queries/index.md
new file mode 100644
index 00000000000..405b4f861cd
--- /dev/null
+++ b/docs/sources/tutorials/create-alerts-from-flux-queries/index.md
@@ -0,0 +1,331 @@
+---
+title: How to create Grafana alerts with InfluxDB and the Flux query language
+summary: Create complex alerts from Flux queries in the new Grafana Alerting
+description: Create complex alerts from Flux queries in the new Grafana Alerting
+id: grafana-alerts-flux-queries
+categories: ['alerting']
+tags: ['advanced']
+status: published
+authors: ['grant_pinkos']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 70
+---
+
+# How to create Grafana alerts with InfluxDB and the Flux query language
+
+[Grafana Alerting](/docs/grafana/latest/alerting/) represents a powerful new approach to systems observability and incident response management. While the alerting platform is perhaps best known for its strong integrations with Prometheus, the system works with numerous popular data sources including InfluxDB. In this tutorial we will learn how to create Grafana alerts using InfluxDB and the newer Flux query language. We will cover five common scenarios from the most basic to the most complex. Together, these five scenarios will provide an excellent guide for almost any type of alerting query that you wish to create using Grafana and Flux.
+
+Before we dive into our alerting scenarios, it is worth considering the development of InfluxDB's two popular query languages: InfluxQL and Flux. Originally, InfluxDB used [InfluxQL](https://docs.influxdata.com/influxdb/v2.5/reference/syntax/influxql/spec/) as their query language, which uses a SQL-like syntax. But beginning with InfluxDB v1.8, the company introduced [Flux](https://docs.influxdata.com/flux/v0.x/), "an open source functional data scripting language designed for querying, analyzing, and acting on data." "Flux," its official documentation goes on to state, "unifies code for querying, processing, writing, and acting on data into a single syntax. The language is designed to be usable, readable, flexible, composable, testable, contributable, and shareable."
+
+In the following five examples we will see just how powerful and flexible the new Flux query language can be. We will also see just how well Flux pairs with Grafana Alerting.
+
+## Example 1: Create an alert when a value is above or below a set threshold
+
+Our first example uses a common real-world scenario for InfluxDB and Grafana Alerting. Popular with IoT and edge applications, InfluxDB excels at on-site, real-time observability. In this example, and in fact for many of the following examples, we will consider the hypothetical scenario where we are monitoring a number of fluid tanks in a manufacturing plant. This scenario, [based on an actual application of InfluxDB and Alerting](/go/grafanaconline/2021/plant-efficiency-grafana-cloud/), will allow us to work through Grafana's various alerting setups, progressing from the simplest to the most complex.
+
+For Example 1, let's consider the following scenario: we are monitoring one tank, `A5`, for which we are storing real-time temperature data. We need to make sure that the temperature in this tank is always greater than 30 °C and less than 60 °C.
+
+We want to write a Grafana alert that will trigger whenever the temperature in tank `A5` crosses the lower threshold of 30 °C or the upper threshold of 60 °C.
+
+To do this, we'll: 1. create a Grafana alert rule. 1. add a Flux query. 1. add expressions to the alert rule.
+
+### Create a Grafana Alert rule
+
+1. Open the Grafana alerting menu and select **Alert rules**.
+1. Click **New alert rule**.
+1. Give your alert rule a name and then select **Grafana managed alert**.
+ For InfluxDB, you will always create a [Grafana managed rule](/docs/grafana/latest/alerting/alerting-rules/create-grafana-managed-rule/#add-grafana-managed-rule).
+
+### Add an initial Flux query to the alert rule
+
+Still in the **Step 2** section of the Alert rule page, you will see three boxes: a query editor (`A`), and then two sections labelled `B` and `C`. You will use these three sections to construct your rule. Let's move through them one by one.
+
+First, we want to query the data in our imaginary InfluxDB instance to obtain a time series graph of the temperature of tank A5. For this you would choose your InfluxDB data source from the dropdown and then write a query like this:
+
+ ```
+ from(bucket: "RetroEncabulator")
+ |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+ |> filter(fn: (r) => r["_measurement"] == "TemperatureData")
+ |> filter(fn: (r) => r["Tank"] == "A5")
+ |> filter(fn: (r) => r["_field"] == "Temperature")
+ |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
+ |> yield(name: "mean")
+ ```
+
+This is a fairly typical Flux query. Let's go through it function by function. We begin using [the `from()` function](https://docs.influxdata.com/flux/v0.x/stdlib/influxdata/influxdb/from/) to choose the correct bucket where our tank data resides. Then we use [a `range()` function](https://docs.influxdata.com/flux/v0.x/stdlib/universe/range/) to filter our rows based on time constraints. Then we pass our data through three [`filter()` functions](https://docs.influxdata.com/flux/v0.x/stdlib/universe/filter/) to narrow our results. We choose a specific [`measurement` (a special keyword in InfluxDB)](https://docs.influxdata.com/influxdb/v1.8/concepts/glossary/#measurement), then our tank in question (`A5`), and then a specific [`field` (another special keyword in InfluxDB)](https://docs.influxdata.com/influxdb/v1.8/concepts/glossary/#field). After this we pass the data into [an `aggregateWindow()` function](https://docs.influxdata.com/flux/v0.x/stdlib/universe/aggregatewindow/), which downsamples our data into specific periods of time, and then finally [a `yield()` function](https://docs.influxdata.com/flux/v0.x/stdlib/universe/yield/), which specifies which final result we want: `mean`.
+
+This Flux query will yield a time-series graph like this:
+
+
+
+### Add expressions to your Grafana Alert rule
+
+With data now appearing in our rule setup, our next step is to create an [expression](/docs/grafana/v9.0/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions/#using-expressions). Move to section `B`. For this scenario, we want to create a Reduce expression that will reduce the above to a single value. In this image, you can see that we have chosen to reduce our time-series data the `Last` value from input `A`. In this case, it returns a value 53 degrees celsius for Tank A5:
+
+
+
+Finally, we need to create a math expression that Grafana will alert on. In our case we will write an expression with two conditions separated by the OR `||` operator. We want to trigger an alert any time our result in section `B` is less than 30 or more than 60. This looks like `$B < 30 || $B > 60`:
+
+
+
+Set the alert condition to `C - expression`. We can now preview our alert. Here is a preview of this alert when the state is `Normal`:
+
+
+
+And here is a preview of this alert when the state is `Alerting`:
+
+
+
+Note that the Reduce expression above is needed. Without it, when previewing the results, Grafana would display `invalid format of evaluation results for the alert definition B: looks like time series data, only reduced data can be alerted on`.
+
+💡Tip: In case your locale is still stubbornly using Fahrenheit, we can modify the above Flux query by adding (before the aggregateWindow statement) a map() function to to convert (or map) the values from °C to °F. Note that we are not creating a new field. We are simply remapping the existing value.
+
+```flux
+|> map(fn: (r) => ({r with _value: r._value * 1.8 + 32.0}))
+```
+
+### Conclusion
+
+Using these three steps you can create a Flux-based Grafana Alert that will trigger on either of two thresholds from a single data source. But what if you need to trigger an alert based on **multiple conditions and from multiple time-series**? In example two we will cover this very scenario.
+
+## Example 2: how to create a Grafana alert from two queries and two conditions
+
+Let's mix things up a bit for example two and leave our imaginary manufacturing plant. Imagine you're an assistant to the great Dr. Emmett Brown from Back to the Future, and Doc has tasked you with the following challenge: "I want an alert sent to me every time both conditions for time travel are met: when the velocity of a vehicle reaches 88 miles per hour and an object generates 1.21 jigowatts of electricity."
+
+Let's assume we are tracking this data in InfluxDB and Grafana. Let's also assume that each of the above data sources comes from different buckets. How do we alert on this? How do we use Grafana and Flux to alert on two distinct conditions originating from two distinct data sources?
+
+### Add two Flux queries to your Grafana Alert rule
+
+Like we did in example 1, let's first mock up our queries. Our query for our vehicle data is very similar to our last query. We use a `from()`, `range()`, and a sequence of `filter()` functions. We then use `AggregateWindow()` and `yield()` to narrow our data even more. In this case, the result is a time series tracking the velocity of our 1983 DeLorean:
+
+```flux
+from(bucket: "vehicles")
+|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+|> filter(fn: (r) => r["_measurement"] == "VehicleData")
+|> filter(fn: (r) => r["VehicleType"] == "DeLorean")
+|> filter(fn: (r) => r["VehicleYear"] == "1983")
+|> filter(fn: (r) => r["_field"] == "velocity")
+|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
+|> yield(name: "mean")
+```
+
+Our second query will trigger an alert whenever our electricity resource (the lightning strike on the Hill Valley clocktower) reaches the needed 1.21 jigowatts. A query like this would look very similar to our vehicle velocity query:
+
+```flux
+from(bucket: "HillValley")
+|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+|> filter(fn: (r) => r["_measurement"] == "ElectricityData")
+|> filter(fn: (r) => r["Location"] == "clocktower")
+|> filter(fn: (r) => r["Source"] == "lightning")
+|> filter(fn: (r) => r["_field"] == "power")
+|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
+|> yield(name: "mean")
+```
+
+We are now ready to modify this data using expressions.
+
+### Add expressions to your Grafana Alert rule
+
+1. Let's now use the same steps to reduce each query to the last (most recent) value. Reducing Query `A` to a single value might look like this:
+
+ 
+
+1. And here we are reducing query `B`:
+
+ 
+
+1. Now, in section `C` we need to create a math expression to be alerted on. In this case we will use the AND `&&` operator to specify that two conditions must be met: the value of `C` (the reduced value from query `A`) must be greater than 88.0 while the value of `D` (the reduced value from query `B`) must be greater than 1.21. We write this as `$C > 88.0 && $D > 1.21`
+
+ 
+
+And here is a preview of our alerts:
+
+
+
+💡Tip: If your data in InfluxDB happens to have an unnecessarily large number of digits to the right of the decimal (such as 1.2104705741732575 shown above), and you want your Grafana alerts to be more legible, try using {{ printf "%.2f" $values.D.Value }}. For example, in the annotation Summary, we could write the following:
+
+```
+{{ $values.D.Labels.Source }} at the {{ $values.D.Labels.Location }} has generated {{ printf "%.2f" $values.D.Value }} jigowatts.`
+```
+
+This will display as follows:
+)
+
+You can reference our documentation on [alert message templating](/docs/grafana/latest/alerting/contact-points/message-templating/) to learn more about this powerful feature.
+
+### Conclusion
+
+In this example we showed how to create a Flux-based alert that uses two distinct conditions from two distinct queries that use data from two distinct data sources. For example three we will switch gears and tackle another popular alerting scenario: how to create an alert based on an aggregated (per day) value.
+
+## Example 3: how to create a Grafana Alert based on an aggregated (per-day) value
+
+One of the most common requests in [Grafana's community forum](https://community.grafana.com) involves graphing daily electrical consumption and production. This sort of data is very often stored in InfluxDB. In this example we will see how to aggregate time series data into a per-day value and then alert on it.
+
+Let’s assume our electricity meter sends a reading to InfluxDB once per hour and contains the total kWh used for that hour. We want to write a query that will aggregate these per-hour values into a per-day value, then create an alert that triggers when the power consumption (kWh) exceeds 5,000 kWh per day.
+
+### Add an initial Flux query to your Grafana Alert rule
+
+1. Let's begin by examining a typical query and the resulting time graph for our hourly data across a 7-day period. A query like this is shown below:
+
+ ```flux
+ from(bucket: "RetroEncabulator")
+ |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+ |> filter(fn: (r) => r["_measurement"] == "ElectricityData")
+ |> filter(fn: (r) => r["Location"] == "PlantD5")
+ |> filter(fn: (r) => r["_field"] == "power_consumed")
+ |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
+ |> yield(name: "power")
+ ```
+
+ We can see the same pattern of Flux functions here that we say in examples 1 and 2. A query like this would produce a graph similar to the following:
+
+ 
+
+1. Now let's adjust our query to calculate daily usage. With many datasources, this can be a rather complex operation. But with Flux, by simply changing the aggregateWindow function parameters we can calculate the daily usage over the same 7-day period:
+
+ ```flux
+ from(bucket: "RetroEncabulator")
+ |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+ |> filter(fn: (r) => r["_measurement"] == "ElectricityData")
+ |> filter(fn: (r) => r["Location"] == "PlantD5")
+ |> filter(fn: (r) => r["_field"] == "power_consumed")
+ |> aggregateWindow(every: 1d, fn: sum)
+ |> yield(name: "power")
+ ```
+
+ Note how we've adjusted our `aggregateWindow()` function to `aggregateWindow(every: 1d, fn: sum)`. This results in a graph like so:
+
+ 
+
+1. Add expressions to your Grafana Alert rule.
+
+ Now that we have our per-day query correct, we can continue using the same pattern as before, adding expressions to reduce and perform math on our results.
+
+ As before, let's reduce our query to a single value:
+
+ 
+
+ Now create a math expression to be alerted on and set the evaluation behavior. In this case we want to write `$B > 5000`:
+
+ 
+
+ And now we are alerting on our daily electricity consumption whenever we exceed 5000 kWh. Here is preview of our alert:
+
+ 
+
+### Conclusion
+
+Plotting and aggregating electrical consumption is a common use case for combining InfluxDB and Grafana. Using Flux, we saw just how easy it can be to group our data by day and then alert on that daily value. In our next two examples we will examine the more complex form of Grafana Alert: multidimensional alerts.
+
+## Example 4: create a dynamic (multidimensional) Grafana Alert using Flux
+
+Let’s return to our fluid tanks from example 1, but this time let’s assume we have 5 tanks (A5, B4, C3, D2, and E1). We are now tracking the temperature in five tanks: A5, B4, C3, D2, and E1.
+
+We want to create one multidimensional alert that will notify us whenever the temperature in any tank is less than 30 °C or greater than 60 °C.
+
+### Add an initial Flux query to your Grafana Alert rule
+
+We begin, as always, by writing our initial query. This is very similar to our query in example 1, but note how our third `filter()` function captures the data from all five tanks and not just `A5`:
+
+```flux
+from(bucket: "HyperEncabulator")
+|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+|> filter(fn: (r) => r["_measurement"] == "TemperatureData")
+|> filter(fn: (r) => r["MeasType"] == "actual")
+|> filter(fn: (r) => r["Tank"] == "A5" or r["Tank"] == "B4" or r["Tank"] == "C3" or r["Tank"] == "D2" or r["Tank"] == "E1")
+|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
+|> yield(name: "mean")
+```
+
+💡Tip: If the tanks were shut down every night from 23:00 to 07:00, they would possibly fall below the 30 °C threshold. If one did not want to receive alerts during those hours, one can use the Flux function hourSelection() which filters rows by time values in a specified hour range.
+
+```flux
+|> hourSelection(start: 7, stop: 23)`
+```
+
+A query like the one above will produce a time series graph like this:
+
+
+
+### Add expressions to your Grafana Alert rule
+
+1. We create a Reduce expression that will reduce the time series for each tank to a single value. This gives us five distinct temperatures:
+
+ )
+
+1. Create a math expression to be alerted on. This is the exact same expression from example 1, `$B < 30 || $B > 60`:
+
+ 
+
+As we can see three tanks are within the acceptable thresholds while two tanks have crossed the upper boundary. This would trigger an alert for tanks `D2` and `E1`.
+
+### Conclusion
+
+With multidimensional alerts we can avoid repeating ourselves. But what if the scenario were even more complex? In the next and final example, we will examine how to use multidimensional alerts to create the most dynamic alerts possible.
+
+## Example 5: how to create a dynamic (multidimensional) Grafana Alert using multiple queries and multiple thresholds with Flux
+
+For this final example let's continue with our five fluid tanks and their five datasets.Let’s assume again that each tank has a temperature controller with a setpoint value that is stored in InfluxDB. Let’s mix things up and assume that each tank has a _different_ setpoint, where we always need to be within 3 degrees of the setpoint.
+
+We want to create one multidimensional alert that will cover each unique scenario for each tank, triggering an alert whenever any tank's temperature moves beyond its unique allowable range.
+
+To better visualize this challenge, here is a table representing our five tanks, their temperature setpoints, and their allowable range:
+
+| Tank | Setpoint | Allowable Range (±3) |
+| ---- | -------- | -------------------- |
+| A5 | 45 | 42 to 48 |
+| B4 | 55 | 52 to 58 |
+| C3 | 60 | 57 to 63 |
+| D2 | 72 | 69 to 75 |
+| E1 | 80 | 77 to 83 |
+
+With Grafana Alerting, we can create a single multidimensional rule to cover all 5 tanks, and we can use Flux to compare the setpoint and actual value for each tank. In other words, one multidimensional alert can monitor 5 separate tanks, each with different setpoints and actual values, but all with one common "allowable threshold" (i.e. a temperature difference of ±3 degrees).
+
+### Add an initial Flux query to your Grafana Alert rule
+
+Let's begin with our data query. It is similar to our past queries, only now more complex. We must add extra functions to get our data into the proper format, including a `pivot()`, `map()`, `rename()`, `keep()`, and `drop()` function:
+
+```flux
+from(bucket: "HyperEncabulator")
+ |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
+ |> filter(fn: (r) => r["_measurement"] == "TemperatureData")
+ |> filter(fn: (r) => r["MeasType"] == "actual" or r["MeasType"] == "setpoint")
+ |> filter(fn: (r) => r["Tank"] == "A5" or r["Tank"] == "B4" or r["Tank"] == "C3" or r["Tank"] == "D2" or r["Tank"] == "E1")
+ |> filter(fn: (r) => r["_field"] == "Temperature")
+ |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
+ |> pivot(rowKey:["_time"], columnKey: ["MeasType"], valueColumn: "_value")
+ |> map(fn: (r) => ({ r with _value: (r.setpoint - r.actual)}))
+ |> rename(columns: {_value: "difference"})
+ |> keep(columns: ["_time", "difference", "Tank"])
+ |> drop(columns: ["actual", "setpoint"])
+ |> yield(name: "mean")
+```
+
+Note in the above that we are calculating the difference between the actual and the setpoint. The way Grafana parses the result from InfluxDB is that if a \_value column is found, it is assumed to be a time-series. The quick workaround is to add the following `rename()` function:
+
+```flux
+ |> rename(columns: {_value: "something"})
+```
+
+The above query results in this time series:
+
+
+
+### Add expressions to your Grafana Alert rule
+
+1. Again, we create a Reduce expression for the above query to reduce each of the above to a single value. This value represents the temperature differential between each tank's setpoint and its actual real-time temperature:
+
+ 
+
+1. Now we create a math expression to be alerted on. This time we will create a condition that checks if the absolute value of our reduce calculation is greater than 3, `abs($(B))>3.0`:
+
+ 
+
+We can now see that two tanks, `D2` and `E1`, are evaluating to true. When we preview the alert we can see that those two tanks will trigger a notification and change their state from `Normal` to `Alerting`:
+
+
+
+
+### Conclusion
+
+Flux queries and Grafana Unified Alerting are a powerful combination to identify practically any alertable conditions in your dataset, or across your entire system. For more information on Grafana Alerting, [visit the documentation here](/docs/grafana/latest/alerting/). For more information on the Flux query language, [you can visit that documentation as well](https://docs.influxdata.com/flux/v0.x/).
diff --git a/docs/sources/tutorials/create-users-and-teams/index.md b/docs/sources/tutorials/create-users-and-teams/index.md
new file mode 100644
index 00000000000..23b02d1d663
--- /dev/null
+++ b/docs/sources/tutorials/create-users-and-teams/index.md
@@ -0,0 +1,236 @@
+---
+title: Create users and teams
+summary: Learn how to set up teams and users.
+description: Learn how to set up teams and users.
+id: create-users-and-teams
+categories: ['administration']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 20
+---
+
+## Introduction
+
+This tutorial is for admins or anyone that wants to learn how to manage
+users in Grafana. You'll add multiple local users, organize them into teams,
+and make sure they're only able to access the resources they need.
+
+### Scenario
+
+_Graphona_, a fictional telemarketing company, has asked you to configure Grafana
+for their teams.
+
+In this scenario, you'll:
+
+- Create users and organize them into teams.
+- Manage resource access for each user and team through roles and folders.
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Grafana 7.0 or newer, this tutorial was tested with Grafana 8.5.
+- A user with the Admin or Server Admin role.
+ {{% /class %}}
+
+## Add users
+
+In Grafana, all users are granted an _organization role_ that determines what
+resources they can access.
+
+There are three types of organization roles in Grafana. The **Grafana Admin** is
+a global role, the default `admin` user has this role.
+
+- **Grafana Admin -** Manage organizations, users, and view server-wide settings.
+- **Organization Administrator -** Manage data sources, teams, and users within an organization.
+- **Editor -** Create and edit dashboards.
+- **Viewer -** View dashboards.
+
+> **Note**: You can also configure Grafana to allow [anonymous access](/docs/grafana/latest/auth/overview/#anonymous-authentication), to make dashboards available even to those who don't have a Grafana user account. That's how Grafana Labs made https://play.grafana.org publicly available.
+
+### Exercise
+
+Graphona has asked you to add a group of early adopters that work in the Marketing and Engineering teams. They'll need to be able to edit their own team's dashboards, but want to have view access to dashboards that belong to the other team.
+
+| Name | Email | Username |
+| ----------------- | ----------------------------- | ----------------- |
+| Almaz Russom | almaz.russom@example.com | almaz.russom |
+| Brenda Tilman | brenda.tilman@example.com | brenda.tilman |
+| Mada Rawdha Tahan | mada.rawdha.tahan@example.com | mada.rawdha.tahan |
+| Yuan Yang | yuan.yang@example.com | yuan.yang |
+
+#### Add users
+
+Repeat the following steps for each of the employees in the table above to create the new user accounts:
+
+1. Log in as a user that has the **Server Admin** role.
+1. On the sidebar, click the **Server Admin** (shield) icon.
+1. Choose **Users** from the menu drop-down, then click **New User**.
+1. Enter the **Name**, **Email**, **Username**, and **Password** from the table above.
+1. Click the **Create User** button to create the account.
+
+When you create a user they are granted the Viewer role by default, which means that they won't be able to make any changes to any of the resources in Grafana. That's ok for now, you'll grant more user permissions by adding users to _teams_ in the next step.
+
+## Assign users to teams
+
+Teams let you grant permissions to a group of users, instead of granting permissions to individual users one at a time.
+
+Teams are useful when onboarding new colleagues. When you add a user to a team, they get access to all resources assigned to that team.
+
+### Exercise
+
+In this step, you'll create two teams and assign users to them.
+
+| Username | Team |
+| ----------------- | ----------- |
+| brenda.tilman | Marketing |
+| mada.rawdha.tahan | Marketing |
+| almaz.russom | Engineering |
+| yuan.yang | Engineering |
+
+#### Create a team
+
+Create the _Marketing_ and _Engineering_ teams.
+
+1. In the sidebar, hover your mouse over the **Configuration** (gear) icon and
+ then click **Teams**.
+1. Click **New team**.
+1. In **Name**, enter the name of the team: either _Marketing_ or _Engineering_.
+ You do not need to enter an email.
+1. Click **Create**.
+1. Click on the **Teams** link at the top of the page to return to teams page and create the second team.
+
+#### Add a user to a team
+
+Repeat these steps for each user to assign them to their team. Refer to the table above for team assignments.
+
+1. Click the team name _Marketing_ or _Engineering_ to add members to that team.
+1. Click **Add member**.
+1. In the **Add team member** box, click the drop-down arrow to choose the user you want to add to the team .
+1. Click **Add to team**.
+
+When you're done, you'll have two teams with two users assigned to each.
+
+## Manage resource access with folders
+
+It's a good practice to use folders to organize collections of related dashboards. You can assign permissions at the folder level to individual users or teams.
+
+### Exercise
+
+The Marketing team is going to use Grafana for analytics, while the Engineering team wants to monitor the application they're building.
+
+You'll create two folders, _Analytics_ and _Application_, where each team can add their own dashboards. The teams still want to be able to view each other's dashboards.
+
+| Folder | Team | Permissions |
+| ----------- | ----------- | ----------- |
+| Analytics | Marketing | Edit |
+| | Engineering | View |
+| Application | Marketing | View |
+| | Engineering | Edit |
+
+Repeat the following steps for each folder. You'll move through all three steps for each folder before moving on to the next one.
+
+#### Add a folder for each team
+
+1. In the sidebar, hover your cursor over the **Dashboards** (four squares) icon and then click **Browse**.
+1. To create a folder, click **New Folder**.
+1. In **Name**, enter the folder name.
+1. Click **Create**.
+1. Stay in the folder view and move on to the next sections to edit permissions for this folder.
+
+#### Remove the viewer role from folder permissions
+
+By default, when you create a folder, all users with the Viewer role are granted permission to view the folder.
+
+In this example, Graphona wants to explicitly grant teams access to folders. To support this, you need to remove the Viewer role from the list of permissions:
+
+1. Go to the **Permissions** tab.
+1. Remove the Viewer role from the list, by clicking the red button on the right.
+1. Stay in the permissions tab and move on to the next section to grant folder permissions for each team.
+
+#### Grant folder permissions to a team:
+
+1. Click **Add Permission**.
+1. In the **Add Permission For** dialog, make sure "Team" is selected in the first box.
+1. In the second box, select the team to grant access to.
+1. In the third box, select the access you want to grant.
+1. Click **Save**.
+1. Repeat for the other team.
+1. Click the **Dashboards** link at the top of the page to return to the dashboard list.
+
+When you're finished, you'll have two empty folders, the contents of which can only be viewed by members of the Marketing or Engineering teams. Only Marketing team members can edit the contents of the Analytics folder, only Engineering team members can edit the contents of the Application folder.
+
+## Define granular permissions
+
+By using folders and teams, you avoid having to manage permissions for individual users.
+
+However, there are times when you need to configure permissions on a more granular level. For these cases, Grafana allows you to override permissions for specific dashboards.
+
+### Exercise
+
+Graphona has hired a consultant to assist the Marketing team. The consultant should only be able to access the SEO dashboard in the Analytics folder.
+
+| Name | Email | Username |
+| ---------- | -------------------------------- | ---------- |
+| Luc Masson | luc.masson@exampleconsulting.com | luc.masson |
+
+#### Add a new user
+
+1. In the sidebar, click the **Server Admin** (shield) icon.
+1. In the Users tab, click **New user**.
+1. In **Name**, enter the name of the user.
+1. In **E-mail**, enter the email of the user.
+1. In **Username**, enter the username that the user will use to log in.
+1. In **Password**, enter a password. The user can change their password once they log in.
+1. Click **Create user** to create the user account.
+
+#### Create a dashboard
+
+1. In the sidebar, click the **Create** (plus) icon to create a new dashboard.
+1. In the top right corner, click the cog icon to go to **Dashboard settings**.
+1. In **Name**, enter **SEO**.
+1. Click **Save Dashboard**.
+1. In the **Save dashboard as...** pop-up, choose the **Analytics** folder from the drop-down and click **Save**.
+
+#### Grant a user permission to view dashboard
+
+1. In the top right corner of your dashboard, click the cog icon to go to **Dashboard settings**.
+1. Go to the **Permissions** tab, and click **Add Permission**.
+1. In the **Add Permission For** dialog, select **User** in the first box.
+1. In the second box, select the user to grant access to: Luc Masson.
+1. In the third box, select **View**.
+1. Click **Save**.
+1. Click **Save dashboard**.
+1. Add a note about giving Luc Masson Viewer permission for the dashboard and then click **Save**.
+
+You've created a new user and given them unique permissions to view a single dashboard within a folder.
+
+#### Check your work
+
+You can repeat these steps to log in as the other users you've created see the differences in the viewer and editor roles.
+
+For this example, you can log in as the user `luc.masson` to see that they can only access the SEO dashboard.
+
+1. Click the profile (avatar) button in the bottom left corner, choose **Sign out**.
+1. Enter `luc.masson` as the username.
+1. Enter the password you created for Luc.
+1. Click **Log in**.
+1. In the sidebar, hover your cursor over the **Dashboards** (four squares) icon and then click **Browse**.
+1. You'll notice that you won't see the **Analytics** folder in the folder view because we did not give Luc folder permission.
+1. Click on the list icon (3 lines) to see the dashboard list.
+1. Click on the **SEO dashboard**, there shouldn't be any editing permissions since we assigned Luc the viewer role.
+
+## Summary
+
+In this tutorial, you've configured Grafana for an organization:
+
+- You added users to your organization.
+- You created teams to manage permissions for groups of users.
+- You configured permissions for folders and dashboard.
+
+### Learn more
+
+- [Organization Roles](/docs/grafana/next/administration/manage-users-and-permissions/about-users-and-permissions/#organization-roles)
+- [Permissions Overview](/docs/grafana/latest/administration/manage-users-and-permissions/about-users-and-permissions/#about-users-and-permissions)
diff --git a/docs/sources/tutorials/grafana-fundamentals/index.md b/docs/sources/tutorials/grafana-fundamentals/index.md
new file mode 100644
index 00000000000..5374ac91ae0
--- /dev/null
+++ b/docs/sources/tutorials/grafana-fundamentals/index.md
@@ -0,0 +1,354 @@
+---
+title: Grafana fundamentals
+summary: Get familiar with Grafana
+description: Get familiar with Grafana
+id: grafana-fundamentals
+categories: ['fundamentals']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 10
+---
+
+## Introduction
+
+In this tutorial, you'll learn how to use Grafana to set up a monitoring solution for your application.
+
+In this tutorial, you'll:
+
+- Explore metrics and logs
+- Build dashboards
+- Annotate dashboards
+- Set up alerts
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- [Docker](https://docs.docker.com/install/)
+- [Docker Compose](https://docs.docker.com/compose/) (included in Docker for Desktop for macOS and Windows)
+- [Git](https://git-scm.com/)
+ {{% /class %}}
+
+## Set up the sample application
+
+This tutorial uses a sample application to demonstrate some of the features in Grafana. To complete the exercises in this tutorial, you need to download the files to your local machine.
+
+In this step, you'll set up the sample application, as well as supporting services, such as [Prometheus](https://prometheus.io/) and [Loki](/oss/loki/).
+
+1. Clone the [github.com/grafana/tutorial-environment](https://github.com/grafana/tutorial-environment) repository.
+
+ ```
+ git clone https://github.com/grafana/tutorial-environment.git
+ ```
+
+1. Change to the directory where you cloned this repository:
+
+ ```
+ cd tutorial-environment
+ ```
+
+1. Make sure Docker is running:
+
+ ```
+ docker ps
+ ```
+
+ No errors means it is running. If you get an error, then start Docker and then run the command again.
+
+1. Start the sample application:
+
+ ```
+ docker-compose up -d
+ ```
+
+ The first time you run `docker-compose up -d`, Docker downloads all the necessary resources for the tutorial. This might take a few minutes, depending on your internet connection.
+
+ > **Note:** If you already have Grafana, Loki, or Prometheus running on your system, then you might see errors because the Docker image is trying to use ports that your local installations are already using. Stop the services, then run the command again.
+
+1. Ensure all services are up-and-running:
+
+ ```
+ docker-compose ps
+ ```
+
+ In the **State** column, it should say `Up` for all services.
+
+1. Browse to the sample application on [localhost:8081](http://localhost:8081).
+
+### Grafana News
+
+The sample application, Grafana News, lets you post links and vote for the ones you like.
+
+To add a link:
+
+1. In **Title**, enter **Example**.
+1. In **URL**, enter **https://example.com**.
+1. Click **Submit** to add the link.
+
+ The link appears in the list under the Grafana News heading.
+
+To vote for a link, click the triangle icon next to the name of the link.
+
+## Log in to Grafana
+
+Grafana is an open-source platform for monitoring and observability that lets you visualize and explore the state of your systems.
+
+1. Open a new tab.
+1. Browse to [localhost:3000](http://localhost:3000).
+1. In **email or username**, enter **admin**.
+1. In **password**, enter **admin**.
+1. Click **Log In**.
+
+ The first time you log in, you're asked to change your password:
+
+1. In **New password**, enter your new password.
+1. In **Confirm new password**, enter the same password.
+1. Click **Save**.
+
+The first thing you see is the Home dashboard, which helps you get started.
+
+To the far left you can see the _sidebar_, a set of quick access icons for navigating Grafana.
+
+## Add a metrics data source
+
+The sample application exposes metrics which are stored in [Prometheus](https://prometheus.io/), a popular time series database (TSDB).
+
+To be able to visualize the metrics from Prometheus, you first need to add it as a data source in Grafana.
+
+1. In the sidebar, hover your cursor over the **Configuration** (gear) icon, and then click **Data sources**.
+1. Click **Add data source**.
+1. In the list of data sources, click **Prometheus**.
+1. In the URL box, enter **http\://prometheus:9090**.
+1. Click **Save & test**.
+
+ Prometheus is now available as a data source in Grafana.
+
+## Explore your metrics
+
+Grafana Explore is a workflow for troubleshooting and data exploration. In this step, you'll be using Explore to create ad-hoc queries to understand the metrics exposed by the sample application.
+
+> Ad-hoc queries are queries that are made interactively, with the purpose of exploring data. An ad-hoc query is commonly followed by another, more specific query.
+
+1. In the sidebar, click the **Explore** (compass) icon.
+1. In the **Query editor**, where it says _Enter a PromQL query…_, enter `tns_request_duration_seconds_count` and then press Shift + Enter.
+ A graph appears.
+1. In the top right corner, click the dropdown arrow on the **Run Query** button, and then select **5s**. Grafana runs your query and updates the graph every 5 seconds.
+
+ You just made your first _PromQL_ query! [PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/) is a powerful query language that lets you select and aggregate time series data stored in Prometheus.
+
+ `tns_request_duration_seconds_count` is a _counter_, a type of metric whose value only ever increases. Rather than visualizing the actual value, you can use counters to calculate the _rate of change_, i.e. how fast the value increases.
+
+1. Add the [`rate`](https://prometheus.io/docs/prometheus/latest/querying/functions/#rate) function to your query to visualize the rate of requests per second. Enter the following in the **Query editor** and then press Shift + Enter.
+
+ ```
+ rate(tns_request_duration_seconds_count[5m])
+ ```
+
+ Immediately below the graph there's an area where each time series is listed with a colored icon next to it. This area is called the _legend_.
+
+ PromQL lets you group the time series by their labels, using the [`sum`](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators) aggregation operator.
+
+1. Add the `sum` aggregation operator to your query to group time series by route:
+
+ ```
+ sum(rate(tns_request_duration_seconds_count[5m])) by(route)
+ ```
+
+1. Go back to the [sample application](http://localhost:8081) and generate some traffic by adding new links, voting, or just refresh the browser.
+
+1. In the upper-right corner, click the _time picker_, and select **Last 5 minutes**. By zooming in on the last few minutes, it's easier to see when you receive new data.
+
+Depending on your use case, you might want to group on other labels. Try grouping by other labels, such as `status_code`, by changing the `by(route)` part of the query.
+
+## Add a logging data source
+
+Grafana supports log data sources, like [Loki](/oss/loki/). Just like for metrics, you first need to add your data source to Grafana.
+
+1. In the sidebar, hover your cursor over the **Configuration** (gear) icon, and then click **Data Sources**.
+1. Click **Add data source**.
+1. In the list of data sources, click **Loki**.
+1. In the URL box, enter [http://loki:3100](http://loki:3100).
+1. Click **Save & Test** to save your changes.
+
+Loki is now available as a data source in Grafana.
+
+## Explore your logs
+
+Grafana Explore not only lets you make ad-hoc queries for metrics, but lets you explore your logs as well.
+
+1. In the sidebar, click the **Explore** (compass) icon.
+1. In the data source list at the top, select the **Loki** data source.
+1. In the **Query editor**, enter:
+
+ ```
+ {filename="/var/log/tns-app.log"}
+ ```
+
+1. Grafana displays all logs within the log file of the sample application. The height of each bar in the graph encodes the number of logs that were generated at that time.
+
+1. Click and drag across the bars in the graph to filter logs based on time.
+
+Not only does Loki let you filter logs based on labels, but on specific occurrences.
+
+Let's generate an error, and analyze it with Explore.
+
+1. In the [sample application](http://localhost:8081), post a new link without a URL to generate an error in your browser that says `empty url`.
+1. Go back to Grafana and enter the following query to filter log lines based on a substring:
+
+ ```
+ {filename="/var/log/tns-app.log"} |= "error"
+ ```
+
+1. Click on the log line that says `level=error msg="empty url"` to see more information about the error.
+
+ > **Note:** If you're in Live mode, clicking logs will not show more information about the error. Instead, stop and exit the live stream, then click the log line there.
+
+Logs are helpful for understanding what went wrong. Later in this tutorial, you'll see how you can correlate logs with metrics from Prometheus to better understand the context of the error.
+
+## Build a dashboard
+
+A _dashboard_ gives you an at-a-glance view of your data and lets you track metrics through different visualizations.
+
+Dashboards consist of _panels_, each representing a part of the story you want your dashboard to tell.
+
+Every panel consists of a _query_ and a _visualization_. The query defines _what_ data you want to display, whereas the visualization defines _how_ the data is displayed.
+
+1. In the sidebar, hover your cursor over the **Create** (plus sign) icon and then click **Dashboard**.
+1. Click **Add a new panel**.
+1. In the **Query editor** below the graph, enter the query from earlier and then press Shift + Enter:
+
+ ```
+ sum(rate(tns_request_duration_seconds_count[5m])) by(route)
+ ```
+
+1. In the **Legend** field, enter **{{route}}** to rename the time series in the legend. The graph legend updates when you click outside the field.
+1. In the Panel editor on the right, under **Settings**, change the panel title to "Traffic".
+1. Click **Apply** in the top-right corner to save the panel and go back to the dashboard view.
+1. Click the **Save dashboard** (disk) icon at the top of the dashboard to save your dashboard.
+1. Enter a name in the **Dashboard name** field and then click **Save**.
+
+## Annotate events
+
+When things go bad, it often helps if you understand the context in which the failure occurred. Time of last deploy, system changes, or database migration can offer insight into what might have caused an outage. Annotations allow you to represent such events directly on your graphs.
+
+In the next part of the tutorial, we will simulate some common use cases that someone would add annotations for.
+
+1. To manually add an annotation, click anywhere in your graph, then click **Add annotation**.
+1. In **Description**, enter **Migrated user database**.
+1. Click **Save**.
+
+ Grafana adds your annotation to the graph. Hover your mouse over the base of the annotation to read the text.
+
+Grafana also lets you annotate a time interval, with _region annotations_.
+
+Add a region annotation:
+
+1. Press Ctrl (or Cmd on macOS), then click and drag across the graph to select an area.
+1. In **Description**, enter **Performed load tests**.
+1. In **Tags**, enter **testing**.
+
+Manually annotating your dashboard is fine for those single events. For regularly occurring events, such as deploying a new release, Grafana supports querying annotations from one of your data sources. Let's create an annotation using the Loki data source we added earlier.
+
+1. At the top of the dashboard, click the **Dashboard settings** (gear) icon.
+1. Go to **Annotations** and click **Add annotation query**.
+1. In **Name**, enter **Errors**.
+1. In **Data source**, select **Loki**.
+1. In **Query**, enter the following query:
+
+ ```
+ {filename="/var/log/tns-app.log"} |= "error"
+ ```
+
+
+
+1. Click **Add**. Grafana displays the Annotations list, with your new annotation.
+1. Click the **Go back** arrow to return to your dashboard.
+1. At the top of your dashboard, there is now a toggle to display the results of the newly created annotation query. Press it so that it's enabled.
+
+The log lines returned by your query are now displayed as annotations in the graph.
+
+Being able to combine data from multiple data sources in one graph allows you to correlate information from both Prometheus and Loki.
+
+Annotations also work very well alongside alerts. In the next and final section, we will set up an alert for our app `grafana.news` and then we will trigger it. This will provide a quick intro to our new Alerting platform.
+
+## Create a Grafana Managed Alert
+
+Alerts allow you to identify problems in your system moments after they occur. By quickly identifying unintended changes in your system, you can minimize disruptions to your services.
+
+Grafana's new alerting platform debuted with Grafana 8. A year later, with Grafana 9, it became the default alerting method. In this step we will create a Grafana Managed Alert. Then we will trigger our new alert and send a test message to a dummy endpoint.
+
+The most basic alert consists of two parts:
+
+1. A _Contact Point_ - A Contact point defines how Grafana delivers an alert. When the conditions of an _alert rule_ are met, Grafana notifies the contact points, or channels, configured for that alert. Some popular channels include email, webhooks, Slack notifications, and PagerDuty notifications.
+1. An _Alert rule_ - An Alert rule defines one or more _conditions_ that Grafana regularly evaluates. When these evaluations meet the rule's criteria, the alert is triggered.
+
+To begin, let's set up a webhook Contact Point. Once we have a usable endpoint, we'll write an alert rule and trigger a notification.
+
+### Create a Contact Point for Grafana Managed Alerts
+
+In this step, we'll set up a new Contact Point. This contact point will use the _webhooks_ channel. In order to make this work, we also need an endpoint for our webhook channel to receive the alert. We will use [requestbin.com](https://requestbin.com) to quickly set up that test endpoint. This way we can make sure that our alert is actually sending a notification somewhere.
+
+1. Browse to [requestbin.com](https://requestbin.com).
+1. Under the **Create Request Bin** button, click the **public bin** link.
+
+Your request bin is now waiting for the first request.
+
+1. Copy the endpoint URL.
+
+Next, let's configure a Contact Point in Grafana's Alerting UI to send notifications to our Request Bin.
+
+1. Return to Grafana. In Grafana's sidebar, hover your cursor over the **Alerting** (bell) icon and then click **Contact points**.
+1. Click **+ New contact point**.
+1. In **Name**, write **RequestBin**.
+1. In **Contact point type**, choose **Webhook**.
+1. In **Url**, paste the endpoint to your request bin.
+1. Click **Test** to send a test alert to your request bin.
+1. Navigate back to the request bin you created earlier. On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent.
+1. Return to Grafana and click **Save contact point**.
+
+We have now created a dummy webhook endpoint and created a new Alerting Contact Point in Grafana. Now we can create an alert rule and link it to this new channel.
+
+### Add an Alert Rule to Grafana
+
+Now that Grafana knows how to notify us, it's time to set up an alert rule:
+
+1. In Grafana's sidebar, hover the cursor over the **Alerting** (bell) icon and then click **Alert rules**.
+1. Click **+ New alert rule**.
+1. For **Section 1**, name the rule `fundamentals-test`, and set **Rule type** to **Grafana Managed Alert**. For **Folder** type `fundamentals` and in the box that appears, press **Create: fundamentals**.
+1. For **Section 2**, find the **query A** box. Choose your Prometheus datasource and enter the same query that we used in our earlier panel: `sum(rate(tns_request_duration_seconds_count[5m])) by(route)`. Press **Run queries**. You should see some data in the graph.
+1. Now scroll down to the **query B** box. For **Operation** choose `Classic condition`. [You can read more about classic and multi-dimensional conditions here](/docs/grafana/latest/alerting/unified-alerting/alerting-rules/create-grafana-managed-rule/#single-and-multi-dimensional-rule). For conditions enter the following: `WHEN last() OF A IS ABOVE 0.2`
+1. In **Section 3**, enter `30s` for the **Evaluate every** field. For the purposes of this tutorial, the evaluation interval is intentionally short. This makes it easier to test. In the **for** field, enter `0m`. This setting makes Grafana wait until an alert has fired for a given time before Grafana sends the notification.
+1. In **Section 4**, you can add some sample text to your summary message. [Read more about message templating here](/docs/grafana/latest/alerting/unified-alerting/message-templating/).
+1. Click **Save and exit** at the top of the page.
+1. In Grafana's sidebar, hover the cursor over the **Alerting** (bell) icon and then click **Notification policies**.
+1. Under **Root policy**, press **Edit** and change the **Default contact point** to **RequestBin**. As a system grows, admins can use the **Notification policies** setting to organize and match alert rules to specific contact points.
+
+### Trigger a Grafana Managed Alert
+
+We have now configured an alert rule and a contact point. Now let's see if we can trigger a Grafana Managed Alert by generating some traffic on our sample application.
+
+1. Browse to [localhost:8081](http://localhost:8081).
+1. Repeatedly click the vote button or refresh the page to generate a traffic spike.
+
+Once the query `sum(rate(tns_request_duration_seconds_count[5m])) by(route)` returns a value greater than `0.2` Grafana will trigger our alert. Browse to the Request Bin we created earlier and find the sent Grafana alert notification with details and metadata.
+
+## Summary
+
+In this tutorial you learned about fundamental features of Grafana. To do so, we ran several Docker containers on your local machine. When you are ready to clean up this local tutorial environment, run the following command:
+
+```
+docker-compose down -v
+```
+
+### Learn more
+
+Check out the links below to continue your learning journey with Grafana's LGTM stack.
+
+- [Prometheus](/docs/grafana/latest/features/datasources/prometheus/)
+- [Loki](/docs/grafana/latest/features/datasources/loki/)
+- [Explore](/docs/grafana/latest/features/explore/)
+- [Alerting Overview](/docs/grafana/latest/alerting/)
+- [Alert rules](/docs/grafana/latest/alerting/create-alerts/)
+- [Contact Points](/docs/grafana/latest/alerting/notifications/)
diff --git a/docs/sources/tutorials/iis/index.md b/docs/sources/tutorials/iis/index.md
new file mode 100644
index 00000000000..ec4a190a128
--- /dev/null
+++ b/docs/sources/tutorials/iis/index.md
@@ -0,0 +1,146 @@
+---
+title: Use IIS with URL Rewrite as a reverse proxy
+summary: Learn how to set up Grafana behind IIS with URL Rewrite.
+description: Learn how to set up Grafana behind IIS with URL Rewrite.
+id: iis
+categories: ['administration']
+tags: ['advanced']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+aliases: ['/docs/grafana/latest/tutorials/iis/']
+---
+
+# Use IIS with URL Rewrite as a reverse proxy
+
+If you want Grafana to be a subpath/subfolder under a website in IIS then the Application Request Routing (ARR) and URL Rewrite modules for ISS can be used to support this.
+
+Example:
+
+- Parent site: http://yourdomain.com:8080
+- Grafana: http://localhost:3000
+
+Grafana as a subpath: http://yourdomain.com:8080/grafana
+
+Other Examples:
+
+- If the application is only served on the local server, the parent site can also look like http://localhost:8080.
+- If your domain is served using https on port 443, and thus the port is not normally entered in the address of your site, then the need to specify a port for the parent site in the configuration steps below can be eliminated.
+
+## Setup
+
+Install the URL Rewrite module for IIS.
+
+- Download and install the URL Rewrite module for IIS: https://www.iis.net/downloads/microsoft/url-rewrite
+
+You will also need the Application Request Routing (ARR) module for IIS for proxy forwarding
+
+- Download and install ARR module for IIS: https://www.iis.net/downloads/microsoft/application-request-routing
+
+## Grafana Config
+
+The Grafana config can be set by creating a file named/editing the existing file named `custom.ini` in the `conf` subdirectory of your Grafana installation. See the [installation instructions](http://docs.grafana.org/installation/windows/#configure) for more details.
+
+Using the example from above, if the subpath is `grafana` (you can set this to whatever is required) and the parent site is `yourdomain.com:8080`, then you would add this to the `custom.ini` config file:
+
+```bash
+[server]
+domain = yourdomain.com:8080
+root_url = %(protocol)s://%(domain)s/grafana/
+```
+
+Restart the Grafana server after changing the config file.
+
+Configured address to serve Grafana: http://yourdomain.com:8080/grafana
+
+---
+
+If you already have a subpath on your domain, configure it as follows:
+
+- Your Parent Site Address: http://yourdomain.com/existingsubpath
+
+```bash
+[server]
+domain = yourdomain.com/existingsubpath
+root_url = %(protocol)s://%(domain)s/grafana/
+```
+
+Restart the Grafana server after changing the config file.
+
+Configured address to serve Grafana: http://yourdomain.com/existingsubpath/grafana
+
+## IIS Config
+
+### Step 1: Forward Proxy
+
+1. Open the IIS Manager and click on the server
+2. In the admin console for the server, double click on the Application Request Routing option:
+3. Click the `Server Proxy Settings` action on the right-hand pane
+4. Select the `Enable proxy` checkbox so that it is enabled
+5. Click `Apply` and proceed with the URL Rewriting configuration
+
+**Note:** If you don't enable the Forward Proxy, you will most likely get 404 Not Found if you only apply the URL Rewrite rule
+
+### Step 2: URL Rewriting
+
+1. In the IIS Manager, click on the website that grafana will run under. For example, select the website that is bound to the http://yourdomain.com domain.
+2. In the admin console for this website, double click on the URL Rewrite option:
+
+{{< figure src="/static/img/docs/tutorials/IIS_admin_console.png" max-width="800px" >}}
+
+3. Click on the `Add Rule(s)...` action
+4. Choose the Blank Rule template for an Inbound Rule
+
+{{< figure src="/static/img/docs/tutorials/IIS_add_inbound_rule.png" max-width="800px" >}}
+
+5. Create an Inbound Rule for the website with the following settings:
+
+- pattern: `grafana(/)?(.*)` (if you have customised the subpath that will be used, use that instead of `grafana`)
+- check the `Ignore case` checkbox
+- rewrite URL set to `http://localhost:3000/{R:2}`
+- check the `Append query string` checkbox
+- check the `Stop processing of subsequent rules` checkbox
+
+{{< figure src="/static/img/docs/tutorials/IIS_url_rewrite.png" max-width="800px" >}}
+
+6. If your version of Grafana is greater than 8.3.5, you also need to configure the reverse proxy to preserve host headers.
+
+- This can be achieved by configuring the IIS config file by running this in a cmd prompt
+ `%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/proxy -preserveHostHeader:true /commit:apphost`
+- More information here https://github.com/grafana/grafana/issues/45261
+
+Finally, navigate to `http://yourdomain.com:8080/grafana` and you should come to the Grafana login page.
+
+## Troubleshooting
+
+### 404 error
+
+When navigating to the Grafana URL (`http://yourdomain.com:8080/grafana`) and a `HTTP Error 404.0 - Not Found` error is returned, then either:
+
+- The pattern for the Inbound Rule is incorrect. Edit the rule, click on the `Test pattern...` button, test the part of the URL after `http://yourdomain.com:8080/` and make sure it matches. For `grafana/login` the test should return 3 capture groups: {R:0}: `grafana` {R:1}: `/` and {R:2}: `login`.
+- The `root_url` setting in the Grafana config file does not match the parent URL with subpath.
+
+### Grafana Website only shows text with no images or css
+
+{{< figure src="/static/img/docs/tutorials/IIS_proxy_error.png" max-width="800px" >}}
+
+1. The `root_url` setting in the Grafana config file does not match the parent URL with subpath. This could happen if the root_url is commented out by mistake (`;` is used for commenting out a line in .ini files):
+
+ `; root_url = %(protocol)s://%(domain)s/grafana/`
+
+2. or if the subpath in the `root_url` setting does not match the subpath used in the pattern in the Inbound Rule in IIS:
+
+ `root_url = %(protocol)s://%(domain)s/grafana/`
+
+ pattern in Inbound Rule: `wrongsubpath(/)?(.*)`
+
+3. or if the Rewrite URL in the Inbound Rule is incorrect.
+
+ The Rewrite URL should not include the subpath.
+
+ The Rewrite URL should contain the capture group from the pattern matching that returns the part of the URL after the subpath. The pattern used above returns three capture groups and the third one {R:2} returns the part of the URL after `http://yourdomain.com:8080/grafana/`.
+
+### You see an 'Error updating options: origin not allowed' error
+
+- Ensure you have undertaken step 6 above, to configure IIS to preserve host headers by edit IIS config by running this in cmd prompt:
+ `%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/proxy -preserveHostHeader:true /commit:apphost`
diff --git a/docs/sources/tutorials/install-grafana-on-raspberry-pi/index.md b/docs/sources/tutorials/install-grafana-on-raspberry-pi/index.md
new file mode 100644
index 00000000000..16870ac0f92
--- /dev/null
+++ b/docs/sources/tutorials/install-grafana-on-raspberry-pi/index.md
@@ -0,0 +1,147 @@
+---
+title: Install Grafana on Raspberry Pi
+summary: Get Grafana set up on your Raspberry Pi.
+description: Get Grafana set up on your Raspberry Pi.
+id: install-grafana-on-raspberry-pi
+categories: ['administration']
+tags: ['beginner']
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+---
+
+## Introduction
+
+The Raspberry Pi is a tiny, affordable, yet capable computer that can run a range of different applications. Even Grafana!
+
+Many people are running Grafana on Raspberry Pi as a way to monitor their home, for things like indoor temperature, humidity, or energy usage.
+
+In this tutorial, you'll:
+
+- Set up a Raspberry Pi using a version of Raspberry Pi OS (previously called "Raspbian") that does not require you to connect a keyboard or monitor (this is often called "headless").
+- Install Grafana on your Raspberry Pi.
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Raspberry Pi
+- SD card
+ {{% /class %}}
+
+## Set up your Raspberry Pi
+
+Before we can install Grafana, you first need to set up your Raspberry Pi.
+
+For this tutorial, you'll configure your Raspberry Pi to be _headless_. This means you don't need to connect a monitor, keyboard, or mouse to your Raspberry Pi. All configuration is done from your regular computer.
+
+#### Download and install Raspberry Pi Imager
+
+Before we get started, you need to download and install the [Raspberry Pi Imager](https://www.raspberrypi.org/software/).
+
+We'll use the Raspberry Pi Imager to flash the operating system image to the SD card. You download the imager directly from the official Raspberry Pi website and it's available for Ubuntu Linux, macOS, and Windows.
+
+Follow the directions on the website to download and install the imager.
+
+#### Install Raspberry Pi OS
+
+Now it is time to install Raspberry Pi OS.
+
+1. Insert the SD card into your regular computer from which you plan to install Raspberry Pi OS.
+1. Run the Raspberry Pi Imager that you downloaded and installed.
+1. To select an operating system, click **Choose OS** in the imager. You will be shown a list of available options.
+1. From the list, select **Raspberry Pi OS (other)** and then select **Raspberry Pi OS Lite**, which is a Debian-based operating system for the Raspberry Pi. Since you're going to run a headless Raspberry Pi, you won't need the desktop dependencies.
+1. To select where you want to put the operating system image, click **Choose Storage** in the imager and then select the SD card you already inserted into your computer.
+1. The final step in the imager to click **Write**. When you do, the imager will write the Raspberry Pi OS Lite image to the SD card and verify that it has been written correctly.
+1. Eject the SD card from your computer, and insert it again.
+
+While you _could_ fire up the Raspberry Pi now, we don't yet have any way of accessing it.
+
+1. Create an empty file called `ssh` in the boot directory. This enables SSH so that you can log in remotely.
+
+ The next step is only required if you want the Raspberry Pi to connect to your wireless network. Otherwise, connect the it to your network by using a network cable.
+
+1. **(Optional)** Create a file called `wpa_supplicant.conf` in the boot directory:
+
+ ```
+ ctrl_interface=/var/run/wpa_supplicant
+ update_config=1
+ country=
+
+ network={
+ ssid=""
+ psk=""
+ }
+ ```
+
+All the necessary files are now on the SD card. Let's start up the Raspberry Pi.
+
+1. Eject the SD card and insert it into the SD card slot on the Raspberry Pi.
+1. Connect the power cable and make sure the LED lights are on.
+1. Find the IP address of the Raspberry Pi. Usually you can find the address in the control panel for your WiFi router.
+
+#### Connect remotely via SSH
+
+1. Open up your terminal and enter the following command:
+ ```
+ ssh pi@
+ ```
+1. SSH warns you that the authenticity of the host can't be established. Type "yes" to continue connecting.
+1. When asked for a password, enter the default password: `raspberry`.
+1. Once you're logged in, change the default password:
+ ```
+ passwd
+ ```
+
+Congratulations! You've now got a tiny Linux machine running that you can hide in a closet and access from your normal workstation.
+
+## Install Grafana
+
+Now that you've got the Raspberry Pi up and running, the next step is to install Grafana.
+
+1. Add the APT key used to authenticate packages:
+
+ ```
+ wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
+ ```
+
+1. Add the Grafana APT repository:
+
+ ```
+ echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
+ ```
+
+1. Install Grafana:
+ ```
+ sudo apt-get update
+ sudo apt-get install -y grafana
+ ```
+
+Grafana is now installed, but not yet running. To make sure Grafana starts up even if the Raspberry Pi is restarted, we need to enable and start the Grafana Systemctl service.
+
+1. Enable the Grafana server:
+
+ ```
+ sudo /bin/systemctl enable grafana-server
+ ```
+
+1. Start the Grafana server:
+
+ ```
+ sudo /bin/systemctl start grafana-server
+ ```
+
+ Grafana is now running on the machine and is accessible from any device on the local network.
+
+1. Open a browser and go to `http://:3000`, where the IP address is the address that you used to connect to the Raspberry Pi earlier. You're greeted with the Grafana login page.
+1. Log in to Grafana with the default username `admin`, and the default password `admin`.
+1. Change the password for the admin user when asked.
+
+Congratulations! Grafana is now running on your Raspberry Pi. If the Raspberry Pi is ever restarted or turned off, Grafana will start up whenever the machine regains power.
+
+## Summary
+
+If you want to use Grafana without having to go through a full installation process, check out [Grafana Cloud](/products/cloud/), which is designed to get users up and running quickly and easily. Grafana Cloud offers a forever free plan that is genuinely useful for hobbyists, testing, and small teams.
+
+### Learn more
+
+- [Raspberry Pi Documentation](https://www.raspberrypi.org/documentation/)
diff --git a/docs/sources/tutorials/integrate-hubot/index.md b/docs/sources/tutorials/integrate-hubot/index.md
new file mode 100644
index 00000000000..0d0af2f3821
--- /dev/null
+++ b/docs/sources/tutorials/integrate-hubot/index.md
@@ -0,0 +1,118 @@
+---
+title: Integrate Hubot with Grafana
+summary: Learn how to integrate Hubot with Grafana
+description: Learn how to integrate Hubot with Grafana
+id: integrate-hubot
+categories: ['administration']
+tags: ['advanced']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+aliases: ['/docs/grafana/latest/tutorials/hubot_howto/']
+---
+
+# Integrate Hubot with Grafana
+
+Grafana 2.0 shipped with a great feature that enables it to render any graph or panel to a PNG image.
+
+No matter what data source you are using, the PNG image of the Graph will look the same as it does in your browser.
+
+This guide will show you how to install and configure the [Hubot-Grafana](https://github.com/stephenyeargin/hubot-grafana) plugin. This plugin allows you to tell hubot to render any dashboard or graph right from a channel in Slack, Hipchat or Basecamp. The bot will respond with an image of the graph and a link that will take you to the graph.
+
+> _Amazon S3 Required_: The hubot-grafana script will upload the rendered graphs to Amazon S3. This
+> is so Hipchat and Slack can show them reliably (they require the image to be publicly available).
+
+{{< figure src="/static/img/docs/tutorials/hubot_grafana.png" max-width="800px" >}}
+
+## What is Hubot?
+
+[Hubot](https://hubot.github.com/) is an universal and extensible chat bot that can be used with many chat services and has a huge library of third party plugins that allow you to automate anything from your chat rooms.
+
+## Install Hubot
+
+Hubot is very easy to install and host. If you do not already have a bot up and running please read the official [Getting Started With Hubot](https://hubot.github.com/docs/) guide.
+
+## Install Hubot-Grafana script
+
+In your Hubot project repo install the Grafana plugin using `npm`:
+
+```bash
+npm install hubot-grafana --save
+```
+
+Edit the file external-scripts.json, and add hubot-grafana to the list of plugins.
+
+```json
+["hubot-pugme", "hubot-shipit", "hubot-grafana"]
+```
+
+## Configure
+
+The `hubot-grafana` plugin requires a number of environment variables to be set in order to work properly.
+
+```bash
+export HUBOT_GRAFANA_HOST=https://play.grafana.org
+export HUBOT_GRAFANA_API_KEY=abcd01234deadbeef01234
+export HUBOT_GRAFANA_S3_BUCKET=mybucket
+export HUBOT_GRAFANA_S3_ACCESS_KEY_ID=ABCDEF123456XYZ
+export HUBOT_GRAFANA_S3_SECRET_ACCESS_KEY=aBcD01234dEaDbEef01234
+export HUBOT_GRAFANA_S3_PREFIX=graphs
+export HUBOT_GRAFANA_S3_REGION=us-standard
+```
+
+### Grafana server side rendering
+
+The hubot plugin will take advantage of the Grafana server side rendering feature that can render any panel on the server using phantomjs. Grafana ships with a phantomjs binary (Linux only).
+
+To verify that this feature works try the `Direct link to rendered image` link in the panel share dialog. If you do not get an image when opening this link verify that the required font packages are installed for phantomjs to work.
+
+### Grafana API Key
+
+{{< figure src="/static/img/docs/v2/orgdropdown_api_keys.png" max-width="150px" class="docs-image--right">}}
+
+You need to set the environment variable `HUBOT_GRAFANA_API_KEY` to a Grafana API Key. You can add these from the API Keys page which you find in the Organization dropdown.
+
+### Amazon S3
+
+The `S3` options are optional but for the images to work properly in services like Slack and Hipchat they need to publicly available. By specifying the `S3` options the hubot-grafana script will publish the rendered panel to `S3` and it will use that URL when it posts to Slack or Hipchat.
+
+## Hubot commands
+
+- `hubot graf list`
+ - Lists the available dashboards
+- `hubot graf db graphite-carbon-metrics`
+ - Graph all panels in the dashboard
+- `hubot graf db graphite-carbon-metrics:3`
+ - Graph only panel with id 3 of a particular dashboard
+- `hubot graf db graphite-carbon-metrics:cpu`
+ - Graph only the panels containing "cpu" (case insensitive) in the title
+- `hubot graf db graphite-carbon-metrics now-12hr`
+ - Get a dashboard with a window of 12 hours ago to now
+- `hubot graf db graphite-carbon-metrics now-24hr now-12hr`
+ - Get a dashboard with a window of 24 hours ago to 12 hours ago
+- `hubot graf db graphite-carbon-metrics:3 now-8d now-1d`
+ - Get only the third panel of a particular dashboard with a window of 8 days ago to yesterday
+- `hubot graf db graphite-carbon-metrics host=carbon-a`
+ - Get a templated dashboard with the `$host` parameter set to `carbon-a`
+
+## Aliases
+
+Some of the hubot commands above can lengthy and you might have to remember the dashboard slug (url id). If you have a few favorite graphs you want to be able check up on often (let's say from your mobile) you can create hubot command aliases with the hubot script `hubot-alias`.
+
+Install it:
+
+```bash
+npm i --save hubot-alias
+```
+
+Now add `hubot-alias` to the list of plugins in `external-scripts.json` and restart hubot.
+
+Now you can add an alias like this:
+
+- `hubot alias graf-lb=graf db loadbalancers:2 now-20m`
+
+{{< figure src="/static/img/docs/tutorials/hubot_grafana2.png" max-width="800px" >}}
+
+## Summary
+
+Grafana is going to ship with integrated Slack and Hipchat features some day but you do not have to wait for that. Grafana 2 shipped with a very clever server side rendering feature that can render any panel to a png using phantomjs. The hubot plugin for Grafana is something you can install and use today!
diff --git a/docs/sources/tutorials/provision-dashboards-and-data-sources/index.md b/docs/sources/tutorials/provision-dashboards-and-data-sources/index.md
new file mode 100644
index 00000000000..4554b67073f
--- /dev/null
+++ b/docs/sources/tutorials/provision-dashboards-and-data-sources/index.md
@@ -0,0 +1,260 @@
+---
+title: Provision dashboards and data sources
+summary: Treat your configuration as code.
+description: Treat your configuration as code.
+id: provision-dashboards-and-data-sources
+categories: ['administration']
+tags: ['intermediate']
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 40
+---
+
+## Introduction
+
+Learn how you can reuse dashboards and data sources across multiple teams by provisioning Grafana from version-controlled configuration files.
+
+In this tutorial, you'll:
+
+- Provision dashboards.
+- Provision data sources.
+
+{{% class "prerequisite-section" %}}
+
+### Prerequisites
+
+- Grafana 7.0
+- Administrator privileges on the system you are doing the tutorial on
+ {{% /class %}}
+
+## Configuration as code
+
+Configuration as code is the practice of storing the configuration of your system as a set of version controlled, human-readable configuration files, rather than in a database. These configuration files can be reused across environments to avoid duplicated resources.
+
+As the number of dashboards and data sources grows within your organization, manually managing changes can become tedious and error-prone. Encouraging reuse becomes important to avoid multiple teams redesigning the same dashboards.
+
+Grafana supports configuration as code through _provisioning_. The resources that currently supports provisioning are:
+
+- [Dashboards](/docs/grafana/latest/administration/provisioning/#dashboards)
+- [Data sources](/docs/grafana/latest/administration/provisioning/#datasources)
+- [Alert notification channels](/docs/grafana/latest/administration/provisioning/#alert-notification-channels)
+
+## Set the provisioning directory
+
+Before you can start provisioning resources, Grafana needs to know where to find the _provisioning directory_. The provisioning directory contains configuration files that are applied whenever Grafana starts and continuously updated while running.
+
+By default, Grafana looks for a provisioning directory in the configuration directory (grafana > conf) on the system where Grafana is installed. However, if you are a Grafana Administrator, then you might want to place the config files in a shared resource like a network folder, so you would need to change the path to the provisioning directory.
+
+You can set a different path by setting the `paths.provisioning` property in the main config file:
+
+```ini
+[paths]
+provisioning =
+```
+
+For more information about configuration files, refer to [Configuration](/docs/grafana/latest/installation/configuration/) in the [Grafana documentation](/docs/grafana/latest/).
+
+The provisioning directory assumes the following structure:
+
+```
+provisioning/
+ datasources/
+
+ dashboards/
+
+ notifiers/
+
+```
+
+Next, we'll look at how to provision a data source.
+
+## Provision a data source
+
+Each data source provisioning config file contains a _manifest_ that specifies the desired state of a set of provisioned data sources.
+
+At startup, Grafana loads the configuration files and provisions the data sources listed in the manifests.
+
+Let's configure a [TestData DB](/docs/grafana/latest/features/datasources/testdata/) data source that you can use for your dashboards.
+
+#### Create a data source manifest
+
+1. In the `provisioning/datasources/` directory, create a file called `default.yaml` with the following content:
+
+ ```yaml
+ apiVersion: 1
+
+ datasources:
+ - name: TestData DB
+ type: testdata
+ ```
+
+1. Restart Grafana to load the new changes.
+1. In the sidebar, hover the cursor over the **Configuration** (gear) icon and click **Data Sources**. The TestData DB appears in the list of data sources.
+
+> The configuration options can vary between different types of data sources. For more information on how to configure a specific data source, refer to [Data sources](/docs/grafana/latest/administration/provisioning/#datasources).
+
+## Provision a dashboard
+
+Each dashboard config file contains a manifest that specifies the desired state of a set of _dashboard providers_.
+
+A dashboard provider tells Grafana where to find the dashboard definitions and where to put them.
+
+Grafana regularly checks for changes to the dashboard definitions (by default every 10 seconds).
+
+Let's define a dashboard provider so that Grafana knows where to find the dashboards we want to provision.
+
+#### Define a dashboard provider
+
+In the `provisioning/dashboards/` directory, create a file called `default.yaml` with the following content:
+
+```yaml
+apiVersion: 1
+
+providers:
+ - name: Default # A uniquely identifiable name for the provider
+ folder: Services # The folder where to place the dashboards
+ type: file
+ options:
+ path:
+
+ # Default path for Windows: C:/Program Files/GrafanaLabs/grafana/public/dashboards
+ # Default path for Linux is: /var/lib/grafana/dashboards
+```
+
+For more information on how to configure dashboard providers, refer to [Dashboards](/docs/grafana/latest/administration/provisioning/#dashboards).
+
+#### Create a dashboard definition
+
+1. In the dashboard definitions directory you specified in the dashboard provider, i.e. `options.path`, create a file called `cluster.json` with the following content:
+
+ ```json
+ {
+ "__inputs": [],
+ "__requires": [],
+ "annotations": {
+ "list": []
+ },
+ "editable": false,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "hideControls": false,
+ "id": null,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "TestData DB",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 2,
+ "legend": {
+ "alignAsTable": false,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "repeat": null,
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "CPU Usage",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ]
+ }
+ ],
+ "refresh": "",
+ "rows": [],
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": ["kubernetes"],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
+ "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ },
+ "timezone": "browser",
+ "title": "Cluster",
+ "version": 0
+ }
+ ```
+
+1. Restart Grafana to provision the new dashboard or wait 10 seconds for Grafana to automatically create the dashboard.
+1. In the sidebar, hover the cursor over **Dashboards** (squares) icon, and then click **Manage**. The dashboard appears in a **Services** folder.
+
+> If you don't specify an `id` in the dashboard definition, then Grafana assigns one during provisioning. You can set the `id` yourself if you want to reference the dashboard from other dashboards. Be careful to not use the same `id` for multiple dashboards, as this will cause a conflict.
+
+## Summary
+
+In this tutorial you learned how you to reuse dashboards and data sources across multiple teams by provisioning Grafana from version-controlled configuration files.
+
+Dashboard definitions can get unwieldy as more panels and configurations are added to them. There are a number of open source tools available to make it easier to manage dashboard definitions:
+
+- [grafana-dash-gen](https://github.com/uber/grafana-dash-gen) (Javascript)
+- [grafanalib](https://github.com/weaveworks/grafanalib) (Python)
+- [grafonnet-lib](https://github.com/grafana/grafonnet-lib) (Jsonnet)
+- [grafyaml](https://docs.openstack.org/infra/grafyaml/) (YAML)
+
+### Learn more
+
+- [Provisioning Grafana](/docs/grafana/latest/administration/provisioning/)
diff --git a/docs/sources/tutorials/run-grafana-behind-a-proxy/index.md b/docs/sources/tutorials/run-grafana-behind-a-proxy/index.md
new file mode 100644
index 00000000000..10be2dee35f
--- /dev/null
+++ b/docs/sources/tutorials/run-grafana-behind-a-proxy/index.md
@@ -0,0 +1,222 @@
+---
+title: Run Grafana behind a reverse proxy
+summary: Learn how to run Grafana behind a reverse proxy
+description: Learn how to run Grafana behind a reverse proxy
+id: run-grafana-behind-a-proxy
+categories: ['administration']
+tags: ['advanced']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+aliases: ['/docs/grafana/latest/installation/behind_proxy/']
+---
+
+## Introduction
+
+In this tutorial, you'll configure Grafana to run behind a reverse proxy.
+
+When running Grafana behind a proxy, you need to configure the domain name to let Grafana know how to render links and redirects correctly.
+
+- In the Grafana configuration file, change `server.domain` to the domain name you'll be using:
+
+```bash
+[server]
+domain = example.com
+```
+
+- Restart Grafana for the new changes to take effect.
+
+You can also serve Grafana behind a _sub path_, such as `http://example.com/grafana`.
+
+To serve Grafana behind a sub path:
+
+- Include the sub path at the end of the `root_url`.
+- Set `serve_from_sub_path` to `true`.
+
+```bash
+[server]
+domain = example.com
+root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
+serve_from_sub_path = true
+```
+
+Next, you need to configure your reverse proxy.
+
+## Configure NGINX
+
+[NGINX](https://www.nginx.com) is a high performance load balancer, web server, and reverse proxy.
+
+- In your NGINX configuration file inside `http` section, add the following:
+
+```nginx
+# this is required to proxy Grafana Live WebSocket connections.
+map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+
+upstream grafana {
+ server localhost:3000;
+}
+
+server {
+ listen 80;
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+
+ location / {
+ proxy_set_header Host $http_host;
+ proxy_pass http://grafana;
+ }
+
+ # Proxy Grafana Live WebSocket connections.
+ location /api/live/ {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_set_header Host $http_host;
+ proxy_pass http://grafana;
+ }
+}
+```
+
+- Reload the NGINX configuration.
+- Navigate to port 80 on the machine NGINX is running on. You're greeted by the Grafana login page.
+
+For Grafana Live which uses WebSocket connections you may have to raise Nginx [worker_connections](https://nginx.org/en/docs/ngx_core_module.html#worker_connections) option which is 512 by default – which limits the number of possible concurrent connections with Grafana Live.
+
+Also, be aware that the above configuration will work only when the `proxy_pass` value for `location /` is a literal string. If you are using a variable here, [read this GitHub issue](https://github.com/grafana/grafana/issues/18299). You will need to add [an appropriate NGINX rewrite rule](https://www.nginx.com/blog/creating-nginx-rewrite-rules/).
+
+To configure NGINX to serve Grafana under a _sub path_, update the `location` block:
+
+```nginx
+# this is required to proxy Grafana Live WebSocket connections.
+map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+
+upstream grafana {
+ server localhost:3000;
+}
+
+server {
+ listen 80;
+ root /usr/share/nginx/www;
+ index index.html index.htm;
+
+ location /grafana/ {
+ rewrite ^/grafana/(.*) /$1 break;
+ proxy_set_header Host $http_host;
+ proxy_pass http://grafana;
+ }
+
+ # Proxy Grafana Live WebSocket connections.
+ location /grafana/api/live/ {
+ rewrite ^/grafana/(.*) /$1 break;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_set_header Host $http_host;
+ proxy_pass http://grafana;
+ }
+}
+```
+
+## Configure HAProxy
+
+To configure HAProxy to serve Grafana under a _sub path_:
+
+```bash
+frontend http-in
+ bind *:80
+ use_backend grafana_backend if { path /grafana } or { path_beg /grafana/ }
+
+backend grafana_backend
+ # Requires haproxy >= 1.6
+ http-request set-path %[path,regsub(^/grafana/?,/)]
+
+ # Works for haproxy < 1.6
+ # reqrep ^([^\ ]*\ /)grafana[/]?(.*) \1\2
+
+ server grafana localhost:3000
+```
+
+## Configure IIS
+
+> IIS requires that the URL Rewrite module is installed.
+
+To configure IIS to serve Grafana under a _sub path_, create an Inbound Rule for the parent website in IIS Manager with the following settings:
+
+- pattern: `grafana(/)?(.*)`
+- check the `Ignore case` checkbox
+- rewrite URL set to `http://localhost:3000/{R:2}`
+- check the `Append query string` checkbox
+- check the `Stop processing of subsequent rules` checkbox
+
+This is the rewrite rule that is generated in the `web.config`:
+
+```xml
+
+
+
+
+
+
+
+
+```
+
+See the [tutorial on IIS URL Rewrites](/tutorials/iis/) for more in-depth instructions.
+
+## Configure Traefik
+
+[Traefik](https://traefik.io/traefik/) Cloud Native Reverse Proxy / Load Balancer / Edge Router
+
+Using the docker provider the following labels will configure the router and service for a domain or subdomain routing.
+
+```yaml
+labels:
+ traefik.http.routers.grafana.rule: Host(`grafana.example.com`)
+ traefik.http.services.grafana.loadbalancer.server.port: 3000
+```
+
+To deploy on a _sub path_
+
+```yaml
+labels:
+ traefik.http.routers.grafana.rule: Host(`example.com`) && PathPrefix(`/grafana`)
+ traefik.http.services.grafana.loadbalancer.server.port: 3000
+```
+
+Examples using the file provider.
+
+```yaml
+http:
+ routers:
+ grafana:
+ rule: Host(`grafana.example.com`)
+ service: grafana
+ services:
+ grafana:
+ loadBalancer:
+ servers:
+ - url: http://192.168.30.10:3000
+```
+
+```yaml
+http:
+ routers:
+ grafana:
+ rule: Host(`example.com`) && PathPrefix(`/grafana`)
+ service: grafana
+ services:
+ grafana:
+ loadBalancer:
+ servers:
+ - url: http://192.168.30.10:3000
+```
+
+## Summary
+
+In this tutorial you learned how to run Grafana behind a reverse proxy.
diff --git a/docs/sources/tutorials/stream-metrics-from-telegraf-to-grafana/index.md b/docs/sources/tutorials/stream-metrics-from-telegraf-to-grafana/index.md
new file mode 100644
index 00000000000..822006b2fcf
--- /dev/null
+++ b/docs/sources/tutorials/stream-metrics-from-telegraf-to-grafana/index.md
@@ -0,0 +1,101 @@
+---
+title: Stream metrics from Telegraf to Grafana
+summary: Use Telegraf to stream live metrics to Grafana.
+description: Use Telegraf to stream live metrics to Grafana.
+id: stream-metrics-from-telegraf-to-grafana
+categories: ['administration']
+tags: ['beginner']
+status: Published
+authors: ['grafana_labs']
+Feedback Link: https://github.com/grafana/tutorials/issues/new
+weight: 75
+---
+
+## Introduction
+
+Grafana v8 introduced streaming capabilities – a way to push data to UI panels in near real-time. In this tutorial we show how Grafana real-time streaming capabilities can be used together with Telegraf to instantly display system measurements.
+
+In this tutorial, you'll:
+
+- Setup Telegraf and output measurements directly to Grafana time-series panel in near real-time
+
+{{% class "prerequisite-section" %}}
+
+#### Prerequisites
+
+- Grafana 8.0+
+- Telegraf
+ {{% /class %}}
+
+## Run Grafana and create admin token
+
+1. Run Grafana following [installation instructions](/docs/grafana/latest/installation/) for your operating system
+1. Log in and go to Configuration -> API Keys
+1. Press "Add API key" button and create a new API token with **Admin** role
+
+## Configure and run Telegraf
+
+Telegraf is a plugin-driven server agent for collecting and sending metrics and events from databases, systems, and IoT sensors.
+
+You can install it following [official installation instructions](https://docs.influxdata.com/telegraf/latest/introduction/installation/).
+
+In this tutorial we will be using Telegraf HTTP output plugin to send metrics in Influx format to Grafana. We can use a configuration like this:
+
+```
+[agent]
+ interval = "1s"
+ flush_interval = "1s"
+
+[[inputs.cpu]]
+ percpu = false
+ totalcpu = true
+
+[[outputs.http]]
+ url = "http://localhost:3000/api/live/push/custom_stream_id"
+ data_format = "influx"
+ [outputs.http.headers]
+ Authorization = "Bearer "
+```
+
+Make sure to replace `` placeholder with your actual API key created in the previous step. Save this config into `telegraf.conf` file and run Telegraf pointing to this config file. Telegraf will periodically (once in a second) report the state of total CPU usage on a host to Grafana (which is supposed to be running on `http://localhost:3000`). Of course you can replace `custom_stream_id` to something more meaningful for your use case.
+
+Inside Grafana Influx data is converted to Grafana data frames and then frames are published to Grafana Live channels. In this case, the channel where CPU data will be published is `stream/custom_stream_id/cpu`. The `stream` scope is constant, the `custom_stream_id` namespace is the last part of API URL set in Telegraf configuration (`http://localhost:3000/api/live/push/telegraf`) and the path is `cpu` - the name of a measurement.
+
+The only thing left here is to create a dashboard with streaming data.
+
+## Create dashboard with streaming data
+
+1. Create new dashboard
+1. Press Add empty panel
+1. Select `-- Grafana --` datasource
+1. Select `Live Measurements` query type
+1. Find and select `stream/custom_stream_id/cpu` measurement for Channel field
+1. Save dashboard changes
+
+After making these steps Grafana UI should subscribe to the channel `stream/custom_stream_id/cpu` and you should see CPU data updates coming from Telegraf in near real-time.
+
+## Stream using WebSocket endpoint
+
+If you aim for a high-frequency update sending then you may want to use the WebSocket output plugin of Telegraf (introduced in Telegraf v1.19.0) instead of the HTTP output plugin we used above. Configure WebSocket output plugin like this:
+
+```
+[agent]
+ interval = "500ms"
+ flush_interval = "500ms"
+
+[[inputs.cpu]]
+ percpu = false
+ totalcpu = true
+
+[[outputs.websocket]]
+ url = "ws://localhost:3000/api/live/push/custom_stream_id"
+ data_format = "influx"
+ [outputs.websocket.headers]
+ Authorization = "Bearer "
+```
+
+WebSocket avoids running all Grafana HTTP middleware on each request from Telegraf thus reducing Grafana backend CPU usage significantly.
+
+## Summary
+
+In this tutorial you learned how to use Telegraf to stream live metrics to Grafana.
diff --git a/e2e/run-suite b/e2e/run-suite
index d74a41d67f4..8c446f3145b 100755
--- a/e2e/run-suite
+++ b/e2e/run-suite
@@ -18,6 +18,7 @@ args=("$@")
CMD="start"
PARAMS=""
+CLEANUP=""
declare -A env=(
[BASE_URL]=${BASE_URL:-"http://$HOST:$PORT"}
@@ -25,6 +26,7 @@ declare -A env=(
)
testFilesForSingleSuite="*.spec.ts"
+rootForEnterpriseSuite="extensions-suite"
declare -A cypressConfig=(
[integrationFolder]=../../e2e
@@ -64,6 +66,27 @@ case "$1" in
cypressConfig[screenshotsFolder]=../../e2e/benchmarks/"${args[1]}"/screenshots
cypressConfig[testFiles]=$testFilesForSingleSuite
;;
+ "enterprise")
+ echo "Enterprise"
+ CLEANUP="rm -rf ../../e2e/extensions-suite"
+ SETUP="cp -Lr ../../e2e/extensions ../../e2e/extensions-suite"
+ enterpriseSuite=$(basename "${args[1]}")
+ case "$2" in
+ "debug")
+ echo -e "Debug mode"
+ env[SLOWMO]=1
+ PARAMS="--no-exit"
+ enterpriseSuite=$(basename "${args[2]}")
+ ;;
+ "dev")
+ echo "Dev mode"
+ CMD="open"
+ enterpriseSuite=$(basename "${args[2]}")
+ ;;
+ esac
+ cypressConfig[testFiles]=$rootForEnterpriseSuite/$enterpriseSuite/*-suite/*.spec.ts
+ $CLEANUP && $SETUP
+ ;;
"")
;;
*)
@@ -96,3 +119,5 @@ function join () {
yarn $CMD --env "$(join env)" \
--config "$(join cypressConfig)" \
$PARAMS
+
+$CLEANUP
diff --git a/e2e/start-and-run-suite b/e2e/start-and-run-suite
index 2c5c7d1d996..d29b7775b96 100755
--- a/e2e/start-and-run-suite
+++ b/e2e/start-and-run-suite
@@ -2,11 +2,21 @@
. scripts/grafana-server/variables
+LICENSE_PATH=""
+
+if [ "$1" = "enterprise" ]; then
+ if [ "$2" != "dev" ] && [ "$2" != "debug" ]; then
+ LICENSE_PATH=$2/license.jwt
+ else
+ LICENSE_PATH=$3/license.jwt
+ fi
+fi
+
if [ "$BASE_URL" != "" ]; then
echo -e "BASE_URL set, skipping starting server"
else
# Start it in the background
- ./scripts/grafana-server/start-server 2>&1 > scripts/grafana-server/server.log &
+ ./scripts/grafana-server/start-server $LICENSE_PATH 2>&1 > scripts/grafana-server/server.log &
./scripts/grafana-server/wait-for-grafana
fi
diff --git a/go.mod b/go.mod
index 85d53c9f914..108a98b42c1 100644
--- a/go.mod
+++ b/go.mod
@@ -59,9 +59,9 @@ require (
github.com/google/uuid v1.3.0
github.com/google/wire v0.5.0
github.com/gorilla/websocket v1.5.0
- github.com/grafana/alerting v0.0.0-20230119191910-5ebb70a85264
+ github.com/grafana/alerting v0.0.0-20230125210216-facc6b27b9e0
github.com/grafana/cuetsy v0.1.5
- github.com/grafana/grafana-aws-sdk v0.11.0
+ github.com/grafana/grafana-aws-sdk v0.12.0
github.com/grafana/grafana-azure-sdk-go v1.5.1
github.com/grafana/grafana-plugin-sdk-go v0.147.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
@@ -126,7 +126,7 @@ require (
gopkg.in/square/go-jose.v2 v2.5.1
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1
- xorm.io/builder v0.3.6
+ xorm.io/builder v0.3.6 // indirect
xorm.io/core v0.7.3
xorm.io/xorm v0.8.2
)
@@ -311,6 +311,7 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
github.com/gophercloud/gophercloud v0.24.0 // indirect
+ github.com/grafana/sqlds/v2 v2.3.10 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
github.com/hashicorp/memberlist v0.5.0 // indirect
diff --git a/go.sum b/go.sum
index 4cd7978beac..7e2c44649b6 100644
--- a/go.sum
+++ b/go.sum
@@ -1394,8 +1394,8 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
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/grafana/alerting v0.0.0-20230119191910-5ebb70a85264 h1:ApQsGfr05Yma0dpd1+ib11cJQcpmet+E/Ke/aQkdT1Y=
-github.com/grafana/alerting v0.0.0-20230119191910-5ebb70a85264/go.mod h1:NoSLbfmUwE+omWFReFrLtbtOItmvTbuQERJ6XFYp9ME=
+github.com/grafana/alerting v0.0.0-20230125210216-facc6b27b9e0 h1:BzkQNnj+eevX30EMqJiUS1w3CPoGc8kp7pDf/ari/4Y=
+github.com/grafana/alerting v0.0.0-20230125210216-facc6b27b9e0/go.mod h1:NoSLbfmUwE+omWFReFrLtbtOItmvTbuQERJ6XFYp9ME=
github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw=
github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
github.com/grafana/cuetsy v0.1.5 h1:mnFwAXdbqCsyL8r7kkdUMJ4kOAR26cxIPmrZj7JzTeY=
@@ -1404,12 +1404,13 @@ github.com/grafana/dskit v0.0.0-20211011144203-3a88ec0b675f h1:FvvSVEbnGeM2bUivG
github.com/grafana/dskit v0.0.0-20211011144203-3a88ec0b675f/go.mod h1:uPG2nyK4CtgNDmWv7qyzYcdI+S90kHHRWvHnBtEMBXM=
github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036 h1:GplhUk6Xes5JIhUUrggPcPBhOn+eT8+WsHiebvq7GgA=
github.com/grafana/go-mssqldb v0.0.0-20210326084033-d0ce3c521036/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
-github.com/grafana/grafana-aws-sdk v0.11.0 h1:ncPD/UN0wNcKq3kEU90RdvrnK/6R4VW2Lo5dPcGk9t0=
-github.com/grafana/grafana-aws-sdk v0.11.0/go.mod h1:5Iw3xY7iXJfNaYHrRHMXa/kaB2lWoyntg71PPLGvSs8=
+github.com/grafana/grafana-aws-sdk v0.12.0 h1:eUjFdFZeZE+nyu/RMRz+qFxTBew69ToLBrbRhTbjkfM=
+github.com/grafana/grafana-aws-sdk v0.12.0/go.mod h1:rCXLYoMpPqF90U7XqgVJ1HIAopFVF0bB3SXBVEJIm3I=
github.com/grafana/grafana-azure-sdk-go v1.5.1 h1:d+wwqvWLYvPeGltxU50LNjvCykyJjoyd5exMYJW4nLM=
github.com/grafana/grafana-azure-sdk-go v1.5.1/go.mod h1:OJJuBJ3MOoaq2mqD6xlPsArpL2R5j80TrDqPYr35Zak=
github.com/grafana/grafana-google-sdk-go v0.0.0-20211104130251-b190293eaf58 h1:2ud7NNM7LrGPO4x0NFR8qLq68CqI4SmB7I2yRN2w9oE=
github.com/grafana/grafana-google-sdk-go v0.0.0-20211104130251-b190293eaf58/go.mod h1:Vo2TKWfDVmNTELBUM+3lkrZvFtBws0qSZdXhQxRdJrE=
+github.com/grafana/grafana-plugin-sdk-go v0.94.0/go.mod h1:3VXz4nCv6wH5SfgB3mlW39s+c+LetqSCjFj7xxPC5+M=
github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
github.com/grafana/grafana-plugin-sdk-go v0.147.0 h1:VavvJOa/Ubs+wzalzWIl+FQmdaD4vEK8KVYU0a8rf+E=
github.com/grafana/grafana-plugin-sdk-go v0.147.0/go.mod h1:NMgO3t2gR5wyLx8bWZ9CTmpDk5Txp4wYFccFLHdYn3Q=
@@ -1419,6 +1420,8 @@ github.com/grafana/prometheus-alertmanager v0.25.1-0.20230119183635-ec19b0a443b7
github.com/grafana/prometheus-alertmanager v0.25.1-0.20230119183635-ec19b0a443b7/go.mod h1:MnBfDPXJqXmmfPwQlCLvVUdqfnvrAw+hSPtDeaaFwj4=
github.com/grafana/saml v0.4.13-0.20230123091136-3b6b1ec6c3cb h1:9PLj02xp4DeLTM2+ZyBMcN1sh0ir8GuF/1xXKyF+yws=
github.com/grafana/saml v0.4.13-0.20230123091136-3b6b1ec6c3cb/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
+github.com/grafana/sqlds/v2 v2.3.10 h1:HWKhE0vR6LoEiE+Is8CSZOgaB//D1yqb2ntkass9Fd4=
+github.com/grafana/sqlds/v2 v2.3.10/go.mod h1:c6ibxnxRVGxV/0YkEgvy7QpQH/lyifFyV7K/14xvdIs=
github.com/grafana/thema v0.0.0-20230122235053-b4b6714dd1c9 h1:nAdsZkvPYNH6wDPkAi9JaDSIf5i2iVz4+Rqk4AOt6sE=
github.com/grafana/thema v0.0.0-20230122235053-b4b6714dd1c9/go.mod h1:5j2nf4xmWhKr+1vyGouML8eJ8xERS5Jw/lhjs0eyz78=
github.com/grafana/xorm v0.8.3-0.20220614223926-2fcda7565af6 h1:I9dh1MXGX0wGyxdV/Sl7+ugnki4Dfsy8lv2s5Yf887o=
@@ -1966,6 +1969,7 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mjibson/esc v0.2.0/go.mod h1:9Hw9gxxfHulMF5OJKCyhYD7PzlSdhzXyaGEBRPH1OPs=
diff --git a/jest.config.js b/jest.config.js
index 7db7afdf68c..3bb98ec70d4 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -3,6 +3,8 @@
// 2. Any wrong timezone handling could be hidden if we use UTC/GMT local time (which would happen in CI).
process.env.TZ = 'Pacific/Easter';
+const esModules = ['ol', 'd3', 'd3-color', 'd3-interpolate', 'delaunator', 'internmap', 'robust-predicates'].join('|');
+
module.exports = {
verbose: false,
testEnvironment: 'jsdom',
@@ -10,7 +12,7 @@ module.exports = {
'^.+\\.(ts|tsx|js|jsx)$': [require.resolve('ts-jest'), { isolatedModules: true }],
},
transformIgnorePatterns: [
- 'node_modules/(?!(ol)/)', // <- exclude the open layers library
+ `/node_modules/(?!${esModules})`, // exclude es modules to prevent TS complaining
],
moduleDirectories: ['public'],
roots: ['/public/app', '/public/test', '/packages'],
diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue
index 74a478b786d..2aa725ad809 100644
--- a/kinds/dashboard/dashboard_kind.cue
+++ b/kinds/dashboard/dashboard_kind.cue
@@ -30,11 +30,12 @@ lineage: seqs: [
tags?: [...string] @grafanamaturity(NeedsExpertReview)
// Theme of dashboard.
style: "light" | *"dark" @grafanamaturity(NeedsExpertReview)
- // Timezone of dashboard,
- timezone?: *"browser" | "utc" | "" @grafanamaturity(NeedsExpertReview)
+ // Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc".
+ timezone?: string | *"browser"
// Whether a dashboard is editable or not.
- editable: bool | *true
- graphTooltip: #DashboardCursorSync @grafanamaturity(NeedsExpertReview)
+ editable: bool | *true
+ // Configuration of dashboard cursor sync behavior.
+ graphTooltip: #DashboardCursorSync
// Time range for dashboard, e.g. last 6 hours, last 7 days, etc
time?: {
from: string | *"now-6h"
@@ -55,15 +56,15 @@ lineage: seqs: [
// TODO docs
time_options: [...string] | *["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
} @grafanamaturity(NeedsExpertReview)
- // TODO docs
- fiscalYearStartMonth?: uint8 & <13 @grafanamaturity(NeedsExpertReview)
+ // The month that the fiscal year starts on. 0 = January, 11 = December
+ fiscalYearStartMonth?: uint8 & <12 | *0
// TODO docs
liveNow?: bool @grafanamaturity(NeedsExpertReview)
// TODO docs
weekStart?: string @grafanamaturity(NeedsExpertReview)
- // TODO docs
- refresh?: string | false @grafanamaturity(NeedsExpertReview)
+ // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d".
+ refresh?: string | false
// Version of the JSON schema, incremented each time a Grafana update brings
// changes to said schema.
// TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion
@@ -291,7 +292,7 @@ lineage: seqs: [
// 0 for no shared crosshair or tooltip (default).
// 1 for shared crosshair.
// 2 for shared crosshair AND shared tooltip.
- #DashboardCursorSync: *0 | 1 | 2 @cuetsy(kind="enum",memberNames="Off|Crosshair|Tooltip") @grafanamaturity(NeedsExpertReview)
+ #DashboardCursorSync: *0 | 1 | 2 @cuetsy(kind="enum",memberNames="Off|Crosshair|Tooltip")
// Schema for panel targets is specified by datasource
// plugins. We use a placeholder definition, which the Go
diff --git a/kinds/preferences/preferences_kind.cue b/kinds/preferences/preferences_kind.cue
new file mode 100644
index 00000000000..1e4be8b9b82
--- /dev/null
+++ b/kinds/preferences/preferences_kind.cue
@@ -0,0 +1,36 @@
+package kind
+
+name: "Preferences"
+maturity: "merged"
+
+lineage: seqs: [
+ {
+ schemas: [
+ {
+ // UID for the home dashboard
+ homeDashboardUID?: string
+
+ // The timezone selection
+ // TODO: this should use the timezone defined in common
+ timezone?: string
+
+ // day of the week (sunday, monday, etc)
+ weekStart?: string
+
+ // light, dark, empty is default
+ theme?: string
+
+ // Selected language (beta)
+ language?: string
+
+ // Explore query history preferences
+ queryHistory?: #QueryHistoryPreference
+
+ #QueryHistoryPreference: {
+ // one of: '' | 'query' | 'starred';
+ homeTab?: string
+ } @cuetsy(kind="interface") //0.0
+ },
+ ]
+ },
+]
diff --git a/kinds/publicdashboard/public_dashboard_kind.cue b/kinds/publicdashboard/public_dashboard_kind.cue
new file mode 100644
index 00000000000..1e960fa7cb4
--- /dev/null
+++ b/kinds/publicdashboard/public_dashboard_kind.cue
@@ -0,0 +1,26 @@
+package kind
+
+name: "PublicDashboard"
+maturity: "merged"
+
+lineage: seqs: [
+ {
+ schemas: [
+ // 0.0
+ {
+ // Unique public dashboard identifier
+ uid: string
+ // Dashboard unique identifier referenced by this public dashboard
+ dashboardUid: string
+ // Unique public access token
+ accessToken?: string
+ // Flag that indicates if the public dashboard is enabled
+ isEnabled: bool
+ // Flag that indicates if annotations are enabled
+ annotationsEnabled: bool
+ // Flag that indicates if the time range picker is enabled
+ timeSelectionEnabled: bool
+ },
+ ]
+ },
+]
diff --git a/kinds/serviceaccount/serviceaccount_kind.cue b/kinds/serviceaccount/serviceaccount_kind.cue
new file mode 100644
index 00000000000..afa0f818394
--- /dev/null
+++ b/kinds/serviceaccount/serviceaccount_kind.cue
@@ -0,0 +1,46 @@
+package kind
+
+name: "ServiceAccount"
+maturity: "merged"
+
+lineage: seqs: [
+ {
+ schemas: [
+ // v0.0
+ {
+ // ID is the unique identifier of the service account in the database.
+ id: int64 @grafanamaturity(ToMetadata="sys")
+ // OrgId is the ID of an organisation the service account belongs to.
+ orgId: int64 @grafanamaturity(ToMetadata="sys")
+ // Name of the service account.
+ name: string
+ // Login of the service account.
+ login: string
+ // IsDisabled indicates if the service account is disabled.
+ isDisabled: bool
+ // Role is the Grafana organization role of the service account which can be 'Viewer', 'Editor', 'Admin'.
+ role: #OrgRole @grafanamaturity(ToMetadata="kind")
+ // Tokens is the number of active tokens for the service account.
+ // Tokens are used to authenticate the service account against Grafana.
+ tokens: int64 @grafanamaturity(ToMetadata="kind")
+ // AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front
+ // of the service account.
+ avatarUrl: string @grafanamaturity(ToMetadata="kind")
+ // AccessControl metadata associated with a given resource.
+ accessControl?: {
+ [string]: bool @grafanamaturity(ToMetadata="sys")
+ }
+
+ // Teams is a list of teams the service account belongs to.
+ teams?: [...string] @grafanamaturity(ToMetadata="sys")
+ // Created indicates when the service account was created.
+ created?: int64 @grafanamaturity(ToMetadata="sys")
+ // Updated indicates when the service account was updated.
+ updated?: int64 @grafanamaturity(ToMetadata="sys")
+
+ // OrgRole is a Grafana Organization Role which can be 'Viewer', 'Editor', 'Admin'.
+ #OrgRole: "Admin" | "Editor" | "Viewer" @cuetsy(kind="type")
+ },
+ ]
+ },
+]
diff --git a/latest.json b/latest.json
index f617f9d7f46..57614a6e872 100644
--- a/latest.json
+++ b/latest.json
@@ -1,4 +1,4 @@
{
- "stable": "9.3.1",
+ "stable": "9.3.6",
"testing": "9.3.0"
}
diff --git a/package.json b/package.json
index 99c2f4ef579..75bb1dbfeeb 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,9 @@
"e2e:debug": "./e2e/start-and-run-suite debug",
"e2e:dev": "./e2e/start-and-run-suite dev",
"e2e:benchmark:live": "./e2e/start-and-run-suite benchmark live",
+ "e2e:enterprise": "./e2e/start-and-run-suite enterprise",
+ "e2e:enterprise:dev": "./e2e/start-and-run-suite enterprise dev",
+ "e2e:enterprise:debug": "./e2e/start-and-run-suite enterprise debug",
"test": "jest --notify --watch",
"test:coverage": "jest --coverage",
"test:coverage:changes": "jest --coverage --changedSince=origin/main",
@@ -23,9 +26,9 @@
"test:ci": "yarn i18n:compile && mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%}",
"lint:fix": "yarn lint:ts --fix",
"packages:build": "lerna run build --ignore @grafana-plugins/input-datasource",
- "packages:clean": "lerna run clean --parallel",
+ "packages:clean": "rimraf ./npm-artifacts && lerna run clean --parallel",
"packages:prepare": "lerna version --no-push --no-git-tag-version --force-publish --exact",
- "packages:pack": "lerna exec --no-private -- yarn pack",
+ "packages:pack": "mkdir -p ./npm-artifacts && lerna exec --no-private -- yarn pack --out \"../../npm-artifacts/%s-%v.tgz\"",
"packages:publish": "lerna exec --no-private -- npm publish package.tgz",
"packages:publishCanary": "lerna exec --no-private -- npm publish package.tgz --tag canary",
"packages:publishLatest": "lerna exec --no-private -- npm publish package.tgz",
@@ -43,7 +46,7 @@
"stats": "webpack --mode production --config scripts/webpack/webpack.prod.js --profile --json > compilation-stats.json",
"storybook": "yarn workspace @grafana/ui storybook --ci",
"storybook:build": "yarn workspace @grafana/ui storybook:build",
- "themes:generate": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts",
+ "themes:generate": "esbuild ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node",
"typecheck": "tsc --noEmit && yarn run packages:typecheck",
"plugins:build-bundled": "find plugins-bundled -name package.json -not -path '*/node_modules/*' -execdir yarn build \\;",
"watch": "yarn start -d watch,start core:start --watchTheme",
@@ -54,7 +57,8 @@
"i18n:compile": "echo 'no i18n compile yet, all good'",
"betterer": "betterer",
"betterer:merge": "betterer merge",
- "betterer:stats": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/reportBettererStats.ts"
+ "betterer:stats": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/reportBettererStats.ts",
+ "betterer:issues": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/generateBettererIssues.ts"
},
"grafana": {
"whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v9-2/",
@@ -111,7 +115,7 @@
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@swc/core": "1.3.11",
"@swc/helpers": "0.4.12",
- "@testing-library/dom": "8.19.0",
+ "@testing-library/dom": "8.20.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
@@ -120,9 +124,10 @@
"@types/angular-route": "1.7.2",
"@types/common-tags": "^1.8.0",
"@types/d3": "7.4.0",
- "@types/d3-force": "^2.1.0",
- "@types/d3-scale-chromatic": "1.3.1",
+ "@types/d3-force": "^3.0.0",
+ "@types/d3-scale-chromatic": "3.0.0",
"@types/debounce-promise": "3.1.5",
+ "@types/dompurify": "^2",
"@types/eslint": "8.4.9",
"@types/file-saver": "2.0.5",
"@types/glob": "^8.0.0",
@@ -163,6 +168,7 @@
"@types/testing-library__jest-dom": "5.14.5",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "8.3.4",
+ "@types/yargs": "17.0.12",
"@typescript-eslint/eslint-plugin": "5.42.0",
"@typescript-eslint/parser": "5.42.0",
"autoprefixer": "10.4.13",
@@ -172,6 +178,8 @@
"babel-plugin-macros": "3.1.0",
"blob-polyfill": "7.0.20220408",
"browserslist": "^4.21.4",
+ "chance": "^1.0.10",
+ "codeowners": "^5.1.1",
"copy-webpack-plugin": "9.0.1",
"css-loader": "6.7.1",
"css-minimizer-webpack-plugin": "4.2.2",
@@ -179,10 +187,10 @@
"esbuild": "0.16.17",
"esbuild-loader": "2.21.0",
"esbuild-plugin-browserslist": "^0.6.0",
- "eslint": "8.31.0",
+ "eslint": "8.32.0",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-import": "^2.26.0",
- "eslint-plugin-jest": "27.1.3",
+ "eslint-plugin-jest": "27.2.1",
"eslint-plugin-jsdoc": "39.6.2",
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-lodash": "7.4.0",
@@ -190,9 +198,9 @@
"eslint-plugin-react-hooks": "4.6.0",
"eslint-webpack-plugin": "3.2.0",
"expose-loader": "4.0.0",
- "fork-ts-checker-webpack-plugin": "7.2.13",
+ "fork-ts-checker-webpack-plugin": "7.3.0",
"fs-extra": "10.1.0",
- "glob": "8.0.3",
+ "glob": "8.1.0",
"html-loader": "4.2.0",
"html-webpack-plugin": "5.5.0",
"http-server": "14.1.1",
@@ -243,7 +251,8 @@
"webpack-cli": "4.10.0",
"webpack-dev-server": "4.11.1",
"webpack-manifest-plugin": "5.0.0",
- "webpack-merge": "5.8.0"
+ "webpack-merge": "5.8.0",
+ "yargs": "^17.5.1"
},
"dependencies": {
"@daybrush/utils": "1.10.0",
@@ -262,7 +271,6 @@
"@grafana/scenes": "latest",
"@grafana/schema": "workspace:*",
"@grafana/ui": "workspace:*",
- "@jaegertracing/jaeger-ui-components": "workspace:*",
"@kusto/monaco-kusto": "5.3.6",
"@leeoniya/ufuzzy": "0.9.1",
"@lezer/common": "1.0.1",
@@ -292,8 +300,8 @@
"@visx/event": "2.17.0",
"@visx/gradient": "2.10.0",
"@visx/group": "2.10.0",
- "@visx/scale": "2.2.2",
- "@visx/shape": "2.12.2",
+ "@visx/scale": "3.0.0",
+ "@visx/shape": "3.0.0",
"@visx/tooltip": "2.16.0",
"@welldone-software/why-did-you-render": "7.0.1",
"angular": "1.8.3",
@@ -307,15 +315,17 @@
"calculate-size": "1.1.1",
"centrifuge": "3.1.0",
"classnames": "2.3.2",
+ "combokeys": "^3.0.0",
"comlink": "4.3.1",
"common-tags": "1.8.2",
"core-js": "3.27.1",
- "d3": "5.15.0",
- "d3-force": "^2.1.1",
- "d3-scale-chromatic": "1.5.0",
+ "d3": "7.8.2",
+ "d3-force": "3.0.0",
+ "d3-scale-chromatic": "3.0.0",
"dangerously-set-html-content": "1.0.9",
"date-fns": "2.29.3",
"debounce-promise": "3.1.2",
+ "dompurify": "^2.4.1",
"emotion": "11.0.0",
"eventemitter3": "4.0.7",
"fast-deep-equal": "^3.1.3",
@@ -326,15 +336,17 @@
"hoist-non-react-statics": "3.3.2",
"i18next": "^22.0.0",
"immer": "9.0.16",
- "immutable": "4.1.0",
+ "immutable": "4.2.2",
"jquery": "3.6.1",
"js-yaml": "^4.1.0",
+ "json-markup": "^1.1.0",
"json-source-map": "0.6.1",
"jsurl": "^0.1.5",
"kbar": "0.1.0-beta.36",
"lodash": "4.17.21",
"logfmt": "^1.3.2",
"lru-cache": "7.14.0",
+ "lru-memoize": "^1.1.0",
"memoize-one": "6.0.0",
"moment": "2.29.4",
"moment-timezone": "0.5.38",
@@ -351,8 +363,8 @@
"prop-types": "15.8.1",
"pseudoizer": "^0.1.0",
"rc-cascader": "3.8.0",
- "rc-drawer": "6.0.1",
- "rc-slider": "10.0.1",
+ "rc-drawer": "6.1.2",
+ "rc-slider": "10.1.0",
"rc-time-picker": "3.7.3",
"rc-tree": "5.7.0",
"re-resizable": "6.9.9",
@@ -379,6 +391,7 @@
"react-table": "7.8.0",
"react-transition-group": "4.4.5",
"react-use": "17.4.0",
+ "react-virtual": "2.8.2",
"react-virtualized-auto-sizer": "1.0.7",
"react-window": "1.8.8",
"react-window-infinite-loader": "1.0.8",
@@ -400,7 +413,8 @@
"tether-drop": "https://github.com/torkelo/drop",
"tinycolor2": "1.4.2",
"tslib": "2.4.1",
- "uplot": "1.6.23",
+ "tween-functions": "^1.2.0",
+ "uplot": "1.6.24",
"uuid": "9.0.0",
"vendor": "link:./public/vendor",
"visjs-network": "4.25.0",
@@ -434,7 +448,7 @@
]
},
"engines": {
- "node": ">= 16"
+ "node": ">= 18"
},
"packageManager": "yarn@3.3.0"
}
diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json
index 8aa90a27e4e..27a713d6c83 100644
--- a/packages/grafana-data/package.json
+++ b/packages/grafana-data/package.json
@@ -37,8 +37,8 @@
"dependencies": {
"@braintree/sanitize-url": "6.0.1",
"@grafana/schema": "9.4.0-pre",
- "@types/d3-interpolate": "^1.4.0",
- "d3-interpolate": "1.4.0",
+ "@types/d3-interpolate": "^3.0.0",
+ "d3-interpolate": "3.0.1",
"date-fns": "2.29.3",
"eventemitter3": "4.0.7",
"fast_array_intersect": "1.1.0",
@@ -54,7 +54,7 @@
"rxjs": "7.5.7",
"tinycolor2": "1.4.2",
"tslib": "2.4.1",
- "uplot": "1.6.23",
+ "uplot": "1.6.24",
"xss": "1.0.14"
},
"devDependencies": {
@@ -62,8 +62,7 @@
"@rollup/plugin-commonjs": "23.0.2",
"@rollup/plugin-json": "5.0.1",
"@rollup/plugin-node-resolve": "15.0.1",
- "@swc/helpers": "0.4.12",
- "@testing-library/dom": "8.19.0",
+ "@testing-library/dom": "8.20.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts
index f830ed1271a..3d802346614 100644
--- a/packages/grafana-data/src/field/fieldOverrides.test.ts
+++ b/packages/grafana-data/src/field/fieldOverrides.test.ts
@@ -642,7 +642,7 @@ describe('getLinksSupplier', () => {
);
const links = supplier({ valueRowIndex: 0 });
- const encodeURIParams = `{"datasource":"${datasourceUid}","queries":["12345"],"panelsState":{}}`;
+ const encodeURIParams = `{"datasource":"${datasourceUid}","queries":["12345"]}`;
expect(links.length).toBe(1);
expect(links[0]).toEqual(
expect.objectContaining({
diff --git a/packages/grafana-data/src/text/text.ts b/packages/grafana-data/src/text/text.ts
index 5b98ba11a6a..0791641a9dc 100644
--- a/packages/grafana-data/src/text/text.ts
+++ b/packages/grafana-data/src/text/text.ts
@@ -24,7 +24,7 @@ export function findHighlightChunksInText({
}
const cleanNeedle = (needle: string): string => {
- return needle.replace(/[[{(][\w,.-?:*+]+$/, '');
+ return needle.replace(/[[{(][\w,.\/:;<=>?:*+]+$/, '');
};
/**
@@ -35,14 +35,17 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
if (!haystack || !needle) {
return [];
}
+
const matches: TextMatch[] = [];
const { cleaned, flags } = parseFlags(cleanNeedle(needle));
let regexp: RegExp;
+
try {
regexp = new RegExp(`(?:${cleaned})`, flags);
} catch (error) {
return matches;
}
+
haystack.replace(regexp, (substring, ...rest) => {
if (substring) {
const offset = rest[rest.length - 2];
@@ -55,6 +58,7 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
}
return '';
});
+
return matches;
}
diff --git a/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts b/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts
index 53fa647eb5a..8fa028285bc 100644
--- a/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts
+++ b/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts
@@ -1,3 +1,5 @@
+import { Subscription } from 'rxjs';
+
import { toDataFrame, toDataFrameDTO } from '../../dataframe';
import { DataFrame, DataTransformerConfig, FieldDTO, FieldType } from '../../types';
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
@@ -6,11 +8,73 @@ import { transformDataFrame } from '../transformDataFrame';
import { DataTransformerID } from './ids';
import { LabelsToFieldsMode, LabelsToFieldsOptions, labelsToFieldsTransformer } from './labelsToFields';
+function labelsToFieldTransform(source: DataFrame[]): Promise {
+ const cfg: DataTransformerConfig = {
+ id: DataTransformerID.labelsToFields,
+ options: {
+ mode: LabelsToFieldsMode.Rows,
+ },
+ };
+
+ const observable = transformDataFrame([cfg], source);
+
+ return new Promise((resolve, reject) => {
+ const subscription = new Subscription();
+
+ subscription.add(
+ observable.subscribe({
+ next: (value) => {
+ subscription.unsubscribe();
+ resolve(JSON.parse(JSON.stringify(value)));
+ },
+ error: (err) => {
+ subscription.unsubscribe();
+ reject(err);
+ },
+ })
+ );
+ });
+}
+
describe('Labels as Columns', () => {
beforeAll(() => {
mockTransformationsRegistry([labelsToFieldsTransformer]);
});
+ it('transform keep the refId of dataFrames', async () => {
+ const input = [
+ toDataFrame({
+ refId: 'the-ref-id-A',
+ fields: [
+ { name: 'time', type: FieldType.time, values: [1000, 2000] },
+ { name: 'Value', type: FieldType.number, values: [1, 2], labels: { labelA: 'valueA', labelB: 'valueB' } },
+ ],
+ }),
+ toDataFrame({
+ refId: 'the-ref-id-B',
+ fields: [
+ { name: 'time', type: FieldType.time, values: [1000, 2000] },
+ { name: 'Value', type: FieldType.number, values: [1, 2], labels: { labelA: 'valueA', labelB: 'valueB' } },
+ ],
+ }),
+ ];
+
+ const output = await labelsToFieldTransform(input);
+
+ const expectedOutput = [
+ {
+ refId: 'the-ref-id-A',
+ },
+ {
+ refId: 'the-ref-id-B',
+ },
+ ];
+
+ for (let i = 0; i < output.length; i++) {
+ expect(output[i]).toMatchObject(expectedOutput[i]);
+ }
+ });
+
it('data frame with two labels', async () => {
const cfg: DataTransformerConfig = {
id: DataTransformerID.labelsToFields,
diff --git a/packages/grafana-data/src/transformations/transformers/labelsToFields.ts b/packages/grafana-data/src/transformations/transformers/labelsToFields.ts
index 92a9742dfce..1bdb30df52c 100644
--- a/packages/grafana-data/src/transformations/transformers/labelsToFields.ts
+++ b/packages/grafana-data/src/transformations/transformers/labelsToFields.ts
@@ -90,6 +90,7 @@ export const labelsToFieldsTransformer: SynchronousDataTransformerInfo void): React.ReactNode;
}
export const hasLogsContextSupport = (datasource: unknown): datasource is DataSourceWithLogsContextSupport => {
@@ -195,11 +202,23 @@ export enum SupplementaryQueryType {
* @internal
*/
export interface DataSourceWithSupplementaryQueriesSupport {
+ /**
+ * Returns an observable that will be used to fetch supplementary data based on the provided
+ * supplementary query type and original request.
+ */
getDataProvider(
type: SupplementaryQueryType,
request: DataQueryRequest
): Observable | undefined;
+ /**
+ * Returns supplementary query types that data source supports.
+ */
getSupportedSupplementaryQueryTypes(): SupplementaryQueryType[];
+ /**
+ * Returns a supplementary query to be used to fetch supplementary data based on the provided type and original query.
+ * If provided query is not suitable for provided supplementary query type, undefined should be returned.
+ */
+ getSupplementaryQuery(type: SupplementaryQueryType, query: TQuery): TQuery | undefined;
}
export const hasSupplementaryQuerySupport = (
@@ -214,6 +233,17 @@ export const hasSupplementaryQuerySupport = (
return (
withSupplementaryQueriesSupport.getDataProvider !== undefined &&
+ withSupplementaryQueriesSupport.getSupplementaryQuery !== undefined &&
withSupplementaryQueriesSupport.getSupportedSupplementaryQueryTypes().includes(type)
);
};
+
+export const hasLogsContextUiSupport = (datasource: unknown): datasource is DataSourceWithLogsContextSupport => {
+ if (!datasource) {
+ return false;
+ }
+
+ const withLogsSupport = datasource as DataSourceWithLogsContextSupport;
+
+ return withLogsSupport.getLogRowContextUi !== undefined;
+};
diff --git a/packages/grafana-data/src/utils/dataLinks.test.ts b/packages/grafana-data/src/utils/dataLinks.test.ts
index accaf0aab22..89784452605 100644
--- a/packages/grafana-data/src/utils/dataLinks.test.ts
+++ b/packages/grafana-data/src/utils/dataLinks.test.ts
@@ -1,4 +1,4 @@
-import { DataLink, FieldType } from '../types';
+import { DataLink, FieldType, TimeRange } from '../types';
import { ArrayVector } from '../vector';
import { mapInternalLinkToExplore } from './dataLinks';
@@ -19,7 +19,7 @@ describe('mapInternalLinkToExplore', () => {
link: dataLink,
internalLink: dataLink.internal,
scopedVars: {},
- range: {} as any,
+ range: {} as unknown as TimeRange,
field: {
name: 'test',
type: FieldType.number,
@@ -32,9 +32,7 @@ describe('mapInternalLinkToExplore', () => {
expect(link).toEqual(
expect.objectContaining({
title: 'dsName',
- href: `/explore?left=${encodeURIComponent(
- '{"datasource":"uid","queries":[{"query":"12344"}],"panelsState":{}}'
- )}`,
+ href: `/explore?left=${encodeURIComponent('{"datasource":"uid","queries":[{"query":"12344"}]}')}`,
onClick: undefined,
})
);
@@ -62,7 +60,7 @@ describe('mapInternalLinkToExplore', () => {
link: dataLink,
internalLink: dataLink.internal!,
scopedVars: {},
- range: {} as any,
+ range: {} as unknown as TimeRange,
field: {
name: 'test',
type: FieldType.number,
@@ -82,4 +80,56 @@ describe('mapInternalLinkToExplore', () => {
})
);
});
+
+ it('interpolates query correctly', () => {
+ const dataLink = {
+ url: '',
+ title: '',
+ internal: {
+ datasourceUid: 'uid',
+ datasourceName: 'dsName',
+ query: {
+ query: '$var $var',
+ // Should not interpolate keys
+ $var: 'foo',
+ nested: {
+ something: '$var',
+ },
+ num: 1,
+ arr: ['$var', 'non var'],
+ },
+ },
+ };
+
+ const link = mapInternalLinkToExplore({
+ link: dataLink,
+ internalLink: dataLink.internal,
+ scopedVars: {
+ var1: { text: '', value: 'val1' },
+ },
+ range: {} as unknown as TimeRange,
+ field: {
+ name: 'test',
+ type: FieldType.number,
+ config: {},
+ values: new ArrayVector([2]),
+ },
+ replaceVariables: (val, scopedVars) => val.replace(/\$var/g, scopedVars!['var1'].value),
+ });
+
+ expect(decodeURIComponent(link.href)).toEqual(
+ `/explore?left=${JSON.stringify({
+ datasource: 'uid',
+ queries: [
+ {
+ query: 'val1 val1',
+ $var: 'foo',
+ nested: { something: 'val1' },
+ num: 1,
+ arr: ['val1', 'non var'],
+ },
+ ],
+ })}`
+ );
+ });
});
diff --git a/packages/grafana-data/src/utils/dataLinks.ts b/packages/grafana-data/src/utils/dataLinks.ts
index 4aadad9ac0c..b0b47b0e373 100644
--- a/packages/grafana-data/src/utils/dataLinks.ts
+++ b/packages/grafana-data/src/utils/dataLinks.ts
@@ -88,30 +88,38 @@ function generateInternalHref(
);
}
-function interpolateObject(
- object: T | undefined,
+function interpolateObject(
+ obj: T | undefined,
+ scopedVars: ScopedVars,
+ replaceVariables: InterpolateFunction
+): T | undefined {
+ if (!obj) {
+ return obj;
+ }
+ if (typeof obj === 'string') {
+ // @ts-ignore this is complaining we are returning string, but we are checking if obj is a string so should be fine.
+ return replaceVariables(obj, scopedVars);
+ }
+ const copy = JSON.parse(JSON.stringify(obj));
+ return interpolateObjectRecursive(copy, scopedVars, replaceVariables);
+}
+
+function interpolateObjectRecursive(
+ obj: T,
scopedVars: ScopedVars,
replaceVariables: InterpolateFunction
): T {
- let stringifiedQuery = '';
- try {
- stringifiedQuery = JSON.stringify(object || {});
- } catch (err) {
- // should not happen and not much to do about this, possibly something non stringifiable in the query
- console.error(err);
+ for (const k of Object.keys(obj)) {
+ // Honestly not sure how to type this to make TS happy.
+ // @ts-ignore
+ if (typeof obj[k] === 'string') {
+ // @ts-ignore
+ obj[k] = replaceVariables(obj[k], scopedVars);
+ // @ts-ignore
+ } else if (typeof obj[k] === 'object' && obj[k] !== null) {
+ // @ts-ignore
+ obj[k] = interpolateObjectRecursive(obj[k], scopedVars, replaceVariables);
+ }
}
-
- // Replace any variables inside the query. This may not be the safest as it can also replace keys etc so may not
- // actually work with every datasource query right now.
- stringifiedQuery = replaceVariables(stringifiedQuery, scopedVars);
-
- let replacedQuery = {} as T;
- try {
- replacedQuery = JSON.parse(stringifiedQuery);
- } catch (err) {
- // again should not happen and not much to do about this, probably some issue with how we replaced the variables.
- console.error(stringifiedQuery, err);
- }
-
- return replacedQuery;
+ return obj;
}
diff --git a/packages/grafana-e2e/src/flows/addDataSource.ts b/packages/grafana-e2e/src/flows/addDataSource.ts
index c38947bba44..4737d1e726a 100644
--- a/packages/grafana-e2e/src/flows/addDataSource.ts
+++ b/packages/grafana-e2e/src/flows/addDataSource.ts
@@ -26,7 +26,7 @@ export const addDataSource = (config?: Partial) => {
form: () => {},
name: `e2e-${uuidv4()}`,
skipTlsVerify: false,
- type: 'TestData DB',
+ type: 'TestData',
...config,
};
diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json
index b39f8343576..41a95599839 100644
--- a/packages/grafana-runtime/package.json
+++ b/packages/grafana-runtime/package.json
@@ -52,7 +52,7 @@
"@grafana/tsconfig": "^1.2.0-rc1",
"@rollup/plugin-commonjs": "23.0.2",
"@rollup/plugin-node-resolve": "15.0.1",
- "@testing-library/dom": "8.19.0",
+ "@testing-library/dom": "8.20.0",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "14.4.3",
diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts
index a9ef4dbf9b3..c6614026f04 100644
--- a/packages/grafana-runtime/src/config.ts
+++ b/packages/grafana-runtime/src/config.ts
@@ -27,6 +27,7 @@ export interface AzureSettings {
export class GrafanaBootConfig implements GrafanaConfig {
isPublicDashboardView: boolean;
+ snapshotEnabled = true;
datasources: { [str: string]: DataSourceInstanceSettings } = {};
panels: { [key: string]: PanelPluginMeta } = {};
auth: AuthSettings = {};
@@ -110,6 +111,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
pluginAdminEnabled = true;
pluginAdminExternalManageEnabled = false;
pluginCatalogHiddenPlugins: string[] = [];
+ pluginsCDNBaseURL = '';
expressionsEnabled = false;
customTheme?: undefined;
awsAllowedAuthProviders: string[] = [];
diff --git a/packages/grafana-schema/package.json b/packages/grafana-schema/package.json
index ccf6c391f71..1656ca5f07b 100644
--- a/packages/grafana-schema/package.json
+++ b/packages/grafana-schema/package.json
@@ -40,7 +40,6 @@
"@rollup/plugin-commonjs": "23.0.2",
"@rollup/plugin-json": "5.0.1",
"@rollup/plugin-node-resolve": "15.0.1",
- "@swc/helpers": "0.4.12",
"esbuild": "0.16.17",
"rimraf": "3.0.2",
"rollup": "2.79.1",
diff --git a/packages/grafana-schema/src/common/common.gen.ts b/packages/grafana-schema/src/common/common.gen.ts
index 9557cee3648..b4bf0b99b55 100644
--- a/packages/grafana-schema/src/common/common.gen.ts
+++ b/packages/grafana-schema/src/common/common.gen.ts
@@ -51,6 +51,27 @@ export interface DataQuery {
refId: string;
}
+export interface BaseDimensionConfig {
+ field?: string;
+ fixed: (string | number);
+}
+
+export interface ScaleDimensionConfig extends BaseDimensionConfig {
+ max: number;
+ min: number;
+}
+
+/**
+ * This is actually an empty interface used mainly for naming?
+ */
+export interface ColorDimensionConfig extends BaseDimensionConfig {}
+
+export enum TextDimensionMode {
+ Field = 'field',
+ Fixed = 'fixed',
+ Template = 'template',
+}
+
export interface MapLayerOptions {
/**
* Custom options depending on the type
@@ -464,6 +485,11 @@ export enum BigValueTextMode {
*/
export type FieldTextAlignment = ('auto' | 'left' | 'right' | 'center');
+/**
+ * Controls the value alignment in the TimelineChart component
+ */
+export type TimelineValueAlignment = ('center' | 'left' | 'right');
+
/**
* TODO docs
*/
@@ -651,6 +677,10 @@ export interface DataSourceRef {
uid?: string;
}
+export interface TextDimensionConfig extends BaseDimensionConfig {
+ mode: TextDimensionMode;
+}
+
export interface FrameGeometrySource {
/**
* Path to Gazetteer
diff --git a/packages/grafana-schema/src/common/dimensions.cue b/packages/grafana-schema/src/common/dimensions.cue
new file mode 100644
index 00000000000..14899a59c47
--- /dev/null
+++ b/packages/grafana-schema/src/common/dimensions.cue
@@ -0,0 +1,25 @@
+package common
+
+BaseDimensionConfig: {
+ field?: string
+ fixed: string | number
+}@cuetsy(kind="interface")
+
+ScaleDimensionConfig: {
+ BaseDimensionConfig
+ min: int32
+ max: int32
+}@cuetsy(kind="interface")
+
+// This is actually an empty interface used mainly for naming?
+ColorDimensionConfig: {
+ BaseDimensionConfig
+ _empty: _
+}@cuetsy(kind="interface")
+
+TextDimensionMode: "fixed" | "field" | "template" @cuetsy(kind="enum")
+
+TextDimensionConfig: {
+ BaseDimensionConfig
+ mode: TextDimensionMode
+}@cuetsy(kind="interface")
diff --git a/packages/grafana-schema/src/common/mudball.cue b/packages/grafana-schema/src/common/mudball.cue
index 537c6549570..bdb88a689e5 100644
--- a/packages/grafana-schema/src/common/mudball.cue
+++ b/packages/grafana-schema/src/common/mudball.cue
@@ -193,6 +193,9 @@ BigValueTextMode: "auto" | "value" | "value_and_name" | "name" | "none" @cuetsy(
// TODO docs
FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(kind="type")
+// Controls the value alignment in the TimelineChart component
+TimelineValueAlignment: "center" | "left" | "right" @cuetsy(kind="type")
+
// TODO docs
VizTextDisplayOptions: {
// Explicit title text size
diff --git a/packages/grafana-schema/src/index.gen.ts b/packages/grafana-schema/src/index.gen.ts
index 9fdf476a0e4..a7a76a0c3df 100644
--- a/packages/grafana-schema/src/index.gen.ts
+++ b/packages/grafana-schema/src/index.gen.ts
@@ -95,6 +95,24 @@ export type {
// Raw generated enums and default consts from playlist kind.
export { defaultPlaylist } from './raw/playlist/x/playlist_types.gen';
+// Raw generated types from Preferences kind.
+export type {
+ Preferences,
+ QueryHistoryPreference
+} from './raw/preferences/x/preferences_types.gen';
+
+// Raw generated types from PublicDashboard kind.
+export type { PublicDashboard } from './raw/publicdashboard/x/publicdashboard_types.gen';
+
+// Raw generated types from ServiceAccount kind.
+export type {
+ ServiceAccount,
+ OrgRole
+} from './raw/serviceaccount/x/serviceaccount_types.gen';
+
+// Raw generated enums and default consts from serviceaccount kind.
+export { defaultServiceAccount } from './raw/serviceaccount/x/serviceaccount_types.gen';
+
// Raw generated types from Team kind.
export type { Team } from './raw/team/x/team_types.gen';
diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts
index fad06ea56cf..706d6522ba9 100644
--- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts
+++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts
@@ -647,10 +647,13 @@ export interface Dashboard {
*/
editable: boolean;
/**
- * TODO docs
+ * The month that the fiscal year starts on. 0 = January, 11 = December
*/
fiscalYearStartMonth?: number;
gnetId?: string;
+ /**
+ * Configuration of dashboard cursor sync behavior.
+ */
graphTooltip: DashboardCursorSync;
/**
* Unique numeric identifier for the dashboard.
@@ -667,7 +670,7 @@ export interface Dashboard {
liveNow?: boolean;
panels?: Array<(Panel | RowPanel | GraphPanel | HeatmapPanel)>;
/**
- * TODO docs
+ * Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d".
*/
refresh?: (string | false);
/**
@@ -774,9 +777,9 @@ export interface Dashboard {
time_options: Array;
};
/**
- * Timezone of dashboard,
+ * Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc".
*/
- timezone?: ('browser' | 'utc' | '');
+ timezone?: string;
/**
* Title of dashboard.
*/
@@ -797,6 +800,7 @@ export interface Dashboard {
export const defaultDashboard: Partial = {
editable: true,
+ fiscalYearStartMonth: 0,
graphTooltip: DashboardCursorSync.Off,
links: [],
panels: [],
diff --git a/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts b/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts
new file mode 100644
index 00000000000..e18a9375d62
--- /dev/null
+++ b/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts
@@ -0,0 +1,44 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+//
+// Generated by:
+// kinds/gen.go
+// Using jennies:
+// TSTypesJenny
+// LatestMajorsOrXJenny
+//
+// Run 'make gen-cue' from repository root to regenerate.
+
+export interface QueryHistoryPreference {
+ /**
+ * one of: '' | 'query' | 'starred';
+ */
+ homeTab?: string;
+}
+
+export interface Preferences {
+ /**
+ * UID for the home dashboard
+ */
+ homeDashboardUID?: string;
+ /**
+ * Selected language (beta)
+ */
+ language?: string;
+ /**
+ * Explore query history preferences
+ */
+ queryHistory?: QueryHistoryPreference;
+ /**
+ * light, dark, empty is default
+ */
+ theme?: string;
+ /**
+ * The timezone selection
+ * TODO: this should use the timezone defined in common
+ */
+ timezone?: string;
+ /**
+ * day of the week (sunday, monday, etc)
+ */
+ weekStart?: string;
+}
diff --git a/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts b/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts
new file mode 100644
index 00000000000..b94e4baefe3
--- /dev/null
+++ b/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts
@@ -0,0 +1,36 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+//
+// Generated by:
+// kinds/gen.go
+// Using jennies:
+// TSTypesJenny
+// LatestMajorsOrXJenny
+//
+// Run 'make gen-cue' from repository root to regenerate.
+
+export interface PublicDashboard {
+ /**
+ * Unique public access token
+ */
+ accessToken?: string;
+ /**
+ * Flag that indicates if annotations are enabled
+ */
+ annotationsEnabled: boolean;
+ /**
+ * Dashboard unique identifier referenced by this public dashboard
+ */
+ dashboardUid: string;
+ /**
+ * Flag that indicates if the public dashboard is enabled
+ */
+ isEnabled: boolean;
+ /**
+ * Flag that indicates if the time range picker is enabled
+ */
+ timeSelectionEnabled: boolean;
+ /**
+ * Unique public dashboard identifier
+ */
+ uid: string;
+}
diff --git a/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts b/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts
new file mode 100644
index 00000000000..a06b06ff31f
--- /dev/null
+++ b/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts
@@ -0,0 +1,71 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+//
+// Generated by:
+// kinds/gen.go
+// Using jennies:
+// TSTypesJenny
+// LatestMajorsOrXJenny
+//
+// Run 'make gen-cue' from repository root to regenerate.
+
+/**
+ * OrgRole is a Grafana Organization Role which can be 'Viewer', 'Editor', 'Admin'.
+ */
+export type OrgRole = ('Admin' | 'Editor' | 'Viewer');
+
+export interface ServiceAccount {
+ /**
+ * AccessControl metadata associated with a given resource.
+ */
+ accessControl?: Record;
+ /**
+ * AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front
+ * of the service account.
+ */
+ avatarUrl: string;
+ /**
+ * Created indicates when the service account was created.
+ */
+ created?: number;
+ /**
+ * ID is the unique identifier of the service account in the database.
+ */
+ id: number;
+ /**
+ * IsDisabled indicates if the service account is disabled.
+ */
+ isDisabled: boolean;
+ /**
+ * Login of the service account.
+ */
+ login: string;
+ /**
+ * Name of the service account.
+ */
+ name: string;
+ /**
+ * OrgId is the ID of an organisation the service account belongs to.
+ */
+ orgId: number;
+ /**
+ * Role is the Grafana organization role of the service account which can be 'Viewer', 'Editor', 'Admin'.
+ */
+ role: OrgRole;
+ /**
+ * Teams is a list of teams the service account belongs to.
+ */
+ teams?: Array;
+ /**
+ * Tokens is the number of active tokens for the service account.
+ * Tokens are used to authenticate the service account against Grafana.
+ */
+ tokens: number;
+ /**
+ * Updated indicates when the service account was updated.
+ */
+ updated?: number;
+}
+
+export const defaultServiceAccount: Partial = {
+ teams: [],
+};
diff --git a/packages/grafana-schema/src/veneer/common.types.ts b/packages/grafana-schema/src/veneer/common.types.ts
index 1e35686cbbe..5ae07a1e2c1 100644
--- a/packages/grafana-schema/src/veneer/common.types.ts
+++ b/packages/grafana-schema/src/veneer/common.types.ts
@@ -12,5 +12,14 @@ export interface DataQuery extends raw.DataQuery {
// TODO remove explicit nulls
datasource?: raw.DataSourceRef | null;
}
+export interface BaseDimensionConfig extends Omit {
+ fixed: T;
+}
+
+export interface ScaleDimensionConfig extends BaseDimensionConfig, Omit {}
+
+export interface TextDimensionConfig extends BaseDimensionConfig, Omit {}
+
+export interface ColorDimensionConfig extends BaseDimensionConfig, Omit {}
export * from '../common/common.gen';
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/Dockerfile b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/Dockerfile
deleted file mode 100644
index 3907fe9c05a..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM alpine:3.15.6
-
-USER root
-
-COPY scripts scripts
-COPY install /usr/local
-
-WORKDIR scripts
-
-RUN ./deploy.sh
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/README.md b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/README.md
deleted file mode 100644
index 94b42aefd3d..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/README.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Using this docker image
-
-Uploaded to dockerhub as grafana/grafana-plugin-ci:latest-alpine
-
-Based off of `circleci/node:12-browsers`
-
-## User
-
-The user will be `circleci`
-The home directory will be `/home/circleci`
-
-## Node
-
-- node 12 is installed
-- yarn is installed globally
-- npm is installed globally
-
-## Go
-
-- Go is installed in `/usr/local/bin/go`
-- golangci-lint is installed in `/usr/local/bin/golangci-lint`
-- mage is installed in `/home/circleci/go/bin/mage`
-
-All of the above directories are in the path, so there is no need to specify fully qualified paths.
-
-## Grafana
-
-- Installed in `/home/circleci/src/grafana`
-- `yarn install` has been run
-
-## Integration/Release Testing
-
-There are 4 previous versions pre-downloaded to /usr/local/grafana. These versions are:
-
-1. 6.6.2
-2. 6.5.3
-3. 6.4.5
-4. 6.3.7
-
-To test, your CircleCI config will need a run section with something similar to the following
-
-```
-- run:
- name: Setup Grafana (local install)
- command: |
- sudo dpkg -i /usr/local/grafana/deb/grafana_6.6.2_amd64.deb
- sudo cp ci/grafana-test-env/custom.ini /usr/share/grafana/conf/custom.ini
- sudo cp ci/grafana-test-env/custom.ini /etc/grafana/grafana.ini
- sudo service grafana-server start
- grafana-cli --version
-```
-
-# Building
-
-To build, cd to `/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine`
-
-```
-./build.sh
-```
-
-# Developing/Testing
-
-To test, you should have docker-compose installed.
-
-```
-cd test
-./start.sh
-```
-
-You will be in /home/circleci/test with the buildscripts installed to the local directory.
-Do your edits/run tests. When saving, your edits will be available in the container immediately.
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/build.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/build.sh
deleted file mode 100755
index f024e617055..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/build.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-set -eo pipefail
-
-source ./common.sh
-
-#
-# No longer required, but useful to keep just in case we want to deploy
-# changes in toolkit directly to the docker image
-#
-if [ -n "$INCLUDE_TOOLKIT" ]; then
- /bin/rm -rfv install/grafana-toolkit
- mkdir -pv install/grafana-toolkit
- cp -rv ../../bin install/grafana-toolkit
- cp -rv ../../src install/grafana-toolkit
- cp -v ../../package.json install/grafana-toolkit
- cp -v ../../tsconfig.json install/grafana-toolkit
-fi
-
-docker build -t ${DOCKER_IMAGE_NAME} .
-docker push $DOCKER_IMAGE_NAME
-
-[ -n "$INCLUDE_TOOLKIT" ] && /bin/rm -rfv install/grafana-toolkit
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/common.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/common.sh
deleted file mode 100755
index 8a99e1e649b..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/common.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-##
-## Common variable declarations
-## Find the latest tags on https://hub.docker.com/r/grafana/grafana-plugin-ci/tags?page=1&name=alpine
-##
-
-DOCKER_IMAGE_NAME="grafana/grafana-plugin-ci:1.6.1-alpine"
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/cp b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/cp
deleted file mode 100755
index fd23c3db871..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/cp
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-if [ "$1" == "-rn" ]; then
- false | busybox cp -i -r "$2" "$3" 2>/dev/null
-else
- busybox cp $*
-fi
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/ginstall b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/ginstall
deleted file mode 100755
index 537305a1f72..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/ginstall
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/sh
-##
-# gget
-# A script to get and install grafana versions
-# for usage information see "show_help" below.
-#
-
-latest=$(wget -O - 'https://raw.githubusercontent.com/grafana/grafana/main/latest.json' | jq -r '.stable')
-canary=$(wget -O - "https://grafana.com/api/grafana/versions" | jq ".items[0].version" | tr -d '"')
-
-show_help() {
- echo "Usage: gget "
- echo ""
- echo "where can be:"
- echo " 1) A version from https://grafana.com/grafana/download (ex x.y.z)"
- echo " 2) latest (currently $latest)"
- echo " 3) canary (currently $canary)"
- echo ""
- echo " -h, --help: Display this help message"
- echo ""
- exit 0
-}
-
-opts=$(getopt -o h --long help -n 'gget' -- "$@")
-[ $? -eq 0 ] || {
- show_help
-}
-
-eval set -- "$opts"
-while true; do
- case "$1" in
- -h | --help)
- show_help
- ;;
- --)
- shift
- break
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-[ -z "$1" ] && show_help
-
-# Make sure the script is being run as root
-if [ $EUID -ne 0 ]; then
- echo "This script must be run as root"
- exit 1
-fi
-
-##
-# MAIN
-#
-# Enough setup, let's actually do something
-#
-version=$1
-if [ "$version" == "latest" ]; then
- version="$latest"
- wget -O - "https://dl.grafana.com/oss/release/grafana-${version}.linux-amd64.tar.gz" | tar -C /opt -zxf -
-elif [ "$version" == "canary" ]; then
- version="$canary"
- wget -O - "https://dl.grafana.com/oss/main/grafana-${version}.linux-amd64.tar.gz" | tar -C /opt -zxf -
-else
- wget -O - "https://dl.grafana.com/oss/release/grafana-${version}.linux-amd64.tar.gz" | tar -C /opt -zxf -
-fi
-
-/bin/rm -rf /opt/grafana > /dev/null 2>&1 || true
-ln -s /opt/grafana-${version} /opt/grafana
-
-# nohup /opt/grafana/bin/grafana-server -config /opt/grafana/conf/defaults.ini -homepath /opt/grafana >/dev/null 2>&1 &
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/githubRelease.js b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/githubRelease.js
deleted file mode 100644
index 849836da380..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/install/bin/githubRelease.js
+++ /dev/null
@@ -1,154 +0,0 @@
-'use strict';
-Object.defineProperty(exports, '__esModule', { value: true });
-const fs = require('fs');
-const path = require('path');
-const tslib_1 = require('tslib');
-
-const getPluginId_1 = require('../../config/utils/getPluginId');
-const pluginValidation_1 = require('../../config/utils/pluginValidation');
-const env_1 = require('../../plugins/env');
-// @ts-ignore
-// import execa = require('execa');
-const githubClient_1 = tslib_1.__importDefault(require('./githubClient'));
-const resolveContentType = function (extension) {
- if (extension.startsWith('.')) {
- extension = extension.slice(1);
- }
- switch (extension) {
- case 'zip':
- return 'application/zip';
- case 'json':
- return 'application/json';
- case 'sha1':
- return 'text/plain';
- default:
- return 'application/octet-stream';
- }
-};
-const GitHubRelease = /** @class */ (function () {
- function GitHubRelease(token, username, repository, releaseNotes, commitHash) {
- this.token = token;
- this.username = username;
- this.repository = repository;
- this.releaseNotes = releaseNotes;
- this.commitHash = commitHash;
- this.git = new githubClient_1.default({
- required: true,
- repo: repository,
- });
- }
- GitHubRelease.prototype.publishAssets = function (srcLocation, destUrl) {
- const _this = this;
- // Add the assets. Loop through files in the ci/dist folder and upload each asset.
- const files = fs.readdirSync(srcLocation);
- return files.map(function (file) {
- return tslib_1.__awaiter(_this, void 0, void 0, function () {
- let fileStat, fileData;
- return tslib_1.__generator(this, function (_a) {
- fileStat = fs.statSync(srcLocation + '/' + file);
- fileData = fs.readFileSync(srcLocation + '/' + file);
- return [
- 2 /*return*/,
- this.git.client.post(destUrl + '?name=' + file, fileData, {
- headers: {
- 'Content-Type': resolveContentType(path.extname(file)),
- 'Content-Length': fileStat.size,
- },
- maxContentLength: fileStat.size * 2 * 1024 * 1024,
- }),
- ];
- });
- });
- });
- };
- GitHubRelease.prototype.release = function () {
- let _a, _b, _c, _d;
- return tslib_1.__awaiter(this, void 0, void 0, function () {
- let ciDir,
- distDir,
- distContentDir,
- pluginJsonFile,
- pluginInfo,
- PUBLISH_DIR,
- commitHash,
- latestRelease,
- reason_1,
- newReleaseResponse,
- publishPromises,
- reason_2;
- return tslib_1.__generator(this, function (_e) {
- switch (_e.label) {
- case 0:
- ciDir = env_1.getCiFolder();
- distDir = path.resolve(ciDir, 'dist');
- distContentDir = path.resolve(distDir, getPluginId_1.getPluginId());
- pluginJsonFile = path.resolve(distContentDir, 'plugin.json');
- pluginInfo = pluginValidation_1.getPluginJson(pluginJsonFile).info;
- PUBLISH_DIR = path.resolve(env_1.getCiFolder(), 'packages');
- commitHash = this.commitHash || ((_a = pluginInfo.build) === null || _a === void 0 ? void 0 : _a.hash);
- _e.label = 1;
- case 1:
- _e.trys.push([1, 5, , 6]);
- return [4 /*yield*/, this.git.client.get('releases/tags/v' + pluginInfo.version)];
- case 2:
- latestRelease = _e.sent();
- if (!(latestRelease.data.tag_name === 'v' + pluginInfo.version)) {
- return [3 /*break*/, 4];
- }
- return [4 /*yield*/, this.git.client.delete('releases/' + latestRelease.data.id)];
- case 3:
- _e.sent();
- _e.label = 4;
- case 4:
- return [3 /*break*/, 6];
- case 5:
- reason_1 = _e.sent();
- if (reason_1.response.status !== 404) {
- // 404 just means no release found. Not an error. Anything else though, re throw the error
- throw reason_1;
- }
- return [3 /*break*/, 6];
- case 6:
- _e.trys.push([6, 9, , 10]);
- return [
- 4 /*yield*/,
- this.git.client.post('releases', {
- tag_name: 'v' + pluginInfo.version,
- target_commitish: commitHash,
- name: 'v' + pluginInfo.version,
- body: this.releaseNotes,
- draft: false,
- prerelease: false,
- }),
- ];
- case 7:
- newReleaseResponse = _e.sent();
- publishPromises = this.publishAssets(
- PUBLISH_DIR,
- 'https://uploads.github.com/repos/' +
- this.username +
- '/' +
- this.repository +
- '/releases/' +
- newReleaseResponse.data.id +
- '/assets'
- );
- return [4 /*yield*/, Promise.all(publishPromises)];
- case 8:
- _e.sent();
- return [3 /*break*/, 10];
- case 9:
- reason_2 = _e.sent();
- console.log(reason_2);
- // Rethrow the error so that we can trigger a non-zero exit code to circle-ci
- throw reason_2;
- case 10:
- return [2 /*return*/];
- }
- });
- });
- };
- return GitHubRelease;
-})();
-exports.GitHubRelease = GitHubRelease;
-//# sourceMappingURL=githubRelease.js.map7027e10521e9
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy-common.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy-common.sh
deleted file mode 100755
index 9263691386f..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy-common.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/sh
-
-##
-# Script to deploy a docker image. Must return exit code 0
-#
-do_exit() {
- message="$1"
- exit_code="$2"
-
- echo "$message"
- exit $exit_code
-}
-
-
-##
-# Get file, get's a file, validates the SHA
-# @param filename
-# @param expected sha value
-# @returns 0 if successful, -1 of checksum validation failed.
-#
-get_file () {
- [ -n "$1" ] && url=$1 || do_exit "url required" 1
- [ -n "$2" ] && dest=$2 || do_exit "destination required" 2
- sha=$3
- file=$(basename $dest)
-
- curl -fL "${url}" -o "$dest"
- if [ -n "$sha" ]; then
- echo "$sha $dest" | sha256sum || do_exit "Checksum validation failed for $file. Exiting" 1
- fi
-}
-
-untar_file () {
- [ -n "$1" ] && src=$1 || do_exit "src required" 1
- [ -n "$2" ] && dest=$2 || dest="/usr/local"
-
- tar -C "$dest" -xf "$src" && /bin/rm -rf "$src"
-}
-
-##
-# WIP: Just started this and not finished.
-# The intent it to download a release from a git repo,
-# compile, and install
-get_latest_release () {
- tarsrc=$(curl -sL "https://api.github.com/repos/$1/$2/releases/latest" | jq ".tarball_url" | tr -d '"')
- curl -fL -o /tmp/autoretrieved.tar.gz "$tarsrc"
- origdir=$PWD
- reponame=$(tar zxvf autoretrieved.tar.gz | tail -1 | awk -F / '{print $1}')
- cd "/tmp/$reponame"
- #perform compile
- cd $origdir
-}
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy-user.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy-user.sh
deleted file mode 100755
index f8926ec4678..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy-user.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-source "./deploy-common.sh"
-
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy.sh
deleted file mode 100755
index 94345950078..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/scripts/deploy.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/sh
-set -eo pipefail
-
-source "./deploy-common.sh"
-
-# Make libgcc compatible
-mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
-
-# Replace cp with something that mocks the one that ci-package needs
-rm /bin/cp
-mv /usr/local/bin/cp /bin/cp
-
-apk add --no-cache curl npm yarn build-base openssh git-lfs perl-utils coreutils python3
-
-#
-# Only relevant for testing, but cypress does not work with musl/alpine.
-#
-# apk add --no-cache xvfb glib nss nspr gdk-pixbuf "gtk+3.0" pango atk cairo dbus-libs libxcomposite libxrender libxi libxtst libxrandr libxscrnsaver alsa-lib at-spi2-atk at-spi2-core cups-libs gcompat libc6-compat
-
-# Install Go
-filename="go1.19.4.linux-amd64.tar.gz"
-get_file "https://dl.google.com/go/$filename" "/tmp/$filename" "c9c08f783325c4cf840a94333159cc937f05f75d36a8b307951d5bd959cf2ab8"
-untar_file "/tmp/$filename"
-
-# Install golangci-lint
-GOLANGCILINT_VERSION=1.50.0
-filename="golangci-lint-${GOLANGCILINT_VERSION}-linux-amd64"
-get_file "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCILINT_VERSION}/$filename.tar.gz" \
- "/tmp/$filename.tar.gz" \
- "b4b329efcd913082c87d0e9606711ecb57415b5e6ddf233fde9e76c69d9b4e8b"
-untar_file "/tmp/$filename.tar.gz"
-ln -s /usr/local/${filename}/golangci-lint /usr/local/bin/golangci-lint
-ln -s /usr/local/go/bin/go /usr/local/bin/go
-ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt
-chmod 755 /usr/local/bin/golangci-lint
-
-# Install dependencies
-apk add --no-cache fontconfig zip jq
-
-# Install code climate
-get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64" \
- "/usr/local/bin/cc-test-reporter" \
- "20d1d4e2b399d0287d91e65faeee8ffbef08e3262b0be5eda7def7b3c2799ddd"
-chmod 755 /usr/local/bin/cc-test-reporter
-
-curl -fL -o /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.27/grabpl"
-
-apk add --no-cache git
-# Install Mage
-mkdir -pv /tmp/mage $HOME/go/bin
-git clone https://github.com/magefile/mage.git /tmp/mage
-cd /tmp/mage && go run bootstrap.go
-mv $HOME/go/bin/mage /usr/local/bin
-
-wget -O - -q https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b /usr/local/bin v2.14.0
-
-source "/etc/profile"
-sh -l -c "go get -u github.com/mgechev/revive"
-for file in $(ls $HOME/go/bin); do
- mv -v $HOME/go/bin/$file /usr/local/bin/$file
-done
-
-# Install grafana-toolkit deps
-current_dir=$PWD
-cd /usr/local/grafana-toolkit && yarn install && cd $current_dir
-ln -s /usr/local/grafana-toolkit/bin/grafana-toolkit.js /usr/local/bin/grafana-toolkit
-
-GOOGLE_SDK_VERSION=365.0.1
-GOOGLE_SDK_CHECKSUM=17003cdba67a868c2518ac16efa60dc6175533b7a9fb87304459784308e30fb0
-
-curl -fLO https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz
-echo "${GOOGLE_SDK_CHECKSUM} google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz" | sha256sum --check --status
-tar xvzf google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz -C /opt
-rm google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz
-ln -s /opt/google-cloud-sdk/bin/gsutil /usr/bin/gsutil
-ln -s /opt/google-cloud-sdk/bin/gcloud /usr/bin/gcloud
-
-# Cleanup after yourself
-/bin/rm -rf /tmp/mage
-/bin/rm -rf $HOME/go
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/test/docker-compose.yml b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/test/docker-compose.yml
deleted file mode 100644
index 9a3e53f2cc3..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/test/docker-compose.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-version: '3'
-services:
- citest:
- image: "amd64/alpine"
- user: root
- volumes:
- - ../scripts:/home/circleci/scripts
- - ../install:/home/circleci/install
- - ${HOME}/.ssh:/root/.ssh
- - ../../..:/home/circleci/grafana-toolkit
- cibuilt:
- image: "grafana/grafana-plugin-ci:latest-alpine"
- user: root
- volumes:
- - ../scripts:/home/circleci/scripts
- - ../install:/home/circleci/install
- - ${HOME}/.ssh:/root/.ssh
- - ../../..:/home/circleci/grafana-toolkit
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/test/start.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/test/start.sh
deleted file mode 100755
index f5551eb3b6e..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-alpine/test/start.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-function finish {
- echo "Exiting and cleaning up docker image"
- docker-compose down
-}
-trap finish EXIT
-
-# Enter the docker container
-if [ "$1" = "built" ]; then
- docker-compose run cibuilt sh -c "cd /home/circleci; exec sh --login -i"
-else
- docker-compose run citest sh -c "cd /home/circleci; exec sh --login -i"
-fi
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/Dockerfile b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/Dockerfile
deleted file mode 100644
index 6aa2682c27b..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM debian:buster-slim
-
-ENV DEBIAN_FRONTEND=noninteractive
-
-COPY scripts scripts
-COPY install /usr/local
-
-RUN cd scripts && ./deploy.sh
-ENV DEBIAN_FRONTEND=newt
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/README.md b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/README.md
deleted file mode 100644
index 1b37557a0b3..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/README.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Using this docker image
-
-## User
-
-The user will be `circleci`
-The home directory will be `/home/circleci`
-
-## Node
-
-- node 14 is installed
-- yarn is installed globally
-- npm is installed globally
-
-## Go
-
-- Go is installed in `/usr/local/bin/go`
-- golangci-lint is installed in `/usr/local/bin/golangci-lint`
-- mage is installed in `/usr/local/bin/mage`
-
-All of the above directories are in the path, so there is no need to specify fully qualified paths.
-
-## Grafana
-
-- Installed in `/home/circleci/src/grafana`
-- `yarn install` has been run
-
-## Integration/Release Testing
-
-There are 4 previous versions pre-downloaded to /usr/local/grafana. These versions are:
-
-1. 6.6.2
-2. 6.5.3
-3. 6.4.5
-4. 6.3.7
-
-To test, your CircleCI config will need a run section with something similar to the following
-
-```
-- run:
- name: Setup Grafana (local install)
- command: |
- sudo dpkg -i /usr/local/grafana/deb/grafana_6.6.2_amd64.deb
- sudo cp ci/grafana-test-env/custom.ini /usr/share/grafana/conf/custom.ini
- sudo cp ci/grafana-test-env/custom.ini /etc/grafana/grafana.ini
- sudo service grafana-server start
- grafana-cli --version
-```
-
-# Building
-
-To build, cd to `/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e`
-
-```
-./build.sh
-```
-
-# Developing/Testing
-
-To test, you should have docker-compose installed.
-
-```
-cd test
-./start.sh
-```
-
-You will be in /home/circleci/test with the buildscripts installed to the local directory.
-Do your edits/run tests. When saving, your edits will be available in the container immediately.
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/build.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/build.sh
deleted file mode 100755
index 14ca02a1dc9..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/build.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-set -eo pipefail
-
-source ./common.sh
-
-#
-# No longer required, but useful to keep just in case we want to deploy
-# changes in toolkit directly to the docker image
-#
-if [ -n "$INCLUDE_TOOLKIT" ]; then
- /bin/rm -rfv install/grafana-toolkit
- mkdir -pv install/grafana-toolkit
- cp -rv ../../bin install/grafana-toolkit
- cp -rv ../../src install/grafana-toolkit
- cp -v ../../package.json install/grafana-toolkit
- cp -v ../../tsconfig.json install/grafana-toolkit
-fi
-
-docker build -t ${DOCKER_IMAGE_NAME} .
-docker push $DOCKER_IMAGE_NAME
-docker tag ${DOCKER_IMAGE_NAME} ${DOCKER_IMAGE_BASE_NAME}:latest
-docker push ${DOCKER_IMAGE_BASE_NAME}:latest
-
-[ -n "$INCLUDE_TOOLKIT" ] && /bin/rm -rfv install/grafana-toolkit
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/common.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/common.sh
deleted file mode 100755
index 29714cd5a84..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/common.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-##
-## Common variable declarations
-## Find the latest tag on https://hub.docker.com/r/grafana/grafana-plugin-ci-e2e/tags
-##
-
-DOCKER_IMAGE_BASE_NAME="grafana/grafana-plugin-ci-e2e"
-DOCKER_IMAGE_VERSION="1.6.1"
-DOCKER_IMAGE_NAME="${DOCKER_IMAGE_BASE_NAME}:${DOCKER_IMAGE_VERSION}"
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/install/ginstall b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/install/ginstall
deleted file mode 100755
index a71019bdbef..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/install/ginstall
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash
-##
-# gget
-# A script to get and install grafana versions
-# for usage information see "show_help" below.
-#
-
-latest=$(wget -O - 'https://raw.githubusercontent.com/grafana/grafana/main/latest.json' | jq -r '.stable')
-canary=$(wget -O - "https://grafana.com/api/grafana-enterprise/versions" | jq ".items[0].version" | tr -d '"')
-
-show_help() {
- echo "Usage: gget "
- echo ""
- echo "where can be:"
- echo " 1) A version from https://grafana.com/grafana/download (ex x.y.z)"
- echo " 2) latest (currently $latest)"
- echo " 3) canary (currently $canary)"
- echo ""
- echo " -h, --help: Display this help message"
- echo ""
- exit 0
-}
-
-opts=$(getopt -o h --long help -n 'gget' -- "$@")
-[ $? -eq 0 ] || {
- show_help
-}
-
-eval set -- "$opts"
-while true; do
- case "$1" in
- -h | --help)
- show_help
- ;;
- --)
- shift
- break
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-[ -z "$1" ] && show_help
-
-# Make sure the script is being run as root
-if [ $EUID -ne 0 ]; then
- echo "This script must be run as root"
- exit 1
-fi
-
-##
-# MAIN
-#
-# Enough setup, let's actually do something
-#
-version=$1
-if [ "$version" == "latest" ]; then
- version="$latest"
- wget -O - "https://dl.grafana.com/enterprise/release/grafana-enterprise-${version}.linux-amd64.tar.gz" | tar -C /opt -zxf -
-elif [ "$version" == "canary" ]; then
- version="$canary"
- wget -O - "https://dl.grafana.com/enterprise/main/grafana-enterprise-${version}.linux-amd64.tar.gz" | tar -C /opt -zxf -
-else
- wget -O - "https://dl.grafana.com/enterprise/release/grafana-enterprise-${version}.linux-amd64.tar.gz" | tar -C /opt -zxf -
-fi
-
-/bin/rm -rf /opt/grafana > /dev/null 2>&1 || true
-ln -s /opt/grafana-${version} /opt/grafana
-
-# nohup /opt/grafana/bin/grafana-server -config /opt/grafana/conf/defaults.ini -homepath /opt/grafana >/dev/null 2>&1 &
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-common.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-common.sh
deleted file mode 100755
index af585d2eb19..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-common.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-##
-# Script to deploy a docker image. Must return exit code 0
-#
-do_exit() {
- message="$1"
- exit_code="$2"
-
- echo "$message"
- exit $exit_code
-}
-
-##
-# Get file, get's a file, validates the SHA
-# @param filename
-# @param expected sha value
-# @returns 0 if successful, -1 of checksum validation failed.
-#
-get_file () {
- [ -n "$1" ] && url=$1 || do_exit "url required" -1
- [ -n "$2" ] && dest=$2 || do_exit "destination required" -2
- sha=$3
- file=$(basename $dest)
-
- wget "$url" -O "$dest"
- if [ -n "$sha" ]; then
- echo "$sha $dest" | sha256sum --check --status || do_exit "Checksum validation failed for $file. Exiting" -1
- fi
-}
-
-untar_file () {
- [ -n "$1" ] && src=$1 || do_exit "src required" -1
- [ -n "$2" ] && dest=$2 || dest="/usr/local"
-
- tar -C "$dest" -xf "$src" && /bin/rm -rf "$src"
-}
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-slim.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-slim.sh
deleted file mode 100755
index 9c3c0a31e2d..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-slim.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-source "/etc/profile"
-
-apt-get --allow-insecure-repositories update
-apt-get install --allow-unauthenticated -y \
- build-essential \
- wget git sudo adduser \
- libfontconfig1 \
- locate \
- libnss3 libnspr4 \
- libgdk-pixbuf2.0-0 \
- libgtk-3-0 \
- libpangocairo-1.0-0 \
- libpango-1.0-0 \
- libatk1.0-0 \
- libcairo2 \
- libdbus-1-3 \
- libxcomposite1 libxrender1 libxcursor1 libxi6 libxtst6 libxrandr2 libxss1 xauth xvfb \
- libasound2 \
- libatk-bridge2.0-0 \
- libatspi2.0-0 \
- libcups2 \
- jq net-tools git-lfs unzip pkg-config zip \
- libaio1 libaio-dev \
- netcat \
- libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
-
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-user.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-user.sh
deleted file mode 100755
index 08f2b7b950a..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy-user.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-source "./deploy-common.sh"
-
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy.sh
deleted file mode 100755
index 99e711aee87..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/scripts/deploy.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/bash
-set -eo pipefail
-
-source "/etc/profile"
-source "./deploy-slim.sh"
-source "./deploy-common.sh"
-
-NODEVER="v16.13.2"
-# Install Node
-wget -O - "https://nodejs.org/dist/${NODEVER}/node-${NODEVER}-linux-x64.tar.xz" | tar Jvxf - -C "/tmp"
-
-# Move node to /usr/local so it's in the path
-pushd /tmp/node-${NODEVER}-linux-x64
-/bin/rm -f CHANGELOG.md README.md LICENSE
-/bin/cp -r * /usr/local
-popd
-/bin/rm -rf /tmp/node-${NODEVER}
-
-# Resource the profile so we know our path is being honoured
-source "/etc/profile"
-# Install Yarn. Not in the path yet so fully qualified
-npm i -g yarn
-
-# Install Go
-filename="go1.19.4.linux-amd64.tar.gz"
-get_file "https://dl.google.com/go/$filename" "/tmp/$filename" "c9c08f783325c4cf840a94333159cc937f05f75d36a8b307951d5bd959cf2ab8"
-untar_file "/tmp/$filename"
-
-# Install golangci-lint
-GOLANGCILINT_VERSION=1.50.0
-filename="golangci-lint-${GOLANGCILINT_VERSION}-linux-amd64"
-get_file "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCILINT_VERSION}/$filename.tar.gz" \
- "/tmp/$filename.tar.gz" \
- "b4b329efcd913082c87d0e9606711ecb57415b5e6ddf233fde9e76c69d9b4e8b"
-untar_file "/tmp/$filename.tar.gz"
-ln -s /usr/local/${filename}/golangci-lint /usr/local/bin/golangci-lint
-ln -s /usr/local/go/bin/go /usr/local/bin/go
-ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt
-chmod 755 /usr/local/bin/golangci-lint
-
-# Install code climate
-get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64" \
- "/usr/local/bin/cc-test-reporter" \
- "20d1d4e2b399d0287d91e65faeee8ffbef08e3262b0be5eda7def7b3c2799ddd"
-chmod 755 /usr/local/bin/cc-test-reporter
-
-wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.27/grabpl"
-chmod +x /usr/local/bin/grabpl
-
-# Install Mage
-mkdir -pv /tmp/mage $HOME/go/bin
-git clone https://github.com/magefile/mage.git /tmp/mage
-pushd /tmp/mage && go run bootstrap.go && popd
-mv $HOME/go/bin/mage /usr/local/bin
-# Cleanup after yourself
-/bin/rm -rf /tmp/mage
-/bin/rm -rf $HOME/go
-
-# add cypress
-yarn global add cypress
-# verify cypress install
-cypress verify
-
-# Get the size down
-/bin/rm -rf /var/lib/apt/lists
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/test/docker-compose.yml b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/test/docker-compose.yml
deleted file mode 100644
index 5d6297c4833..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/test/docker-compose.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-version: '3'
-services:
- citest:
- image: "debian:buster-slim"
- user: root
- volumes:
- - ../scripts:/root/scripts
- - ../install:/root/install
- - ${HOME}/.ssh:/root/.ssh
- - ../../..:/root/grafana-toolkit
- cibuilt:
- image: "grafana/grafana-plugin-ci-e2e"
- user: root
- volumes:
- - ../scripts:/root/scripts
- - ../install:/root/install
- - ${HOME}/.ssh:/root/.ssh
- - ../../..:/root/grafana-toolkit
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/test/start.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/test/start.sh
deleted file mode 100755
index 2552c964a85..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci-e2e/test/start.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-function finish {
- echo "Exiting and cleaning up docker image"
- docker-compose down
-}
-trap finish EXIT
-
-# Enter the docker container
-if [ "$1" = "built" ]; then
- docker-compose run cibuilt sh -c "cd /root; exec bash --login -i"
-else
- docker-compose run citest sh -c "cd /root; exec bash --login -i"
-fi
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/Dockerfile b/packages/grafana-toolkit/docker/grafana-plugin-ci/Dockerfile
deleted file mode 100644
index 46228193273..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/Dockerfile
+++ /dev/null
@@ -1,8 +0,0 @@
-FROM debian:testing-20210111-slim
-USER root
-COPY scripts scripts
-WORKDIR scripts
-RUN apt-get update && \
- apt-get install -y wget && \
- ./deploy.sh
-COPY install/gget /usr/local/bin/gget
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/README.md b/packages/grafana-toolkit/docker/grafana-plugin-ci/README.md
deleted file mode 100644
index 18bc1aec592..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/README.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Using this docker image
-
-Currently tagged and uploaded to dockerhub as srclosson/integrations-ci-build
-
-Based off of `circleci/node:12-browsers`
-
-## User
-
-The user will be `circleci`
-The home directory will be `/home/circleci`
-
-## Node
-
-- node 12 is installed
-- yarn is installed globally
-- npm is installed globally
-
-## Go
-
-- Go is installed in `/usr/local/bin/go`
-- golangci-lint is installed in `/usr/local/bin/golangci-lint`
-- mage is installed in `/home/circleci/go/bin/mage`
-
-All of the above directories are in the path, so there is no need to specify fully qualified paths.
-
-## Grafana
-
-- Installed in `/home/circleci/src/grafana`
-- `yarn install` has been run
-
-## Integration/Release Testing
-
-There are 4 previous versions pre-downloaded to /usr/local/grafana. These versions are:
-
-1. 6.6.2
-2. 6.5.3
-3. 6.4.5
-4. 6.3.7
-
-To test, your CircleCI config will need a run section with something similar to the following
-
-```
-- run:
- name: Setup Grafana (local install)
- command: |
- sudo dpkg -i /usr/local/grafana/deb/grafana_6.6.2_amd64.deb
- sudo cp ci/grafana-test-env/custom.ini /usr/share/grafana/conf/custom.ini
- sudo cp ci/grafana-test-env/custom.ini /etc/grafana/grafana.ini
- sudo service grafana-server start
- grafana-cli --version
-```
-
-# Building
-
-To build, cd to `/packages/grafana-toolkit/docker/grafana-plugin-ci`
-
-```
-./build.sh
-```
-
-# Developing/Testing
-
-To test, you should have docker-compose installed.
-
-```
-cd test
-./start.sh
-```
-
-You will be in /home/circleci/test with the buildscripts installed to the local directory.
-Do your edits/run tests. When saving, your edits will be available in the container immediately.
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/build.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci/build.sh
deleted file mode 100755
index 3d8d3d7110a..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/build.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-set -eo pipefail
-
-source ./common.sh
-
-docker build -t ${DOCKER_IMAGE_NAME} .
-docker push $DOCKER_IMAGE_NAME
-
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/common.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci/common.sh
deleted file mode 100755
index 47d2b068d8d..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/common.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-##
-## Common variable declarations
-## Find the latest tags on https://hub.docker.com/r/grafana/grafana-plugin-ci/tags
-##
-
-DOCKER_IMAGE_NAME="grafana/grafana-plugin-ci:1.6.1"
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/install/gget b/packages/grafana-toolkit/docker/grafana-plugin-ci/install/gget
deleted file mode 100755
index 3e38e2d5911..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/install/gget
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/bash
-##
-# gget
-# A script to get and install grafana versions
-# for usage information see "show_help" below.
-#
-
-latest=$(curl -s 'https://raw.githubusercontent.com/grafana/grafana/main/latest.json' | jq -r '.stable')
-canary=$(curl -s "https://grafana.com/api/grafana/versions" | jq ".items[0].version" | tr -d '"')
-
-show_help() {
- echo "Usage: gget "
- echo ""
- echo "where can be:"
- echo " 1) A version from https://grafana.com/grafana/download (ex x.y.z)"
- echo " 2) latest (currently $latest)"
- echo " 3) canary (currently $canary)"
- echo ""
- echo " -h, --help: Display this help message"
- echo ""
- exit 0
-}
-
-opts=$(getopt -o h --long help -n 'gget' -- "$@")
-[ $? -eq 0 ] || {
- show_help
-}
-
-eval set -- "$opts"
-while true; do
- case "$1" in
- -h | --help)
- show_help
- ;;
- --)
- shift
- break
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-[ -z "$1" ] && show_help
-
-# Make sure the script is being run as root
-if [ $EUID -ne 0 ]; then
- echo "This script must be run as root"
- exit 1
-fi
-
-##
-# MAIN
-#
-# Enough setup, let's actually do something
-#
-version=$1
-[ "$version" == "latest" ] && version="$latest"
-[ "$version" == "canary" ] && version="$canary"
-wget "https://dl.grafana.com/oss/release/grafana_${version}_amd64.deb" -O "/tmp/grafana_${version}_amd64.deb"
-dpkg -i "/tmp/grafana_${version}_amd64.deb" && /bin/rm -rfv "/tmp/grafana_${version}_amd64.deb"
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-common.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-common.sh
deleted file mode 100755
index 524bb5e4ec6..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-common.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-
-##
-# Script to deploy a docker image. Must return exit code 0
-#
-do_exit() {
- message="$1"
- exit_code="$2"
-
- echo "$message"
- exit $exit_code
-}
-
-
-##
-# Get file, get's a file, validates the SHA
-# @param filename
-# @param expected sha value
-# @returns 0 if successful, -1 of checksum validation failed.
-#
-get_file () {
- [ -n "$1" ] && url=$1 || do_exit "url required" -1
- [ -n "$2" ] && dest=$2 || do_exit "destination required" -2
- sha=$3
- file=$(basename $dest)
-
- wget "$url" -O "$dest"
- if [ -n "$sha" ]; then
- echo "$sha $dest" | sha256sum --check --status || do_exit "Checksum validation failed for $file. Exiting" -1
- fi
-}
-
-untar_file () {
- [ -n "$1" ] && src=$1 || do_exit "src required" -1
- [ -n "$2" ] && dest=$2 || dest="/usr/local"
-
- tar -C "$dest" -xf "$src" && /bin/rm -rf "$src"
-}
\ No newline at end of file
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-user.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-user.sh
deleted file mode 100755
index 08f2b7b950a..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-user.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-source "./deploy-common.sh"
-
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy.sh
deleted file mode 100755
index 3ee0bff1e16..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-source "./deploy-common.sh"
-
-# Install Go
-filename="go1.19.4.linux-amd64.tar.gz"
-get_file "https://dl.google.com/go/$filename" "/tmp/$filename" "c9c08f783325c4cf840a94333159cc937f05f75d36a8b307951d5bd959cf2ab8"
-untar_file "/tmp/$filename"
-
-# Install golangci-lint
-GOLANGCILINT_VERSION=1.50.0
-filename="golangci-lint-${GOLANGCILINT_VERSION}-linux-amd64"
-get_file "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCILINT_VERSION}/$filename.tar.gz" \
- "/tmp/$filename.tar.gz" \
- "b4b329efcd913082c87d0e9606711ecb57415b5e6ddf233fde9e76c69d9b4e8b"
-untar_file "/tmp/$filename.tar.gz"
-ln -s /usr/local/${filename}/golangci-lint /usr/local/bin/golangci-lint
-ln -s /usr/local/go/bin/go /usr/local/bin/go
-ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt
-chmod 755 /usr/local/bin/golangci-lint
-
-# Install dependencies
-apt-get update -y && apt-get install -y adduser libfontconfig1 locate && /bin/rm -rf /var/lib/apt/lists/*
-
-# Install code climate
-get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64" \
- "/usr/local/bin/cc-test-reporter" \
- "20d1d4e2b399d0287d91e65faeee8ffbef08e3262b0be5eda7def7b3c2799ddd"
-chmod 755 /usr/local/bin/cc-test-reporter
-
-wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.9.27/grabpl"
-chmod +x /usr/local/bin/grabpl
-
-# Install Mage
-mkdir -pv /tmp/mage $HOME/go/bin
-git clone https://github.com/magefile/mage.git /tmp/mage
-pushd /tmp/mage && go run bootstrap.go && popd
-mv $HOME/go/bin/mage /usr/local/bin
-
-GOOGLE_SDK_VERSION=365.0.1
-GOOGLE_SDK_CHECKSUM=17003cdba67a868c2518ac16efa60dc6175533b7a9fb87304459784308e30fb0
-
-curl -fLO https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz
-echo "${GOOGLE_SDK_CHECKSUM} google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz" | sha256sum --check --status
-tar xvzf google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz -C /opt
-rm google-cloud-sdk-${GOOGLE_SDK_VERSION}-linux-x86_64.tar.gz
-ln -s /opt/google-cloud-sdk/bin/gsutil /usr/bin/gsutil
-ln -s /opt/google-cloud-sdk/bin/gcloud /usr/bin/gcloud
-
-# Cleanup after yourself
-/bin/rm -rf /tmp/mage
-/bin/rm -rf $HOME/go
-
-# Perform user specific initialization
-sudo -u circleci ./deploy-user.sh
-
-# Get the size down
-/bin/rm -rf /var/lib/apt/lists
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/test/docker-compose.yml b/packages/grafana-toolkit/docker/grafana-plugin-ci/test/docker-compose.yml
deleted file mode 100644
index d734b3a38e6..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/test/docker-compose.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-version: '3'
-services:
- citest:
- image: "circleci/node:12-browsers"
- user: root
- volumes:
- - ../scripts:/home/circleci/scripts
- - ../install:/home/circleci/install
- - ${HOME}/.ssh:/root/.ssh
- - ../../..:/home/circleci/grafana-toolkit
diff --git a/packages/grafana-toolkit/docker/grafana-plugin-ci/test/start.sh b/packages/grafana-toolkit/docker/grafana-plugin-ci/test/start.sh
deleted file mode 100755
index af3d91cb4a0..00000000000
--- a/packages/grafana-toolkit/docker/grafana-plugin-ci/test/start.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-function finish {
- echo "Exiting and cleaning up docker image"
- docker-compose down
-}
-trap finish EXIT
-
-# Enter the docker container
-docker-compose run citest bash -c "cd /home/circleci; exec bash --login -i"
diff --git a/packages/grafana-ui/.storybook/main.ts b/packages/grafana-ui/.storybook/main.ts
index ad5a021197e..e737d0126c5 100644
--- a/packages/grafana-ui/.storybook/main.ts
+++ b/packages/grafana-ui/.storybook/main.ts
@@ -1,6 +1,7 @@
import path from 'path';
import type { StorybookConfig } from '@storybook/react/types';
-import { getAvailableIcons, IconName } from '../src/types/icon';
+// avoid importing from @grafana/data to prevent node error: ERR_REQUIRE_ESM
+import { availableIconsIndex, IconName } from '../../grafana-data/src/types/icon';
import { getIconSubDir } from '../src/components/Icon/utils';
const stories = ['../src/**/*.story.@(tsx|mdx)'];
@@ -11,7 +12,7 @@ if (process.env.NODE_ENV !== 'production') {
// We limit icon paths to only the available icons so publishing
// doesn't require uploading 1000s of unused assets.
-const iconPaths = getAvailableIcons()
+const iconPaths = Object.keys(availableIconsIndex)
.filter((iconName) => !iconName.includes('fa'))
.map((iconName) => {
const subDir = getIconSubDir(iconName as IconName, 'default');
diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json
index dc219b691c4..73d828f5be1 100644
--- a/packages/grafana-ui/package.json
+++ b/packages/grafana-ui/package.json
@@ -67,11 +67,11 @@
"calculate-size": "1.1.1",
"classnames": "2.3.2",
"core-js": "3.27.1",
- "d3": "5.15.0",
+ "d3": "7.8.2",
"date-fns": "2.29.3",
"hoist-non-react-statics": "3.3.2",
"i18next": "^22.0.0",
- "immutable": "4.1.0",
+ "immutable": "4.2.2",
"is-hotkey": "0.2.0",
"jquery": "3.6.1",
"lodash": "4.17.21",
@@ -81,10 +81,10 @@
"ol": "7.1.0",
"prismjs": "1.29.0",
"rc-cascader": "3.8.0",
- "rc-drawer": "6.0.1",
- "rc-slider": "10.0.1",
+ "rc-drawer": "6.1.2",
+ "rc-slider": "10.1.0",
"rc-time-picker": "^3.7.3",
- "rc-tooltip": "5.2.2",
+ "rc-tooltip": "5.3.1",
"react-beautiful-dnd": "13.1.1",
"react-calendar": "3.9.0",
"react-colorful": "5.6.1",
@@ -109,7 +109,7 @@
"slate-react": "0.22.10",
"tinycolor2": "1.4.2",
"tslib": "2.4.1",
- "uplot": "1.6.23",
+ "uplot": "1.6.24",
"uuid": "9.0.0"
},
"devDependencies": {
@@ -133,8 +133,7 @@
"@storybook/preset-scss": "1.0.3",
"@storybook/react": "6.5.14",
"@storybook/theming": "6.5.14",
- "@swc/helpers": "0.4.12",
- "@testing-library/dom": "8.19.0",
+ "@testing-library/dom": "8.20.0",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx
index 71b0f25ccc1..2b5953b7638 100644
--- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx
+++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx
@@ -30,7 +30,7 @@ export const colorPickerFactory = (
popover: React.ComponentType>,
displayName = 'ColorPicker'
) => {
- return class ColorPicker extends Component {
+ return class ColorPicker extends Component {
static displayName = displayName;
pickerTriggerRef = createRef();
diff --git a/packages/grafana-ui/src/components/DataSourceSettings/SecureSocksProxySettings.tsx b/packages/grafana-ui/src/components/DataSourceSettings/SecureSocksProxySettings.tsx
new file mode 100644
index 00000000000..79e244a99b3
--- /dev/null
+++ b/packages/grafana-ui/src/components/DataSourceSettings/SecureSocksProxySettings.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+import { DataSourceJsonData, DataSourcePluginOptionsEditorProps } from '@grafana/data';
+
+import { InlineSwitch } from '../../components/Switch/Switch';
+import { InlineField } from '../Forms/InlineField';
+
+export interface Props
+ extends Pick, 'options' | 'onOptionsChange'> {}
+
+export interface SecureSocksProxyConfig extends DataSourceJsonData {
+ enableSecureSocksProxy?: boolean;
+}
+
+export function SecureSocksProxySettings({
+ options,
+ onOptionsChange,
+}: Props): JSX.Element {
+ return (
+ <>
+
;
+ }
+
const svgSize = getSvgSize(size);
const svgHgt = svgSize;
- const svgWid = name?.startsWith('gf-bar-align') ? 16 : name?.startsWith('gf-interp') ? 30 : svgSize;
+ const svgWid = name.startsWith('gf-bar-align') ? 16 : name.startsWith('gf-interp') ? 30 : svgSize;
const subDir = getIconSubDir(name, type);
const svgPath = `${iconRoot}${subDir}/${name}.svg`;
diff --git a/packages/grafana-ui/src/components/Menu/hooks.ts b/packages/grafana-ui/src/components/Menu/hooks.ts
index 3ca3d2baf5b..f4983e80ff5 100644
--- a/packages/grafana-ui/src/components/Menu/hooks.ts
+++ b/packages/grafana-ui/src/components/Menu/hooks.ts
@@ -98,11 +98,10 @@ export const useMenuFocus = ({
menuItems?.[focusedItem]?.click();
break;
case 'Escape':
- event.preventDefault();
- event.stopPropagation();
onClose?.();
break;
case 'Tab':
+ event.preventDefault();
onClose?.();
break;
default:
diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx
index 2d2169e4812..68eaeebc3f9 100644
--- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx
+++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.test.tsx
@@ -1,6 +1,8 @@
import { screen, render } from '@testing-library/react';
import React from 'react';
+import { LoadingState } from '@grafana/data';
+
import { PanelChrome, PanelChromeProps } from './PanelChrome';
const setup = (propOverrides?: Partial) => {
@@ -35,18 +37,50 @@ it('renders an empty panel with padding', () => {
expect(screen.getByText("Panel's Content").parentElement).not.toHaveStyle({ padding: '0px' });
});
-it('renders panel with a header if prop title', () => {
+// Check for backwards compatibility
+it('renders panel header if prop title', () => {
setup({ title: 'Test Panel Header' });
expect(screen.getByTestId('header-container')).toBeInTheDocument();
});
-it('renders panel with a header with title in place if prop title', () => {
+// Check for backwards compatibility
+it('renders panel with title in place if prop title', () => {
setup({ title: 'Test Panel Header' });
expect(screen.getByText('Test Panel Header')).toBeInTheDocument();
});
+// Check for backwards compatibility
+it('renders panel with a header if prop leftItems', () => {
+ setup({
+ leftItems: [
This should be a self-contained node
],
+ });
+
+ expect(screen.getByTestId('header-container')).toBeInTheDocument();
+});
+
+// todo implement when hoverHeader is implemented
+it.skip('renders panel without header if no title, no leftItems, and hoverHeader is undefined', () => {
+ setup();
+
+ expect(screen.getByTestId('header-container')).toBeInTheDocument();
+});
+
+// todo implement when hoverHeader is implemented
+it.skip('renders panel with a fixed header if prop hoverHeader is false', () => {
+ setup({ hoverHeader: false });
+
+ expect(screen.getByTestId('header-container')).toBeInTheDocument();
+});
+
+// todo implement when hoverHeader is implemented
+it.skip('renders panel with a hovering header if prop hoverHeader is true', () => {
+ setup({ title: 'Test Panel Header', hoverHeader: true });
+
+ expect(screen.queryByTestId('header-container')).not.toBeInTheDocument();
+});
+
it('renders panel with a header if prop titleItems', () => {
setup({
titleItems: [
This should be a self-contained node
],
@@ -63,11 +97,6 @@ it('renders panel with a header with icons in place if prop titleItems', () => {
expect(screen.getByTestId('title-items-container')).toBeInTheDocument();
});
-it.skip('renders panel with a fixed header if prop hoverHeader is false', () => {
- // setup({ title: 'Test Panel Header', hoverHeader: false });
- // expect(screen.getByTestId('header-container')).toBeInTheDocument();
-});
-
it('renders panel with a header if prop menu', () => {
setup({ menu:
Menu
});
@@ -81,8 +110,38 @@ it('renders panel with a show-on-hover menu icon if prop menu', () => {
expect(screen.getByTestId('panel-menu-button')).not.toBeVisible();
});
-it.skip('renders states in the panel header if any given', () => {});
+it('renders error status in the panel header if any given', () => {
+ setup({ statusMessage: 'Error test' });
-it.skip('renders leftItems in the panel header if any given when no states prop is given', () => {});
+ expect(screen.getByLabelText('Panel status')).toBeInTheDocument();
+});
-it.skip('renders states in the panel header if both leftItems and states are given', () => {});
+it('does not render error status in the panel header if loadingState is error, but no statusMessage', () => {
+ setup({ loadingState: LoadingState.Error, statusMessage: '' });
+
+ expect(screen.queryByTestId('panel-status')).not.toBeInTheDocument();
+});
+
+it('renders loading indicator in the panel header if loadingState is loading', () => {
+ setup({ loadingState: LoadingState.Loading });
+
+ expect(screen.getByLabelText('Panel loading bar')).toBeInTheDocument();
+});
+
+it('renders loading indicator in the panel header if loadingState is loading regardless of not having a header', () => {
+ setup({ loadingState: LoadingState.Loading, hoverHeader: true });
+
+ expect(screen.getByLabelText('Panel loading bar')).toBeInTheDocument();
+});
+
+it('renders loading indicator in the panel header if loadingState is loading regardless of having a header', () => {
+ setup({ loadingState: LoadingState.Loading, hoverHeader: false });
+
+ expect(screen.getByLabelText('Panel loading bar')).toBeInTheDocument();
+});
+
+it('renders streaming indicator in the panel header if loadingState is streaming', () => {
+ setup({ loadingState: LoadingState.Streaming });
+
+ expect(screen.getByTestId('panel-streaming')).toBeInTheDocument();
+});
diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
index 9f91c0cc315..5ea484bbf2b 100644
--- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
+++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx
@@ -26,10 +26,13 @@ export interface PanelChromeProps {
description?: string | (() => string);
titleItems?: ReactNode[];
menu?: ReactElement | (() => ReactElement);
- /** dragClass, hoverHeader not yet implemented */
dragClass?: string;
dragClassCancel?: string;
hoverHeader?: boolean;
+ /**
+ * Use only to indicate loading or streaming data in the panel.
+ * Any other values of loadingState are ignored.
+ */
loadingState?: LoadingState;
/**
* Used to display status message (used for panel errors currently)
@@ -39,11 +42,13 @@ export interface PanelChromeProps {
* Handle opening error details view (like inspect / error tab)
*/
statusMessageOnClick?: (e: React.SyntheticEvent) => void;
- /** @deprecated in favor of props
- * status for errors and loadingState for loading and streaming
+ /**
+ * @deprecated in favor of props
+ * statusMessage for error messages
+ * and loadingState for loading and streaming data
* which will serve the same purpose
- * of showing/interacting with the panel's data state
- * */
+ * of showing/interacting with the panel's state
+ */
leftItems?: ReactNode[];
}
@@ -64,14 +69,13 @@ export function PanelChrome({
description = '',
titleItems = [],
menu,
- // dragClass,
+ dragClass,
+ dragClassCancel,
hoverHeader = false,
loadingState,
statusMessage,
statusMessageOnClick,
leftItems,
- dragClass,
- dragClassCancel,
}: PanelChromeProps) {
const theme = useTheme2();
const styles = useStyles2(getStyles);
@@ -108,7 +112,9 @@ export function PanelChrome({
return (