diff --git a/.betterer.results b/.betterer.results
index a07a231954f..b593c39ca68 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -491,6 +491,9 @@ exports[`better eslint`] = {
"packages/grafana-runtime/src/services/pluginExtensions/usePluginExtensions.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
+ "packages/grafana-runtime/src/services/pluginExtensions/usePluginFunctions.ts:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
"packages/grafana-runtime/src/utils/DataSourceWithBackend.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@@ -515,46 +518,15 @@ exports[`better eslint`] = {
"packages/grafana-sql/src/components/visual-query-builder/AwesomeQueryBuilder.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
- "packages/grafana-ui/src/components/Alert/Alert.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/Card/Card.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
- "packages/grafana-ui/src/components/Cascader/Cascader.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
"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"]
],
- "packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/ColorPicker/SeriesColorPickerPopover.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/Combobox/Combobox.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"],
- [0, 0, 0, "Do not use any type assertions.", "1"]
- ],
"packages/grafana-ui/src/components/Combobox/MultiCombobox.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
- "packages/grafana-ui/src/components/Combobox/ValuePill.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
- "packages/grafana-ui/src/components/ConfirmModal/ConfirmContent.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
- "packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"]
+ "packages/grafana-ui/src/components/Combobox/useOptions.ts:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
],
"packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@@ -562,81 +534,14 @@ exports[`better eslint`] = {
"packages/grafana-ui/src/components/DataLinks/DataLinkSuggestions.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
- "packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
- "packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/DataSourceSettings/AlertingSettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/DataSourceSettings/BasicAuthSettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
"packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "6"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "7"]
- ],
- "packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"]
- ],
- "packages/grafana-ui/src/components/DataSourceSettings/HttpProxySettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"]
- ],
- "packages/grafana-ui/src/components/DataSourceSettings/SecureSocksProxySettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/DataSourceSettings/TLSAuthSettings.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"packages/grafana-ui/src/components/DataSourceSettings/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
- "packages/grafana-ui/src/components/DateTimePickers/DateTimePicker/DateTimePicker.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
- ],
"packages/grafana-ui/src/components/FileDropzone/FileDropzone.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
@@ -1003,9 +908,8 @@ exports[`better eslint`] = {
"public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
- "public/app/core/components/AppChrome/News/NewsContainer.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
+ "public/app/core/components/AppChrome/News/NewsDrawer.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
"public/app/core/components/AppChrome/News/NewsWrapper.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
@@ -1016,10 +920,12 @@ exports[`better eslint`] = {
"public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
+ "public/app/core/components/AppChrome/TopBar/ProfileButton.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
+ ],
"public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
],
"public/app/core/components/AppChrome/TopBar/TopSearchBarCommandPaletteTrigger.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/themes/mixins\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
@@ -1194,9 +1100,6 @@ exports[`better eslint`] = {
"public/app/core/components/TagFilter/TagOption.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
- "public/app/core/components/TimePicker/TimePickerWithHistory.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
"public/app/core/components/TimeSeries/TimeSeries.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/uPlot/PlotLegend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
[0, 0, 0, "\'@grafana/ui/src/components/uPlot/config/UPlotConfigBuilder\' import is restricted from being used by a pattern. Import from the public export instead.", "1"],
@@ -1325,8 +1228,10 @@ exports[`better eslint`] = {
[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, "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.", "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"]
],
"public/app/core/utils/richHistory.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@@ -1348,32 +1253,15 @@ exports[`better eslint`] = {
[0, 0, 0, "\'@grafana/ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup\' import is restricted from being used by a pattern. Import from the public export instead.", "3"],
[0, 0, 0, "\'@grafana/ui/src/components/JSONFormatter/JSONFormatter\' import is restricted from being used by a pattern. Import from the public export instead.", "4"],
[0, 0, 0, "\'@grafana/ui/src/themes\' import is restricted from being used by a pattern. Import from the public export instead.", "5"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"],
+ [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "6"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "13"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"]
],
"public/app/features/actions/ActionEditorModalContent.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/Button\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
[0, 0, 0, "\'@grafana/ui/src/components/Modal/Modal\' import is restricted from being used by a pattern. Import from the public export instead.", "1"]
],
- "public/app/features/actions/ActionsInlineEditor.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/components/Button\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "\'@grafana/ui/src/components/Modal/Modal\' import is restricted from being used by a pattern. Import from the public export instead.", "1"],
- [0, 0, 0, "\'@grafana/ui/src/themes\' import is restricted from being used by a pattern. Import from the public export instead.", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"]
- ],
- "public/app/features/actions/ActionsListItem.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/components/Icon/Icon\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "\'@grafana/ui/src/components/IconButton/IconButton\' import is restricted from being used by a pattern. Import from the public export instead.", "1"],
- [0, 0, 0, "\'@grafana/ui/src/themes\' import is restricted from being used by a pattern. Import from the public export instead.", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"]
- ],
"public/app/features/actions/ParamsEditor.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/IconButton/IconButton\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
[0, 0, 0, "\'@grafana/ui/src/components/Input/Input\' import is restricted from being used by a pattern. Import from the public export instead.", "1"],
@@ -1558,23 +1446,10 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "4"]
],
- "public/app/features/alerting/unified/CloneRuleEditor.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
- ],
- "public/app/features/alerting/unified/ExistingRuleEditor.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
- ],
"public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/datetime/rangeutil\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "5"],
@@ -1587,7 +1462,8 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "12"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "13"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "14"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "15"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "15"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "16"]
],
"public/app/features/alerting/unified/NotificationPoliciesPage.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
@@ -1597,9 +1473,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
"public/app/features/alerting/unified/RedirectToRuleViewer.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
@@ -1611,15 +1485,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "6"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "7"]
],
- "public/app/features/alerting/unified/RuleEditor.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
- ],
- "public/app/features/alerting/unified/RuleViewer.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
"public/app/features/alerting/unified/Settings.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"],
@@ -1658,10 +1523,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
],
- "public/app/features/alerting/unified/components/InfoPausedRule.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
- ],
"public/app/features/alerting/unified/components/InvalidIntervalWarning.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"],
@@ -2258,10 +2119,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "13"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "10"]
],
"public/app/features/alerting/unified/components/rule-editor/GrafanaFolderAndLabelsStep.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
@@ -2383,6 +2241,9 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "10"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "11"]
],
+ "public/app/features/alerting/unified/components/rule-editor/dag.test.ts:5381": [
+ [0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
+ ],
"public/app/features/alerting/unified/components/rule-editor/labels/LabelsButtons.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
@@ -2501,29 +2362,11 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
],
"public/app/features/alerting/unified/components/rule-viewer/RuleViewer.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
- ],
- "public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx:5381": [
- [0, 0, 0, "No untranslated strings. Wrap text with ", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "5"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "6"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "7"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "8"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "9"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "10"]
- ],
- "public/app/features/alerting/unified/components/rule-viewer/tabs/Query.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "4"]
],
"public/app/features/alerting/unified/components/rule-viewer/tabs/Routing.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
@@ -2923,14 +2766,30 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "5"],
- [0, 0, 0, "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.", "5"]
],
"public/app/features/alerting/unified/plugins/PluginOriginBadge.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/Icon/utils\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
],
+ "public/app/features/alerting/unified/rule-editor/CloneRuleEditor.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
+ ],
+ "public/app/features/alerting/unified/rule-editor/ExistingRuleEditor.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
+ ],
+ "public/app/features/alerting/unified/rule-editor/RuleEditor.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
+ ],
"public/app/features/alerting/unified/rule-list/FilterView.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
@@ -2958,6 +2817,9 @@ exports[`better eslint`] = {
"public/app/features/alerting/unified/rule-list/components/RuleListIcon.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/Text/Text\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
],
+ "public/app/features/alerting/unified/state/AlertingQueryRunner.test.ts:5381": [
+ [0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
+ ],
"public/app/features/alerting/unified/state/actions.ts:5381": [
[0, 0, 0, "\'@grafana/runtime/src/utils/logging\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
],
@@ -3183,8 +3045,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"]
],
"public/app/features/connections/tabs/ConnectData/ConnectData.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
+ [0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
@@ -3234,13 +3095,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "6"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "7"]
],
- "public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
- ],
- "public/app/features/dashboard-scene/embedding/EmbeddedDashboard.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
"public/app/features/dashboard-scene/embedding/EmbeddedDashboardTestPage.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
],
@@ -3283,8 +3137,7 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/pages/DashboardScenePage.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, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "3"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
@@ -3301,7 +3154,7 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataAlertingTab.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx:5381": [
@@ -3415,40 +3268,6 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/text/sanitize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
],
- "public/app/features/dashboard-scene/scene/NavToolbarActions.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "7"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "10"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "11"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "12"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "13"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "14"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "15"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "16"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "17"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "18"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "19"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "20"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "21"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "22"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "23"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "24"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "25"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "26"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "27"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "28"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "29"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "30"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "31"]
- ],
"public/app/features/dashboard-scene/scene/PanelLinks.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
@@ -3461,28 +3280,6 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/scene/UnlinkModal.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
- "public/app/features/dashboard-scene/scene/row-actions/RowActions.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
- ],
- "public/app/features/dashboard-scene/scene/row-actions/RowOptionsButton.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
- "public/app/features/dashboard-scene/scene/row-actions/RowOptionsForm.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
- ],
- "public/app/features/dashboard-scene/scene/row-actions/RowOptionsModal.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
- "public/app/features/dashboard-scene/scene/types.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/dashboard-scene/serialization/angularMigration.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@@ -3503,9 +3300,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"]
],
- "public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
"public/app/features/dashboard-scene/serialization/transformToV1TypesUtils.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@@ -3737,8 +3531,7 @@ exports[`better eslint`] = {
],
"public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/UpsertSnapshot.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/Input/Input\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
],
"public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawerConfirmAction.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/components/ConfirmModal/ConfirmContent\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
@@ -3773,7 +3566,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
],
- "public/app/features/dashboard/api/v0.ts:5381": [
+ "public/app/features/dashboard/api/v1.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/dashboard/components/AddLibraryPanelWidget/AddLibraryPanelWidget.tsx:5381": [
@@ -4218,6 +4011,9 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
],
+ "public/app/features/dashboard/containers/PublicDashboardPage.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
+ ],
"public/app/features/dashboard/containers/SoloPanelPage.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
@@ -4236,9 +4032,6 @@ 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/features/dashboard/services/TimeSrv.ts:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
"public/app/features/dashboard/state/DashboardMigrator.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@@ -4656,13 +4449,9 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "8"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "9"]
],
- "public/app/features/explore/ExplorePage.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
- ],
"public/app/features/explore/ExploreRunQueryButton.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
],
"public/app/features/explore/ExploreToolbar.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
@@ -4720,10 +4509,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "4"]
],
- "public/app/features/explore/Logs/LogsNavigation.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
- ],
"public/app/features/explore/Logs/LogsSamplePanel.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"],
@@ -4799,27 +4584,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
- "public/app/features/explore/QueryLibrary/QueryLibraryExpmInfo.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"]
- ],
- "public/app/features/explore/QueryLibrary/QueryTemplateForm.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/components/Input/Input\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
- "public/app/features/explore/QueryLibrary/QueryTemplatesList.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"]
- ],
- "public/app/features/explore/QueryLibrary/QueryTemplatesTable/QueryDescriptionCell.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
- ],
"public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
],
@@ -5038,9 +4802,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"]
],
"public/app/features/explore/extensions/ConfirmNavigationModal.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"],
@@ -5069,9 +4831,6 @@ exports[`better eslint`] = {
"public/app/features/explore/state/time.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
- "public/app/features/explore/state/time.ts:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
"public/app/features/explore/state/utils.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
@@ -5132,12 +4891,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"]
],
"public/app/features/expressions/components/Threshold.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
"public/app/features/expressions/guards.ts:5381": [
[0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
@@ -5383,9 +5137,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
- "public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx:5381": [
- [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"]
- ],
"public/app/features/manage-dashboards/components/SnapshotListTable.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
@@ -5577,12 +5328,6 @@ exports[`better eslint`] = {
"public/app/features/plugins/admin/components/GetStartedWithPlugin/index.ts:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./GetStartedWithPlugin\`)", "0"]
],
- "public/app/features/plugins/admin/components/InstallControls/ExternallyManagedButton.tsx:5381": [
- [0, 0, 0, "No untranslated strings. Wrap text with ", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
- ],
"public/app/features/plugins/admin/components/InstallControls/InstallControlsButton.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
@@ -5646,7 +5391,8 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
],
"public/app/features/plugins/admin/components/PluginDetailsPanel.tsx:5381": [
- [0, 0, 0, "\'@grafana/runtime/src/components/PluginPage\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
+ [0, 0, 0, "\'@grafana/runtime/src/components/PluginPage\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
],
"public/app/features/plugins/admin/components/PluginDetailsSignature.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
@@ -5747,11 +5493,10 @@ exports[`better eslint`] = {
[0, 0, 0, "\'@grafana/data/src/types/pluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
],
"public/app/features/plugins/extensions/usePluginComponents.tsx:5381": [
- [0, 0, 0, "\'@grafana/runtime/src/services/pluginExtensions/getPluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "Do not use any type assertions.", "1"]
+ [0, 0, 0, "Do not use any type assertions.", "0"]
],
- "public/app/features/plugins/extensions/usePluginLinks.tsx:5381": [
- [0, 0, 0, "\'@grafana/runtime/src/services/pluginExtensions/getPluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
+ "public/app/features/plugins/extensions/usePluginFunctions.tsx:5381": [
+ [0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/plugins/extensions/validators.ts:5381": [
[0, 0, 0, "\'@grafana/data/src/types/pluginExtensions\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
@@ -5802,9 +5547,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "5"]
],
- "public/app/features/query-library/api/query.ts:5381": [
- [0, 0, 0, "\'@grafana/runtime/src/services/backendSrv\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
"public/app/features/query/components/QueryEditorRow.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
@@ -5979,9 +5721,6 @@ exports[`better eslint`] = {
"public/app/features/search/utils.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
- "public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
"public/app/features/serviceaccounts/ServiceAccountPage.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
@@ -6182,8 +5921,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
],
"public/app/features/trails/DataTrailSettings.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
],
"public/app/features/trails/DataTrailsHistory.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/datetime/rangeutil\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
@@ -6210,9 +5948,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "7"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "8"]
],
- "public/app/features/trails/MetricSelect/NativeHistogramBadge.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
- ],
"public/app/features/trails/MetricsHeader.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
@@ -6226,8 +5961,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "2"]
],
"public/app/features/trails/banners/NativeHistogramBanner.tsx:5381": [
- [0, 0, 0, "\'@grafana/ui/src/utils/i18n\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
"public/app/features/transformers/FilterByValueTransformer/FilterByValueFilterEditor.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/transformations/transformers/filterByValue\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
@@ -6248,8 +5982,7 @@ exports[`better eslint`] = {
],
"public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
+ [0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/transformers/FilterByValueTransformer/ValueMatchers/NoopMatcherEditor.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@@ -6257,15 +5990,11 @@ exports[`better eslint`] = {
"public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RangeMatcherEditor.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "4"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "5"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "6"]
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
+ [0, 0, 0, "Unexpected any. Specify a different type.", "3"]
],
"public/app/features/transformers/FilterByValueTransformer/ValueMatchers/RegexMatcherEditor.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
"public/app/features/transformers/FilterByValueTransformer/ValueMatchers/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -6448,10 +6177,7 @@ exports[`better eslint`] = {
[0, 0, 0, "\'@grafana/data/src/transformations/transformers/histogram\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "5"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "6"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"]
],
"public/app/features/transformers/editors/JoinByFieldTransformerEditor.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/transformations/transformers/joinByField\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
@@ -6470,9 +6196,7 @@ exports[`better eslint`] = {
],
"public/app/features/transformers/editors/LimitTransformerEditor.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/transformations/transformers/limit\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
],
"public/app/features/transformers/editors/MergeTransformerEditor.tsx:5381": [
[0, 0, 0, "\'@grafana/data/src/transformations/transformers/merge\' import is restricted from being used by a pattern. Import from the public export instead.", "0"],
@@ -7578,8 +7302,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/plugins/datasource/zipkin/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.", "0"]
],
"public/app/plugins/datasource/zipkin/utils/transforms.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk
index 56a916a0d25..80b5946b497 100644
--- a/.bingo/Variables.mk
+++ b/.bingo/Variables.mk
@@ -5,6 +5,10 @@ GOPATH ?= $(shell go env GOPATH)
GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin
GO ?= $(shell which go)
+# Add this near the top of the file, after the initial variable definitions
+ifndef VARIABLES_MK
+VARIABLES_MK := 1
+
# Below generated variables ensure that every time a tool under each variable is invoked, the correct version
# will be used; reinstalling only if needed.
# For example for bra variable:
@@ -65,3 +69,4 @@ $(SWAGGER): $(BINGO_DIR)/swagger.mod
@echo "(re)installing $(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37 "github.com/go-swagger/go-swagger/cmd/swagger"
+endif
diff --git a/.drone.yml b/.drone.yml
index f81d8bbbc69..491c607a83b 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -18,7 +18,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -69,7 +69,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- go install github.com/bazelbuild/buildtools/buildifier@latest
@@ -112,7 +112,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -170,7 +170,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -179,7 +179,7 @@ steps:
name: yarn-install
- commands:
- apk add --update git bash
- - yarn betterer ci
+ - yarn betterer:ci
depends_on:
- yarn-install
image: node:22.11.0-alpine
@@ -309,7 +309,7 @@ steps:
path: /github-app
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -339,6 +339,16 @@ steps:
- yarn-install
image: node:22-bookworm
name: verify-i18n
+- commands:
+ - yarn generate-apis
+ - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\"
+ ]; then\n echo $file_diff\n echo \"\nAPI client
+ generation has not been committed. Please run 'yarn generate-apis', commit the
+ changes and push again.\"\n exit 1\n fi\n "
+ depends_on:
+ - yarn-install
+ image: node:22-bookworm
+ name: verify-api-clients
trigger:
event:
- pull_request
@@ -417,7 +427,7 @@ steps:
path: /github-app
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -479,6 +489,7 @@ trigger:
- public/app/plugins/**/plugin.json
- docs/sources/setup-grafana/configure-grafana/feature-toggles/**
- devenv/**
+ - apps/**
type: docker
volumes:
- host:
@@ -506,7 +517,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -588,6 +599,7 @@ trigger:
- public/app/plugins/**/plugin.json
- devenv/**
- .bingo/**
+ - apps/**
type: docker
volumes:
- host:
@@ -630,11 +642,11 @@ steps:
path: /github-app
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -694,12 +706,14 @@ steps:
token:
from_secret: drone_token
- commands:
- - docker run --privileged --rm tonistiigi/binfmt --install all
+ - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
+ - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
+ - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
- /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
-a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu
-a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7
-a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.5 --yarn-cache=$$YARN_CACHE_FOLDER
- --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3
+ --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3
--tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{
.version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD
> packages.txt
@@ -756,7 +770,7 @@ steps:
GF_APP_MODE: development
GF_SERVER_HTTP_PORT: "3001"
GF_SERVER_ROUTER_LOGGING: "1"
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: grafana-server
- commands:
- ./bin/build e2e-tests --port 3001 --suite dashboards-suite
@@ -1087,7 +1101,7 @@ steps:
path: /github-app
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -1100,7 +1114,7 @@ steps:
name: compile-build-cmd
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -1266,7 +1280,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -1623,7 +1637,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -1694,7 +1708,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -1703,7 +1717,7 @@ steps:
name: yarn-install
- commands:
- apk add --update git bash
- - yarn betterer ci
+ - yarn betterer:ci
depends_on:
- yarn-install
image: node:22.11.0-alpine
@@ -1752,7 +1766,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -1782,6 +1796,16 @@ steps:
- yarn-install
image: node:22-bookworm
name: verify-i18n
+- commands:
+ - yarn generate-apis
+ - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\"
+ ]; then\n echo $file_diff\n echo \"\nAPI client
+ generation has not been committed. Please run 'yarn generate-apis', commit the
+ changes and push again.\"\n exit 1\n fi\n "
+ depends_on:
+ - yarn-install
+ image: node:22-bookworm
+ name: verify-api-clients
trigger:
branch: main
event:
@@ -1818,7 +1842,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -1898,7 +1922,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -1964,7 +1988,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -2039,11 +2063,11 @@ steps:
path: /github-app
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -2102,12 +2126,14 @@ steps:
image: node:22.11.0-alpine
name: build-frontend-packages
- commands:
- - docker run --privileged --rm tonistiigi/binfmt --install all
+ - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
+ - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
+ - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
- /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
-a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu
-a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7
-a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.5 --yarn-cache=$$YARN_CACHE_FOLDER
- --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3
+ --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3
--tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{
.version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD
> packages.txt
@@ -2168,7 +2194,7 @@ steps:
GF_APP_MODE: development
GF_SERVER_HTTP_PORT: "3001"
GF_SERVER_ROUTER_LOGGING: "1"
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: grafana-server
- commands:
- ./bin/build e2e-tests --port 3001 --suite dashboards-suite
@@ -2572,7 +2598,7 @@ services:
steps:
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -2585,7 +2611,7 @@ steps:
name: compile-build-cmd
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -2830,7 +2856,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -2839,7 +2865,7 @@ steps:
name: yarn-install
- commands:
- apk add --update git bash
- - yarn betterer ci
+ - yarn betterer:ci
depends_on:
- yarn-install
image: node:22.11.0-alpine
@@ -2886,7 +2912,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -2916,6 +2942,16 @@ steps:
- yarn-install
image: node:22-bookworm
name: verify-i18n
+- commands:
+ - yarn generate-apis
+ - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\"
+ ]; then\n echo $file_diff\n echo \"\nAPI client
+ generation has not been committed. Please run 'yarn generate-apis', commit the
+ changes and push again.\"\n exit 1\n fi\n "
+ depends_on:
+ - yarn-install
+ image: node:22-bookworm
+ name: verify-api-clients
trigger:
branch:
- instant
@@ -2950,7 +2986,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -3028,7 +3064,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
@@ -3120,7 +3156,7 @@ services:
steps:
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -3133,7 +3169,7 @@ steps:
name: compile-build-cmd
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -3336,11 +3372,11 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -3468,11 +3504,11 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
@@ -3958,7 +3994,7 @@ steps:
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
- ALPINE_BASE: alpine:3.20.3
+ ALPINE_BASE: alpine:3.21.3
CDN_DESTINATION:
from_secret: rgm_cdn_destination
DESTINATION:
@@ -4033,7 +4069,7 @@ steps:
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
- ALPINE_BASE: alpine:3.20.3
+ ALPINE_BASE: alpine:3.21.3
CDN_DESTINATION:
from_secret: rgm_cdn_destination
DESTINATION:
@@ -4083,51 +4119,6 @@ volumes:
path: /var/run/docker.sock
name: docker
---
-clone:
- retries: 3
-depends_on: []
-environment:
- EDITION: oss
-image_pull_secrets:
-- gcr
-- gar
-kind: pipeline
-name: release-whatsnew-checker
-node:
- type: no-parallel
-platform:
- arch: amd64
- os: linux
-services: []
-steps:
-- commands:
- - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
- depends_on: []
- environment:
- CGO_ENABLED: 0
- image: golang:1.23.5-alpine
- name: compile-build-cmd
-- commands:
- - ./bin/build whatsnew-checker
- depends_on:
- - compile-build-cmd
- image: golang:1.23.5-alpine
- name: whats-new-checker
-trigger:
- event:
- exclude:
- - promote
- ref:
- exclude:
- - refs/tags/*-cloud*
- include:
- - refs/tags/v*
-type: docker
-volumes:
-- host:
- path: /var/run/docker.sock
- name: docker
----
clone:
retries: 3
depends_on:
@@ -4195,7 +4186,7 @@ steps:
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
- ALPINE_BASE: alpine:3.20.3
+ ALPINE_BASE: alpine:3.21.3
CDN_DESTINATION:
from_secret: rgm_cdn_destination
DESTINATION:
@@ -4297,7 +4288,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- yarn install --immutable || yarn install --immutable
@@ -4306,7 +4297,7 @@ steps:
name: yarn-install
- commands:
- apk add --update git bash
- - yarn betterer ci
+ - yarn betterer:ci
depends_on:
- yarn-install
image: node:22.11.0-alpine
@@ -4351,7 +4342,7 @@ services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -4432,7 +4423,7 @@ steps:
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
- ALPINE_BASE: alpine:3.20.3
+ ALPINE_BASE: alpine:3.21.3
CDN_DESTINATION:
from_secret: rgm_cdn_destination
DESTINATION:
@@ -4576,7 +4567,7 @@ steps:
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
- ALPINE_BASE: alpine:3.20.3
+ ALPINE_BASE: alpine:3.21.3
CDN_DESTINATION:
from_secret: rgm_cdn_destination
DESTINATION:
@@ -4696,15 +4687,15 @@ steps:
path: /github-app
- commands:
- export GITHUB_TOKEN=$(cat /github-app/token)
- - 'dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF}
- --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} '
- - --go-version=1.23.5
+ - dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF}
+ --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION}
+ --go-version=1.23.5
depends_on:
- github-app-generate-token
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
- ALPINE_BASE: alpine:3.20.3
+ ALPINE_BASE: alpine:3.21.3
CDN_DESTINATION:
from_secret: rgm_cdn_destination
DESTINATION:
@@ -4743,6 +4734,8 @@ steps:
- printenv GCP_KEY_BASE64 | base64 -d > /tmp/key.json
- gcloud auth activate-service-account --key-file=/tmp/key.json
- gcloud storage cp -r dist/* $${UPLOAD_TO}
+ depends_on:
+ - rgm-build
environment:
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
from_secret: dagger_token
@@ -4783,6 +4776,8 @@ volumes:
name: docker
- name: github-app
path: /github-app
+- name: github-app
+ temp: {}
---
clone:
retries: 3
@@ -4837,13 +4832,13 @@ services:
steps:
- commands:
- mkdir -p bin
- - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl
+ - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- echo $DRONE_RUNNER_NAME
- image: alpine:3.20.3
+ image: alpine:3.21.3
name: identify-runner
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -5267,7 +5262,7 @@ steps:
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-deploy:1.3.3
- - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.20.3
+ - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.21.3
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM ubuntu:22.04
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM byrnedo/alpine-curl:0.1.8
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack
@@ -5305,7 +5300,7 @@ steps:
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm
- trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0
- trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-deploy:1.3.3
- - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.20.3
+ - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.21.3
- trivy --exit-code 1 --severity HIGH,CRITICAL ubuntu:22.04
- trivy --exit-code 1 --severity HIGH,CRITICAL byrnedo/alpine-curl:0.1.8
- trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack
@@ -5569,6 +5564,6 @@ kind: secret
name: gcr_credentials
---
kind: signature
-hmac: 154c1f072294cef09ba4f579eed30b1e29bc5c263897c862f4574432d321502c
+hmac: f16a4715c7a4e6a4ffb1fe041b42fb966310fd5da455239614e9a239493aff82
...
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index bb3c9871f91..92581c315aa 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -38,7 +38,7 @@
/docs/sources/ @irenerl24
-/docs/sources/alerting/ @brendamuir
+/docs/sources/alerting/ @JohnnyK-Grafana
/docs/sources/dashboards/ @imatwawana
/docs/sources/datasources/ @lwandz13
/docs/sources/panels-visualizations/ @imatwawana
@@ -66,9 +66,12 @@
/scripts/go-workspace @grafana/grafana-app-platform-squad
/hack/ @grafana/grafana-app-platform-squad
+/pkg/apis/provisioning @grafana/grafana-git-ui-sync-team
+/pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team
+
/apps/alerting/ @grafana/alerting-backend
/apps/playlist/ @grafana/grafana-app-platform-squad
-/apps/investigation/ @fcjack @matryer
+/apps/investigations/ @fcjack @matryer @svennergr
/apps/advisor/ @grafana/plugins-platform-backend
/pkg/api/ @grafana/grafana-backend-group
/pkg/apis/ @grafana/grafana-app-platform-squad
@@ -238,6 +241,7 @@
/devenv/dev-dashboards/extensions/ @grafana/plugins-platform-frontend
/devenv/docker/blocks/alert_webhook_listener/ @grafana/alerting-backend
+/devenv/docker/blocks/stateful_webhook/ @grafana/alerting-backend
/devenv/docker/blocks/caddy_tls/ @grafana/alerting-backend
/devenv/docker/blocks/clickhouse/ @grafana/partner-datasources
/devenv/docker/blocks/collectd/ @grafana/observability-metrics
@@ -377,6 +381,11 @@
/crowdin.yml @grafana/grafana-frontend-platform
/public/locales/ @grafana/grafana-frontend-platform
+/public/locales/de-DE @grafanabot
+/public/locales/es-ES @grafanabot
+/public/locales/fr-FR @grafanabot
+/public/locales/pt-BR @grafanabot
+/public/locales/zh-Hans @grafanabot
/public/app/core/internationalization/ @grafana/grafana-frontend-platform
/e2e/ @grafana/grafana-frontend-platform
/e2e/cloud-plugins-suite/ @grafana/partner-datasources
@@ -402,7 +411,7 @@
/packages/grafana-schema/src/**/*canvas* @grafana/dataviz-squad
/packages/grafana-schema/src/**/*tempo* @grafana/observability-traces-and-profiling
/packages/grafana-sql/ @grafana/partner-datasources @grafana/oss-big-tent
-/packages/grafana-ui/.storybook/ @grafana/plugins-platform-frontend
+/packages/grafana-ui/.storybook/ @grafana/grafana-frontend-platform
/packages/grafana-ui/src/components/ @grafana/grafana-frontend-platform
/packages/grafana-ui/src/components/BarGauge/ @grafana/dataviz-squad
/packages/grafana-ui/src/components/DataLinks/ @grafana/dataviz-squad
@@ -421,7 +430,7 @@
/packages/grafana-ui/src/graveyard/Graph/ @grafana/dataviz-squad
/packages/grafana-ui/src/graveyard/GraphNG/ @grafana/dataviz-squad
/packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad
-/packages/grafana-ui/src/utils/storybook/ @grafana/plugins-platform-frontend
+/packages/grafana-ui/src/utils/storybook/ @grafana/grafana-frontend-platform
# root files, mostly frontend
/.browserslistrc @grafana/frontend-ops
@@ -435,6 +444,7 @@
/.betterer.eslint.config.js @grafana/frontend-ops
/.gitattributes @grafana/frontend-ops
/.gitignore @grafana/frontend-ops
+/.ignore @grafana/frontend-ops
/.nvmrc @grafana/frontend-ops
/.prettierignore @grafana/frontend-ops
/.yarn @grafana/frontend-ops
@@ -455,6 +465,7 @@
playwright.config.ts @grafana/plugins-platform-frontend
# public folder
+/public/app/api/ @grafana/grafana-frontend-platform
/public/app/core/ @grafana/grafana-frontend-platform
/public/app/core/components/TimePicker/ @grafana/grafana-frontend-platform
/public/app/core/components/Layers/ @grafana/dataviz-squad
@@ -491,6 +502,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/explore/ @grafana/observability-traces-and-profiling
/public/app/features/expressions/ @grafana/observability-metrics
/public/app/features/folders/ @grafana/grafana-frontend-platform
+/public/app/features/iam/ @grafana/grafana-frontend-platform
/public/app/features/inspector/ @grafana/dashboards-squad
/public/app/features/invites/ @grafana/grafana-frontend-platform
/public/app/features/library-panels/ @grafana/dashboards-squad
@@ -504,7 +516,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/playlist/ @grafana/dashboards-squad
/public/app/features/plugins/ @grafana/plugins-platform-frontend
/public/app/features/profile/ @grafana/grafana-frontend-platform
-/public/app/features/query-library/ @grafana/grafana-frontend-platform
/public/app/features/runtime/ @ryantxu
/public/app/features/query/ @grafana/dashboards-squad
/public/app/features/sandbox/ @grafana/grafana-frontend-platform
@@ -533,6 +544,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/plugins/panel/heatmap/ @grafana/dataviz-squad
/public/app/plugins/panel/histogram/ @grafana/dataviz-squad
/public/app/plugins/panel/logs/ @grafana/observability-logs
+/public/app/plugins/panel/logs-new/ @grafana/observability-logs
/public/app/plugins/panel/nodeGraph/ @grafana/observability-traces-and-profiling @grafana/app-o11y-visualizations
/public/app/plugins/panel/traces/ @grafana/observability-traces-and-profiling
/public/app/plugins/panel/flamegraph/ @grafana/observability-traces-and-profiling
@@ -581,6 +593,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/explore/NodeGraph/ @grafana/observability-traces-and-profiling
/public/app/features/explore/FlameGraph/ @grafana/observability-traces-and-profiling
/public/app/features/explore/TraceView/ @grafana/observability-traces-and-profiling
+/public/app/features/explore/QueryLibrary/ @grafana/grafana-frontend-platform
/public/api-merged.json @grafana/grafana-backend-group
/public/api-enterprise-spec.json @grafana/grafana-backend-group
@@ -593,9 +606,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/AppWrapper.tsx @grafana/frontend-ops
/public/app/partials/ @grafana/grafana-frontend-platform
-
-
-
/scripts/benchmark-access-control.sh @grafana/access-squad
/scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend
/scripts/ci-* @grafana/grafana-developer-enablement-squad
@@ -620,6 +630,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/scripts/cleanup-husky.sh @grafana/frontend-ops
/scripts/verify-repo-update/ @grafana/grafana-developer-enablement-squad
/scripts/generate-rtk-apis.ts @grafana/grafana-frontend-platform
+/scripts/process-specs.ts @grafana/grafana-frontend-platform
/scripts/generate-alerting-rtk-apis.ts @grafana/alerting-frontend
/scripts/levitate-parse-json-report.js @grafana/plugins-platform-frontend
/scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend
@@ -633,9 +644,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
.betterer.results @grafanabot
.betterer.ts @grafana/grafana-frontend-platform
-# @grafana/ui component documentation
-*.mdx @grafana/plugins-platform-frontend
-
# Design system
/public/img/icons/unicons/ @grafana/design-system
@@ -746,14 +754,19 @@ embed.go @grafana/grafana-as-code
/.github/pr-checks.json @tolzhabayev
/.github/pr-commands.json @tolzhabayev
/.github/renovate.json5 @grafana/frontend-ops
+/.github/workflows/add-to-whats-new.yml @grafana/docs-tooling
/.github/workflows/auto-triager/ @grafana/plugins-platform-frontend
/.github/workflows/alerting-swagger-gen.yml @grafana/alerting-backend
+/.github/workflows/alerting-update-module.yml @grafana/alerting-backend
/.github/workflows/auto-milestone.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/backport.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/bump-version.yml @grafana/grafana-developer-enablement-squad
+/.github/workflows/backend-coverage.yml @Proximyst
/.github/workflows/close-milestone.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-pr.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-comms.yml @grafana/grafana-developer-enablement-squad
+/.github/workflows/migrate-prs.yml @grafana/grafana-developer-enablement-squad
+/.github/workflows/create-next-release-branch.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/codeowners-validator.yml @tolzhabayev
/.github/workflows/codeql-analysis.yml @DanCech
/.github/workflows/commands.yml @torkelo
@@ -777,7 +790,6 @@ embed.go @grafana/grafana-as-code
/.github/workflows/publish-technical-documentation-next.yml @grafana/docs-tooling
/.github/workflows/publish-technical-documentation-release.yml @grafana/docs-tooling
/.github/workflows/remove-milestone.yml @grafana/grafana-developer-enablement-squad
-/.github/workflows/sbom-report.yml @grafana/security-team
/.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend
/.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/update-changelog.yml @grafana/grafana-developer-enablement-squad
@@ -787,6 +799,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/publish-kinds-release.yml @grafana/platform-monitoring
/.github/workflows/verify-kinds.yml @grafana/platform-monitoring
/.github/workflows/dashboards-issue-add-label.yml @grafana/dashboards-squad
+/.github/workflows/run-schema-v2-e2e.yml @grafana/dashboards-squad
/.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-backend-services-squad
/.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/core-plugins-build-and-release.yml @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
diff --git a/.github/commands.json b/.github/commands.json
index fb10220fb21..f2896d61551 100644
--- a/.github/commands.json
+++ b/.github/commands.json
@@ -640,7 +640,7 @@
"name": "area/configuration",
"action": "addToProject",
"addToProject": {
- "url": "https://github.com/orgs/grafana/projects/96"
+ "url": "https://github.com/orgs/grafana/projects/665"
}
},
{
@@ -744,7 +744,7 @@
"name": "area/backend/db/postgres",
"action": "addToProject",
"addToProject": {
- "url": "https://github.com/orgs/grafana/projects/96"
+ "url": "https://github.com/orgs/grafana/projects/835"
}
},
{
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f37f7a732f6..0de94ce207d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -8,7 +8,7 @@ updates:
directories:
- "/"
- "/apps/playlist"
- - "/apps/investigation"
+ - "/apps/investigations"
- "/pkg/aggregator"
- "/pkg/apimachinery"
- "/pkg/apiserver"
diff --git a/.github/pr-commands.json b/.github/pr-commands.json
index 1803aa29a8a..d211b5cce3a 100644
--- a/.github/pr-commands.json
+++ b/.github/pr-commands.json
@@ -247,7 +247,8 @@
"/pkg/services/sqlstore/migrations/ualert/**/*",
"/pkg/services/alerting/**/*",
"/public/app/features/alerting/**/*",
- "/pkg/tests/api/alerting/**/*"
+ "/pkg/tests/api/alerting/**/*",
+ "/pkg/tests/alertmanager/**/*"
],
"action": "updateLabel",
"addLabel": "area/alerting"
diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 6cc2ac80423..a782ac2c0a0 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -13,7 +13,6 @@
"slate-react", // we don't want to continue using this on the long run, use Monaco editor instead of Slate
"@types/slate-react", // we don't want to continue using this on the long run, use Monaco editor instead of Slate
"@types/slate", // we don't want to continue using this on the long run, use Monaco editor instead of Slate
- "storybook-dark-mode", // 4.0.2 causes storybook 8.4 to break with react hooks errors
// Temporarily pause updating lerna and nx until we resolve build issues
"lerna",
"nx"
diff --git a/.github/workflows/actions/changelog/index.js b/.github/workflows/actions/changelog/index.js
index 48da9dcf33a..b658caf349c 100644
--- a/.github/workflows/actions/changelog/index.js
+++ b/.github/workflows/actions/changelog/index.js
@@ -69,8 +69,17 @@ const graphql = async (ghtoken, query, variables) => {
},
body: JSON.stringify({ query, variables }),
});
- const { data } = await results.json();
- return data;
+
+ const res = await results.json();
+
+ LOG(
+ JSON.stringify({
+ status: results.status,
+ text: results.statusText,
+ })
+ );
+
+ return res.data;
};
// Using Github GraphQL API find the timestamp for the given tag/commit hash.
@@ -99,20 +108,20 @@ const getCommitishDate = async (name, owner, target) => {
// Using Github GraphQL API get a list of PRs between the two "commitish" items.
// This resoves the "since" item's timestamp first and iterates over all PRs
// till "target" using naïve pagination.
-const getHistory = async (name, owner, target, sinceDate) => {
- LOG(`Fetching ${owner}/${name} PRs since ${sinceDate} till ${target}`);
+const getHistory = async (name, owner, from, to) => {
+ LOG(`Fetching ${owner}/${name} PRs between ${from} and ${to}`);
const query = `
query findCommitsWithAssociatedPullRequests(
$name: String!
$owner: String!
- $target: String!
- $sinceDate: GitTimestamp
+ $from: String!
+ $to: String!
$cursor: String
) {
repository(name: $name, owner: $owner) {
- object(expression: $target) {
- ... on Commit {
- history(first: 50, since: $sinceDate, after: $cursor) {
+ ref(qualifiedName: $from) {
+ compare(headRef: $to) {
+ commits(first: 25, after: $cursor) {
totalCount
pageInfo {
hasNextPage
@@ -155,13 +164,13 @@ const getHistory = async (name, owner, target, sinceDate) => {
const result = await graphql(ghtoken, query, {
name,
owner,
- target,
- sinceDate,
+ from,
+ to,
cursor,
});
LOG(`GraphQL: ${JSON.stringify(result)}`);
- nodes = [...nodes, ...result.repository.object.history.nodes];
- const { hasNextPage, endCursor } = result.repository.object.history.pageInfo;
+ nodes = [...nodes, ...result.repository.ref.compare.commits.nodes];
+ const { hasNextPage, endCursor } = result.repository.ref.compare.commits.pageInfo;
if (!hasNextPage) {
break;
}
@@ -175,11 +184,11 @@ const getHistory = async (name, owner, target, sinceDate) => {
// feature, deprecation, breaking change and plugin fixes/enhancements).
//
// PR grouping relies on Github labels only, not on the PR contents.
-const getChangeLogItems = async (name, owner, sinceDate, to) => {
+const getChangeLogItems = async (name, owner, from, to) => {
// check if a node contains a certain label
const hasLabel = ({ labels }, label) => labels.nodes.some(({ name }) => name === label);
// get all the PRs between the two "commitish" items
- const history = await getHistory(name, owner, to, sinceDate);
+ const history = await getHistory(name, owner, from, to);
const items = history.flatMap((node) => {
// discard PRs without a "changelog" label
@@ -231,13 +240,10 @@ const previous = process.argv[3] || process.env.INPUT_PREVIOUS || (await getPrev
LOG(`Previous tag/commit: ${previous}`);
-const sinceDate = await getCommitishDate('grafana', 'grafana', previous);
-LOG(`Previous tag/commit timestamp: ${sinceDate}`);
-
// Get all changelog items from Grafana OSS
-const oss = await getChangeLogItems('grafana', 'grafana', sinceDate, target);
+const oss = await getChangeLogItems('grafana', 'grafana', previous, target);
// Get all changelog items from Grafana Enterprise
-const entr = await getChangeLogItems('grafana-enterprise', 'grafana', sinceDate, target);
+const entr = await getChangeLogItems('grafana-enterprise', 'grafana', previous, target);
LOG(`Found OSS PRs: ${oss.length}`);
LOG(`Found Enterprise PRs: ${entr.length}`);
diff --git a/.github/workflows/add-to-whats-new.yml b/.github/workflows/add-to-whats-new.yml
new file mode 100644
index 00000000000..900ab9615bd
--- /dev/null
+++ b/.github/workflows/add-to-whats-new.yml
@@ -0,0 +1,16 @@
+name: Add comment about adding a What's new note
+on:
+ pull_request:
+ types: [labeled]
+
+jobs:
+ add-comment:
+ if: ${{ ! github.event.pull_request.head.repo.fork && contains(github.event.pull_request.labels.*.name, 'add to what''s new') }}
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
+ with:
+ message: |
+ Since you've added the `Add to what's new` label, consider drafting a [What's new note](https://admin.grafana.com/content-admin/#/collections/whats-new/new) for this feature.
diff --git a/.github/workflows/alerting-update-module.yml b/.github/workflows/alerting-update-module.yml
new file mode 100644
index 00000000000..1d919f3dc4b
--- /dev/null
+++ b/.github/workflows/alerting-update-module.yml
@@ -0,0 +1,131 @@
+name: Update Alerting Module
+
+on:
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ update-grafana:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+ id-token: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
+
+ - name: Check if update branch exists
+ run: |
+ if git ls-remote --heads origin update-alerting-module | grep -q 'update-alerting-module'; then
+ echo "Branch 'update-alerting-module' already exists. There might be an open PR with Grafana updates."
+ echo "Please review and merge/close the existing PR before running this workflow again."
+ exit 1
+ fi
+
+ - name: Setup Go
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # 5.3.0
+ with:
+ "go-version-file": "go.mod"
+
+ - name: Extract current commit hash of alerting module
+ id: current-commit
+ run: |
+ FROM_COMMIT=$(go list -m -json github.com/grafana/alerting | jq -r '.Version' | grep -oP '(?<=-)[a-f0-9]+$')
+ echo "from_commit=$FROM_COMMIT" >> $GITHUB_OUTPUT
+
+ - name: Get latest commit
+ id: latest-commit
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ TO_COMMIT=$(gh api repos/grafana/alerting/commits/main --jq '.sha')
+ if [ -z "$TO_COMMIT" ]; then
+ echo "Failed to fetch latest commit"
+ exit 1
+ fi
+ echo "to_commit=$TO_COMMIT" >> $GITHUB_OUTPUT
+
+ - name: Compare commit hashes
+ run: |
+ FROM_COMMIT="${{ steps.current-commit.outputs.from_commit }}"
+ TO_COMMIT="${{ steps.latest-commit.outputs.to_commit }}"
+
+ # Compare just the length of the shorter hash
+ SHORT_TO_COMMIT="${TO_COMMIT:0:${#FROM_COMMIT}}"
+
+ if [ "$FROM_COMMIT" = "$SHORT_TO_COMMIT" ]; then
+ echo "Current version ($FROM_COMMIT) is already at latest ($SHORT_TO_COMMIT). No update needed."
+ exit 0
+ fi
+ echo "Updates available: $FROM_COMMIT -> $TO_COMMIT"
+
+ - name: Check for commit history
+ id: check-commits
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ # get all commits that contains 'Alerting:' in the message
+ ALERTING_COMMITS=$(gh api repos/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} \
+ --jq '.commits[].commit.message | split("\n")[0]') || true
+
+ # Use printf instead of echo -e for better multiline handling
+ printf "%s\n" "$ALERTING_COMMITS"
+
+ # make the list for markdown and replace PR numbers with links
+ ALERTING_COMMITS_FORMATTED=$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done)
+
+ echo "alerting_commits<> $GITHUB_OUTPUT
+ echo "$ALERTING_COMMITS_FORMATTED" >> $GITHUB_OUTPUT
+ echo "EOF" >> $GITHUB_OUTPUT
+
+ - name: Update alerting module
+ env:
+ GOSUMDB: off
+ run: |
+ go get github.com/grafana/alerting@${{ steps.latest-commit.outputs.to_commit }}
+ make update-workspace
+
+ - id: get-secrets
+ uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # 1.1.0
+ with:
+ repo_secrets: |
+ GITHUB_APP_ID=alerting-team:app-id
+ GITHUB_APP_PRIVATE_KEY=alerting-team:private-key
+
+ - name: "Generate token"
+ id: generate_token
+ uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # 1.11.5
+ with:
+ app-id: ${{ env.GITHUB_APP_ID }}
+ private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }}
+
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # 7.0.6
+ id: create-pr
+ with:
+ token: '${{ steps.generate_token.outputs.token }}'
+ title: 'Alerting: Update alerting module to ${{ steps.latest-commit.outputs.to_commit }}'
+ branch: alerting/update-alerting-module
+ delete-branch: true
+ body: |
+ Updates Grafana Alerting module to latest version.
+
+ Compare changes: https://github.com/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }}
+
+ Commits
+
+ ${{ steps.check-commits.outputs.alerting_commits }}
+
+
+
+ Created by: [GitHub Action Job](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
+ - name: Add PR URL to Summary
+ if: steps.create-pr.outputs.pull-request-url != ''
+ run: |
+ echo "## Pull Request Created" >> $GITHUB_STEP_SUMMARY
+ echo "🔗 [View Pull Request](${{ steps.create-pr.outputs.pull-request-url }})" >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/backend-coverage.yml b/.github/workflows/backend-coverage.yml
new file mode 100644
index 00000000000..f103e5b1eed
--- /dev/null
+++ b/.github/workflows/backend-coverage.yml
@@ -0,0 +1,109 @@
+name: Backend Coverage (OSS)
+on:
+ push:
+ paths:
+ - pkg/**
+ - .github/workflows/backend-coverage.yml
+ - go.*
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ unit-tests:
+ name: Run unit tests (OSS)
+ runs-on: ubuntu-latest
+ if: github.repository == 'grafana/grafana'
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4.2.2
+ - name: Setup Go environment
+ uses: actions/setup-go@v5.3.0
+ with:
+ go-version-file: go.work
+ - name: Run tests
+ run: make gen-go test-go-unit
+ - name: Upload coverage file
+ uses: actions/upload-artifact@v4
+ if: success() || failure()
+ with:
+ name: unit-cov
+ path: unit.cov
+ retention-days: 1
+ compression-level: 9 # this is raw text, and includes a _lot_ of repetition. compressing it should yield pretty good results.
+ integration-tests:
+ name: Run integration tests (OSS)
+ runs-on: ubuntu-latest
+ if: github.repository == 'grafana/grafana'
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4.2.2
+ - name: Setup Go environment
+ uses: actions/setup-go@v5.3.0
+ with:
+ go-version-file: go.work
+ - name: Run tests
+ run: make gen-go test-go-integration
+ - name: Upload coverage file
+ uses: actions/upload-artifact@v4
+ if: success() || failure()
+ with:
+ name: integration-cov
+ path: integration.cov
+ retention-days: 1
+ compression-level: 9 # this is raw text, and includes a _lot_ of repetition. compressing it should yield pretty good results.
+ report-coverage:
+ name: Report coverage from unit and integration tests (OSS)
+ needs: [unit-tests, integration-tests]
+ runs-on: ubuntu-latest
+ # we don't do always() so as to not run even if the workflow is cancelled
+ if: github.repository == 'grafana/grafana' && (success() || failure())
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4.2.2
+ - name: Setup Go environment
+ uses: actions/setup-go@v5.3.0
+ with:
+ go-version-file: go.work
+ - name: Generate Go
+ run: make gen-go
+ - uses: actions/download-artifact@v4
+ with:
+ pattern: '*-cov'
+ path: .
+ merge-multiple: true
+ - name: Join coverage outputs
+ run: |
+ cp unit.cov backend.cov
+ tail -n+2 integration.cov >> backend.cov
+ - name: Convert coverage info to per-func stats
+ run: go tool cover -func backend.cov > backend-funcs.log
+ # The HTML can be useful in some UI to browse the artifacts easily.
+ - name: Convert coverage info to HTML
+ run: go tool cover -html backend.cov -o backend.html
+ - name: Upload coverage file
+ uses: actions/upload-artifact@v4
+ with:
+ name: backend-cov
+ path: |
+ backend.cov
+ backend-funcs.log
+ backend.html
+ retention-days: 30
+ compression-level: 9 # this is raw text, and includes a _lot_ of repetition. compressing it should yield pretty good results.
+ - name: Delete old coverage files
+ # This is a community tool, not one provided by GitHub. Hence it shall be pinned to a hash.
+ # https://github.com/GeekyEggo/delete-artifact/tree/v5
+ uses: GeekyEggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b
+ with:
+ name: |
+ unit-cov
+ integration-cov
+ failOnError: false # oh well, we'll just have the extra artifacts...
+ - name: Set summary to total coverage
+ # We use single quotes here to disable the bash backtick behaviour of executing commands.
+ run: |
+ echo '# Coverage' >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ grep 'total:' backend-funcs.log | tr '\t' ' ' >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
\ No newline at end of file
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 18407b67f76..5f21ab276ee 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -10,24 +10,18 @@ jobs:
if: github.repository == 'grafana/grafana'
runs-on: ubuntu-latest
steps:
- - name: Checkout Actions
+ - name: Checkout
uses: actions/checkout@v4
- with:
- repository: "grafana/grafana-github-actions"
- path: ./actions
- ref: main
- - name: Install Actions
- run: npm install --production --prefix ./actions
- name: "Generate token"
id: generate_token
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
with:
app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ - run: git config --global user.email '132647405+grafana-delivery-bot[bot]@users.noreply.github.com'
+ - run: git config --global user.name 'grafana-delivery-bot[bot]'
+ - run: git remote set-url origin "https://grafana-delivery-bot:${{ steps.generate_token.outputs.token }}@github.com/grafana/grafana.git"
- name: Run backport
- uses: ./actions/backport
+ uses: grafana/grafana-github-actions-go/backport@main
with:
- metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}}
token: ${{ steps.generate_token.outputs.token }}
- labelsToAdd: "backport"
- title: "[{{base}}] {{originalTitle}}"
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 14981389deb..c1f90ceb831 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -52,7 +52,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -67,4 +67,4 @@ jobs:
make build-go
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/create-next-release-branch.yml b/.github/workflows/create-next-release-branch.yml
new file mode 100644
index 00000000000..8fc01cd442d
--- /dev/null
+++ b/.github/workflows/create-next-release-branch.yml
@@ -0,0 +1,53 @@
+name: Create next release branch
+on:
+ workflow_call:
+ inputs:
+ ownerRepo:
+ type: string
+ description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana')
+ required: true
+ source:
+ description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created)
+ type: string
+ required: true
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID:
+ required: true
+ GRAFANA_DELIVERY_BOT_APP_PEM:
+ required: true
+ outputs:
+ branch:
+ description: The new branch that was created
+ value: ${{ jobs.main.outputs.branch }}
+ workflow_dispatch:
+ inputs:
+ ownerRepo:
+ description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana')
+ source:
+ description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created)
+ type: string
+ required: true
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID:
+ required: true
+ GRAFANA_DELIVERY_BOT_APP_PEM:
+ required: true
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ outputs:
+ branch: ${{ steps.branch.outputs.branch }}
+ steps:
+ - name: "Generate token"
+ id: generate_token
+ uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
+ with:
+ app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
+ private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ - name: Create release branch
+ id: branch
+ uses: grafana/grafana-github-actions-go/bump-release@main
+ with:
+ ownerRepo: ${{ inputs.ownerRepo }}
+ source: ${{ inputs.source }}
+ token: ${{ steps.generate_token.outputs.token }}
diff --git a/.github/workflows/create-security-patch-from-security-mirror.yml b/.github/workflows/create-security-patch-from-security-mirror.yml
index e187149b8b4..f85239241ad 100644
--- a/.github/workflows/create-security-patch-from-security-mirror.yml
+++ b/.github/workflows/create-security-patch-from-security-mirror.yml
@@ -11,6 +11,7 @@ on:
branches:
- "main"
- "v*.*.*"
+ - "release-*.*.*"
# This is run before the pull request has been merged, so we'll run against the src branch
jobs:
diff --git a/.github/workflows/deploy-pr-preview.yml b/.github/workflows/deploy-pr-preview.yml
index dcb8fcd1c2a..bfad92ebf73 100644
--- a/.github/workflows/deploy-pr-preview.yml
+++ b/.github/workflows/deploy-pr-preview.yml
@@ -11,14 +11,21 @@ on:
jobs:
deploy-pr-preview:
- if: ${{ ! github.event.pull_request.head.repo.fork }}
+ if: "!github.event.pull_request.head.repo.fork"
uses: grafana/writers-toolkit/.github/workflows/deploy-preview.yml@main
with:
- sha: ${{ github.event.pull_request.head.sha }}
branch: ${{ github.head_ref }}
event_number: ${{ github.event.number }}
- title: ${{ github.event.pull_request.title }}
repo: grafana
- website_directory: content/docs/grafana/latest
- relative_prefix: /docs/grafana/latest/
- index_file: true
+ sha: ${{ github.event.pull_request.head.sha }}
+ sources: |
+ [
+ {
+ "index_file": "content/docs/grafana/_index.md",
+ "relative_prefix": "/docs/grafana/latest/",
+ "repo": "grafana",
+ "source_directory": "docs/sources",
+ "website_directory": "content/docs/grafana/latest"
+ }
+ ]
+ title: ${{ github.event.pull_request.title }}
diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml
index 0f041220ec9..4c688968a09 100644
--- a/.github/workflows/documentation-ci.yml
+++ b/.github/workflows/documentation-ci.yml
@@ -10,10 +10,10 @@ jobs:
container:
image: grafana/vale:latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@v4
with:
persist-credentials: false
- - uses: grafana/writers-toolkit/vale-action@13205961f20ad13843505a9b84fdf032f911a3f4 # vale-action/v1.1.0
+ - uses: grafana/writers-toolkit/vale-action@vale-action/v1
with:
filter: '.Name in ["Grafana.WordList", "Grafana.Spelling", "Grafana.ProductPossessives"]'
token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/i18n-crowdin-download.yml b/.github/workflows/i18n-crowdin-download.yml
index cba14abe43b..e0c3c50b9bb 100644
--- a/.github/workflows/i18n-crowdin-download.yml
+++ b/.github/workflows/i18n-crowdin-download.yml
@@ -3,7 +3,7 @@ name: Crowdin Download Action
on:
workflow_dispatch:
schedule:
- - cron: "0 * * * *"
+ - cron: "0 0 * * *"
jobs:
download-sources-from-crowdin:
@@ -12,6 +12,7 @@ jobs:
permissions:
contents: write # needed to commit changes into the PR
pull-requests: write # needed to update PR description, labels, etc
+ id-token: write # needed to get vault secrets
steps:
- name: Generate token
@@ -41,17 +42,11 @@ jobs:
pull_request_body: |
:robot: Automatic download of translations from Crowdin.
- Steps for merging:
- 1. A quick sanity check of the changes and approve. Things to look out for:
- - No changes in the English file. The source of truth is in the main branch, NOT in Crowdin.
- - Translations maybe be removed if the English phrase was removed, but there should not be many of these
- - Anything else that looks 'funky'. Ask if you're not sure.
- 2. Approve & (Auto-)merge. :tada:
+ This runs once per day and will merge automatically if all the required checks pass.
- If there's a conflict, close the pull request and **delete the branch**. A GH action will recreate the pull request.
- Remember, the longer this pull request is open, the more likely it is that it'll get conflicts.
+ If there's a conflict, close the pull request and **delete the branch**.
+ You can then either wait for the schedule to trigger a new PR, or rerun the action manually.
pull_request_labels: 'area/frontend, area/internationalization, no-changelog, no-backport'
- pull_request_reviewers: 'grafana-frontend-platform'
pull_request_base_branch_name: 'main'
base_url: 'https://grafana.api.crowdin.com'
config: 'crowdin.yml'
@@ -119,3 +114,45 @@ jobs:
with:
pr: ${{ steps.crowdin-download.outputs.pull_request_number }}
token: ${{ steps.generate_token.outputs.token }}
+
+ - name: Get vault secrets
+ id: vault-secrets
+ uses: grafana/shared-workflows/actions/get-vault-secrets@main
+ with:
+ # Secrets placed in ci/repo/grafana/grafana/grafana-pr-approver
+ repo_secrets: |
+ GRAFANA_PR_APPROVER_APP_ID=grafana-pr-approver:app-id
+ GRAFANA_PR_APPROVER_APP_PEM=grafana-pr-approver:private-key
+
+ - name: Generate approver token
+ if: steps.crowdin-download.outputs.pull_request_url
+ id: generate_approver_token
+ uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
+ with:
+ app_id: ${{ env.GRAFANA_PR_APPROVER_APP_ID }}
+ private_key: ${{ env.GRAFANA_PR_APPROVER_APP_PEM }}
+
+ - name: Approve and automerge PR
+ if: steps.crowdin-download.outputs.pull_request_url
+ shell: bash
+ # Only approve if:
+ # - the PR does not modify files other than json files under the public/locales/ directory
+ # - the PR does not modify the en-US locale
+ run: |
+ filesChanged=$(gh pr diff --name-only ${{ steps.crowdin-download.outputs.pull_request_url }})
+
+ if [[ $(echo $filesChanged | grep -v 'public/locales/[a-zA-Z\-]*/grafana.json' | wc -l) -ne 0 ]]; then
+ echo "Non-i18n changes detected, not approving"
+ exit 1
+ fi
+
+ if [[ $(echo $filesChanged | grep "public/locales/en-US" | wc -l) -ne 0 ]]; then
+ echo "public/locales/en-US changes detected, not approving"
+ exit 1
+ fi
+
+ echo "Approving and enabling automerge"
+ gh pr review ${{ steps.crowdin-download.outputs.pull_request_url }} --approve
+ gh pr merge --auto --squash ${{ steps.crowdin-download.outputs.pull_request_url }}
+ env:
+ GITHUB_TOKEN: ${{ steps.generate_approver_token.outputs.token }}
diff --git a/.github/workflows/migrate-prs.yml b/.github/workflows/migrate-prs.yml
new file mode 100644
index 00000000000..31bb8f9f9da
--- /dev/null
+++ b/.github/workflows/migrate-prs.yml
@@ -0,0 +1,60 @@
+name: Migrate open PRs
+# Migrate open PRs from a superseded release branch to the current release branch and notify authors
+on:
+ workflow_call:
+ inputs:
+ from:
+ description: 'The base branch to check for open PRs'
+ required: true
+ type: string
+ to:
+ description: 'The base branch to migrate open PRs to'
+ required: true
+ type: string
+ ownerRepo:
+ description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana')
+ required: true
+ type: string
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID:
+ required: true
+ GRAFANA_DELIVERY_BOT_APP_PEM:
+ required: true
+ workflow_dispatch:
+ inputs:
+ from:
+ description: 'The base branch to check for open PRs'
+ required: true
+ type: string
+ to:
+ description: 'The base branch to migrate open PRs to'
+ required: true
+ type: string
+ ownerRepo:
+ description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana')
+ required: true
+ type: string
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID:
+ required: true
+ GRAFANA_DELIVERY_BOT_APP_PEM:
+ required: true
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: "Generate token"
+ id: generate_token
+ uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
+ with:
+ app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
+ private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ - name: Migrate PRs
+ uses: grafana/grafana-github-actions-go/migrate-open-prs@main
+ with:
+ token: ${{ steps.generate_token.outputs.token }}
+ ownerRepo: ${{ inputs.ownerRepo }}
+ from: ${{ inputs.from }}
+ to: ${{ inputs.to }}
+ binary_release_tag: 'dev'
diff --git a/.github/workflows/publish-technical-documentation-release.yml b/.github/workflows/publish-technical-documentation-release.yml
index b36eb48885a..57d779660c5 100644
--- a/.github/workflows/publish-technical-documentation-release.yml
+++ b/.github/workflows/publish-technical-documentation-release.yml
@@ -3,7 +3,7 @@ name: publish-technical-documentation-release
on:
push:
branches:
- - v[0-9]+.[0-9]+.x
+ - release-[0-9]+.[0-9]+.[0-9]+
tags:
- v[0-9]+.[0-9]+.[0-9]+
paths:
@@ -23,7 +23,7 @@ jobs:
- uses: grafana/writers-toolkit/publish-technical-documentation-release@publish-technical-documentation-release/v2
with:
release_tag_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
- release_branch_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.x$"
- release_branch_with_patch_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
+ release_branch_regexp: "^release-(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
+ release_branch_with_patch_regexp: "^release-(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
website_directory: content/docs/grafana
version_suffix: ""
diff --git a/.github/workflows/release-comms.yml b/.github/workflows/release-comms.yml
index 02d53057184..720948e659b 100644
--- a/.github/workflows/release-comms.yml
+++ b/.github/workflows/release-comms.yml
@@ -19,7 +19,7 @@ on:
- closed
branches:
- 'main'
- - 'v*.*.*'
+ - 'release-*.*.*'
jobs:
setup:
@@ -27,6 +27,7 @@ jobs:
name: Setup and establish latest
outputs:
version: ${{ steps.output.outputs.version }}
+ release_branch: ${{ steps.output.outputs.release_branch }}
dry_run: ${{ steps.output.outputs.dry_run }}
latest: ${{ steps.output.outputs.latest }}
runs-on: ubuntu-latest
@@ -49,9 +50,54 @@ jobs:
echo "latest: $LATEST"
echo "version: $VERSION"
+ echo "release_branch=$(echo $VERSION | sed -s 's/^v/release-/g')" >> "$GITHUB_OUTPUT"
echo "dry_run=$DRY_RUN" >> "$GITHUB_OUTPUT"
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+ create_next_release_branch_grafana:
+ name: Create next release branch (Grafana)
+ needs: setup
+ uses: ./.github/workflows/create-next-release-branch.yml
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
+ GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ with:
+ ownerRepo: 'grafana/grafana'
+ source: ${{ needs.setup.outputs.release_branch }}
+ create_next_release_branch_enterprise:
+ name: Create next release branch (Grafana Enterprise)
+ needs: setup
+ uses: ./.github/workflows/create-next-release-branch.yml
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
+ GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ with:
+ ownerRepo: 'grafana/grafana-enterprise'
+ source: ${{ needs.setup.outputs.release_branch }}
+ migrate_prs_grafana:
+ needs:
+ - setup
+ - create_next_release_branch_grafana
+ uses: ./.github/workflows/migrate-prs.yml
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
+ GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ with:
+ ownerRepo: 'grafana/grafana'
+ from: ${{ needs.setup.outputs.release_branch }}
+ to: ${{ needs.create_next_release_branch_grafana.outputs.branch }}
+ migrate_prs_enterprise:
+ needs:
+ - setup
+ - create_next_release_branch_enterprise
+ uses: ./.github/workflows/migrate-prs.yml
+ secrets:
+ GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
+ GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ with:
+ ownerRepo: 'grafana/grafana-enterprise'
+ from: ${{ needs.setup.outputs.release_branch }}
+ to: ${{ needs.create_next_release_branch_enterprise.outputs.branch }}
post_changelog_on_forum:
needs: setup
uses: ./.github/workflows/community-release.yml
diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml
index d6bcd7d5446..1234cdc75ef 100644
--- a/.github/workflows/release-pr.yml
+++ b/.github/workflows/release-pr.yml
@@ -4,7 +4,7 @@
# Please refrain from including any processes that do not result in code changes in this workflow. Instead, they should
# either be triggered in the release promotion process or in the release comms process (that is triggered by merging
# this PR).
-name: Complete a Grafana release
+name: Grafana Release PR
on:
workflow_dispatch:
inputs:
@@ -19,7 +19,7 @@ on:
target:
required: true
type: string
- description: The base branch that these changes are being merged into
+ description: The release branch pattern (eg v9.5.x) that these changes are being merged into
backport:
required: false
type: string
@@ -61,12 +61,20 @@ jobs:
with:
app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
+ - name: Get release branch
+ id: branch
+ uses: grafana/grafana-github-actions-go/latest-release-branch@main
+ with:
+ token: ${{ steps.generate_token.outputs.token }}
+ ownerRepo: 'grafana/grafana'
+ pattern: ${{ inputs.target }}
- name: Checkout Grafana
uses: actions/checkout@v4
with:
- ref: ${{ inputs.target }}
+ ref: ${{ steps.branch.outputs.branch }}
fetch-depth: 0
fetch-tags: true
+ token: ${{ steps.generate_token.outputs.token }}
- name: Checkout Grafana (main)
uses: actions/checkout@v4
with:
@@ -74,6 +82,7 @@ jobs:
fetch-depth: '0'
fetch-tags: 'false'
path: .grafana-main
+ token: ${{ steps.generate_token.outputs.token }}
- name: Setup nodejs environment
uses: actions/setup-node@v4
with:
@@ -124,7 +133,6 @@ jobs:
rm -f CHANGELOG.part changelog_items.md
git diff CHANGELOG.md
-
- name: "Prettify CHANGELOG.md"
run: npx prettier --write CHANGELOG.md
- name: Commit CHANGELOG.md changes
@@ -138,6 +146,7 @@ jobs:
- name: Add package.json changes
run: |
git add package.json lerna.json yarn.lock packages public
+ test -e e2e/test-plugins && git add e2e/test-plugins
git commit -m "Update version to ${{ inputs.version }}"
- name: Git push
@@ -151,7 +160,7 @@ jobs:
$( [ "x${{ inputs.latest }}" == "xtrue" ] && printf %s '-l "release/latest"') \
-l "no-changelog" \
--dry-run=${{ inputs.dry_run }} \
- -B "${{ inputs.target }}" \
+ -B "${{ steps.branch.outputs.branch }}" \
--title "Release: ${{ inputs.version }}" \
--body "These code changes must be merged after a release is complete"
env:
@@ -165,7 +174,7 @@ jobs:
-l "product-approved" \
-l "no-changelog" \
--dry-run=${{ inputs.dry_run }} \
- -B "${{ inputs.target }}" \
+ -B "${{ steps.branch.outputs.branch }}" \
--title "Release: ${{ inputs.version }}" \
--body "These code changes must be merged after a release is complete"
env:
diff --git a/.github/workflows/run-schema-v2-e2e.yml b/.github/workflows/run-schema-v2-e2e.yml
new file mode 100644
index 00000000000..8b55aa4c430
--- /dev/null
+++ b/.github/workflows/run-schema-v2-e2e.yml
@@ -0,0 +1,44 @@
+name: Run dashboard schema v2 e2e
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - '**'
+
+env:
+ ARCH: linux-amd64
+
+jobs:
+ dashboard-schema-v2-e2e:
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ if: github.event.pull_request.draft == false
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Pin Go version to mod file
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: 'go.mod'
+ - run: go version
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'yarn'
+ - name: Install dependencies
+ run: yarn install --immutable
+ - name: Build grafana
+ run: make build
+ - name: Install Cypress dependencies
+ uses: cypress-io/github-action@v6
+ with:
+ runTests: false
+ - name: Run dashboard scenes e2e
+ run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs"
+
+ - name: Always succeed # This is a workaround to make the job pass even if the previous step fails
+ if: failure()
+ run: exit 0
\ No newline at end of file
diff --git a/.github/workflows/sbom-report.yml b/.github/workflows/sbom-report.yml
deleted file mode 100644
index efd188da331..00000000000
--- a/.github/workflows/sbom-report.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: syft-sbom-ci
-on:
- release:
- types: [ published ]
- workflow_dispatch:
-jobs:
- syft-sbom:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- - name: Anchore SBOM Action
- uses: grafana/shared-workflows/actions@syft-sbom-v0.0.1
- with:
- artifact-name: ${{ github.event.repository.name }}-spdx.json
diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml
index b1346236770..25104af8f29 100644
--- a/.github/workflows/trivy-scan.yml
+++ b/.github/workflows/trivy-scan.yml
@@ -27,11 +27,14 @@ jobs:
trivy fs --no-progress --download-db-only --db-repository public.ecr.aws/aquasecurity/trivy-db
- name: Run Trivy vulnerability scanner (table output)
# Use the trivy binary rather than the aquasecurity/trivy-action action
- # to avoid a few bugs
- # scan the filesystem, rather than building a Docker image prior - the
- # downside is we won't catch dependencies that are only installed in the
- # image, but the upside is we'll only catch vulnerabilities that are
- # explicitly in the our dependencies
+ # to avoid a few bugs.
+ #
+ # We scan the file system rather than building the Docker image to only scan
+ # our direct dependencies. The Docker images are still scanned by
+ # Vulnerability Observability:
+ # - OSS: https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/projects/sources/1
+ # - Enterprise: https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/projects/sources/12
+ # (If these links are outdated, just go to the list and find the images manually.)
run: |
trivy fs \
--scanners vuln \
diff --git a/.gitignore b/.gitignore
index ac5ad6f5f7d..c043094a016 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ vendor/
/requests
tsconfig.tsbuildinfo
__debug_bin*
+*.cov
# yarn
.yarn/cache
@@ -40,6 +41,7 @@ __debug_bin*
# This is the new place of the block, but I leave the previous here for a while
/devenv/docker/blocks/auth/saml-enterprise
/devenv/docker/blocks/auth/signer
+/devenv/docker/blocks/spanner_tests
/tmp
tools/phantomjs/phantomjs
@@ -103,14 +105,19 @@ profile.cov
/pkg/cmd/grafana-cli/grafana-cli
/pkg/cmd/grafana-server/grafana-server
/pkg/cmd/grafana-server/debug
-/pkg/extensions/*
-/pkg/build/cmd/artifactspage.go
-/pkg/build/cmd/artifactspage.tmpl.html
-/pkg/build/cmd/exportversion.go
-/pkg/server/wireexts_enterprise.go
+
+# Extensions
/pkg/cmd/grafana-cli/runner/wireexts_enterprise.go
+/pkg/server/wireexts_enterprise.go
+/pkg/build/cmd/enterprise.go
+/pkg/extensions/*
+!/pkg/extensions/.keep
!/pkg/extensions/main.go
+!/pkg/extensions/enterprise_imports.go
/public/app/extensions
+!/public/app/extensions/.keep
+
+
debug.test
/examples/*/dist
/packaging/**/*.rpm
@@ -170,13 +177,13 @@ compilation-stats.json
/e2e/benchmarks/**/results
/e2e/build_results.zip
/e2e/extensions
+!/e2e/extensions/.keep
/e2e/extensions-suite
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
-
# grafana server
/scripts/grafana-server/server.log
diff --git a/.golangci.yml b/.golangci.yml
index 9a49288fb2b..35b9bd1d4ec 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,5 +1,5 @@
run:
- timeout: 10m
+ timeout: 15m
concurrency: 10
allow-parallel-runners: true
linters-settings:
@@ -191,10 +191,10 @@ linters-settings:
allow: []
deny:
- pkg: github.com/grafana/grafana/pkg
- desc: apps/investigation is not allowed to import grafana core
+ desc: apps/investigations is not allowed to import grafana core
files:
- - ./apps/investigation/*
- - ./apps/investigation/**/*
+ - ./apps/investigations/*
+ - ./apps/investigations/**/*
gocritic:
enabled-checks:
- ruleguard
diff --git a/.ignore b/.ignore
new file mode 100644
index 00000000000..01e1bb8d6e2
--- /dev/null
+++ b/.ignore
@@ -0,0 +1,22 @@
+# This is a trick to ignore files only by git but not by other tools
+!/public/app/extensions
+!/pkg/extensions/*
+!/pkg/cmd/grafana-cli/runner/wireexts_enterprise.go
+!/pkg/server/wireexts_enterprise.go
+!/pkg/build/cmd/enterprise.go
+!/pkg/extensions/*
+
+# Enterprise emails
+!/emails/templates/enterprise_*
+!/public/emails/enterprise_*
+
+# Enterprise reporting fonts
+!/public/fonts/dejavu
+
+# Enterprise devenv
+!/devenv/docker/blocks/grafana-enterprise
+!/devenv/docker/blocks/saml-enterprise
+# This is the new place of the block, but I leave the previous here for a while
+!/devenv/docker/blocks/auth/saml-enterprise
+!/devenv/docker/blocks/auth/signer
+!/devenv/docker/blocks/spanner_tests
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 80bafb80307..67c4f82699b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -96,6 +96,15 @@
"NODE_ENV": "test"
}
},
+ {
+ "name": "Debug ESLint",
+ "type": "node",
+ "request": "launch",
+ "runtimeExecutable": "yarn",
+ "runtimeArgs": ["run", "eslint", "${file}"],
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen"
+ },
{
"name": "Debug Go test",
"type": "go",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6bb3ea46a8e..5b7107fc70a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,407 @@
+
+
+# 11.5.2 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#99918](https://github.com/grafana/grafana/pull/99918), [@DanCech](https://github.com/DanCech)
+- **TransformationFilter:** Include transformation outputs in transformation filtering options [#99878](https://github.com/grafana/grafana/pull/99878), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
+- **grafana-ui:** Update InlineField error prop type to React.ReactNode [#100373](https://github.com/grafana/grafana/pull/100373), [@Clarity-89](https://github.com/Clarity-89)
+
+### Bug fixes
+
+- **Alerting:** Allow specifying uid for new rules added to groups [#100450](https://github.com/grafana/grafana/pull/100450), [@yuri-tceretian](https://github.com/yuri-tceretian)
+- **Alerting:** Allow specifying uid for new rules added to groups [#100450](https://github.com/grafana/grafana/pull/100450), [@yuri-tceretian](https://github.com/yuri-tceretian)
+- **Alerting:** Call RLock() before reading sendAlertsTo map [#99880](https://github.com/grafana/grafana/pull/99880), [@santihernandezc](https://github.com/santihernandezc)
+- **Auth:** Fix redirect with JWT auth URL login [#100355](https://github.com/grafana/grafana/pull/100355), [@mgyongyosi](https://github.com/mgyongyosi)
+- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#100582](https://github.com/grafana/grafana/pull/100582), [@kalleep](https://github.com/kalleep)
+- **Azure:** Correctly set application insights resource values [#99599](https://github.com/grafana/grafana/pull/99599), [@aangelisc](https://github.com/aangelisc)
+- **CodeEditor:** Fix cursor alignment [#99863](https://github.com/grafana/grafana/pull/99863), [@ashharrison90](https://github.com/ashharrison90)
+- **DashboardList:** Throttle the re-renders [#100046](https://github.com/grafana/grafana/pull/100046), [@bfmatei](https://github.com/bfmatei)
+- **Dashboards:** Bring back scripted dashboards [#100633](https://github.com/grafana/grafana/pull/100633), [@dprokop](https://github.com/dprokop)
+- **Plugin Metrics:** Eliminate data race in plugin metrics middleware [#100078](https://github.com/grafana/grafana/pull/100078), [@clord](https://github.com/clord)
+- **RBAC:** Don't check folder access if `annotationPermissionUpdate` FT is enabled [#100117](https://github.com/grafana/grafana/pull/100117), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
+
+
+
+
+# 11.4.2 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#99924](https://github.com/grafana/grafana/pull/99924), [@DanCech](https://github.com/DanCech)
+
+### Bug fixes
+
+- **Auth:** Fix redirect with JWT auth URL login [#100494](https://github.com/grafana/grafana/pull/100494), [@mgyongyosi](https://github.com/mgyongyosi)
+- **AuthN:** Refetch user on "ErrUserAlreadyExists" [#100585](https://github.com/grafana/grafana/pull/100585), [@kalleep](https://github.com/kalleep)
+- **Azure:** Correctly set application insights resource values [#99598](https://github.com/grafana/grafana/pull/99598), [@aangelisc](https://github.com/aangelisc)
+- **Dashboards:** Bring back scripted dashboards [#100629](https://github.com/grafana/grafana/pull/100629), [@dprokop](https://github.com/dprokop)
+- **Plugin Metrics:** Eliminate data race in plugin metrics middleware [#100077](https://github.com/grafana/grafana/pull/100077), [@clord](https://github.com/clord)
+- **RBAC:** Don't check folder access if `annotationPermissionUpdate` FT is enabled [#100116](https://github.com/grafana/grafana/pull/100116), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
+
+
+
+
+# 11.3.4 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#99923](https://github.com/grafana/grafana/pull/99923), [@DanCech](https://github.com/DanCech)
+
+### Bug fixes
+
+- **Auth:** Fix redirect with JWT auth URL login [#100495](https://github.com/grafana/grafana/pull/100495), [@mgyongyosi](https://github.com/mgyongyosi)
+- **Azure:** Correctly set application insights resource values [#99597](https://github.com/grafana/grafana/pull/99597), [@aangelisc](https://github.com/aangelisc)
+- **Dashboards:** Bring back scripted dashboards [#100627](https://github.com/grafana/grafana/pull/100627), [@dprokop](https://github.com/dprokop)
+- **Plugin Metrics:** Eliminate data race in plugin metrics middleware [#100076](https://github.com/grafana/grafana/pull/100076), [@clord](https://github.com/clord)
+
+
+
+
+# 11.2.7 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#99922](https://github.com/grafana/grafana/pull/99922), [@DanCech](https://github.com/DanCech)
+
+### Bug fixes
+
+- **Azure:** Correctly set application insights resource values [#99596](https://github.com/grafana/grafana/pull/99596), [@aangelisc](https://github.com/aangelisc)
+
+
+
+
+# 11.1.12 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#99917](https://github.com/grafana/grafana/pull/99917), [@DanCech](https://github.com/DanCech)
+
+### Bug fixes
+
+- **Azure:** Correctly set application insights resource values [#99595](https://github.com/grafana/grafana/pull/99595), [@aangelisc](https://github.com/aangelisc)
+
+
+
+
+# 11.0.11 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#100730](https://github.com/grafana/grafana/pull/100730), [@DanCech](https://github.com/DanCech)
+
+### Bug fixes
+
+- **Azure:** Correctly set application insights resource values [#99594](https://github.com/grafana/grafana/pull/99594), [@aangelisc](https://github.com/aangelisc)
+
+
+
+
+# 10.4.16 (2025-02-18)
+
+### Features and enhancements
+
+- **Docker:** Use our own glibc 2.40 binaries [#99920](https://github.com/grafana/grafana/pull/99920), [@DanCech](https://github.com/DanCech)
+
+### Bug fixes
+
+- **Dashboard:** Fix for overwriting an edited dashboard in the old architecture [#100288](https://github.com/grafana/grafana/pull/100288), [@bfmatei](https://github.com/bfmatei)
+
+
+
+
+# 11.5.1 (2025-02-03)
+
+### Bug fixes
+
+- **CodeEditor:** Fix cursor alignment [#99090](https://github.com/grafana/grafana/pull/99090), [@ashharrison90](https://github.com/ashharrison90)
+- **TransformationFilter**: Include transformation outputs in transformation filtering options: Include transformation outputs in transformation filtering options [#98323](https://github.com/grafana/grafana/pull/98323), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
+
+
+
+
+# 11.5.0 (2025-01-28)
+
+### Features and enhancements
+
+- ** CloudMigration:** Create authapi service [#96581](https://github.com/grafana/grafana/pull/96581), [@leandro-deveikis](https://github.com/leandro-deveikis)
+- **Alerting:** Add new button for exporting new alert rule in HCL format [#96785](https://github.com/grafana/grafana/pull/96785), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Add option to show inactive alerts in alert list panel [#96888](https://github.com/grafana/grafana/pull/96888), [@bradleypettit](https://github.com/bradleypettit)
+- **Alerting:** Add state_periodic_save_batch_size config option [#98019](https://github.com/grafana/grafana/pull/98019), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
+- **Alerting:** Change default for max_attempts to 3. [#97461](https://github.com/grafana/grafana/pull/97461), [@stevesg](https://github.com/stevesg)
+- **Alerting:** Consume k8s API for notification policies tree [#96147](https://github.com/grafana/grafana/pull/96147), [@konrad147](https://github.com/konrad147)
+- **Alerting:** Enable flag alertingApiServer by default [#98282](https://github.com/grafana/grafana/pull/98282), [@yuri-tceretian](https://github.com/yuri-tceretian)
+- **Alerting:** Explore button in Insights view [#96496](https://github.com/grafana/grafana/pull/96496), [@ppcano](https://github.com/ppcano)
+- **Alerting:** Improve performance ash page [#97619](https://github.com/grafana/grafana/pull/97619), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Make alert rule policies preview use k8s API [#97070](https://github.com/grafana/grafana/pull/97070), [@tomratcliffe](https://github.com/tomratcliffe)
+- **Alerting:** Return default builtin templates in k8s templategroup API and UI [#96330](https://github.com/grafana/grafana/pull/96330), [@JacobsonMT](https://github.com/JacobsonMT)
+- **Alerting:** Simplify notification step [#96430](https://github.com/grafana/grafana/pull/96430), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Update state manager to take image only once per rule evaluation [#98289](https://github.com/grafana/grafana/pull/98289), [@yuri-tceretian](https://github.com/yuri-tceretian)
+- **Analytics Views:** Deprecate :dashboardID endpoints in favor of uid/:dashboardUID (Enterprise)
+- **Analytics:** Summaries: Deprecate dashboard_id endpoints in favor of dashboard_uid (Enterprise)
+- **Announcement Banners:** Enable feature for all cloud tiers (Enterprise)
+- **Announcement banner:** Remove feature toggle [#98782](https://github.com/grafana/grafana/pull/98782), [@Clarity-89](https://github.com/Clarity-89)
+- **Announcement banner:** Remove feature toggle (Enterprise)
+- **Announcement banner:** Sort by last updated (Enterprise)
+- **Auth:** Return error when retries have been exhausted for OAuth token refresh [#98034](https://github.com/grafana/grafana/pull/98034), [@mgyongyosi](https://github.com/mgyongyosi)
+- **Azure Monitor:** Add a feature flag to toggle user auth for Azure Monitor only [#96858](https://github.com/grafana/grafana/pull/96858), [@adamyeats](https://github.com/adamyeats)
+- **Azure:** Improve Azure Prometheus exemplars UI/UX [#97198](https://github.com/grafana/grafana/pull/97198), [@aangelisc](https://github.com/aangelisc)
+- **Azure:** Unify credentials in frontend for Prometheus [#96568](https://github.com/grafana/grafana/pull/96568), [@yjsong11](https://github.com/yjsong11)
+- **Chore:** Bump Go to 1.23.4 [#98853](https://github.com/grafana/grafana/pull/98853), [@Proximyst](https://github.com/Proximyst)
+- **Chore:** Bump Go to 1.23.4 (Enterprise)
+- **Chore:** Remove experimental Storage UI [#96887](https://github.com/grafana/grafana/pull/96887), [@ryantxu](https://github.com/ryantxu)
+- **Chore:** Update to node 22 [#97779](https://github.com/grafana/grafana/pull/97779), [@ashharrison90](https://github.com/ashharrison90)
+- **CloudMigrations:** Enable feature toggle by default in 11.5 [#98686](https://github.com/grafana/grafana/pull/98686), [@mmandrus](https://github.com/mmandrus)
+- **CloudMigrations:** Introduce RBAC role for migration assistant [#98588](https://github.com/grafana/grafana/pull/98588), [@macabu](https://github.com/macabu)
+- **CloudWatch:** Add OpenSearch PPL and SQL support in Logs Insights [#97508](https://github.com/grafana/grafana/pull/97508), [@idastambuk](https://github.com/idastambuk)
+- **CloudWatch:** Batch different time ranges separately [#98230](https://github.com/grafana/grafana/pull/98230), [@iwysiu](https://github.com/iwysiu)
+- **Cloudwatch:** Accept empty string for logstimeout and mark errors downstream [#96947](https://github.com/grafana/grafana/pull/96947), [@iwysiu](https://github.com/iwysiu)
+- **Cloudwatch:** Update grafana-aws-sdk for AWS/AmplifyHosting metrics [#97799](https://github.com/grafana/grafana/pull/97799), [@iwysiu](https://github.com/iwysiu)
+- **Dashboard Scene:** Shows usages in variables list [#96000](https://github.com/grafana/grafana/pull/96000), [@harisrozajac](https://github.com/harisrozajac)
+- **Dashboards:** Add option to specify explicit percent change text size for stat panels [#96952](https://github.com/grafana/grafana/pull/96952), [@XZCendence](https://github.com/XZCendence)
+- **Dashboards:** Allow DashboardDS subqueries in MixedDS [#97116](https://github.com/grafana/grafana/pull/97116), [@mdvictor](https://github.com/mdvictor)
+- **Dashboards:** Update docs of the `overwrite` param in Save Dashboard API Call [#97011](https://github.com/grafana/grafana/pull/97011), [@ArturWierzbicki](https://github.com/ArturWierzbicki)
+- **Datasources:** Add toggle to control default behaviour of 'Manage alerts via Alerts UI' toggle [#98441](https://github.com/grafana/grafana/pull/98441), [@macabu](https://github.com/macabu)
+- **Datasources:** Allow clearing trace to logs, metrics and profiles datasource pickers [#96554](https://github.com/grafana/grafana/pull/96554), [@adrapereira](https://github.com/adrapereira)
+- **Docker:** Don't use legacy ENV syntax [#93218](https://github.com/grafana/grafana/pull/93218), [@simPod](https://github.com/simPod)
+- **Elasticsearch:** Health endpoint should handle http errors [#96803](https://github.com/grafana/grafana/pull/96803), [@iwysiu](https://github.com/iwysiu)
+- **Elasticsearch:** Use \_field_caps instead of \_mapping to get fields [#97607](https://github.com/grafana/grafana/pull/97607), [@iwysiu](https://github.com/iwysiu)
+- **Explore Profiles:** Preinstall for onprem Grafana instances [#97775](https://github.com/grafana/grafana/pull/97775), [@ifrost](https://github.com/ifrost)
+- **Explore metrics:** Consolidate filters with the OTel experience [#98371](https://github.com/grafana/grafana/pull/98371), [@bohandley](https://github.com/bohandley)
+- **Explore:** Show links to queryless apps [#96625](https://github.com/grafana/grafana/pull/96625), [@ifrost](https://github.com/ifrost)
+- **Expressions:** Add notification for Strict Mode behavior in Reduce component [#97224](https://github.com/grafana/grafana/pull/97224), [@shubhankarunhale](https://github.com/shubhankarunhale)
+- **Faro:** Improve performance of TRACKING_URLS regex [#98022](https://github.com/grafana/grafana/pull/98022), [@kpelelis](https://github.com/kpelelis)
+- **FeatureToggles:** Make newFiltersUI feature toggle generally available [#97460](https://github.com/grafana/grafana/pull/97460), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
+- **Features:** Remove cloudwatchMetricInsightsCrossAccount feature toggle [#98826](https://github.com/grafana/grafana/pull/98826), [@idastambuk](https://github.com/idastambuk)
+- **Frontend Sandbox:** Add switch to toggle plugins frontend sandbox via catalog UI (Enterprise)
+- **Graphite:** Set `maxDataPoints` based on user value in alerting [#97178](https://github.com/grafana/grafana/pull/97178), [@aangelisc](https://github.com/aangelisc)
+- **Licensing:** Tidy up license token database code (Enterprise)
+- **LoginAttempt:** Add setting to control max number of attempts before user login gets locked [#97091](https://github.com/grafana/grafana/pull/97091), [@kalleep](https://github.com/kalleep)
+- **Logs Panel:** Add infinite scrolling support for Dashboards and Apps [#97095](https://github.com/grafana/grafana/pull/97095), [@matyax](https://github.com/matyax)
+- **Logs Panel:** Allow text selection without changing Log Details state [#96995](https://github.com/grafana/grafana/pull/96995), [@matyax](https://github.com/matyax)
+- **Logs Panel:** Limit displayed characters to MAX_CHARACTERS [#96997](https://github.com/grafana/grafana/pull/96997), [@matyax](https://github.com/matyax)
+- **Logs:** Added option to show the log line body when displayed fields are used [#97209](https://github.com/grafana/grafana/pull/97209), [@matyax](https://github.com/matyax)
+- **Logs:** Added support to disable and re-enable the popover menu [#98254](https://github.com/grafana/grafana/pull/98254), [@matyax](https://github.com/matyax)
+- **Logs:** Allow scroll to reach the bottom of the log list before loading more [#96668](https://github.com/grafana/grafana/pull/96668), [@matyax](https://github.com/matyax)
+- **Loki:** Added support for disabled operations in Query Builder [#96751](https://github.com/grafana/grafana/pull/96751), [@matyax](https://github.com/matyax)
+- **Loki:** Added support to show label types in Log Details [#97284](https://github.com/grafana/grafana/pull/97284), [@matyax](https://github.com/matyax)
+- **Loki:** Allow regex in `label` derived field [#96609](https://github.com/grafana/grafana/pull/96609), [@svennergr](https://github.com/svennergr)
+- **Loki:** Hide internal labels [#97323](https://github.com/grafana/grafana/pull/97323), [@svennergr](https://github.com/svennergr)
+- **Loki:** Sync query direction with sort order in Explore and Dashboards [#98722](https://github.com/grafana/grafana/pull/98722), [@matyax](https://github.com/matyax)
+- **OAuth:** Support client_secret_jwt for oauth providers when doing token exchange [#95455](https://github.com/grafana/grafana/pull/95455), [@naizerjohn-ms](https://github.com/naizerjohn-ms)
+- **OAuth:** Use the attached external session data in OAuthToken and OAuthTokenSync [#96655](https://github.com/grafana/grafana/pull/96655), [@mgyongyosi](https://github.com/mgyongyosi)
+- **Org Selection:** Show correct selected org when select is open [#96601](https://github.com/grafana/grafana/pull/96601), [@yincongcyincong](https://github.com/yincongcyincong)
+- **PDF:** Add new zoom options (Enterprise)
+- **Plugin Extensions:** Only load app plugins when necessary [#86624](https://github.com/grafana/grafana/pull/86624), [@leventebalogh](https://github.com/leventebalogh)
+- **Plugins:** Add token to gcom requests [#96261](https://github.com/grafana/grafana/pull/96261), [@oshirohugo](https://github.com/oshirohugo)
+- **Plugins:** Add token to gcom requests (Enterprise)
+- **Plugins:** Disable version install when angular version is not supported [#97189](https://github.com/grafana/grafana/pull/97189), [@oshirohugo](https://github.com/oshirohugo)
+- **Plugins:** Disable version installation for specific plugin types [#98597](https://github.com/grafana/grafana/pull/98597), [@oshirohugo](https://github.com/oshirohugo)
+- **Plugins:** Update to latest go plugin SDK (v0.260.3) w/ arrow v18 [#97561](https://github.com/grafana/grafana/pull/97561), [@ryantxu](https://github.com/ryantxu)
+- **Plugins:** Use grafana-com sso_api_token [#97096](https://github.com/grafana/grafana/pull/97096), [@oshirohugo](https://github.com/oshirohugo)
+- **Plugins:** Use grafana-com sso_api_token (Enterprise)
+- **Prometheus datasource:** Show info annotations in the UI [#97978](https://github.com/grafana/grafana/pull/97978), [@zenador](https://github.com/zenador)
+- **Prometheus:** Improve handling of special chars in label values [#96067](https://github.com/grafana/grafana/pull/96067), [@NWRichmond](https://github.com/NWRichmond)
+- **PublicDashboards:** Remove publicDashboards FF [#96578](https://github.com/grafana/grafana/pull/96578), [@juanicabanas](https://github.com/juanicabanas)
+- **Reporting:** Add allow list email domain configuration (Enterprise)
+- **Reporting:** Include the apiserver by default and deprecated internal ids (Enterprise)
+- **RuntimeDataSource:** Support in core for runtime registered data sources [#93956](https://github.com/grafana/grafana/pull/93956), [@torkelo](https://github.com/torkelo)
+- **SAML:** Add the ability to specify EntityID (Enterprise)
+- **SAML:** Implement correct SLO with NameID and SessionIndex handling (Enterprise)
+- **Security:** Update to Go 1.23.5 - Backport to v11.5.x [#99122](https://github.com/grafana/grafana/pull/99122), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.23.5 - Backport to v11.5.x (Enterprise)
+- **Snapshots:** Add RBAC roles for creating and deleting [#96126](https://github.com/grafana/grafana/pull/96126), [@evictorero](https://github.com/evictorero)
+- **Storage:** Removes integration tests for MySQL 5.7 since it is EOL [#98013](https://github.com/grafana/grafana/pull/98013), [@inf0rmer](https://github.com/inf0rmer)
+- **Tempo:** Add support for TraceQL Metrics exemplars [#96859](https://github.com/grafana/grafana/pull/96859), [@adrapereira](https://github.com/adrapereira)
+- **Tempo:** Honor datasource TLS settings for gRPC requests [#97484](https://github.com/grafana/grafana/pull/97484), [@mdisibio](https://github.com/mdisibio)
+- **Tempo:** Improve handling of multiple values in the Search tab query generation [#98427](https://github.com/grafana/grafana/pull/98427), [@adrapereira](https://github.com/adrapereira)
+- **ToolbarButton:** Auto width on smaller screen sizes [#96023](https://github.com/grafana/grafana/pull/96023), [@yincongcyincong](https://github.com/yincongcyincong)
+- **Trace View:** Set span filters as panel options [#98328](https://github.com/grafana/grafana/pull/98328), [@adrapereira](https://github.com/adrapereira)
+- **TransformationFilter:** Implement RefID multi picker [#96841](https://github.com/grafana/grafana/pull/96841), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
+- **Transformations:** Add Delimiter format option to Extract fields [#97340](https://github.com/grafana/grafana/pull/97340), [@tskarhed](https://github.com/tskarhed)
+- **Transformations:** Add RegExp option to Extract fields transformer [#96593](https://github.com/grafana/grafana/pull/96593), [@leeoniya](https://github.com/leeoniya)
+- **Transformations:** GroupToMatrix add 0 as special value [#97642](https://github.com/grafana/grafana/pull/97642), [@tskarhed](https://github.com/tskarhed)
+- **Zipkin:** Run queries through backend [#97754](https://github.com/grafana/grafana/pull/97754), [@ivanahuckova](https://github.com/ivanahuckova)
+
+### Bug fixes
+
+- **Alerting:** AlertingQueryRunner should skip descendant nodes of invalid queries [#97528](https://github.com/grafana/grafana/pull/97528), [@gillesdemey](https://github.com/gillesdemey)
+- **Alerting:** Allow notification policy filters to match quoted matchers [#98525](https://github.com/grafana/grafana/pull/98525), [@gillesdemey](https://github.com/gillesdemey)
+- **Alerting:** Fix alert rule list view summaries [#98433](https://github.com/grafana/grafana/pull/98433), [@yincongcyincong](https://github.com/yincongcyincong)
+- **Alerting:** Fix alert rules unpausing after moving rule to different folder [#97580](https://github.com/grafana/grafana/pull/97580), [@santihernandezc](https://github.com/santihernandezc)
+- **Alerting:** Fix ash not showing history graph in firefox [#98128](https://github.com/grafana/grafana/pull/98128), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix bug when saving a rule more than once [#96658](https://github.com/grafana/grafana/pull/96658), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix data-testid in RuleEditorSection [#97473](https://github.com/grafana/grafana/pull/97473), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix filtering rule group permissions based on their full path (Enterprise)
+- **Alerting:** Fix go template parsing [#97145](https://github.com/grafana/grafana/pull/97145), [@konrad147](https://github.com/konrad147)
+- **Alerting:** Fix label escaping in rule export [#97985](https://github.com/grafana/grafana/pull/97985), [@moustafab](https://github.com/moustafab)
+- **Alerting:** Fix missing instances and history when Grafana rule is stored in folder with / [#97956](https://github.com/grafana/grafana/pull/97956), [@gillesdemey](https://github.com/gillesdemey)
+- **Alerting:** Fix navigating to URLs with "%25" [#96992](https://github.com/grafana/grafana/pull/96992), [@gillesdemey](https://github.com/gillesdemey)
+- **Alerting:** Fix no-change scenario in provisioning rule update API [#98389](https://github.com/grafana/grafana/pull/98389), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
+- **Alerting:** Fix not being able to remove a reducer when using range query [#97757](https://github.com/grafana/grafana/pull/97757), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix recording rules rendering simplified condition [#97497](https://github.com/grafana/grafana/pull/97497), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix removing reducer when inital value is instant [#97054](https://github.com/grafana/grafana/pull/97054), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix simplified query step [#97046](https://github.com/grafana/grafana/pull/97046), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix slack image uploading to use new api [#97817](https://github.com/grafana/grafana/pull/97817), [@moustafab](https://github.com/moustafab)
+- **Alerting:** Fix terraform export of notification policy [#98429](https://github.com/grafana/grafana/pull/98429), [@moustafab](https://github.com/moustafab)
+- **Alerting:** Fix updating condition when refId changes [#97753](https://github.com/grafana/grafana/pull/97753), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Alerting:** Fix using stacks- prefix instead of stack- for checking the namespace in boot data [#97492](https://github.com/grafana/grafana/pull/97492), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
+- **Anonymous User:** Adds validator service for anonymous users (Enterprise)
+- **Auth:** Fix SAML user IsExternallySynced not being set correctly [#98487](https://github.com/grafana/grafana/pull/98487), [@volcanonoodle](https://github.com/volcanonoodle)
+- **Azure Monitor:** Add safety around usage of frame.Meta.Custom struct [#97766](https://github.com/grafana/grafana/pull/97766), [@adamyeats](https://github.com/adamyeats)
+- **Azure/GCM:** Improve error display [#96921](https://github.com/grafana/grafana/pull/96921), [@aangelisc](https://github.com/aangelisc)
+- **CloudWatch:** Fix conditions for fetching wildcards [#98648](https://github.com/grafana/grafana/pull/98648), [@iwysiu](https://github.com/iwysiu)
+- **CloudWatch:** Fix interpolation of log groups when fetching fields [#98054](https://github.com/grafana/grafana/pull/98054), [@idastambuk](https://github.com/idastambuk)
+- **Dashboard:** Fixes issue with compatability of old DashboardModel.annotations [#97328](https://github.com/grafana/grafana/pull/97328), [@torkelo](https://github.com/torkelo)
+- **Dashboards:** Fix issue where filtered panels would not react to variable changes [#98718](https://github.com/grafana/grafana/pull/98718), [@oscarkilhed](https://github.com/oscarkilhed)
+- **Dashboards:** Fixes week relative time ranges when weekStart was changed [#98167](https://github.com/grafana/grafana/pull/98167), [@torkelo](https://github.com/torkelo)
+- **Dashboards:** Panel react for `timeFrom` and `timeShift` changes using variables [#98510](https://github.com/grafana/grafana/pull/98510), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
+- **DateTimePicker:** Fixes issue with date picker showing invalid date [#97888](https://github.com/grafana/grafana/pull/97888), [@torkelo](https://github.com/torkelo)
+- **Fix:** Add support for datasource variable queries [#98098](https://github.com/grafana/grafana/pull/98098), [@sunker](https://github.com/sunker)
+- **Fix:** Do not fetch Orgs if the user is authenticated by apikey/sa or render key [#97162](https://github.com/grafana/grafana/pull/97162), [@mgyongyosi](https://github.com/mgyongyosi)
+- **Fix:** Double encoding of URLs when using data proxy [#98494](https://github.com/grafana/grafana/pull/98494), [@s4kh](https://github.com/s4kh)
+- **Font:** Disable contextual font ligatures [#98521](https://github.com/grafana/grafana/pull/98521), [@ashharrison90](https://github.com/ashharrison90)
+- **GrafanaUI:** Fix inconsistent controlled/uncontrolled state in AutoSizeInput [#96696](https://github.com/grafana/grafana/pull/96696), [@joshhunt](https://github.com/joshhunt)
+- **GrafanaUI:** Revert: Fix inconsistent controlled/uncontrolled state in AutoSizeInput [#97551](https://github.com/grafana/grafana/pull/97551), [@itsmylife](https://github.com/itsmylife)
+- **InfluxDB:** Adhoc filters can use template vars as values [#98567](https://github.com/grafana/grafana/pull/98567), [@bossinc](https://github.com/bossinc)
+- **Library Panel:** Fix issue where library panels did not display panel links. [#98655](https://github.com/grafana/grafana/pull/98655), [@yincongcyincong](https://github.com/yincongcyincong)
+- **LibraryPanel:** Fallback to panel title if library panel title is not set [#99411](https://github.com/grafana/grafana/pull/99411), [@ivanortegaalba](https://github.com/ivanortegaalba)
+- **Loki:** Fix a bug when reading frames without values but warnings [#97197](https://github.com/grafana/grafana/pull/97197), [@svennergr](https://github.com/svennergr)
+- **Loki:** Only hide a set of labels instead of every label starting with `__` [#98730](https://github.com/grafana/grafana/pull/98730), [@svennergr](https://github.com/svennergr)
+- **Org:** Fix redirection logic to work consistently [#96521](https://github.com/grafana/grafana/pull/96521), [@yincongcyincong](https://github.com/yincongcyincong)
+- **Panel inspect:** Fix file names of data download included uninterpolated variable names. [#98832](https://github.com/grafana/grafana/pull/98832), [@alexrosenfeld10](https://github.com/alexrosenfeld10)
+- **Scenes:** Upgrade to 5.36.3 [#98661](https://github.com/grafana/grafana/pull/98661), [@ivanortegaalba](https://github.com/ivanortegaalba)
+- **Snapshot:** Show proper breadcrumb path [#98806](https://github.com/grafana/grafana/pull/98806), [@ashharrison90](https://github.com/ashharrison90)
+- **Time Picker:** Fix "Fiscal year start month" selection behaviour [#98576](https://github.com/grafana/grafana/pull/98576), [@ashharrison90](https://github.com/ashharrison90)
+- **Unified Storage:** Add support for verify-full in postgres [#96825](https://github.com/grafana/grafana/pull/96825), [@chaudyg](https://github.com/chaudyg)
+- **Unified Storage:** Use tls preferred when grafana db using ssl [#97378](https://github.com/grafana/grafana/pull/97378), [@owensmallwood](https://github.com/owensmallwood)
+- **Usage Insights:** Fix usage insight errors being logged as [object Object] [#93502](https://github.com/grafana/grafana/pull/93502), [@mmandrus](https://github.com/mmandrus)
+
+### Breaking changes
+
+- **Loki:** Default to `/labels` API with `query` param instead of `/series` API [#97935](https://github.com/grafana/grafana/pull/97935), [@svennergr](https://github.com/svennergr)
+
+### Plugin development fixes & changes
+
+- **Grafana UI:** Re-add react-router-dom as a dependency [#97540](https://github.com/grafana/grafana/pull/97540), [@leventebalogh](https://github.com/leventebalogh)
+
+
+
+
+# 11.4.1 (2025-01-28)
+
+### Features and enhancements
+
+- **Security:** Update to Go 1.23.5 - Backport to v11.4.x [#99123](https://github.com/grafana/grafana/pull/99123), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.23.5 - Backport to v11.4.x (Enterprise)
+
+### Bug fixes
+
+- **Alerting:** AlertingQueryRunner should skip descendant nodes of invalid queries [#97830](https://github.com/grafana/grafana/pull/97830), [@gillesdemey](https://github.com/gillesdemey)
+- **Alerting:** Fix alert rules unpausing after moving rule to different folder [#97583](https://github.com/grafana/grafana/pull/97583), [@santihernandezc](https://github.com/santihernandezc)
+- **Alerting:** Fix label escaping in rule export [#98649](https://github.com/grafana/grafana/pull/98649), [@moustafab](https://github.com/moustafab)
+- **Alerting:** Fix slack image uploading to use new api [#98066](https://github.com/grafana/grafana/pull/98066), [@moustafab](https://github.com/moustafab)
+- **Azure/GCM:** Improve error display [#97594](https://github.com/grafana/grafana/pull/97594), [@aangelisc](https://github.com/aangelisc)
+- **Dashboards:** Fix issue where filtered panels would not react to variable changes [#98734](https://github.com/grafana/grafana/pull/98734), [@oscarkilhed](https://github.com/oscarkilhed)
+- **Dashboards:** Fixes issue with panel header showing even when hide time override was enabled [#98747](https://github.com/grafana/grafana/pull/98747), [@torkelo](https://github.com/torkelo)
+- **Dashboards:** Fixes week relative time ranges when weekStart was changed [#98269](https://github.com/grafana/grafana/pull/98269), [@torkelo](https://github.com/torkelo)
+- **Dashboards:** Panel react for `timeFrom` and `timeShift` changes using variables [#98659](https://github.com/grafana/grafana/pull/98659), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov)
+- **DateTimePicker:** Fixes issue with date picker showing invalid date [#97971](https://github.com/grafana/grafana/pull/97971), [@torkelo](https://github.com/torkelo)
+- **Fix:** Add support for datasource variable queries [#98119](https://github.com/grafana/grafana/pull/98119), [@sunker](https://github.com/sunker)
+- **InfluxDB:** Adhoc filters can use template vars as values [#98786](https://github.com/grafana/grafana/pull/98786), [@bossinc](https://github.com/bossinc)
+- **LibraryPanel:** Fallback to panel title if library panel title is not set [#99410](https://github.com/grafana/grafana/pull/99410), [@ivanortegaalba](https://github.com/ivanortegaalba)
+
+### Plugin development fixes & changes
+
+- **Grafana UI:** Re-add react-router-dom as a dependency [#98422](https://github.com/grafana/grafana/pull/98422), [@leventebalogh](https://github.com/leventebalogh)
+
+
+
+
+# 11.3.3 (2025-01-28)
+
+### Features and enhancements
+
+- **Azure Monitor:** Add a feature flag to toggle user auth for Azure Monitor only [#97576](https://github.com/grafana/grafana/pull/97576), [@adamyeats](https://github.com/adamyeats)
+- **Security:** Update to Go 1.23.5 - Backport to v11.3.x [#99124](https://github.com/grafana/grafana/pull/99124), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.23.5 - Backport to v11.3.x (Enterprise)
+
+### Bug fixes
+
+- **Alerting:** AlertingQueryRunner should skip descendant nodes of invalid queries [#97829](https://github.com/grafana/grafana/pull/97829), [@gillesdemey](https://github.com/gillesdemey)
+- **Azure/GCM:** Improve error display [#97593](https://github.com/grafana/grafana/pull/97593), [@aangelisc](https://github.com/aangelisc)
+- **Dashboard:** Fixes issue with compatability of old DashboardModel.annotations [#97467](https://github.com/grafana/grafana/pull/97467), [@torkelo](https://github.com/torkelo)
+- **Dashboards:** Fix issue where filtered panels would not react to variable changes [#98733](https://github.com/grafana/grafana/pull/98733), [@oscarkilhed](https://github.com/oscarkilhed)
+- **Dashboards:** Fixes issue with panel header showing even when hide time override was enabled [#97389](https://github.com/grafana/grafana/pull/97389), [@torkelo](https://github.com/torkelo)
+- **Dashboards:** Fixes week relative time ranges when weekStart was changed [#98268](https://github.com/grafana/grafana/pull/98268), [@torkelo](https://github.com/torkelo)
+- **DateTimePicker:** Fixes issue with date picker showing invalid date [#97970](https://github.com/grafana/grafana/pull/97970), [@torkelo](https://github.com/torkelo)
+- **Fix:** Add support for datasource variable queries [#98118](https://github.com/grafana/grafana/pull/98118), [@sunker](https://github.com/sunker)
+- **InfluxDB:** Adhoc filters can use template vars as values [#98785](https://github.com/grafana/grafana/pull/98785), [@bossinc](https://github.com/bossinc)
+- **Unified Storage:** Use tls preferred when grafana db using ssl [#97379](https://github.com/grafana/grafana/pull/97379), [@owensmallwood](https://github.com/owensmallwood)
+
+### Plugin development fixes & changes
+
+- **Grafana UI:** Re-add react-router-dom as a dependency [#98421](https://github.com/grafana/grafana/pull/98421), [@leventebalogh](https://github.com/leventebalogh)
+
+
+
+
+# 11.2.6 (2025-01-28)
+
+### Features and enhancements
+
+- **Azure Monitor:** Add a feature flag to toggle user auth for Azure Monitor only [#97565](https://github.com/grafana/grafana/pull/97565), [@adamyeats](https://github.com/adamyeats)
+- **Security:** Update to Go 1.22.11 - Backport to v11.2.x [#99125](https://github.com/grafana/grafana/pull/99125), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.22.11 - Backport to v11.2.x (Enterprise)
+
+### Bug fixes
+
+- **Azure/GCM:** Improve error display [#97591](https://github.com/grafana/grafana/pull/97591), [@aangelisc](https://github.com/aangelisc)
+
+
+
+
+# 11.1.11 (2025-01-28)
+
+### Features and enhancements
+
+- **Security:** Update to Go 1.22.11 - Backport to v11.1.x [#99126](https://github.com/grafana/grafana/pull/99126), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.22.11 - Backport to v11.1.x (Enterprise)
+
+### Bug fixes
+
+- **Azure/GCM:** Improve error display [#97595](https://github.com/grafana/grafana/pull/97595), [@aangelisc](https://github.com/aangelisc)
+
+
+
+
+# 11.0.10 (2025-01-28)
+
+### Features and enhancements
+
+- **Security:** Update to Go 1.22.11 - Backport to v11.0.x [#99127](https://github.com/grafana/grafana/pull/99127), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.22.11 - Backport to v11.0.x (Enterprise)
+
+### Bug fixes
+
+- **Azure/GCM:** Improve error display [#97592](https://github.com/grafana/grafana/pull/97592), [@aangelisc](https://github.com/aangelisc)
+
+
+
+
+# 10.4.15 (2025-01-28)
+
+### Features and enhancements
+
+- **Security:** Update to Go 1.22.11 - Backport to v10.4.x [#99128](https://github.com/grafana/grafana/pull/99128), [@Proximyst](https://github.com/Proximyst)
+- **Security:** Update to Go 1.22.11 - Backport to v10.4.x (Enterprise)
+
+### Bug fixes
+
+- **Azure/GCM:** Improve error display [#97590](https://github.com/grafana/grafana/pull/97590), [@aangelisc](https://github.com/aangelisc)
+
+
# 11.4.0 (2024-12-05)
diff --git a/Dockerfile b/Dockerfile
index 7f05689eef2..09cba46da26 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,13 +1,19 @@
+# syntax=docker/dockerfile:1
-ARG BASE_IMAGE=alpine:3.20
+# to maintain formatting of multiline commands in vscode, add the following to settings.json:
+# "docker.languageserver.formatter.ignoreMultilineInstructions": true
+
+ARG BASE_IMAGE=alpine:3.21
ARG JS_IMAGE=node:22-alpine
ARG JS_PLATFORM=linux/amd64
ARG GO_IMAGE=golang:1.23.5-alpine
+# Default to building locally
ARG GO_SRC=go-builder
ARG JS_SRC=js-builder
-FROM --platform=${JS_PLATFORM} ${JS_IMAGE} as js-builder
+# Javascript build stage
+FROM --platform=${JS_PLATFORM} ${JS_IMAGE} AS js-builder
ENV NODE_OPTIONS=--max_old_space_size=8000
@@ -32,7 +38,8 @@ COPY emails emails
ENV NODE_ENV=production
RUN yarn build
-FROM ${GO_IMAGE} as go-builder
+# Golang build stage
+FROM ${GO_IMAGE} AS go-builder
ARG COMMIT_SHA=""
ARG BUILD_BRANCH=""
@@ -55,24 +62,24 @@ COPY go.* ./
COPY .bingo .bingo
# Include vendored dependencies
-COPY pkg/util/xorm/go.* pkg/util/xorm/
-COPY pkg/apiserver/go.* pkg/apiserver/
-COPY pkg/apimachinery/go.* pkg/apimachinery/
-COPY pkg/build/go.* pkg/build/
-COPY pkg/build/wire/go.* pkg/build/wire/
-COPY pkg/promlib/go.* pkg/promlib/
-COPY pkg/storage/unified/resource/go.* pkg/storage/unified/resource/
-COPY pkg/storage/unified/apistore/go.* pkg/storage/unified/apistore/
-COPY pkg/semconv/go.* pkg/semconv/
-COPY pkg/aggregator/go.* pkg/aggregator/
-COPY apps/playlist/go.* apps/playlist/
-COPY apps/investigation/go.* apps/investigation/
-COPY apps/advisor/go.* apps/advisor/
+COPY pkg/util/xorm pkg/util/xorm
+COPY pkg/apiserver pkg/apiserver
+COPY pkg/apimachinery pkg/apimachinery
+COPY pkg/build pkg/build
+COPY pkg/build/wire pkg/build/wire
+COPY pkg/promlib pkg/promlib
+COPY pkg/storage/unified/resource pkg/storage/unified/resource
+COPY pkg/storage/unified/apistore pkg/storage/unified/apistore
+COPY pkg/semconv pkg/semconv
+COPY pkg/aggregator pkg/aggregator
+COPY apps/playlist apps/playlist
+COPY apps/investigations apps/investigations
+COPY apps/advisor apps/advisor
COPY apps apps
COPY kindsv2 kindsv2
-COPY apps/alerting/notifications/go.* apps/alerting/notifications/
-COPY pkg/codegen/go.* pkg/codegen/
-COPY pkg/plugins/codegen/go.* pkg/plugins/codegen/
+COPY apps/alerting/notifications apps/alerting/notifications
+COPY pkg/codegen pkg/codegen
+COPY pkg/plugins/codegen pkg/plugins/codegen
RUN go mod download
RUN if [[ "$BINGO" = "true" ]]; then \
@@ -97,7 +104,8 @@ ENV BUILD_BRANCH=${BUILD_BRANCH}
RUN make build-go GO_BUILD_TAGS=${GO_BUILD_TAGS} WIRE_TAGS=${WIRE_TAGS}
-FROM ${BASE_IMAGE} as tgz-builder
+# From-tarball build stage
+FROM ${BASE_IMAGE} AS tgz-builder
WORKDIR /tmp/grafana
@@ -109,13 +117,14 @@ COPY ${GRAFANA_TGZ} /tmp/grafana.tar.gz
RUN tar x -z -f /tmp/grafana.tar.gz --strip-components=1
# helpers for COPY --from
-FROM ${GO_SRC} as go-src
-FROM ${JS_SRC} as js-src
+FROM ${GO_SRC} AS go-src
+FROM ${JS_SRC} AS js-src
# Final stage
FROM ${BASE_IMAGE}
LABEL maintainer="Grafana Labs "
+LABEL org.opencontainers.image.source="https://github.com/grafana/grafana"
ARG GF_UID="472"
ARG GF_GID="0"
@@ -145,19 +154,20 @@ RUN if grep -i -q alpine /etc/issue; then \
fi
# glibc support for alpine x86_64 only
+# docker run --rm --env STDOUT=1 sgerrand/glibc-builder 2.40 /usr/glibc-compat > glibc-bin-2.40.tar.gz
+ARG GLIBC_VERSION=2.40
+
RUN if grep -i -q alpine /etc/issue && [ `arch` = "x86_64" ]; then \
- wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
- wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-2.35-r0.apk \
- -O /tmp/glibc-2.35-r0.apk && \
- wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r0/glibc-bin-2.35-r0.apk \
- -O /tmp/glibc-bin-2.35-r0.apk && \
- apk add --force-overwrite --no-cache /tmp/glibc-2.35-r0.apk /tmp/glibc-bin-2.35-r0.apk && \
- rm -f /lib64/ld-linux-x86-64.so.2 && \
- ln -s /usr/glibc-compat/lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 && \
- rm -f /tmp/glibc-2.35-r0.apk && \
- rm -f /tmp/glibc-bin-2.35-r0.apk && \
- rm -f /lib/ld-linux-x86-64.so.2 && \
- rm -f /etc/ld.so.cache; \
+ wget -qO- "https://dl.grafana.com/glibc/glibc-bin-$GLIBC_VERSION.tar.gz" | tar zxf - -C / \
+ usr/glibc-compat/lib/ld-linux-x86-64.so.2 \
+ usr/glibc-compat/lib/libc.so.6 \
+ usr/glibc-compat/lib/libdl.so.2 \
+ usr/glibc-compat/lib/libm.so.6 \
+ usr/glibc-compat/lib/libpthread.so.0 \
+ usr/glibc-compat/lib/librt.so.1 \
+ usr/glibc-compat/lib/libresolv.so.2 && \
+ mkdir /lib64 && \
+ ln -s /usr/glibc-compat/lib/ld-linux-x86-64.so.2 /lib64; \
fi
COPY --from=go-src /tmp/grafana/conf ./conf
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index cab21a089cc..b1443b33722 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -8,4 +8,4 @@
- @davkal
- Docs:
- @chri2547
- - @brendamuir
+ - @JohnnyK-Grafana
diff --git a/Makefile b/Makefile
index 3df11c69545..bfc5491e782 100644
--- a/Makefile
+++ b/Makefile
@@ -248,8 +248,7 @@ test-go: test-go-unit test-go-integration
.PHONY: test-go-unit
test-go-unit: ## Run unit tests for backend with flags.
@echo "test backend unit tests"
- printf '$(GO_TEST_FILES)' | xargs \
- $(GO) test $(GO_RACE_FLAG) -short -covermode=atomic -timeout=30m
+ $(GO) test $(GO_RACE_FLAG) -short -covermode=atomic -coverprofile=unit.cov -timeout=30m $(GO_TEST_FILES)
.PHONY: test-go-unit-pretty
test-go-unit-pretty: check-tparse
@@ -262,7 +261,7 @@ test-go-unit-pretty: check-tparse
.PHONY: test-go-integration
test-go-integration: ## Run integration tests for backend with flags.
@echo "test backend integration tests"
- $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestIntegration" -covermode=atomic -timeout=5m $(GO_INTEGRATION_TESTS)
+ $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestIntegration" -covermode=atomic -coverprofile=integration.cov -timeout=5m $(GO_INTEGRATION_TESTS)
.PHONY: test-go-integration-alertmanager
test-go-integration-alertmanager: ## Run integration tests for the remote alertmanager (config taken from the mimir_backend block).
@@ -271,6 +270,14 @@ test-go-integration-alertmanager: ## Run integration tests for the remote alertm
AM_URL=http://localhost:8080 AM_TENANT_ID=test \
$(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestIntegrationRemoteAlertmanager" -covermode=atomic -timeout=5m ./pkg/services/ngalert/...
+.PHONY: test-go-integration-grafana-alertmanager
+test-go-integration-grafana-alertmanager: ## Run integration tests for the grafana alertmanager
+ @echo "test grafana alertmanager integration tests"
+ @export GRAFANA_VERSION=11.5.0-81938; \
+ $(GO) run tools/setup_grafana_alertmanager_integration_test_images.go; \
+ $(GO) clean -testcache; \
+ $(GO) test $(GO_RACE_FLAG) -count=1 -run "^TestAlertmanagerIntegration" -covermode=atomic -timeout=10m ./pkg/tests/alertmanager/...
+
.PHONY: test-go-integration-postgres
test-go-integration-postgres: devenv-postgres ## Run integration tests for postgres backend with flags.
@echo "test backend integration postgres tests"
@@ -421,6 +428,7 @@ protobuf: ## Compile protobuf definitions
buf generate pkg/plugins/backendplugin/secretsmanagerplugin --template pkg/plugins/backendplugin/secretsmanagerplugin/buf.gen.yaml
buf generate pkg/storage/unified/resource --template pkg/storage/unified/resource/buf.gen.yaml
buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml
+ buf generate pkg/services/ngalert/store/proto/v1 --template pkg/services/ngalert/store/proto/v1/buf.gen.yaml
.PHONY: clean
clean: ## Clean up intermediate build artifacts.
diff --git a/README.md b/README.md
index 7322e6a1c7d..033ebdc1ea6 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ If you're interested in contributing to the Grafana project:
- If you have a specific question, check out our [discussion forums](https://community.grafana.com/).
- For general discussions, join us on the [official Slack](https://slack.grafana.com) team.
-This project is tested with [BrowserStack](https://www.browserstack.com/)
+This project is tested with [BrowserStack](https://www.browserstack.com/).
## License
diff --git a/apps/advisor/.gitignore b/apps/advisor/.gitignore
new file mode 100644
index 00000000000..9e73bafbae8
--- /dev/null
+++ b/apps/advisor/.gitignore
@@ -0,0 +1 @@
+plugin/src
\ No newline at end of file
diff --git a/apps/advisor/Makefile b/apps/advisor/Makefile
index 926f5a2840f..7a5f1c99365 100644
--- a/apps/advisor/Makefile
+++ b/apps/advisor/Makefile
@@ -1,8 +1,3 @@
.PHONY: generate
generate:
- @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess
- # HACK: Clean up generated CRD files.
- # TODO: The SDK currently omits generating the manifest Go file with `--defencoding=none`,
- # which we would normally use here to skip generating the CRD files.
- # This needs to be addressed.
- @rm -rf definitions
+ @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none
diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod
index 8fcc336a52b..df422dc8f68 100644
--- a/apps/advisor/go.mod
+++ b/apps/advisor/go.mod
@@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/advisor
go 1.23.4
require (
- github.com/grafana/grafana-app-sdk v0.30.0
+ github.com/grafana/grafana-app-sdk v0.31.0
k8s.io/apimachinery v0.32.0
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
diff --git a/apps/advisor/kinds/check.cue b/apps/advisor/kinds/check.cue
index 4ab33fb05df..4aea38d6759 100644
--- a/apps/advisor/kinds/check.cue
+++ b/apps/advisor/kinds/check.cue
@@ -7,28 +7,45 @@ check: {
versions: {
"v0alpha1": {
codegen: {
- frontend: false
+ frontend: true
backend: true
}
+ validation: {
+ operations: [
+ "CREATE",
+ "UPDATE",
+ ]
+ }
schema: {
- spec: {
+ #Data: {
// Generic data input that a check can receive
data?: [string]: string
}
- status: {
- report: {
+ #ErrorLink: {
+ // URL to a page with more information about the error
+ url: string
+ // Human readable error message
+ message: string
+ }
+ #ReportFailure: {
+ // Severity of the failure
+ severity: "high" | "low"
+ // Step ID that the failure is associated with
+ stepID: string
+ // Human readable identifier of the item that failed
+ item: string
+ // Links to actions that can be taken to resolve the failure
+ links: [...#ErrorLink]
+ }
+ #Report: {
// Number of elements analyzed
count: int
- // List of errors
- errors: [...{
- // Severity of the error
- severity: "high" | "low"
- // Human readable reason for the error
- reason: string
- // Action to take to resolve the error
- action: string
- }]
- }
+ // List of failures
+ failures: [...#ReportFailure]
+ }
+ spec: #Data
+ status: {
+ report: #Report
}
}
}
diff --git a/apps/advisor/kinds/checktype.cue b/apps/advisor/kinds/checktype.cue
new file mode 100644
index 00000000000..e2d653e629e
--- /dev/null
+++ b/apps/advisor/kinds/checktype.cue
@@ -0,0 +1,27 @@
+package advisor
+
+checktype: {
+ kind: "CheckType"
+ pluralName: "CheckTypes"
+ current: "v0alpha1"
+ versions: {
+ "v0alpha1": {
+ codegen: {
+ frontend: true
+ backend: true
+ }
+ schema: {
+ #Step: {
+ title: string
+ description: string
+ stepID: string
+ resolution: string
+ }
+ spec: {
+ name: string
+ steps: [...#Step]
+ }
+ }
+ }
+ }
+}
diff --git a/apps/advisor/kinds/manifest.cue b/apps/advisor/kinds/manifest.cue
index 6f96066967d..75a0eee05ef 100644
--- a/apps/advisor/kinds/manifest.cue
+++ b/apps/advisor/kinds/manifest.cue
@@ -5,5 +5,6 @@ manifest: {
groupOverride: "advisor.grafana.app"
kinds: [
check,
+ checktype,
]
}
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go
index 057ed6c4b7e..c9acc5ce31c 100644
--- a/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/check_status_gen.go
@@ -2,6 +2,36 @@
package v0alpha1
+// +k8s:openapi-gen=true
+type CheckErrorLink struct {
+ // URL to a page with more information about the error
+ Url string `json:"url"`
+ // Human readable error message
+ Message string `json:"message"`
+}
+
+// NewCheckErrorLink creates a new CheckErrorLink object.
+func NewCheckErrorLink() *CheckErrorLink {
+ return &CheckErrorLink{}
+}
+
+// +k8s:openapi-gen=true
+type CheckReportFailure struct {
+ // Severity of the failure
+ Severity CheckReportFailureSeverity `json:"severity"`
+ // Step ID that the failure is associated with
+ StepID string `json:"stepID"`
+ // Human readable identifier of the item that failed
+ Item string `json:"item"`
+ // Links to actions that can be taken to resolve the failure
+ Links []CheckErrorLink `json:"links"`
+}
+
+// NewCheckReportFailure creates a new CheckReportFailure object.
+func NewCheckReportFailure() *CheckReportFailure {
+ return &CheckReportFailure{}
+}
+
// +k8s:openapi-gen=true
type CheckstatusOperatorState struct {
// lastEvaluation is the ResourceVersion last evaluated
@@ -37,6 +67,14 @@ func NewCheckStatus() *CheckStatus {
}
}
+// +k8s:openapi-gen=true
+type CheckReportFailureSeverity string
+
+const (
+ CheckReportFailureSeverityHigh CheckReportFailureSeverity = "high"
+ CheckReportFailureSeverityLow CheckReportFailureSeverity = "low"
+)
+
// +k8s:openapi-gen=true
type CheckStatusOperatorStateState string
@@ -46,35 +84,12 @@ const (
CheckStatusOperatorStateStateFailed CheckStatusOperatorStateState = "failed"
)
-// +k8s:openapi-gen=true
-type CheckStatusSeverity string
-
-const (
- CheckStatusSeverityHigh CheckStatusSeverity = "high"
- CheckStatusSeverityLow CheckStatusSeverity = "low"
-)
-
-// +k8s:openapi-gen=true
-type CheckV0alpha1StatusReportErrors struct {
- // Severity of the error
- Severity CheckStatusSeverity `json:"severity"`
- // Human readable reason for the error
- Reason string `json:"reason"`
- // Action to take to resolve the error
- Action string `json:"action"`
-}
-
-// NewCheckV0alpha1StatusReportErrors creates a new CheckV0alpha1StatusReportErrors object.
-func NewCheckV0alpha1StatusReportErrors() *CheckV0alpha1StatusReportErrors {
- return &CheckV0alpha1StatusReportErrors{}
-}
-
// +k8s:openapi-gen=true
type CheckV0alpha1StatusReport struct {
// Number of elements analyzed
Count int64 `json:"count"`
- // List of errors
- Errors []CheckV0alpha1StatusReportErrors `json:"errors"`
+ // List of failures
+ Failures []CheckReportFailure `json:"failures"`
}
// NewCheckV0alpha1StatusReport creates a new CheckV0alpha1StatusReport object.
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_codec_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_codec_gen.go
new file mode 100644
index 00000000000..9881ce9d96f
--- /dev/null
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_codec_gen.go
@@ -0,0 +1,28 @@
+//
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+//
+
+package v0alpha1
+
+import (
+ "encoding/json"
+ "io"
+
+ "github.com/grafana/grafana-app-sdk/resource"
+)
+
+// CheckTypeJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
+type CheckTypeJSONCodec struct{}
+
+// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
+func (*CheckTypeJSONCodec) Read(reader io.Reader, into resource.Object) error {
+ return json.NewDecoder(reader).Decode(into)
+}
+
+// Write writes JSON-encoded bytes into `writer` marshaled from `from`
+func (*CheckTypeJSONCodec) Write(writer io.Writer, from resource.Object) error {
+ return json.NewEncoder(writer).Encode(from)
+}
+
+// Interface compliance checks
+var _ resource.Codec = &CheckTypeJSONCodec{}
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_metadata_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_metadata_gen.go
new file mode 100644
index 00000000000..9998cffb37d
--- /dev/null
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_metadata_gen.go
@@ -0,0 +1,28 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+import (
+ time "time"
+)
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+type CheckTypeMetadata struct {
+ UpdateTimestamp time.Time `json:"updateTimestamp"`
+ CreatedBy string `json:"createdBy"`
+ Uid string `json:"uid"`
+ CreationTimestamp time.Time `json:"creationTimestamp"`
+ DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
+ Finalizers []string `json:"finalizers"`
+ ResourceVersion string `json:"resourceVersion"`
+ Generation int64 `json:"generation"`
+ UpdatedBy string `json:"updatedBy"`
+ Labels map[string]string `json:"labels"`
+}
+
+// NewCheckTypeMetadata creates a new CheckTypeMetadata object.
+func NewCheckTypeMetadata() *CheckTypeMetadata {
+ return &CheckTypeMetadata{}
+}
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_object_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_object_gen.go
new file mode 100644
index 00000000000..80bd02b1cc8
--- /dev/null
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_object_gen.go
@@ -0,0 +1,266 @@
+//
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+//
+
+package v0alpha1
+
+import (
+ "fmt"
+ "github.com/grafana/grafana-app-sdk/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
+ "time"
+)
+
+// +k8s:openapi-gen=true
+type CheckType struct {
+ metav1.TypeMeta `json:",inline" yaml:",inline"`
+ metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
+ Spec CheckTypeSpec `json:"spec" yaml:"spec"`
+ CheckTypeStatus CheckTypeStatus `json:"status" yaml:"status"`
+}
+
+func (o *CheckType) GetSpec() any {
+ return o.Spec
+}
+
+func (o *CheckType) SetSpec(spec any) error {
+ cast, ok := spec.(CheckTypeSpec)
+ if !ok {
+ return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec)
+ }
+ o.Spec = cast
+ return nil
+}
+
+func (o *CheckType) GetSubresources() map[string]any {
+ return map[string]any{
+ "status": o.CheckTypeStatus,
+ }
+}
+
+func (o *CheckType) GetSubresource(name string) (any, bool) {
+ switch name {
+ case "status":
+ return o.CheckTypeStatus, true
+ default:
+ return nil, false
+ }
+}
+
+func (o *CheckType) SetSubresource(name string, value any) error {
+ switch name {
+ case "status":
+ cast, ok := value.(CheckTypeStatus)
+ if !ok {
+ return fmt.Errorf("cannot set status type %#v, not of type CheckTypeStatus", value)
+ }
+ o.CheckTypeStatus = cast
+ return nil
+ default:
+ return fmt.Errorf("subresource '%s' does not exist", name)
+ }
+}
+
+func (o *CheckType) GetStaticMetadata() resource.StaticMetadata {
+ gvk := o.GroupVersionKind()
+ return resource.StaticMetadata{
+ Name: o.ObjectMeta.Name,
+ Namespace: o.ObjectMeta.Namespace,
+ Group: gvk.Group,
+ Version: gvk.Version,
+ Kind: gvk.Kind,
+ }
+}
+
+func (o *CheckType) SetStaticMetadata(metadata resource.StaticMetadata) {
+ o.Name = metadata.Name
+ o.Namespace = metadata.Namespace
+ o.SetGroupVersionKind(schema.GroupVersionKind{
+ Group: metadata.Group,
+ Version: metadata.Version,
+ Kind: metadata.Kind,
+ })
+}
+
+func (o *CheckType) GetCommonMetadata() resource.CommonMetadata {
+ dt := o.DeletionTimestamp
+ var deletionTimestamp *time.Time
+ if dt != nil {
+ deletionTimestamp = &dt.Time
+ }
+ // Legacy ExtraFields support
+ extraFields := make(map[string]any)
+ if o.Annotations != nil {
+ extraFields["annotations"] = o.Annotations
+ }
+ if o.ManagedFields != nil {
+ extraFields["managedFields"] = o.ManagedFields
+ }
+ if o.OwnerReferences != nil {
+ extraFields["ownerReferences"] = o.OwnerReferences
+ }
+ return resource.CommonMetadata{
+ UID: string(o.UID),
+ ResourceVersion: o.ResourceVersion,
+ Generation: o.Generation,
+ Labels: o.Labels,
+ CreationTimestamp: o.CreationTimestamp.Time,
+ DeletionTimestamp: deletionTimestamp,
+ Finalizers: o.Finalizers,
+ UpdateTimestamp: o.GetUpdateTimestamp(),
+ CreatedBy: o.GetCreatedBy(),
+ UpdatedBy: o.GetUpdatedBy(),
+ ExtraFields: extraFields,
+ }
+}
+
+func (o *CheckType) SetCommonMetadata(metadata resource.CommonMetadata) {
+ o.UID = types.UID(metadata.UID)
+ o.ResourceVersion = metadata.ResourceVersion
+ o.Generation = metadata.Generation
+ o.Labels = metadata.Labels
+ o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp)
+ if metadata.DeletionTimestamp != nil {
+ dt := metav1.NewTime(*metadata.DeletionTimestamp)
+ o.DeletionTimestamp = &dt
+ } else {
+ o.DeletionTimestamp = nil
+ }
+ o.Finalizers = metadata.Finalizers
+ if o.Annotations == nil {
+ o.Annotations = make(map[string]string)
+ }
+ if !metadata.UpdateTimestamp.IsZero() {
+ o.SetUpdateTimestamp(metadata.UpdateTimestamp)
+ }
+ if metadata.CreatedBy != "" {
+ o.SetCreatedBy(metadata.CreatedBy)
+ }
+ if metadata.UpdatedBy != "" {
+ o.SetUpdatedBy(metadata.UpdatedBy)
+ }
+ // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields
+ if metadata.ExtraFields != nil {
+ if annotations, ok := metadata.ExtraFields["annotations"]; ok {
+ if cast, ok := annotations.(map[string]string); ok {
+ o.Annotations = cast
+ }
+ }
+ if managedFields, ok := metadata.ExtraFields["managedFields"]; ok {
+ if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok {
+ o.ManagedFields = cast
+ }
+ }
+ if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok {
+ if cast, ok := ownerReferences.([]metav1.OwnerReference); ok {
+ o.OwnerReferences = cast
+ }
+ }
+ }
+}
+
+func (o *CheckType) GetCreatedBy() string {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ return o.ObjectMeta.Annotations["grafana.com/createdBy"]
+}
+
+func (o *CheckType) SetCreatedBy(createdBy string) {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy
+}
+
+func (o *CheckType) GetUpdateTimestamp() time.Time {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"])
+ return parsed
+}
+
+func (o *CheckType) SetUpdateTimestamp(updateTimestamp time.Time) {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
+}
+
+func (o *CheckType) GetUpdatedBy() string {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ return o.ObjectMeta.Annotations["grafana.com/updatedBy"]
+}
+
+func (o *CheckType) SetUpdatedBy(updatedBy string) {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy
+}
+
+func (o *CheckType) Copy() resource.Object {
+ return resource.CopyObject(o)
+}
+
+func (o *CheckType) DeepCopyObject() runtime.Object {
+ return o.Copy()
+}
+
+// Interface compliance compile-time check
+var _ resource.Object = &CheckType{}
+
+// +k8s:openapi-gen=true
+type CheckTypeList struct {
+ metav1.TypeMeta `json:",inline" yaml:",inline"`
+ metav1.ListMeta `json:"metadata" yaml:"metadata"`
+ Items []CheckType `json:"items" yaml:"items"`
+}
+
+func (o *CheckTypeList) DeepCopyObject() runtime.Object {
+ return o.Copy()
+}
+
+func (o *CheckTypeList) Copy() resource.ListObject {
+ cpy := &CheckTypeList{
+ TypeMeta: o.TypeMeta,
+ Items: make([]CheckType, len(o.Items)),
+ }
+ o.ListMeta.DeepCopyInto(&cpy.ListMeta)
+ for i := 0; i < len(o.Items); i++ {
+ if item, ok := o.Items[i].Copy().(*CheckType); ok {
+ cpy.Items[i] = *item
+ }
+ }
+ return cpy
+}
+
+func (o *CheckTypeList) GetItems() []resource.Object {
+ items := make([]resource.Object, len(o.Items))
+ for i := 0; i < len(o.Items); i++ {
+ items[i] = &o.Items[i]
+ }
+ return items
+}
+
+func (o *CheckTypeList) SetItems(items []resource.Object) {
+ o.Items = make([]CheckType, len(items))
+ for i := 0; i < len(items); i++ {
+ o.Items[i] = *items[i].(*CheckType)
+ }
+}
+
+// Interface compliance compile-time check
+var _ resource.ListObject = &CheckTypeList{}
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_schema_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_schema_gen.go
new file mode 100644
index 00000000000..15b4a6fbb11
--- /dev/null
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_schema_gen.go
@@ -0,0 +1,34 @@
+//
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+//
+
+package v0alpha1
+
+import (
+ "github.com/grafana/grafana-app-sdk/resource"
+)
+
+// schema is unexported to prevent accidental overwrites
+var (
+ schemaCheckType = resource.NewSimpleSchema("advisor.grafana.app", "v0alpha1", &CheckType{}, &CheckTypeList{}, resource.WithKind("CheckType"),
+ resource.WithPlural("checktypes"), resource.WithScope(resource.NamespacedScope))
+ kindCheckType = resource.Kind{
+ Schema: schemaCheckType,
+ Codecs: map[resource.KindEncoding]resource.Codec{
+ resource.KindEncodingJSON: &CheckTypeJSONCodec{},
+ },
+ }
+)
+
+// Kind returns a resource.Kind for this Schema with a JSON codec
+func CheckTypeKind() resource.Kind {
+ return kindCheckType
+}
+
+// Schema returns a resource.SimpleSchema representation of CheckType
+func CheckTypeSchema() *resource.SimpleSchema {
+ return schemaCheckType
+}
+
+// Interface compliance checks
+var _ resource.Schema = kindCheckType
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_spec_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_spec_gen.go
new file mode 100644
index 00000000000..528643b69dd
--- /dev/null
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_spec_gen.go
@@ -0,0 +1,27 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+// +k8s:openapi-gen=true
+type CheckTypeStep struct {
+ Title string `json:"title"`
+ Description string `json:"description"`
+ StepID string `json:"stepID"`
+ Resolution string `json:"resolution"`
+}
+
+// NewCheckTypeStep creates a new CheckTypeStep object.
+func NewCheckTypeStep() *CheckTypeStep {
+ return &CheckTypeStep{}
+}
+
+// +k8s:openapi-gen=true
+type CheckTypeSpec struct {
+ Name string `json:"name"`
+ Steps []CheckTypeStep `json:"steps"`
+}
+
+// NewCheckTypeSpec creates a new CheckTypeSpec object.
+func NewCheckTypeSpec() *CheckTypeSpec {
+ return &CheckTypeSpec{}
+}
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_status_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_status_gen.go
new file mode 100644
index 00000000000..87db65316d0
--- /dev/null
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/checktype_status_gen.go
@@ -0,0 +1,44 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+// +k8s:openapi-gen=true
+type CheckTypestatusOperatorState struct {
+ // lastEvaluation is the ResourceVersion last evaluated
+ LastEvaluation string `json:"lastEvaluation"`
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ State CheckTypeStatusOperatorStateState `json:"state"`
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ DescriptiveState *string `json:"descriptiveState,omitempty"`
+ // details contains any extra information that is operator-specific
+ Details map[string]interface{} `json:"details,omitempty"`
+}
+
+// NewCheckTypestatusOperatorState creates a new CheckTypestatusOperatorState object.
+func NewCheckTypestatusOperatorState() *CheckTypestatusOperatorState {
+ return &CheckTypestatusOperatorState{}
+}
+
+// +k8s:openapi-gen=true
+type CheckTypeStatus struct {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ OperatorStates map[string]CheckTypestatusOperatorState `json:"operatorStates,omitempty"`
+ // additionalFields is reserved for future use
+ AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
+}
+
+// NewCheckTypeStatus creates a new CheckTypeStatus object.
+func NewCheckTypeStatus() *CheckTypeStatus {
+ return &CheckTypeStatus{}
+}
+
+// +k8s:openapi-gen=true
+type CheckTypeStatusOperatorStateState string
+
+const (
+ CheckTypeStatusOperatorStateStateSuccess CheckTypeStatusOperatorStateState = "success"
+ CheckTypeStatusOperatorStateStateInProgress CheckTypeStatusOperatorStateState = "in_progress"
+ CheckTypeStatusOperatorStateStateFailed CheckTypeStatusOperatorStateState = "failed"
+)
diff --git a/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go b/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go
index a78d033bb26..79d78cd2766 100644
--- a/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go
+++ b/apps/advisor/pkg/apis/advisor/v0alpha1/zz_openapi_gen.go
@@ -12,13 +12,20 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.Check": schema_pkg_apis_advisor_v0alpha1_Check(ref),
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckList": schema_pkg_apis_advisor_v0alpha1_CheckList(ref),
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec": schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref),
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckStatus": schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref),
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref),
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReportErrors": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReportErrors(ref),
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckstatusOperatorState(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.Check": schema_pkg_apis_advisor_v0alpha1_Check(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink": schema_pkg_apis_advisor_v0alpha1_CheckErrorLink(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckList": schema_pkg_apis_advisor_v0alpha1_CheckList(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure": schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec": schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckStatus": schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckType": schema_pkg_apis_advisor_v0alpha1_CheckType(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeList": schema_pkg_apis_advisor_v0alpha1_CheckTypeList(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec": schema_pkg_apis_advisor_v0alpha1_CheckTypeSpec(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus": schema_pkg_apis_advisor_v0alpha1_CheckTypeStatus(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep": schema_pkg_apis_advisor_v0alpha1_CheckTypeStep(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckTypestatusOperatorState(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref),
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckstatusOperatorState(ref),
}
}
@@ -69,6 +76,35 @@ func schema_pkg_apis_advisor_v0alpha1_Check(ref common.ReferenceCallback) common
}
}
+func schema_pkg_apis_advisor_v0alpha1_CheckErrorLink(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "url": {
+ SchemaProps: spec.SchemaProps{
+ Description: "URL to a page with more information about the error",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "message": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Human readable error message",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"url", "message"},
+ },
+ },
+ }
+}
+
func schema_pkg_apis_advisor_v0alpha1_CheckList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -117,6 +153,59 @@ func schema_pkg_apis_advisor_v0alpha1_CheckList(ref common.ReferenceCallback) co
}
}
+func schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "severity": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Severity of the failure",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "stepID": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Step ID that the failure is associated with",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "item": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Human readable identifier of the item that failed",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "links": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Links to actions that can be taken to resolve the failure",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"severity", "stepID", "item", "links"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink"},
+ }
+}
+
func schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -196,6 +285,272 @@ func schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref common.ReferenceCallback)
}
}
+func schema_pkg_apis_advisor_v0alpha1_CheckType(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
+ },
+ },
+ "spec": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec"),
+ },
+ },
+ "status": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus"),
+ },
+ },
+ },
+ Required: []string{"metadata", "spec", "status"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec", "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
+ }
+}
+
+func schema_pkg_apis_advisor_v0alpha1_CheckTypeList(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
+ },
+ },
+ "items": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckType"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"metadata", "items"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckType", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
+ }
+}
+
+func schema_pkg_apis_advisor_v0alpha1_CheckTypeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "steps": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"name", "steps"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep"},
+ }
+}
+
+func schema_pkg_apis_advisor_v0alpha1_CheckTypeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "operatorStates": {
+ SchemaProps: spec.SchemaProps{
+ Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState"),
+ },
+ },
+ },
+ },
+ },
+ "additionalFields": {
+ SchemaProps: spec.SchemaProps{
+ Description: "additionalFields is reserved for future use",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState"},
+ }
+}
+
+func schema_pkg_apis_advisor_v0alpha1_CheckTypeStep(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "title": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "description": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "stepID": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "resolution": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"title", "description", "stepID", "resolution"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_advisor_v0alpha1_CheckTypestatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "lastEvaluation": {
+ SchemaProps: spec.SchemaProps{
+ Description: "lastEvaluation is the ResourceVersion last evaluated",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "state": {
+ SchemaProps: spec.SchemaProps{
+ Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "descriptiveState": {
+ SchemaProps: spec.SchemaProps{
+ Description: "descriptiveState is an optional more descriptive state field which has no requirements on format",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "details": {
+ SchemaProps: spec.SchemaProps{
+ Description: "details contains any extra information that is operator-specific",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"lastEvaluation", "state"},
+ },
+ },
+ }
+}
+
func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -210,63 +565,26 @@ func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref common.Refer
Format: "int64",
},
},
- "errors": {
+ "failures": {
SchemaProps: spec.SchemaProps{
- Description: "List of errors",
+ Description: "List of failures",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReportErrors"),
+ Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"),
},
},
},
},
},
},
- Required: []string{"count", "errors"},
+ Required: []string{"count", "failures"},
},
},
Dependencies: []string{
- "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReportErrors"},
- }
-}
-
-func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReportErrors(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "severity": {
- SchemaProps: spec.SchemaProps{
- Description: "Severity of the error",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "reason": {
- SchemaProps: spec.SchemaProps{
- Description: "Human readable reason for the error",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "action": {
- SchemaProps: spec.SchemaProps{
- Description: "Action to take to resolve the error",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"severity", "reason", "action"},
- },
- },
+ "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"},
}
}
diff --git a/apps/advisor/pkg/apis/advisor_manifest.go b/apps/advisor/pkg/apis/advisor_manifest.go
index 2e8bb06fb65..e333774303f 100644
--- a/apps/advisor/pkg/apis/advisor_manifest.go
+++ b/apps/advisor/pkg/apis/advisor_manifest.go
@@ -12,9 +12,12 @@ import (
)
var (
- rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"errors":{"description":"List of errors","items":{"properties":{"action":{"description":"Action to take to resolve the error","type":"string"},"reason":{"description":"Human readable reason for the error","type":"string"},"severity":{"description":"Severity of the error","enum":["high","low"],"type":"string"}},"required":["severity","reason","action"],"type":"object"},"type":"array"}},"required":["count","errors"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
- versionSchemaCheckv0alpha1 app.VersionSchema
- _ = json.Unmarshal(rawSchemaCheckv0alpha1, &versionSchemaCheckv0alpha1)
+ rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"failures":{"description":"List of failures","items":{"properties":{"item":{"description":"Human readable identifier of the item that failed","type":"string"},"links":{"description":"Links to actions that can be taken to resolve the failure","items":{"properties":{"message":{"description":"Human readable error message","type":"string"},"url":{"description":"URL to a page with more information about the error","type":"string"}},"required":["url","message"],"type":"object"},"type":"array"},"severity":{"description":"Severity of the failure","enum":["high","low"],"type":"string"},"stepID":{"description":"Step ID that the failure is associated with","type":"string"}},"required":["severity","stepID","item","links"],"type":"object"},"type":"array"}},"required":["count","failures"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
+ versionSchemaCheckv0alpha1 app.VersionSchema
+ _ = json.Unmarshal(rawSchemaCheckv0alpha1, &versionSchemaCheckv0alpha1)
+ rawSchemaCheckTypev0alpha1 = []byte(`{"spec":{"properties":{"name":{"type":"string"},"steps":{"items":{"properties":{"description":{"type":"string"},"resolution":{"type":"string"},"stepID":{"type":"string"},"title":{"type":"string"}},"required":["title","description","stepID","resolution"],"type":"object"},"type":"array"}},"required":["name","steps"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
+ versionSchemaCheckTypev0alpha1 app.VersionSchema
+ _ = json.Unmarshal(rawSchemaCheckTypev0alpha1, &versionSchemaCheckTypev0alpha1)
)
var appManifestData = app.ManifestData{
@@ -27,11 +30,31 @@ var appManifestData = app.ManifestData{
Conversion: false,
Versions: []app.ManifestKindVersion{
{
- Name: "v0alpha1",
+ Name: "v0alpha1",
+ Admission: &app.AdmissionCapabilities{
+ Validation: &app.ValidationCapability{
+ Operations: []app.AdmissionOperation{
+ app.AdmissionOperationCreate,
+ app.AdmissionOperationUpdate,
+ },
+ },
+ },
Schema: &versionSchemaCheckv0alpha1,
},
},
},
+
+ {
+ Kind: "CheckType",
+ Scope: "Namespaced",
+ Conversion: false,
+ Versions: []app.ManifestKindVersion{
+ {
+ Name: "v0alpha1",
+ Schema: &versionSchemaCheckTypev0alpha1,
+ },
+ },
+ },
},
}
diff --git a/apps/advisor/pkg/app/app.go b/apps/advisor/pkg/app/app.go
index e4ccafbcaaf..a640682b0fc 100644
--- a/apps/advisor/pkg/app/app.go
+++ b/apps/advisor/pkg/app/app.go
@@ -2,16 +2,42 @@ package app
import (
"context"
+ "fmt"
"github.com/grafana/grafana-app-sdk/app"
+ "github.com/grafana/grafana-app-sdk/k8s"
"github.com/grafana/grafana-app-sdk/resource"
"github.com/grafana/grafana-app-sdk/simple"
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checkscheduler"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checktyperegisterer"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
func New(cfg app.Config) (app.App, error) {
+ // Read config
+ specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig)
+ if !ok {
+ return nil, fmt.Errorf("invalid config type")
+ }
+ checkRegistry := specificConfig.CheckRegistry
+
+ // Prepare storage client
+ clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{})
+ client, err := clientGenerator.ClientFor(advisorv0alpha1.CheckKind())
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize checks
+ checkMap := map[string]checks.Check{}
+ for _, c := range checkRegistry.Checks() {
+ checkMap[c.ID()] = c
+ }
+
simpleConfig := simple.AppConfig{
Name: "advisor",
KubeConfig: cfg.KubeConfig,
@@ -23,6 +49,27 @@ func New(cfg app.Config) (app.App, error) {
ManagedKinds: []simple.AppManagedKind{
{
Kind: advisorv0alpha1.CheckKind(),
+ Validator: &simple.Validator{
+ ValidateFunc: func(ctx context.Context, req *app.AdmissionRequest) error {
+ if req.Object != nil {
+ _, err := getCheck(req.Object, checkMap)
+ return err
+ }
+ return nil
+ },
+ },
+ Watcher: &simple.Watcher{
+ AddFunc: func(ctx context.Context, obj resource.Object) error {
+ check, err := getCheck(obj, checkMap)
+ if err != nil {
+ return err
+ }
+ return processCheck(ctx, client, obj, check)
+ },
+ },
+ },
+ {
+ Kind: advisorv0alpha1.CheckTypeKind(),
},
},
}
@@ -37,6 +84,20 @@ func New(cfg app.Config) (app.App, error) {
return nil, err
}
+ // Save check types as resources
+ ctr, err := checktyperegisterer.New(cfg)
+ if err != nil {
+ return nil, err
+ }
+ a.AddRunnable(ctr)
+
+ // Start scheduler
+ csch, err := checkscheduler.New(cfg)
+ if err != nil {
+ return nil, err
+ }
+ a.AddRunnable(csch)
+
return a, nil
}
@@ -49,6 +110,7 @@ func GetKinds() map[schema.GroupVersion][]resource.Kind {
return map[schema.GroupVersion][]resource.Kind{
gv: {
advisorv0alpha1.CheckKind(),
+ advisorv0alpha1.CheckTypeKind(),
},
}
}
diff --git a/apps/advisor/pkg/app/authorizer.go b/apps/advisor/pkg/app/authorizer.go
new file mode 100644
index 00000000000..4d216ee9edd
--- /dev/null
+++ b/apps/advisor/pkg/app/authorizer.go
@@ -0,0 +1,31 @@
+package app
+
+import (
+ "context"
+
+ "github.com/grafana/grafana/pkg/apimachinery/identity"
+ "k8s.io/apiserver/pkg/authorization/authorizer"
+)
+
+func GetAuthorizer() authorizer.Authorizer {
+ return authorizer.AuthorizerFunc(func(
+ ctx context.Context, attr authorizer.Attributes,
+ ) (authorized authorizer.Decision, reason string, err error) {
+ if !attr.IsResourceRequest() {
+ return authorizer.DecisionNoOpinion, "", nil
+ }
+
+ // require a user
+ u, err := identity.GetRequester(ctx)
+ if err != nil {
+ return authorizer.DecisionDeny, "valid user is required", err
+ }
+
+ // check if is admin
+ if u.HasRole(identity.RoleAdmin) {
+ return authorizer.DecisionAllow, "", nil
+ }
+
+ return authorizer.DecisionDeny, "forbidden", nil
+ })
+}
diff --git a/apps/advisor/pkg/app/authorizer_test.go b/apps/advisor/pkg/app/authorizer_test.go
new file mode 100644
index 00000000000..1384176969e
--- /dev/null
+++ b/apps/advisor/pkg/app/authorizer_test.go
@@ -0,0 +1,82 @@
+package app
+
+import (
+ "context"
+ "testing"
+
+ "github.com/grafana/grafana/pkg/apimachinery/identity"
+ "github.com/stretchr/testify/assert"
+ "k8s.io/apiserver/pkg/authorization/authorizer"
+)
+
+func TestGetAuthorizer(t *testing.T) {
+ tests := []struct {
+ name string
+ ctx context.Context
+ attr authorizer.Attributes
+ expectedDecision authorizer.Decision
+ expectedReason string
+ expectedErr error
+ }{
+ {
+ name: "non-resource request",
+ ctx: context.TODO(),
+ attr: &mockAttributes{resourceRequest: false},
+ expectedDecision: authorizer.DecisionNoOpinion,
+ expectedReason: "",
+ expectedErr: nil,
+ },
+ {
+ name: "user is admin",
+ ctx: identity.WithRequester(context.TODO(), &mockUser{isGrafanaAdmin: true}),
+ attr: &mockAttributes{resourceRequest: true},
+ expectedDecision: authorizer.DecisionAllow,
+ expectedReason: "",
+ expectedErr: nil,
+ },
+ {
+ name: "user is not admin",
+ ctx: identity.WithRequester(context.TODO(), &mockUser{isGrafanaAdmin: false}),
+ attr: &mockAttributes{resourceRequest: true},
+ expectedDecision: authorizer.DecisionDeny,
+ expectedReason: "forbidden",
+ expectedErr: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ auth := GetAuthorizer()
+ decision, reason, err := auth.Authorize(tt.ctx, tt.attr)
+ assert.Equal(t, tt.expectedDecision, decision)
+ assert.Equal(t, tt.expectedReason, reason)
+ assert.Equal(t, tt.expectedErr, err)
+ })
+ }
+}
+
+type mockAttributes struct {
+ authorizer.Attributes
+ resourceRequest bool
+}
+
+func (m *mockAttributes) IsResourceRequest() bool {
+ return m.resourceRequest
+}
+
+// Implement other methods of authorizer.Attributes as needed
+
+type mockUser struct {
+ identity.Requester
+ isGrafanaAdmin bool
+}
+
+func (m *mockUser) GetIsGrafanaAdmin() bool {
+ return m.isGrafanaAdmin
+}
+
+func (m *mockUser) HasRole(role identity.RoleType) bool {
+ return role == identity.RoleAdmin && m.isGrafanaAdmin
+}
+
+// Implement other methods of identity.Requester as needed
diff --git a/apps/advisor/pkg/app/checkregistry/checkregistry.go b/apps/advisor/pkg/app/checkregistry/checkregistry.go
new file mode 100644
index 00000000000..335b9cc7567
--- /dev/null
+++ b/apps/advisor/pkg/app/checkregistry/checkregistry.go
@@ -0,0 +1,66 @@
+package checkregistry
+
+import (
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks/datasourcecheck"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks/plugincheck"
+ "github.com/grafana/grafana/pkg/plugins"
+ "github.com/grafana/grafana/pkg/plugins/repo"
+ "github.com/grafana/grafana/pkg/services/datasources"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
+)
+
+type CheckService interface {
+ Checks() []checks.Check
+}
+
+type Service struct {
+ datasourceSvc datasources.DataSourceService
+ pluginStore pluginstore.Store
+ pluginContextProvider *plugincontext.Provider
+ pluginClient plugins.Client
+ pluginRepo repo.Service
+ pluginPreinstall plugininstaller.Preinstall
+ managedPlugins managedplugins.Manager
+}
+
+func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore pluginstore.Store,
+ pluginContextProvider *plugincontext.Provider, pluginClient plugins.Client,
+ pluginRepo repo.Service, pluginPreinstall plugininstaller.Preinstall, managedPlugins managedplugins.Manager) *Service {
+ return &Service{
+ datasourceSvc: datasourceSvc,
+ pluginStore: pluginStore,
+ pluginContextProvider: pluginContextProvider,
+ pluginClient: pluginClient,
+ pluginRepo: pluginRepo,
+ pluginPreinstall: pluginPreinstall,
+ managedPlugins: managedPlugins,
+ }
+}
+
+func (s *Service) Checks() []checks.Check {
+ return []checks.Check{
+ datasourcecheck.New(
+ s.datasourceSvc,
+ s.pluginStore,
+ s.pluginContextProvider,
+ s.pluginClient,
+ ),
+ plugincheck.New(
+ s.pluginStore,
+ s.pluginRepo,
+ s.pluginPreinstall,
+ s.managedPlugins,
+ ),
+ }
+}
+
+// AdvisorAppConfig is the configuration received from Grafana to run the app
+type AdvisorAppConfig struct {
+ CheckRegistry CheckService
+ PluginConfig map[string]string
+ StackID string
+}
diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check.go b/apps/advisor/pkg/app/checks/datasourcecheck/check.go
new file mode 100644
index 00000000000..0cdac81e4b8
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/datasourcecheck/check.go
@@ -0,0 +1,177 @@
+package datasourcecheck
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/grafana/grafana-plugin-sdk-go/backend"
+ advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/grafana/grafana/pkg/apimachinery/identity"
+ "github.com/grafana/grafana/pkg/infra/log"
+ "github.com/grafana/grafana/pkg/plugins"
+ "github.com/grafana/grafana/pkg/services/datasources"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
+ "github.com/grafana/grafana/pkg/util"
+)
+
+type check struct {
+ DatasourceSvc datasources.DataSourceService
+ PluginStore pluginstore.Store
+ PluginContextProvider pluginContextProvider
+ PluginClient plugins.Client
+ log log.Logger
+}
+
+func New(
+ datasourceSvc datasources.DataSourceService,
+ pluginStore pluginstore.Store,
+ pluginContextProvider pluginContextProvider,
+ pluginClient plugins.Client,
+) checks.Check {
+ return &check{
+ DatasourceSvc: datasourceSvc,
+ PluginStore: pluginStore,
+ PluginContextProvider: pluginContextProvider,
+ PluginClient: pluginClient,
+ log: log.New("advisor.datasourcecheck"),
+ }
+}
+
+func (c *check) Items(ctx context.Context) ([]any, error) {
+ dss, err := c.DatasourceSvc.GetAllDataSources(ctx, &datasources.GetAllDataSourcesQuery{})
+ if err != nil {
+ return nil, err
+ }
+ res := make([]any, len(dss))
+ for i, ds := range dss {
+ res[i] = ds
+ }
+ return res, nil
+}
+
+func (c *check) ID() string {
+ return "datasource"
+}
+
+func (c *check) Steps() []checks.Step {
+ return []checks.Step{
+ &uidValidationStep{},
+ &healthCheckStep{
+ PluginContextProvider: c.PluginContextProvider,
+ PluginClient: c.PluginClient,
+ log: c.log,
+ },
+ }
+}
+
+type uidValidationStep struct{}
+
+func (s *uidValidationStep) ID() string {
+ return "uid-validation"
+}
+
+func (s *uidValidationStep) Title() string {
+ return "UID validation"
+}
+
+func (s *uidValidationStep) Description() string {
+ return "Checks if the UID of a data source is valid."
+}
+
+func (s *uidValidationStep) Resolution() string {
+ return "Check the documentation for more information or delete the data source and create a new one."
+}
+
+func (s *uidValidationStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
+ ds, ok := i.(*datasources.DataSource)
+ if !ok {
+ return nil, fmt.Errorf("invalid item type %T", i)
+ }
+ // Data source UID validation
+ err := util.ValidateUID(ds.UID)
+ if err != nil {
+ return checks.NewCheckReportFailure(
+ advisor.CheckReportFailureSeverityLow,
+ s.ID(),
+ fmt.Sprintf("%s (%s)", ds.Name, ds.UID),
+ []advisor.CheckErrorLink{},
+ ), nil
+ }
+ return nil, nil
+}
+
+type healthCheckStep struct {
+ PluginContextProvider pluginContextProvider
+ PluginClient plugins.Client
+ log log.Logger
+}
+
+func (s *healthCheckStep) Title() string {
+ return "Health check"
+}
+
+func (s *healthCheckStep) Description() string {
+ return "Checks if a data sources is healthy."
+}
+
+func (s *healthCheckStep) Resolution() string {
+ return "Go to the data source configuration page and address the issues reported."
+}
+
+func (s *healthCheckStep) ID() string {
+ return "health-check"
+}
+
+func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
+ ds, ok := i.(*datasources.DataSource)
+ if !ok {
+ return nil, fmt.Errorf("invalid item type %T", i)
+ }
+
+ // Health check execution
+ requester, err := identity.GetRequester(ctx)
+ if err != nil {
+ return nil, err
+ }
+ pCtx, err := s.PluginContextProvider.GetWithDataSource(ctx, ds.Type, requester, ds)
+ if err != nil {
+ // Unable to check health check
+ s.log.Error("Failed to get plugin context", "datasource_uid", ds.UID, "error", err)
+ return nil, nil
+ }
+ req := &backend.CheckHealthRequest{
+ PluginContext: pCtx,
+ Headers: map[string]string{},
+ }
+ resp, err := s.PluginClient.CheckHealth(ctx, req)
+ if err != nil || resp.Status != backend.HealthStatusOk {
+ if err != nil {
+ s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "error", err)
+ if errors.Is(err, plugins.ErrMethodNotImplemented) || errors.Is(err, plugins.ErrPluginUnavailable) {
+ // The plugin does not support backend health checks
+ return nil, nil
+ }
+ } else {
+ s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "status", resp.Status, "message", resp.Message)
+ }
+ return checks.NewCheckReportFailure(
+ advisor.CheckReportFailureSeverityHigh,
+ s.ID(),
+ ds.Name,
+ []advisor.CheckErrorLink{
+ {
+ Message: "Fix me",
+ Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID),
+ },
+ },
+ ), nil
+ }
+ return nil, nil
+}
+
+type pluginContextProvider interface {
+ GetWithDataSource(ctx context.Context, pluginID string, user identity.Requester, ds *datasources.DataSource) (backend.PluginContext, error)
+}
diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go b/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go
new file mode 100644
index 00000000000..ee8a991555d
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go
@@ -0,0 +1,188 @@
+package datasourcecheck
+
+import (
+ "context"
+ "testing"
+
+ "github.com/grafana/grafana-plugin-sdk-go/backend"
+ advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/pkg/apimachinery/identity"
+ "github.com/grafana/grafana/pkg/infra/log"
+ "github.com/grafana/grafana/pkg/plugins"
+ "github.com/grafana/grafana/pkg/services/datasources"
+ "github.com/grafana/grafana/pkg/services/user"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCheck_Run(t *testing.T) {
+ t.Run("should return no failures when all datasources are healthy", func(t *testing.T) {
+ datasources := []*datasources.DataSource{
+ {UID: "valid-uid-1", Type: "prometheus", Name: "Prometheus"},
+ {UID: "valid-uid-2", Type: "mysql", Name: "MySQL"},
+ }
+
+ mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
+ mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
+ mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}}
+
+ check := &check{
+ DatasourceSvc: mockDatasourceSvc,
+ PluginContextProvider: mockPluginContextProvider,
+ PluginClient: mockPluginClient,
+ log: log.New("advisor.datasourcecheck"),
+ }
+
+ ctx := identity.WithRequester(context.Background(), &user.SignedInUser{})
+ items, err := check.Items(ctx)
+ assert.NoError(t, err)
+ failures := []advisor.CheckReportFailure{}
+ for _, step := range check.Steps() {
+ for _, item := range items {
+ stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item)
+ assert.NoError(t, err)
+ if stepFailures != nil {
+ failures = append(failures, *stepFailures)
+ }
+ }
+ }
+
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(items))
+ assert.Empty(t, failures)
+ })
+
+ t.Run("should return failures when datasource UID is invalid", func(t *testing.T) {
+ datasources := []*datasources.DataSource{
+ {UID: "invalid uid", Type: "prometheus", Name: "Prometheus"},
+ }
+
+ mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
+ mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
+ mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}}
+
+ check := &check{
+ DatasourceSvc: mockDatasourceSvc,
+ PluginContextProvider: mockPluginContextProvider,
+ PluginClient: mockPluginClient,
+ log: log.New("advisor.datasourcecheck"),
+ }
+
+ ctx := identity.WithRequester(context.Background(), &user.SignedInUser{})
+ items, err := check.Items(ctx)
+ assert.NoError(t, err)
+ failures := []advisor.CheckReportFailure{}
+ for _, step := range check.Steps() {
+ for _, item := range items {
+ stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item)
+ assert.NoError(t, err)
+ if stepFailures != nil {
+ failures = append(failures, *stepFailures)
+ }
+ }
+ }
+
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(items))
+ assert.Len(t, failures, 1)
+ assert.Equal(t, "uid-validation", failures[0].StepID)
+ })
+
+ t.Run("should return failures when datasource health check fails", func(t *testing.T) {
+ datasources := []*datasources.DataSource{
+ {UID: "valid-uid-1", Type: "prometheus", Name: "Prometheus"},
+ }
+
+ mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
+ mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
+ mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusError}}
+
+ check := &check{
+ DatasourceSvc: mockDatasourceSvc,
+ PluginContextProvider: mockPluginContextProvider,
+ PluginClient: mockPluginClient,
+ log: log.New("advisor.datasourcecheck"),
+ }
+
+ ctx := identity.WithRequester(context.Background(), &user.SignedInUser{})
+ items, err := check.Items(ctx)
+ assert.NoError(t, err)
+ failures := []advisor.CheckReportFailure{}
+ for _, step := range check.Steps() {
+ for _, item := range items {
+ stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item)
+ assert.NoError(t, err)
+ if stepFailures != nil {
+ failures = append(failures, *stepFailures)
+ }
+ }
+ }
+
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(items))
+ assert.Len(t, failures, 1)
+ assert.Equal(t, "health-check", failures[0].StepID)
+ })
+
+ t.Run("should skip health check when plugin does not support backend health checks", func(t *testing.T) {
+ datasources := []*datasources.DataSource{
+ {UID: "valid-uid-1", Type: "prometheus", Name: "Prometheus"},
+ }
+ mockDatasourceSvc := &MockDatasourceSvc{dss: datasources}
+ mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}}
+ mockPluginClient := &MockPluginClient{err: plugins.ErrMethodNotImplemented}
+
+ check := &check{
+ DatasourceSvc: mockDatasourceSvc,
+ PluginContextProvider: mockPluginContextProvider,
+ PluginClient: mockPluginClient,
+ log: log.New("advisor.datasourcecheck"),
+ }
+
+ ctx := identity.WithRequester(context.Background(), &user.SignedInUser{})
+ items, err := check.Items(ctx)
+ assert.NoError(t, err)
+ failures := []advisor.CheckReportFailure{}
+ for _, step := range check.Steps() {
+ for _, item := range items {
+ stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item)
+ assert.NoError(t, err)
+ if stepFailures != nil {
+ failures = append(failures, *stepFailures)
+ }
+ }
+ }
+
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(items))
+ assert.Len(t, failures, 0)
+ })
+}
+
+type MockDatasourceSvc struct {
+ datasources.DataSourceService
+
+ dss []*datasources.DataSource
+}
+
+func (m *MockDatasourceSvc) GetAllDataSources(ctx context.Context, query *datasources.GetAllDataSourcesQuery) ([]*datasources.DataSource, error) {
+ return m.dss, nil
+}
+
+type MockPluginContextProvider struct {
+ pCtx backend.PluginContext
+}
+
+func (m *MockPluginContextProvider) GetWithDataSource(ctx context.Context, pluginID string, user identity.Requester, ds *datasources.DataSource) (backend.PluginContext, error) {
+ return m.pCtx, nil
+}
+
+type MockPluginClient struct {
+ plugins.Client
+
+ res *backend.CheckHealthResult
+ err error
+}
+
+func (m *MockPluginClient) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
+ return m.res, m.err
+}
diff --git a/apps/advisor/pkg/app/checks/ifaces.go b/apps/advisor/pkg/app/checks/ifaces.go
new file mode 100644
index 00000000000..31456599381
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/ifaces.go
@@ -0,0 +1,31 @@
+package checks
+
+import (
+ "context"
+
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+)
+
+// Check returns metadata about the check being executed and the list of Steps
+type Check interface {
+ // ID returns the unique identifier of the check
+ ID() string
+ // Items returns the list of items that will be checked
+ Items(ctx context.Context) ([]any, error)
+ // Steps returns the list of steps that will be executed
+ Steps() []Step
+}
+
+// Step is a single step in a check, including its metadata
+type Step interface {
+ // ID returns the unique identifier of the step
+ ID() string
+ // Title returns the title of the step
+ Title() string
+ // Description returns the description of the step
+ Description() string
+ // Explains the action that needs to be taken to resolve the issue
+ Resolution() string
+ // Run executes the step for an item and returns a report
+ Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error)
+}
diff --git a/apps/advisor/pkg/app/checks/plugincheck/check.go b/apps/advisor/pkg/app/checks/plugincheck/check.go
new file mode 100644
index 00000000000..45c54d4fdc0
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/plugincheck/check.go
@@ -0,0 +1,199 @@
+package plugincheck
+
+import (
+ "context"
+ "fmt"
+ sysruntime "runtime"
+
+ "github.com/Masterminds/semver/v3"
+ advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
+ "github.com/grafana/grafana/pkg/plugins/repo"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
+)
+
+func New(
+ pluginStore pluginstore.Store,
+ pluginRepo repo.Service,
+ pluginPreinstall plugininstaller.Preinstall,
+ managedPlugins managedplugins.Manager,
+) checks.Check {
+ return &check{
+ PluginStore: pluginStore,
+ PluginRepo: pluginRepo,
+ PluginPreinstall: pluginPreinstall,
+ ManagedPlugins: managedPlugins,
+ }
+}
+
+type check struct {
+ PluginStore pluginstore.Store
+ PluginRepo repo.Service
+ PluginPreinstall plugininstaller.Preinstall
+ ManagedPlugins managedplugins.Manager
+}
+
+func (c *check) ID() string {
+ return "plugin"
+}
+
+func (c *check) Items(ctx context.Context) ([]any, error) {
+ ps := c.PluginStore.Plugins(ctx)
+ res := make([]any, len(ps))
+ for i, p := range ps {
+ res[i] = p
+ }
+ return res, nil
+}
+
+func (c *check) Steps() []checks.Step {
+ return []checks.Step{
+ &deprecationStep{
+ PluginRepo: c.PluginRepo,
+ },
+ &updateStep{
+ PluginRepo: c.PluginRepo,
+ PluginPreinstall: c.PluginPreinstall,
+ ManagedPlugins: c.ManagedPlugins,
+ },
+ }
+}
+
+type deprecationStep struct {
+ PluginRepo repo.Service
+}
+
+func (s *deprecationStep) Title() string {
+ return "Deprecation check"
+}
+
+func (s *deprecationStep) Description() string {
+ return "Check if any installed plugins are deprecated."
+}
+
+func (s *deprecationStep) Resolution() string {
+ return "Check the documentation for recommended steps or delete the plugin."
+}
+
+func (s *deprecationStep) ID() string {
+ return "deprecation"
+}
+
+func (s *deprecationStep) Run(ctx context.Context, _ *advisor.CheckSpec, it any) (*advisor.CheckReportFailure, error) {
+ p, ok := it.(pluginstore.Plugin)
+ if !ok {
+ return nil, fmt.Errorf("invalid item type %T", it)
+ }
+
+ // Skip if it's a core plugin
+ if p.IsCorePlugin() {
+ return nil, nil
+ }
+
+ // Check if plugin is deprecated
+ i, err := s.PluginRepo.PluginInfo(ctx, p.ID)
+ if err != nil {
+ // Unable to check deprecation status
+ return nil, nil
+ }
+ if i.Status == "deprecated" {
+ return checks.NewCheckReportFailure(
+ advisor.CheckReportFailureSeverityHigh,
+ s.ID(),
+ p.ID,
+ []advisor.CheckErrorLink{
+ {
+ Message: "Admin",
+ Url: fmt.Sprintf("/plugins/%s", p.ID),
+ },
+ },
+ ), nil
+ }
+ return nil, nil
+}
+
+type updateStep struct {
+ PluginRepo repo.Service
+ PluginPreinstall plugininstaller.Preinstall
+ ManagedPlugins managedplugins.Manager
+}
+
+func (s *updateStep) Title() string {
+ return "Update check"
+}
+
+func (s *updateStep) Description() string {
+ return "Checks if an installed plugins has a newer version available."
+}
+
+func (s *updateStep) Resolution() string {
+ return "Go to the plugin admin page and upgrade to the latest version."
+}
+
+func (s *updateStep) ID() string {
+ return "update"
+}
+
+func (s *updateStep) Run(ctx context.Context, _ *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) {
+ p, ok := i.(pluginstore.Plugin)
+ if !ok {
+ return nil, fmt.Errorf("invalid item type %T", i)
+ }
+
+ // Skip if it's a core plugin
+ if p.IsCorePlugin() {
+ return nil, nil
+ }
+
+ // Skip if it's managed or pinned
+ if s.isManaged(ctx, p.ID) || s.PluginPreinstall.IsPinned(p.ID) {
+ return nil, nil
+ }
+
+ // Check if plugin has a newer version available
+ compatOpts := repo.NewCompatOpts(services.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH)
+ info, err := s.PluginRepo.GetPluginArchiveInfo(ctx, p.ID, "", compatOpts)
+ if err != nil {
+ // Unable to check updates
+ return nil, nil
+ }
+ if hasUpdate(p, info) {
+ return checks.NewCheckReportFailure(
+ advisor.CheckReportFailureSeverityLow,
+ s.ID(),
+ p.ID,
+ []advisor.CheckErrorLink{
+ {
+ Message: "Upgrade",
+ Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID),
+ },
+ },
+ ), nil
+ }
+
+ return nil, nil
+}
+
+func hasUpdate(current pluginstore.Plugin, latest *repo.PluginArchiveInfo) bool {
+ // If both versions are semver-valid, compare them
+ v1, err1 := semver.NewVersion(current.Info.Version)
+ v2, err2 := semver.NewVersion(latest.Version)
+ if err1 == nil && err2 == nil {
+ return v1.LessThan(v2)
+ }
+ // In other case, assume that a different latest version will always be newer
+ return current.Info.Version != latest.Version
+}
+
+func (s *updateStep) isManaged(ctx context.Context, pluginID string) bool {
+ for _, managedPlugin := range s.ManagedPlugins.ManagedPlugins(ctx) {
+ if managedPlugin == pluginID {
+ return true
+ }
+ }
+ return false
+}
diff --git a/apps/advisor/pkg/app/checks/plugincheck/check_test.go b/apps/advisor/pkg/app/checks/plugincheck/check_test.go
new file mode 100644
index 00000000000..13b2a17ea1b
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/plugincheck/check_test.go
@@ -0,0 +1,210 @@
+package plugincheck
+
+import (
+ "context"
+ "testing"
+
+ advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/pkg/plugins"
+ "github.com/grafana/grafana/pkg/plugins/repo"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller"
+ "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRun(t *testing.T) {
+ tests := []struct {
+ name string
+ plugins []pluginstore.Plugin
+ pluginInfo map[string]*repo.PluginInfo
+ pluginArchives map[string]*repo.PluginArchiveInfo
+ pluginPreinstalled []string
+ pluginManaged []string
+ expectedFailures []advisor.CheckReportFailure
+ }{
+ {
+ name: "No plugins",
+ plugins: []pluginstore.Plugin{},
+ expectedFailures: []advisor.CheckReportFailure{},
+ },
+ {
+ name: "Deprecated plugin",
+ plugins: []pluginstore.Plugin{
+ {JSONData: plugins.JSONData{ID: "plugin1", Info: plugins.Info{Version: "1.0.0"}}},
+ },
+ pluginInfo: map[string]*repo.PluginInfo{
+ "plugin1": {Status: "deprecated"},
+ },
+ pluginArchives: map[string]*repo.PluginArchiveInfo{
+ "plugin1": {Version: "1.0.0"},
+ },
+ expectedFailures: []advisor.CheckReportFailure{
+ {
+ Severity: advisor.CheckReportFailureSeverityHigh,
+ StepID: "deprecation",
+ Item: "plugin1",
+ Links: []advisor.CheckErrorLink{
+ {
+ Url: "/plugins/plugin1",
+ Message: "Admin",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "Plugin with update",
+ plugins: []pluginstore.Plugin{
+ {JSONData: plugins.JSONData{ID: "plugin2", Info: plugins.Info{Version: "1.0.0"}}},
+ },
+ pluginInfo: map[string]*repo.PluginInfo{
+ "plugin2": {Status: "active"},
+ },
+ pluginArchives: map[string]*repo.PluginArchiveInfo{
+ "plugin2": {Version: "1.1.0"},
+ },
+ expectedFailures: []advisor.CheckReportFailure{
+ {
+ Severity: advisor.CheckReportFailureSeverityLow,
+ StepID: "update",
+ Item: "plugin2",
+ Links: []advisor.CheckErrorLink{
+ {
+ Url: "/plugins/plugin2?page=version-history",
+ Message: "Upgrade",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "Plugin with update (non semver)",
+ plugins: []pluginstore.Plugin{
+ {JSONData: plugins.JSONData{ID: "plugin2", Info: plugins.Info{Version: "alpha"}}},
+ },
+ pluginInfo: map[string]*repo.PluginInfo{
+ "plugin2": {Status: "active"},
+ },
+ pluginArchives: map[string]*repo.PluginArchiveInfo{
+ "plugin2": {Version: "beta"},
+ },
+ expectedFailures: []advisor.CheckReportFailure{
+ {
+ Severity: advisor.CheckReportFailureSeverityLow,
+ StepID: "update",
+ Item: "plugin2",
+ Links: []advisor.CheckErrorLink{
+ {
+ Url: "/plugins/plugin2?page=version-history",
+ Message: "Upgrade",
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "Plugin pinned",
+ plugins: []pluginstore.Plugin{
+ {JSONData: plugins.JSONData{ID: "plugin3", Info: plugins.Info{Version: "1.0.0"}}},
+ },
+ pluginInfo: map[string]*repo.PluginInfo{
+ "plugin3": {Status: "active"},
+ },
+ pluginArchives: map[string]*repo.PluginArchiveInfo{
+ "plugin3": {Version: "1.1.0"},
+ },
+ pluginPreinstalled: []string{"plugin3"},
+ expectedFailures: []advisor.CheckReportFailure{},
+ },
+ {
+ name: "Managed plugin",
+ plugins: []pluginstore.Plugin{
+ {JSONData: plugins.JSONData{ID: "plugin4", Info: plugins.Info{Version: "1.0.0"}}},
+ },
+ pluginInfo: map[string]*repo.PluginInfo{
+ "plugin4": {Status: "active"},
+ },
+ pluginArchives: map[string]*repo.PluginArchiveInfo{
+ "plugin4": {Version: "1.1.0"},
+ },
+ pluginManaged: []string{"plugin4"},
+ expectedFailures: []advisor.CheckReportFailure{},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ pluginStore := &mockPluginStore{plugins: tt.plugins}
+ pluginRepo := &mockPluginRepo{
+ pluginInfo: tt.pluginInfo,
+ pluginArchiveInfo: tt.pluginArchives,
+ }
+ pluginPreinstall := &mockPluginPreinstall{pinned: tt.pluginPreinstalled}
+ managedPlugins := &mockManagedPlugins{managed: tt.pluginManaged}
+ check := New(pluginStore, pluginRepo, pluginPreinstall, managedPlugins)
+
+ items, err := check.Items(context.Background())
+ assert.NoError(t, err)
+ failures := []advisor.CheckReportFailure{}
+ for _, step := range check.Steps() {
+ for _, item := range items {
+ stepFailures, err := step.Run(context.Background(), &advisor.CheckSpec{}, item)
+ assert.NoError(t, err)
+ if stepFailures != nil {
+ failures = append(failures, *stepFailures)
+ }
+ }
+ }
+ assert.NoError(t, err)
+ assert.Equal(t, len(tt.plugins), len(items))
+ assert.Equal(t, tt.expectedFailures, failures)
+ })
+ }
+}
+
+type mockPluginStore struct {
+ pluginstore.Store
+ plugins []pluginstore.Plugin
+}
+
+func (m *mockPluginStore) Plugins(ctx context.Context, t ...plugins.Type) []pluginstore.Plugin {
+ return m.plugins
+}
+
+type mockPluginRepo struct {
+ repo.Service
+ pluginInfo map[string]*repo.PluginInfo
+ pluginArchiveInfo map[string]*repo.PluginArchiveInfo
+}
+
+func (m *mockPluginRepo) PluginInfo(ctx context.Context, id string) (*repo.PluginInfo, error) {
+ return m.pluginInfo[id], nil
+}
+
+func (m *mockPluginRepo) GetPluginArchiveInfo(ctx context.Context, id, version string, opts repo.CompatOpts) (*repo.PluginArchiveInfo, error) {
+ return m.pluginArchiveInfo[id], nil
+}
+
+type mockPluginPreinstall struct {
+ plugininstaller.Preinstall
+ pinned []string
+}
+
+func (m *mockPluginPreinstall) IsPinned(pluginID string) bool {
+ for _, p := range m.pinned {
+ if p == pluginID {
+ return true
+ }
+ }
+ return false
+}
+
+type mockManagedPlugins struct {
+ managedplugins.Manager
+ managed []string
+}
+
+func (m *mockManagedPlugins) ManagedPlugins(ctx context.Context) []string {
+ return m.managed
+}
diff --git a/apps/advisor/pkg/app/checks/utils.go b/apps/advisor/pkg/app/checks/utils.go
new file mode 100644
index 00000000000..145c98d0961
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/utils.go
@@ -0,0 +1,40 @@
+package checks
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/grafana/authlib/types"
+ advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+ TypeLabel = "advisor.grafana.app/type"
+ StatusAnnotation = "advisor.grafana.app/status"
+)
+
+func NewCheckReportFailure(
+ severity advisor.CheckReportFailureSeverity,
+ stepID string,
+ item string,
+ links []advisor.CheckErrorLink,
+) *advisor.CheckReportFailure {
+ return &advisor.CheckReportFailure{
+ Severity: severity,
+ StepID: stepID,
+ Item: item,
+ Links: links,
+ }
+}
+
+func GetNamespace(stackID string) (string, error) {
+ if stackID == "" {
+ return metav1.NamespaceDefault, nil
+ }
+ stackId, err := strconv.ParseInt(stackID, 10, 64)
+ if err != nil {
+ return "", fmt.Errorf("invalid stack id: %s", stackID)
+ }
+ return types.CloudNamespaceFormatter(stackId), nil
+}
diff --git a/apps/advisor/pkg/app/checks/utils_test.go b/apps/advisor/pkg/app/checks/utils_test.go
new file mode 100644
index 00000000000..ad0fda89de8
--- /dev/null
+++ b/apps/advisor/pkg/app/checks/utils_test.go
@@ -0,0 +1,46 @@
+package checks
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestGetNamespace(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected string
+ expectedErr string
+ }{
+ {
+ name: "empty stack ID",
+ input: "",
+ expected: metav1.NamespaceDefault,
+ },
+ {
+ name: "valid stack ID",
+ input: "1234567890",
+ expected: "stacks-1234567890",
+ },
+ {
+ name: "invalid stack ID",
+ input: "invalid",
+ expected: "",
+ expectedErr: "invalid stack id: invalid",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result, err := GetNamespace(tt.input)
+ if tt.expectedErr != "" {
+ assert.EqualError(t, err, tt.expectedErr)
+ } else {
+ assert.NoError(t, err)
+ }
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go
new file mode 100644
index 00000000000..2bcb8d43f19
--- /dev/null
+++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go
@@ -0,0 +1,225 @@
+package checkscheduler
+
+import (
+ "context"
+ "fmt"
+ "sort"
+ "strconv"
+ "time"
+
+ "github.com/grafana/grafana-app-sdk/app"
+ "github.com/grafana/grafana-app-sdk/k8s"
+ "github.com/grafana/grafana-app-sdk/resource"
+ "github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/klog/v2"
+)
+
+const defaultEvaluationInterval = 24 * time.Hour
+const defaultMaxHistory = 10
+
+// Runner is a "runnable" app used to be able to expose and API endpoint
+// with the existing checks types. This does not need to be a CRUD resource, but it is
+// the only way existing at the moment to expose the check types.
+type Runner struct {
+ checkRegistry checkregistry.CheckService
+ client resource.Client
+ evaluationInterval time.Duration
+ maxHistory int
+ namespace string
+}
+
+// NewRunner creates a new Runner.
+func New(cfg app.Config) (app.Runnable, error) {
+ // Read config
+ specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig)
+ if !ok {
+ return nil, fmt.Errorf("invalid config type")
+ }
+ checkRegistry := specificConfig.CheckRegistry
+ evalInterval, err := getEvaluationInterval(specificConfig.PluginConfig)
+ if err != nil {
+ return nil, err
+ }
+ maxHistory, err := getMaxHistory(specificConfig.PluginConfig)
+ if err != nil {
+ return nil, err
+ }
+ namespace, err := checks.GetNamespace(specificConfig.StackID)
+ if err != nil {
+ return nil, err
+ }
+
+ // Prepare storage client
+ clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{})
+ client, err := clientGenerator.ClientFor(advisorv0alpha1.CheckKind())
+ if err != nil {
+ return nil, err
+ }
+
+ return &Runner{
+ checkRegistry: checkRegistry,
+ client: client,
+ evaluationInterval: evalInterval,
+ maxHistory: maxHistory,
+ namespace: namespace,
+ }, nil
+}
+
+func (r *Runner) Run(ctx context.Context) error {
+ lastCreated, err := r.checkLastCreated(ctx)
+ if err != nil {
+ return err
+ }
+
+ // do an initial creation if necessary
+ if lastCreated.IsZero() {
+ err = r.createChecks(ctx)
+ if err != nil {
+ klog.Error("Error creating new check reports", "error", err)
+ } else {
+ lastCreated = time.Now()
+ }
+ }
+
+ nextSendInterval := time.Until(lastCreated.Add(r.evaluationInterval))
+ if nextSendInterval < time.Minute {
+ nextSendInterval = 1 * time.Minute
+ }
+
+ ticker := time.NewTicker(nextSendInterval)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ err = r.createChecks(ctx)
+ if err != nil {
+ klog.Error("Error creating new check reports", "error", err)
+ }
+
+ err = r.cleanupChecks(ctx)
+ if err != nil {
+ klog.Error("Error cleaning up old check reports", "error", err)
+ }
+
+ if nextSendInterval != r.evaluationInterval {
+ nextSendInterval = r.evaluationInterval
+ }
+ ticker.Reset(nextSendInterval)
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+}
+
+// checkLastCreated returns the creation time of the last check created
+// regardless of its ID. This assumes that the checks are created in batches
+// so a batch will have a similar creation time.
+func (r *Runner) checkLastCreated(ctx context.Context) (time.Time, error) {
+ list, err := r.client.List(ctx, r.namespace, resource.ListOptions{})
+ if err != nil {
+ return time.Time{}, err
+ }
+ lastCreated := time.Time{}
+ for _, item := range list.GetItems() {
+ itemCreated := item.GetCreationTimestamp().Time
+ if itemCreated.After(lastCreated) {
+ lastCreated = itemCreated
+ }
+ }
+ return lastCreated, nil
+}
+
+// createChecks creates a new check for each check type in the registry.
+func (r *Runner) createChecks(ctx context.Context) error {
+ for _, check := range r.checkRegistry.Checks() {
+ obj := &advisorv0alpha1.Check{
+ ObjectMeta: metav1.ObjectMeta{
+ GenerateName: "check-",
+ Namespace: r.namespace,
+ Labels: map[string]string{
+ checks.TypeLabel: check.ID(),
+ },
+ },
+ Spec: advisorv0alpha1.CheckSpec{},
+ }
+ id := obj.GetStaticMetadata().Identifier()
+ _, err := r.client.Create(ctx, id, obj, resource.CreateOptions{})
+ if err != nil {
+ return fmt.Errorf("error creating check: %w", err)
+ }
+ }
+ return nil
+}
+
+// cleanupChecks deletes the olders checks if the number of checks exceeds the limit.
+func (r *Runner) cleanupChecks(ctx context.Context) error {
+ list, err := r.client.List(ctx, r.namespace, resource.ListOptions{Limit: -1})
+ if err != nil {
+ return err
+ }
+
+ // organize checks by type
+ checksByType := map[string][]resource.Object{}
+ for _, check := range list.GetItems() {
+ labels := check.GetLabels()
+ checkType, ok := labels[checks.TypeLabel]
+ if !ok {
+ klog.Error("Check type not found in labels", "check", check)
+ continue
+ }
+ checksByType[checkType] = append(checksByType[checkType], check)
+ }
+
+ for _, checks := range checksByType {
+ if len(checks) > r.maxHistory {
+ // Sort checks by creation time
+ sort.Slice(checks, func(i, j int) bool {
+ ti := checks[i].GetCreationTimestamp().Time
+ tj := checks[j].GetCreationTimestamp().Time
+ return ti.Before(tj)
+ })
+ // Delete the oldest checks
+ for i := 0; i < len(checks)-r.maxHistory; i++ {
+ check := checks[i]
+ id := check.GetStaticMetadata().Identifier()
+ err := r.client.Delete(ctx, id, resource.DeleteOptions{})
+ if err != nil {
+ return fmt.Errorf("error deleting check: %w", err)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func getEvaluationInterval(pluginConfig map[string]string) (time.Duration, error) {
+ evaluationInterval := defaultEvaluationInterval
+ configEvaluationInterval, ok := pluginConfig["evaluation_interval"]
+ if ok {
+ var err error
+ evaluationInterval, err = gtime.ParseDuration(configEvaluationInterval)
+ if err != nil {
+ return 0, fmt.Errorf("invalid evaluation interval: %w", err)
+ }
+ }
+ return evaluationInterval, nil
+}
+
+func getMaxHistory(pluginConfig map[string]string) (int, error) {
+ maxHistory := defaultMaxHistory
+ configMaxHistory, ok := pluginConfig["max_history"]
+ if ok {
+ var err error
+ maxHistory, err = strconv.Atoi(configMaxHistory)
+ if err != nil {
+ return 0, fmt.Errorf("invalid max history: %w", err)
+ }
+ }
+ return maxHistory, nil
+}
diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go
new file mode 100644
index 00000000000..4fd5876b9ff
--- /dev/null
+++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go
@@ -0,0 +1,279 @@
+package checkscheduler
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/rand/v2"
+ "testing"
+ "time"
+
+ "github.com/grafana/grafana-app-sdk/resource"
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/stretchr/testify/assert"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestRunner_Run_ErrorOnList(t *testing.T) {
+ mockCheckService := &MockCheckService{}
+ mockClient := &MockClient{
+ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ return nil, errors.New("list error")
+ },
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return &advisorv0alpha1.Check{}, nil
+ },
+ }
+
+ runner := &Runner{
+ checkRegistry: mockCheckService,
+ client: mockClient,
+ }
+
+ err := runner.Run(context.Background())
+ assert.Error(t, err)
+}
+
+func TestRunner_checkLastCreated_ErrorOnList(t *testing.T) {
+ mockClient := &MockClient{
+ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ return nil, errors.New("list error")
+ },
+ }
+
+ runner := &Runner{
+ client: mockClient,
+ }
+
+ lastCreated, err := runner.checkLastCreated(context.Background())
+ assert.Error(t, err)
+ assert.True(t, lastCreated.IsZero())
+}
+
+func TestRunner_createChecks_ErrorOnCreate(t *testing.T) {
+ mockCheckService := &MockCheckService{
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check-1",
+ },
+ },
+ }
+ mockClient := &MockClient{
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return nil, errors.New("create error")
+ },
+ }
+
+ runner := &Runner{
+ checkRegistry: mockCheckService,
+ client: mockClient,
+ }
+
+ err := runner.createChecks(context.Background())
+ assert.Error(t, err)
+}
+
+func TestRunner_createChecks_Success(t *testing.T) {
+ mockCheckService := &MockCheckService{
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check-1",
+ },
+ },
+ }
+ mockClient := &MockClient{
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return &advisorv0alpha1.Check{}, nil
+ },
+ }
+
+ runner := &Runner{
+ checkRegistry: mockCheckService,
+ client: mockClient,
+ }
+
+ err := runner.createChecks(context.Background())
+ assert.NoError(t, err)
+}
+
+func TestRunner_cleanupChecks_ErrorOnList(t *testing.T) {
+ mockClient := &MockClient{
+ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ return nil, errors.New("list error")
+ },
+ }
+
+ runner := &Runner{
+ client: mockClient,
+ }
+
+ err := runner.cleanupChecks(context.Background())
+ assert.Error(t, err)
+}
+
+func TestRunner_cleanupChecks_WithinMax(t *testing.T) {
+ mockClient := &MockClient{
+ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ return &advisorv0alpha1.CheckList{
+ Items: []advisorv0alpha1.Check{{}, {}},
+ }, nil
+ },
+ deleteFunc: func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error {
+ return fmt.Errorf("shouldn't be called")
+ },
+ }
+
+ runner := &Runner{
+ client: mockClient,
+ }
+
+ err := runner.cleanupChecks(context.Background())
+ assert.NoError(t, err)
+}
+
+func TestRunner_cleanupChecks_ErrorOnDelete(t *testing.T) {
+ mockClient := &MockClient{
+ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ items := make([]advisorv0alpha1.Check, 0, defaultMaxHistory+1)
+ for i := 0; i < defaultMaxHistory+1; i++ {
+ item := advisorv0alpha1.Check{}
+ item.ObjectMeta.SetLabels(map[string]string{
+ checks.TypeLabel: "mock",
+ })
+ items = append(items, item)
+ }
+ return &advisorv0alpha1.CheckList{
+ Items: items,
+ }, nil
+ },
+ deleteFunc: func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error {
+ return errors.New("delete error")
+ },
+ }
+
+ runner := &Runner{
+ client: mockClient,
+ maxHistory: defaultMaxHistory,
+ }
+ err := runner.cleanupChecks(context.Background())
+ assert.ErrorContains(t, err, "delete error")
+}
+
+func TestRunner_cleanupChecks_Success(t *testing.T) {
+ itemsDeleted := []string{}
+ items := make([]advisorv0alpha1.Check, 0, defaultMaxHistory+1)
+ for i := 0; i < defaultMaxHistory+1; i++ {
+ item := advisorv0alpha1.Check{}
+ item.ObjectMeta.SetName(fmt.Sprintf("check-%d", i))
+ item.ObjectMeta.SetLabels(map[string]string{
+ checks.TypeLabel: "mock",
+ })
+ item.ObjectMeta.SetCreationTimestamp(metav1.NewTime(time.Time{}.Add(time.Duration(i) * time.Hour)))
+ items = append(items, item)
+ }
+ // shuffle the items to ensure the oldest are deleted
+ rand.Shuffle(len(items), func(i, j int) { items[i], items[j] = items[j], items[i] })
+
+ mockClient := &MockClient{
+ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ return &advisorv0alpha1.CheckList{
+ Items: items,
+ }, nil
+ },
+ deleteFunc: func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error {
+ itemsDeleted = append(itemsDeleted, identifier.Name)
+ return nil
+ },
+ }
+
+ runner := &Runner{
+ client: mockClient,
+ maxHistory: defaultMaxHistory,
+ }
+ err := runner.cleanupChecks(context.Background())
+ assert.NoError(t, err)
+ assert.Equal(t, []string{"check-0"}, itemsDeleted)
+}
+
+func Test_getEvaluationInterval(t *testing.T) {
+ t.Run("default", func(t *testing.T) {
+ interval, err := getEvaluationInterval(map[string]string{})
+ assert.NoError(t, err)
+ assert.Equal(t, 24*time.Hour, interval)
+ })
+
+ t.Run("invalid", func(t *testing.T) {
+ interval, err := getEvaluationInterval(map[string]string{"evaluation_interval": "invalid"})
+ assert.Error(t, err)
+ assert.Zero(t, interval)
+ })
+
+ t.Run("custom", func(t *testing.T) {
+ interval, err := getEvaluationInterval(map[string]string{"evaluation_interval": "1h"})
+ assert.NoError(t, err)
+ assert.Equal(t, time.Hour, interval)
+ })
+}
+
+func Test_getMaxHistory(t *testing.T) {
+ t.Run("default", func(t *testing.T) {
+ history, err := getMaxHistory(map[string]string{})
+ assert.NoError(t, err)
+ assert.Equal(t, 10, history)
+ })
+
+ t.Run("invalid", func(t *testing.T) {
+ history, err := getMaxHistory(map[string]string{"max_history": "invalid"})
+ assert.Error(t, err)
+ assert.Zero(t, history)
+ })
+
+ t.Run("custom", func(t *testing.T) {
+ history, err := getMaxHistory(map[string]string{"max_history": "5"})
+ assert.NoError(t, err)
+ assert.Equal(t, 5, history)
+ })
+}
+
+type MockCheckService struct {
+ checks []checks.Check
+}
+
+func (m *MockCheckService) Checks() []checks.Check {
+ return m.checks
+}
+
+type MockClient struct {
+ resource.Client
+ listFunc func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error)
+ createFunc func(ctx context.Context, identifier resource.Identifier, obj resource.Object, options resource.CreateOptions) (resource.Object, error)
+ deleteFunc func(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error
+}
+
+func (m *MockClient) List(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) {
+ return m.listFunc(ctx, namespace, options)
+}
+
+func (m *MockClient) Create(ctx context.Context, identifier resource.Identifier, obj resource.Object, options resource.CreateOptions) (resource.Object, error) {
+ return m.createFunc(ctx, identifier, obj, options)
+}
+
+func (m *MockClient) Delete(ctx context.Context, identifier resource.Identifier, options resource.DeleteOptions) error {
+ return m.deleteFunc(ctx, identifier, options)
+}
+
+type mockCheck struct {
+ checks.Check
+
+ id string
+ steps []checks.Step
+}
+
+func (m *mockCheck) ID() string {
+ return m.id
+}
+
+func (m *mockCheck) Steps() []checks.Step {
+ return m.steps
+}
diff --git a/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go
new file mode 100644
index 00000000000..2ed38e51ecb
--- /dev/null
+++ b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go
@@ -0,0 +1,91 @@
+package checktyperegisterer
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/grafana/grafana-app-sdk/app"
+ "github.com/grafana/grafana-app-sdk/k8s"
+ "github.com/grafana/grafana-app-sdk/resource"
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// Runner is a "runnable" app used to be able to expose and API endpoint
+// with the existing checks types. This does not need to be a CRUD resource, but it is
+// the only way existing at the moment to expose the check types.
+type Runner struct {
+ checkRegistry checkregistry.CheckService
+ client resource.Client
+ namespace string
+}
+
+// NewRunner creates a new Runner.
+func New(cfg app.Config) (app.Runnable, error) {
+ // Read config
+ specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig)
+ if !ok {
+ return nil, fmt.Errorf("invalid config type")
+ }
+ checkRegistry := specificConfig.CheckRegistry
+ namespace, err := checks.GetNamespace(specificConfig.StackID)
+ if err != nil {
+ return nil, err
+ }
+
+ // Prepare storage client
+ clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{})
+ client, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind())
+ if err != nil {
+ return nil, err
+ }
+
+ return &Runner{
+ checkRegistry: checkRegistry,
+ client: client,
+ namespace: namespace,
+ }, nil
+}
+
+func (r *Runner) Run(ctx context.Context) error {
+ for _, t := range r.checkRegistry.Checks() {
+ steps := t.Steps()
+ stepTypes := make([]advisorv0alpha1.CheckTypeStep, len(steps))
+ for i, s := range steps {
+ stepTypes[i] = advisorv0alpha1.CheckTypeStep{
+ Title: s.Title(),
+ Description: s.Description(),
+ StepID: s.ID(),
+ Resolution: s.Resolution(),
+ }
+ }
+ obj := &advisorv0alpha1.CheckType{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: t.ID(),
+ Namespace: r.namespace,
+ },
+ Spec: advisorv0alpha1.CheckTypeSpec{
+ Name: t.ID(),
+ Steps: stepTypes,
+ },
+ }
+ id := obj.GetStaticMetadata().Identifier()
+ _, err := r.client.Create(ctx, id, obj, resource.CreateOptions{})
+ if err != nil {
+ if errors.IsAlreadyExists(err) {
+ // Already exists, update
+ _, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
+ if err != nil {
+ return err
+ } else {
+ continue
+ }
+ }
+ return err
+ }
+ }
+ return nil
+}
diff --git a/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go
new file mode 100644
index 00000000000..97fc7742504
--- /dev/null
+++ b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go
@@ -0,0 +1,196 @@
+package checktyperegisterer
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "testing"
+
+ "github.com/grafana/grafana-app-sdk/resource"
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ k8sErrs "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+func TestCheckTypesRegisterer_Run(t *testing.T) {
+ tests := []struct {
+ name string
+ checks []checks.Check
+ createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error)
+ updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error)
+ expectedErr error
+ }{
+ {
+ name: "successful create",
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check1",
+ steps: []checks.Step{
+ &mockStep{id: "step1", title: "Step 1", description: "Description 1"},
+ },
+ },
+ },
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return obj, nil
+ },
+ updateFunc: nil,
+ expectedErr: nil,
+ },
+ {
+ name: "create already exists, successful update",
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check1",
+ steps: []checks.Step{
+ &mockStep{id: "step1", title: "Step 1", description: "Description 1"},
+ },
+ },
+ },
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
+ },
+ updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
+ return obj, nil
+ },
+ expectedErr: nil,
+ },
+ {
+ name: "create error",
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check1",
+ steps: []checks.Step{
+ &mockStep{id: "step1", title: "Step 1", description: "Description 1"},
+ },
+ },
+ },
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return nil, errors.New("create error")
+ },
+ updateFunc: nil,
+ expectedErr: errors.New("create error"),
+ },
+ {
+ name: "update error",
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check1",
+ steps: []checks.Step{
+ &mockStep{id: "step1", title: "Step 1", description: "Description 1"},
+ },
+ },
+ },
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
+ },
+ updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
+ return nil, errors.New("update error")
+ },
+ expectedErr: errors.New("update error"),
+ },
+ {
+ name: "custom namespace",
+ checks: []checks.Check{
+ &mockCheck{
+ id: "check1",
+ steps: []checks.Step{
+ &mockStep{id: "step1", title: "Step 1", description: "Description 1"},
+ },
+ },
+ },
+ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ if obj.GetNamespace() != "custom-namespace" {
+ return nil, fmt.Errorf("expected namespace %s, got %s", "custom-namespace", obj.GetNamespace())
+ }
+ return obj, nil
+ },
+ expectedErr: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Runner{
+ checkRegistry: &mockCheckRegistry{checks: tt.checks},
+ client: &mockClient{
+ createFunc: tt.createFunc,
+ updateFunc: tt.updateFunc,
+ },
+ namespace: "custom-namespace",
+ }
+ err := r.Run(context.Background())
+ if err != nil {
+ if tt.expectedErr == nil {
+ t.Errorf("unexpected error: %v", err)
+ } else if err.Error() != tt.expectedErr.Error() {
+ t.Errorf("expected error: %v, got: %v", tt.expectedErr, err)
+ }
+ }
+ })
+ }
+}
+
+type mockCheckRegistry struct {
+ checks []checks.Check
+}
+
+func (m *mockCheckRegistry) Checks() []checks.Check {
+ return m.checks
+}
+
+type mockCheck struct {
+ checks.Check
+
+ id string
+ steps []checks.Step
+}
+
+func (m *mockCheck) ID() string {
+ return m.id
+}
+
+func (m *mockCheck) Steps() []checks.Step {
+ return m.steps
+}
+
+type mockStep struct {
+ id string
+ title string
+ description string
+}
+
+func (m *mockStep) ID() string {
+ return m.id
+}
+
+func (m *mockStep) Title() string {
+ return m.title
+}
+
+func (m *mockStep) Description() string {
+ return m.description
+}
+
+func (m *mockStep) Resolution() string {
+ return ""
+}
+
+func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error) {
+ return nil, nil
+}
+
+type mockClient struct {
+ resource.Client
+
+ createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error)
+ updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error)
+}
+
+func (m *mockClient) Create(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
+ return m.createFunc(ctx, id, obj, opts)
+}
+
+func (m *mockClient) Update(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
+ return m.updateFunc(ctx, id, obj, opts)
+}
diff --git a/apps/advisor/pkg/app/utils.go b/apps/advisor/pkg/app/utils.go
new file mode 100644
index 00000000000..8c1b6c00eb8
--- /dev/null
+++ b/apps/advisor/pkg/app/utils.go
@@ -0,0 +1,143 @@
+package app
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+
+ claims "github.com/grafana/authlib/types"
+ "github.com/grafana/grafana-app-sdk/resource"
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/grafana/grafana/pkg/apimachinery/identity"
+ "github.com/grafana/grafana/pkg/apimachinery/utils"
+ "github.com/grafana/grafana/pkg/services/user"
+)
+
+func getCheck(obj resource.Object, checkMap map[string]checks.Check) (checks.Check, error) {
+ labels := obj.GetLabels()
+ objTypeLabel, ok := labels[checks.TypeLabel]
+ if !ok {
+ return nil, errors.New("missing check type as label")
+ }
+ c, ok := checkMap[objTypeLabel]
+ if !ok {
+ supportedTypes := ""
+ for k := range checkMap {
+ supportedTypes += k + ", "
+ }
+ return nil, fmt.Errorf("unknown check type %s. Supported types are: %s", objTypeLabel, supportedTypes)
+ }
+
+ return c, nil
+}
+
+func getStatusAnnotation(obj resource.Object) string {
+ return obj.GetAnnotations()[checks.StatusAnnotation]
+}
+
+func setStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error {
+ annotations := obj.GetAnnotations()
+ annotations[checks.StatusAnnotation] = status
+ return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
+ Operations: []resource.PatchOperation{{
+ Operation: resource.PatchOpAdd,
+ Path: "/metadata/annotations",
+ Value: annotations,
+ }},
+ }, resource.PatchOptions{}, obj)
+}
+
+func processCheck(ctx context.Context, client resource.Client, obj resource.Object, check checks.Check) error {
+ status := getStatusAnnotation(obj)
+ if status != "" {
+ // Check already processed
+ return nil
+ }
+ c, ok := obj.(*advisorv0alpha1.Check)
+ if !ok {
+ return fmt.Errorf("invalid object type")
+ }
+ // Populate ctx with the user that created the check
+ meta, err := utils.MetaAccessor(obj)
+ if err != nil {
+ return err
+ }
+ createdBy := meta.GetCreatedBy()
+ typ, uid, err := claims.ParseTypeID(createdBy)
+ if err != nil {
+ return err
+ }
+ ctx = identity.WithRequester(ctx, &user.SignedInUser{
+ UserUID: uid,
+ FallbackType: typ,
+ })
+ // Get the items to check
+ items, err := check.Items(ctx)
+ if err != nil {
+ setErr := setStatusAnnotation(ctx, client, obj, "error")
+ if setErr != nil {
+ return setErr
+ }
+ return fmt.Errorf("error initializing check: %w", err)
+ }
+ // Run the steps
+ steps := check.Steps()
+ failures, err := runStepsInParallel(ctx, &c.Spec, steps, items)
+ if err != nil {
+ setErr := setStatusAnnotation(ctx, client, obj, "error")
+ if setErr != nil {
+ return setErr
+ }
+ return fmt.Errorf("error running steps: %w", err)
+ }
+
+ report := &advisorv0alpha1.CheckV0alpha1StatusReport{
+ Failures: failures,
+ Count: int64(len(items)),
+ }
+ err = setStatusAnnotation(ctx, client, obj, "processed")
+ if err != nil {
+ return err
+ }
+ return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
+ Operations: []resource.PatchOperation{{
+ Operation: resource.PatchOpAdd,
+ Path: "/status/report",
+ Value: *report,
+ }},
+ }, resource.PatchOptions{}, obj)
+}
+
+func runStepsInParallel(ctx context.Context, spec *advisorv0alpha1.CheckSpec, steps []checks.Step, items []any) ([]advisorv0alpha1.CheckReportFailure, error) {
+ reportFailures := []advisorv0alpha1.CheckReportFailure{}
+ var internalErr error
+ var wg sync.WaitGroup
+ var mu sync.Mutex
+ // Avoid too many concurrent requests
+ limit := make(chan struct{}, 10)
+
+ for _, step := range steps {
+ for _, item := range items {
+ wg.Add(1)
+ limit <- struct{}{}
+ go func(step checks.Step, item any) {
+ defer wg.Done()
+ defer func() { <-limit }()
+ stepErr, err := step.Run(ctx, spec, item)
+ mu.Lock()
+ defer mu.Unlock()
+ if err != nil {
+ internalErr = fmt.Errorf("error running step %s: %w", step.ID(), err)
+ return
+ }
+ if stepErr != nil {
+ reportFailures = append(reportFailures, *stepErr)
+ }
+ }(step, item)
+ }
+ }
+ wg.Wait()
+ return reportFailures, internalErr
+}
diff --git a/apps/advisor/pkg/app/utils_test.go b/apps/advisor/pkg/app/utils_test.go
new file mode 100644
index 00000000000..b3b8d36f60b
--- /dev/null
+++ b/apps/advisor/pkg/app/utils_test.go
@@ -0,0 +1,200 @@
+package app
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "testing"
+
+ "github.com/grafana/grafana-app-sdk/resource"
+ advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/apps/advisor/pkg/app/checks"
+ "github.com/grafana/grafana/pkg/apimachinery/utils"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetCheck(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetLabels(map[string]string{checks.TypeLabel: "testType"})
+
+ checkMap := map[string]checks.Check{
+ "testType": &mockCheck{},
+ }
+
+ check, err := getCheck(obj, checkMap)
+ assert.NoError(t, err)
+ assert.NotNil(t, check)
+}
+
+func TestGetCheck_MissingLabel(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ checkMap := map[string]checks.Check{}
+
+ _, err := getCheck(obj, checkMap)
+ assert.Error(t, err)
+ assert.Equal(t, "missing check type as label", err.Error())
+}
+
+func TestGetCheck_UnknownType(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetLabels(map[string]string{checks.TypeLabel: "unknownType"})
+
+ checkMap := map[string]checks.Check{
+ "testType": &mockCheck{},
+ }
+
+ _, err := getCheck(obj, checkMap)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "unknown check type unknownType")
+}
+
+func TestSetStatusAnnotation(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetAnnotations(map[string]string{})
+ client := &mockClient{}
+ ctx := context.TODO()
+
+ err := setStatusAnnotation(ctx, client, obj, "processed")
+ assert.NoError(t, err)
+ assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
+}
+
+func TestProcessCheck(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetAnnotations(map[string]string{})
+ meta, err := utils.MetaAccessor(obj)
+ if err != nil {
+ t.Fatal(err)
+ }
+ meta.SetCreatedBy("user:1")
+ client := &mockClient{}
+ ctx := context.TODO()
+ check := &mockCheck{
+ items: []any{"item"},
+ }
+
+ err = processCheck(ctx, client, obj, check)
+ assert.NoError(t, err)
+ assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
+}
+
+func TestProcessMultipleCheckItems(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetAnnotations(map[string]string{})
+ meta, err := utils.MetaAccessor(obj)
+ if err != nil {
+ t.Fatal(err)
+ }
+ meta.SetCreatedBy("user:1")
+ client := &mockClient{}
+ ctx := context.TODO()
+ items := make([]any, 100)
+ for i := range items {
+ if i%2 == 0 {
+ items[i] = fmt.Sprintf("item-%d", i)
+ } else {
+ items[i] = errors.New("error")
+ }
+ }
+ check := &mockCheck{
+ items: items,
+ }
+
+ err = processCheck(ctx, client, obj, check)
+ assert.NoError(t, err)
+ assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
+ r := client.lastValue.(advisorv0alpha1.CheckV0alpha1StatusReport)
+ assert.Equal(t, r.Count, int64(100))
+ assert.Len(t, r.Failures, 50)
+}
+
+func TestProcessCheck_AlreadyProcessed(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetAnnotations(map[string]string{checks.StatusAnnotation: "processed"})
+ client := &mockClient{}
+ ctx := context.TODO()
+ check := &mockCheck{}
+
+ err := processCheck(ctx, client, obj, check)
+ assert.NoError(t, err)
+}
+
+func TestProcessCheck_RunError(t *testing.T) {
+ obj := &advisorv0alpha1.Check{}
+ obj.SetAnnotations(map[string]string{})
+ meta, err := utils.MetaAccessor(obj)
+ if err != nil {
+ t.Fatal(err)
+ }
+ meta.SetCreatedBy("user:1")
+ client := &mockClient{}
+ ctx := context.TODO()
+
+ check := &mockCheck{
+ items: []any{"item"},
+ err: errors.New("run error"),
+ }
+
+ err = processCheck(ctx, client, obj, check)
+ assert.Error(t, err)
+ assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation])
+}
+
+type mockClient struct {
+ resource.Client
+ lastValue any
+}
+
+func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions, obj resource.Object) error {
+ m.lastValue = req.Operations[0].Value
+ return nil
+}
+
+type mockCheck struct {
+ err error
+ items []any
+}
+
+func (m *mockCheck) ID() string {
+ return "mock"
+}
+
+func (m *mockCheck) Items(ctx context.Context) ([]any, error) {
+ return m.items, nil
+}
+
+func (m *mockCheck) Steps() []checks.Step {
+ return []checks.Step{
+ &mockStep{err: m.err},
+ }
+}
+
+type mockStep struct {
+ err error
+}
+
+func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, items any) (*advisorv0alpha1.CheckReportFailure, error) {
+ if m.err != nil {
+ return nil, m.err
+ }
+ if _, ok := items.(error); ok {
+ return &advisorv0alpha1.CheckReportFailure{}, nil
+ }
+ return nil, nil
+}
+
+func (m *mockStep) Title() string {
+ return "mock"
+}
+
+func (m *mockStep) Description() string {
+ return "mock"
+}
+
+func (m *mockStep) Resolution() string {
+ return "mock"
+}
+
+func (m *mockStep) ID() string {
+ return "mock"
+}
diff --git a/apps/advisor/plugin/README.md b/apps/advisor/plugin/README.md
new file mode 100644
index 00000000000..5b9e2bc8744
--- /dev/null
+++ b/apps/advisor/plugin/README.md
@@ -0,0 +1,7 @@
+This folder contains the automatically generated types for the frontend that are used in the app plugin.
+
+To update the types:
+
+1. Make any necessary changes in the `kinds` directory
+2. Run `make generate`
+3. Copy the `plugin` directory to the frontend app: https://github.com/grafana/grafana-advisor-app/tree/main/src/plugin
diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod
index 61093d88f4b..74dc1dfe028 100644
--- a/apps/alerting/notifications/go.mod
+++ b/apps/alerting/notifications/go.mod
@@ -6,9 +6,9 @@ replace github.com/grafana/grafana => ../../..
require (
github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible
- github.com/grafana/grafana-app-sdk v0.30.0
- k8s.io/apimachinery v0.32.0
- k8s.io/apiserver v0.32.0
+ github.com/grafana/grafana-app-sdk v0.31.0
+ k8s.io/apimachinery v0.32.1
+ k8s.io/apiserver v0.32.1
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
)
@@ -36,7 +36,7 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d // indirect
+ github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -52,7 +52,7 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.61.0 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
@@ -61,35 +61,35 @@ require (
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
- go.opentelemetry.io/otel v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
- go.opentelemetry.io/otel/metric v1.33.0 // indirect
- go.opentelemetry.io/otel/sdk v1.33.0 // indirect
- go.opentelemetry.io/otel/trace v1.33.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
+ go.opentelemetry.io/otel v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.34.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
- golang.org/x/crypto v0.32.0 // indirect
+ golang.org/x/crypto v0.35.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
- golang.org/x/net v0.34.0 // indirect
- golang.org/x/oauth2 v0.25.0 // indirect
- golang.org/x/sys v0.29.0 // indirect
- golang.org/x/term v0.28.0 // indirect
- golang.org/x/text v0.21.0 // indirect
+ golang.org/x/net v0.35.0 // indirect
+ golang.org/x/oauth2 v0.27.0 // indirect
+ golang.org/x/sys v0.30.0 // indirect
+ golang.org/x/term v0.29.0 // indirect
+ golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.9.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
- google.golang.org/grpc v1.69.4 // indirect
- google.golang.org/protobuf v1.36.1 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+ google.golang.org/grpc v1.70.0 // indirect
+ google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/api v0.32.0 // indirect
- k8s.io/client-go v0.32.0 // indirect
- k8s.io/component-base v0.32.0 // indirect
+ k8s.io/api v0.32.1 // indirect
+ k8s.io/client-go v0.32.1 // indirect
+ k8s.io/component-base v0.32.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum
index a140089cc39..cae5be00879 100644
--- a/apps/alerting/notifications/go.sum
+++ b/apps/alerting/notifications/go.sum
@@ -71,10 +71,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw=
-github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk=
-github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d h1:0WJ4uZ3gtbX7S5RGMarA+0TccmB7qEPhhNm34NcfBzs=
-github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d/go.mod h1:PNmbi49lVrv2b2I8pdu46dTs2728lKEbnVuQD8I5MnM=
+github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk=
+github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs=
+github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 h1:bOKkAzmkCnK2EN0LGsHoq5HyfXMLKXrr8+q35Fh+XQI=
+github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295/go.mod h1:1iWqntBW91TsiyqneHrqTFV12vd1Tei+f5VY2ovkNAo=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY=
@@ -130,8 +130,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
@@ -178,24 +178,24 @@ go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE
go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -207,8 +207,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
-golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
+golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -223,11 +223,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
-golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
-golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -237,14 +237,14 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
-golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
-golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -262,15 +262,15 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
-google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -283,16 +283,16 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
-k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
-k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
-k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/apiserver v0.32.0 h1:VJ89ZvQZ8p1sLeiWdRJpRD6oLozNZD2+qVSLi+ft5Qs=
-k8s.io/apiserver v0.32.0/go.mod h1:HFh+dM1/BE/Hm4bS4nTXHVfN6Z6tFIZPi649n83b4Ag=
-k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
-k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
-k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU=
-k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM=
+k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
+k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
+k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
+k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak=
+k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw=
+k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
+k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
+k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk=
+k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
diff --git a/apps/investigation/Makefile b/apps/investigation/Makefile
deleted file mode 100644
index b4ea1f97c89..00000000000
--- a/apps/investigation/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-.PHONY: generate
-generate:
- @grafana-app-sdk generate -g ./pkg/apis --kindgrouping=group --postprocess --crdencoding none
diff --git a/apps/investigation/kinds/cue.mod/module.cue b/apps/investigation/kinds/cue.mod/module.cue
deleted file mode 100644
index 80ce68f4833..00000000000
--- a/apps/investigation/kinds/cue.mod/module.cue
+++ /dev/null
@@ -1,2 +0,0 @@
-module: "github.com/grafana/grafana/apps/investigation/kinds"
-language: version: "v0.8.2"
diff --git a/apps/investigation/kinds/investigation.cue b/apps/investigation/kinds/investigation.cue
deleted file mode 100644
index 64d4b9b3105..00000000000
--- a/apps/investigation/kinds/investigation.cue
+++ /dev/null
@@ -1,96 +0,0 @@
-package kinds
-
-// This is our Investigation definition, which contains metadata about the kind, and the kind's schema
-investigation: {
- group: "investigation"
- kind: "Investigation"
- pluralName: "Investigations"
- scope: "Namespaced"
-
- apiResource: {
- groupOverride: "investigation.grafana.app"
- }
-
- codegen: {
- frontend: false
- backend: true
- }
-
- current: "v1alpha1"
- versions: {
- "v1alpha1": {
- version: "v1alpha1"
- schema: {
- // spec is the schema of our resource. The spec should include all the user-ediable information for the kind.
- spec: #InvestigationSpec
- }
- }
- }
-}
-
-#InvestigationSpec: {
- title: string
- status: "open" | "closed"
- items: [...#InvestigationItem]
-}
-
-// InvestigationItem is an item in an investigation.
-#InvestigationItem: {
- id: string
- title: string
- // type is the type of the item "timeseries", "heatmap", "log-table" (not an enum to allow for future extensions).
- type: string
- // url is the URL to the item.
- url: string
- // origin is where the item was created from.
- origin: string // "explore-metrics", "explore-logs", "explore-traces" (not an enum to allow for future extensions)
- // iconPath (optional) is the path to the icon for the item.
- iconPath?: string
- // timeRange (optional) is the time range of the item.
- timeRange: #AbsoluteTimeRange
- // note (optional) is a comment on the item.
- note?: [...#Comment]
- // queryType is the type of the query used to generate this item.
- queryType: "logs" | "metrics"
- // dataQuery contains the query used to generate this item.
- dataQuery: #DataQueryLogs | #DataQueryMetrics
-}
-
-// DataQueryLogs is a data query for logs.
-#DataQueryLogs: {
- // refId is the reference ID of the query.
- refId: string
- // datasource is the datasource of the query.
- datasource: #DatasourceRef
- // expr is the expression of the query.
- expr: string
- // maxLines (optional) is used to limit the number of log rows returned.
- maxLines?: int64
-}
-
-// DataQueryMetrics is a data query for metrics.
-#DataQueryMetrics: {
- refId: string
- datasource: #DatasourceRef
- expr: string
-}
-
-// Comment is a comment on an investigation item.
-#Comment: {
- authorUserID: string
- bodyMarkdown: string
-}
-
-// DatasourceRef is a reference to a datasource.
-#DatasourceRef: {
- uid: string
- type: string
- apiVersion: string
- name: string
-}
-
-// AbsoluteTimeRange is a time range specified by absolute timestamps.
-#AbsoluteTimeRange: {
- from: number
- to: number
-}
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_metadata_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_metadata_gen.go
deleted file mode 100644
index 8a8e44d35ac..00000000000
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_metadata_gen.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package v1alpha1
-
-import (
- "time"
-)
-
-// InvestigationMetadata defines model for InvestigationMetadata.
-type InvestigationMetadata struct {
- CreatedBy string `json:"createdBy"`
- CreationTimestamp time.Time `json:"creationTimestamp"`
- DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
- Finalizers []string `json:"finalizers"`
- Generation int64 `json:"generation"`
- Labels map[string]string `json:"labels"`
- ResourceVersion string `json:"resourceVersion"`
- Uid string `json:"uid"`
- UpdateTimestamp time.Time `json:"updateTimestamp"`
- UpdatedBy string `json:"updatedBy"`
-}
-
-// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
-// It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
-// As it is also intended to be generic enough to function with any API Server.
-type InvestigationKubeObjectMetadata struct {
- CreationTimestamp time.Time `json:"creationTimestamp"`
- DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
- Finalizers []string `json:"finalizers"`
- Generation int64 `json:"generation"`
- Labels map[string]string `json:"labels"`
- ResourceVersion string `json:"resourceVersion"`
- Uid string `json:"uid"`
-}
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_spec_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_spec_gen.go
deleted file mode 100644
index 1ed1173238b..00000000000
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_spec_gen.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package v1alpha1
-
-// Defines values for InvestigationInvestigationItemQueryType.
-const (
- InvestigationInvestigationItemQueryTypeLogs InvestigationInvestigationItemQueryType = "logs"
- InvestigationInvestigationItemQueryTypeMetrics InvestigationInvestigationItemQueryType = "metrics"
-)
-
-// Defines values for InvestigationSpecStatus.
-const (
- InvestigationSpecStatusClosed InvestigationSpecStatus = "closed"
- InvestigationSpecStatusOpen InvestigationSpecStatus = "open"
-)
-
-// AbsoluteTimeRange is a time range specified by absolute timestamps.
-// +k8s:openapi-gen=true
-type InvestigationAbsoluteTimeRange struct {
- From float32 `json:"from"`
- To float32 `json:"to"`
-}
-
-// Comment is a comment on an investigation item.
-// +k8s:openapi-gen=true
-type InvestigationComment struct {
- AuthorUserID string `json:"authorUserID"`
- BodyMarkdown string `json:"bodyMarkdown"`
-}
-
-// DataQueryLogs is a data query for logs.
-// +k8s:openapi-gen=true
-type InvestigationDataQueryLogs struct {
- // DatasourceRef is a reference to a datasource.
- Datasource InvestigationDatasourceRef `json:"datasource"`
-
- // expr is the expression of the query.
- Expr string `json:"expr"`
-
- // maxLines (optional) is used to limit the number of log rows returned.
- MaxLines *int64 `json:"maxLines,omitempty"`
-
- // refId is the reference ID of the query.
- RefId string `json:"refId"`
-}
-
-// DataQueryMetrics is a data query for metrics.
-// +k8s:openapi-gen=true
-type InvestigationDataQueryMetrics struct {
- // DatasourceRef is a reference to a datasource.
- Datasource InvestigationDatasourceRef `json:"datasource"`
- Expr string `json:"expr"`
- RefId string `json:"refId"`
-}
-
-// DatasourceRef is a reference to a datasource.
-// +k8s:openapi-gen=true
-type InvestigationDatasourceRef struct {
- ApiVersion string `json:"apiVersion"`
- Name string `json:"name"`
- Type string `json:"type"`
- Uid string `json:"uid"`
-}
-
-// InvestigationItem is an item in an investigation.
-// +k8s:openapi-gen=true
-type InvestigationInvestigationItem struct {
- // dataQuery contains the query used to generate this item.
- DataQuery interface{} `json:"dataQuery"`
-
- // iconPath (optional) is the path to the icon for the item.
- IconPath *string `json:"iconPath,omitempty"`
- Id string `json:"id"`
-
- // note (optional) is a comment on the item.
- Note []InvestigationComment `json:"note,omitempty"`
-
- // origin is where the item was created from.
- Origin string `json:"origin"`
-
- // queryType is the type of the query used to generate this item.
- QueryType InvestigationInvestigationItemQueryType `json:"queryType"`
-
- // AbsoluteTimeRange is a time range specified by absolute timestamps.
- TimeRange InvestigationAbsoluteTimeRange `json:"timeRange"`
- Title string `json:"title"`
-
- // type is the type of the item "timeseries", "heatmap", "log-table" (not an enum to allow for future extensions).
- Type string `json:"type"`
-
- // url is the URL to the item.
- Url string `json:"url"`
-}
-
-// InvestigationInvestigationItemQueryType queryType is the type of the query used to generate this item.
-// +k8s:openapi-gen=true
-type InvestigationInvestigationItemQueryType string
-
-// InvestigationSpec defines model for InvestigationSpec.
-// +k8s:openapi-gen=true
-type InvestigationSpec struct {
- Items []InvestigationInvestigationItem `json:"items"`
- Status InvestigationSpecStatus `json:"status"`
- Title string `json:"title"`
-}
-
-// InvestigationSpecStatus defines model for InvestigationSpec.Status.
-// +k8s:openapi-gen=true
-type InvestigationSpecStatus string
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_status_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_status_gen.go
deleted file mode 100644
index 3f0870f9c04..00000000000
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_status_gen.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package v1alpha1
-
-// Defines values for InvestigationOperatorStateState.
-const (
- InvestigationOperatorStateStateFailed InvestigationOperatorStateState = "failed"
- InvestigationOperatorStateStateInProgress InvestigationOperatorStateState = "in_progress"
- InvestigationOperatorStateStateSuccess InvestigationOperatorStateState = "success"
-)
-
-// Defines values for InvestigationstatusOperatorStateState.
-const (
- InvestigationstatusOperatorStateStateFailed InvestigationstatusOperatorStateState = "failed"
- InvestigationstatusOperatorStateStateInProgress InvestigationstatusOperatorStateState = "in_progress"
- InvestigationstatusOperatorStateStateSuccess InvestigationstatusOperatorStateState = "success"
-)
-
-// InvestigationOperatorState defines model for InvestigationOperatorState.
-// +k8s:openapi-gen=true
-type InvestigationOperatorState struct {
- // descriptiveState is an optional more descriptive state field which has no requirements on format
- DescriptiveState *string `json:"descriptiveState,omitempty"`
-
- // details contains any extra information that is operator-specific
- Details map[string]interface{} `json:"details,omitempty"`
-
- // lastEvaluation is the ResourceVersion last evaluated
- LastEvaluation string `json:"lastEvaluation"`
-
- // state describes the state of the lastEvaluation.
- // It is limited to three possible states for machine evaluation.
- State InvestigationOperatorStateState `json:"state"`
-}
-
-// InvestigationOperatorStateState state describes the state of the lastEvaluation.
-// It is limited to three possible states for machine evaluation.
-// +k8s:openapi-gen=true
-type InvestigationOperatorStateState string
-
-// InvestigationStatus defines model for InvestigationStatus.
-// +k8s:openapi-gen=true
-type InvestigationStatus struct {
- // additionalFields is reserved for future use
- AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
-
- // operatorStates is a map of operator ID to operator state evaluations.
- // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
- OperatorStates map[string]InvestigationstatusOperatorState `json:"operatorStates,omitempty"`
-}
-
-// InvestigationstatusOperatorState defines model for Investigationstatus.#OperatorState.
-// +k8s:openapi-gen=true
-type InvestigationstatusOperatorState struct {
- // descriptiveState is an optional more descriptive state field which has no requirements on format
- DescriptiveState *string `json:"descriptiveState,omitempty"`
-
- // details contains any extra information that is operator-specific
- Details map[string]interface{} `json:"details,omitempty"`
-
- // lastEvaluation is the ResourceVersion last evaluated
- LastEvaluation string `json:"lastEvaluation"`
-
- // state describes the state of the lastEvaluation.
- // It is limited to three possible states for machine evaluation.
- State InvestigationstatusOperatorStateState `json:"state"`
-}
-
-// InvestigationstatusOperatorStateState state describes the state of the lastEvaluation.
-// It is limited to three possible states for machine evaluation.
-// +k8s:openapi-gen=true
-type InvestigationstatusOperatorStateState string
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/zz_openapi_gen.go b/apps/investigation/pkg/apis/investigation/v1alpha1/zz_openapi_gen.go
deleted file mode 100644
index 356c42c01fc..00000000000
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/zz_openapi_gen.go
+++ /dev/null
@@ -1,593 +0,0 @@
-//go:build !ignore_autogenerated
-// +build !ignore_autogenerated
-
-// Code generated by grafana-app-sdk. DO NOT EDIT.
-
-package v1alpha1
-
-import (
- common "k8s.io/kube-openapi/pkg/common"
- spec "k8s.io/kube-openapi/pkg/validation/spec"
-)
-
-func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
- return map[string]common.OpenAPIDefinition{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation": schema_pkg_apis_investigation_v1alpha1_Investigation(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange": schema_pkg_apis_investigation_v1alpha1_InvestigationAbsoluteTimeRange(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment": schema_pkg_apis_investigation_v1alpha1_InvestigationComment(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDataQueryLogs": schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryLogs(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDataQueryMetrics": schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryMetrics(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef": schema_pkg_apis_investigation_v1alpha1_InvestigationDatasourceRef(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem": schema_pkg_apis_investigation_v1alpha1_InvestigationInvestigationItem(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationList": schema_pkg_apis_investigation_v1alpha1_InvestigationList(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationOperatorState": schema_pkg_apis_investigation_v1alpha1_InvestigationOperatorState(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec": schema_pkg_apis_investigation_v1alpha1_InvestigationSpec(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus": schema_pkg_apis_investigation_v1alpha1_InvestigationStatus(ref),
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState": schema_pkg_apis_investigation_v1alpha1_InvestigationstatusOperatorState(ref),
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_Investigation(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "kind": {
- SchemaProps: spec.SchemaProps{
- Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
- Type: []string{"string"},
- Format: "",
- },
- },
- "apiVersion": {
- SchemaProps: spec.SchemaProps{
- Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
- Type: []string{"string"},
- Format: "",
- },
- },
- "metadata": {
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
- },
- },
- "spec": {
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec"),
- },
- },
- "status": {
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus"),
- },
- },
- },
- Required: []string{"metadata", "spec", "status"},
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec", "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationAbsoluteTimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "AbsoluteTimeRange is a time range specified by absolute timestamps.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "from": {
- SchemaProps: spec.SchemaProps{
- Default: 0,
- Type: []string{"number"},
- Format: "float",
- },
- },
- "to": {
- SchemaProps: spec.SchemaProps{
- Default: 0,
- Type: []string{"number"},
- Format: "float",
- },
- },
- },
- Required: []string{"from", "to"},
- },
- },
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationComment(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "Comment is a comment on an investigation item.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "authorUserID": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "bodyMarkdown": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"authorUserID", "bodyMarkdown"},
- },
- },
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryLogs(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "DataQueryLogs is a data query for logs.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "datasource": {
- SchemaProps: spec.SchemaProps{
- Description: "DatasourceRef is a reference to a datasource.",
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"),
- },
- },
- "expr": {
- SchemaProps: spec.SchemaProps{
- Description: "expr is the expression of the query.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "maxLines": {
- SchemaProps: spec.SchemaProps{
- Description: "maxLines (optional) is used to limit the number of log rows returned.",
- Type: []string{"integer"},
- Format: "int64",
- },
- },
- "refId": {
- SchemaProps: spec.SchemaProps{
- Description: "refId is the reference ID of the query.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"datasource", "expr", "refId"},
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryMetrics(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "DataQueryMetrics is a data query for metrics.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "datasource": {
- SchemaProps: spec.SchemaProps{
- Description: "DatasourceRef is a reference to a datasource.",
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"),
- },
- },
- "expr": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "refId": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"datasource", "expr", "refId"},
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationDatasourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "DatasourceRef is a reference to a datasource.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "apiVersion": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "name": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "type": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "uid": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"apiVersion", "name", "type", "uid"},
- },
- },
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationInvestigationItem(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "InvestigationItem is an item in an investigation.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "dataQuery": {
- SchemaProps: spec.SchemaProps{
- Description: "dataQuery contains the query used to generate this item.",
- Type: []string{"object"},
- Format: "",
- },
- },
- "iconPath": {
- SchemaProps: spec.SchemaProps{
- Description: "iconPath (optional) is the path to the icon for the item.",
- Type: []string{"string"},
- Format: "",
- },
- },
- "id": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "note": {
- SchemaProps: spec.SchemaProps{
- Description: "note (optional) is a comment on the item.",
- Type: []string{"array"},
- Items: &spec.SchemaOrArray{
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment"),
- },
- },
- },
- },
- },
- "origin": {
- SchemaProps: spec.SchemaProps{
- Description: "origin is where the item was created from.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "queryType": {
- SchemaProps: spec.SchemaProps{
- Description: "queryType is the type of the query used to generate this item.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "timeRange": {
- SchemaProps: spec.SchemaProps{
- Description: "AbsoluteTimeRange is a time range specified by absolute timestamps.",
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange"),
- },
- },
- "title": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "type": {
- SchemaProps: spec.SchemaProps{
- Description: "type is the type of the item \"timeseries\", \"heatmap\", \"log-table\" (not an enum to allow for future extensions).",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "url": {
- SchemaProps: spec.SchemaProps{
- Description: "url is the URL to the item.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"dataQuery", "id", "origin", "queryType", "timeRange", "title", "type", "url"},
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange", "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationList(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "kind": {
- SchemaProps: spec.SchemaProps{
- Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
- Type: []string{"string"},
- Format: "",
- },
- },
- "apiVersion": {
- SchemaProps: spec.SchemaProps{
- Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
- Type: []string{"string"},
- Format: "",
- },
- },
- "metadata": {
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
- },
- },
- "items": {
- SchemaProps: spec.SchemaProps{
- Type: []string{"array"},
- Items: &spec.SchemaOrArray{
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation"),
- },
- },
- },
- },
- },
- },
- Required: []string{"metadata", "items"},
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "InvestigationOperatorState defines model for InvestigationOperatorState.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "descriptiveState": {
- SchemaProps: spec.SchemaProps{
- Description: "descriptiveState is an optional more descriptive state field which has no requirements on format",
- Type: []string{"string"},
- Format: "",
- },
- },
- "details": {
- SchemaProps: spec.SchemaProps{
- Description: "details contains any extra information that is operator-specific",
- Type: []string{"object"},
- AdditionalProperties: &spec.SchemaOrBool{
- Allows: true,
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Format: "",
- },
- },
- },
- },
- },
- "lastEvaluation": {
- SchemaProps: spec.SchemaProps{
- Description: "lastEvaluation is the ResourceVersion last evaluated",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "state": {
- SchemaProps: spec.SchemaProps{
- Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"lastEvaluation", "state"},
- },
- },
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "InvestigationSpec defines model for InvestigationSpec.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "items": {
- SchemaProps: spec.SchemaProps{
- Type: []string{"array"},
- Items: &spec.SchemaOrArray{
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem"),
- },
- },
- },
- },
- },
- "status": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "title": {
- SchemaProps: spec.SchemaProps{
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"items", "status", "title"},
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "InvestigationStatus defines model for InvestigationStatus.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "additionalFields": {
- SchemaProps: spec.SchemaProps{
- Description: "additionalFields is reserved for future use",
- Type: []string{"object"},
- AdditionalProperties: &spec.SchemaOrBool{
- Allows: true,
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Format: "",
- },
- },
- },
- },
- },
- "operatorStates": {
- SchemaProps: spec.SchemaProps{
- Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.",
- Type: []string{"object"},
- AdditionalProperties: &spec.SchemaOrBool{
- Allows: true,
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Default: map[string]interface{}{},
- Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState"),
- },
- },
- },
- },
- },
- },
- },
- },
- Dependencies: []string{
- "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState"},
- }
-}
-
-func schema_pkg_apis_investigation_v1alpha1_InvestigationstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
- return common.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Description: "InvestigationstatusOperatorState defines model for Investigationstatus.#OperatorState.",
- Type: []string{"object"},
- Properties: map[string]spec.Schema{
- "descriptiveState": {
- SchemaProps: spec.SchemaProps{
- Description: "descriptiveState is an optional more descriptive state field which has no requirements on format",
- Type: []string{"string"},
- Format: "",
- },
- },
- "details": {
- SchemaProps: spec.SchemaProps{
- Description: "details contains any extra information that is operator-specific",
- Type: []string{"object"},
- AdditionalProperties: &spec.SchemaOrBool{
- Allows: true,
- Schema: &spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"object"},
- Format: "",
- },
- },
- },
- },
- },
- "lastEvaluation": {
- SchemaProps: spec.SchemaProps{
- Description: "lastEvaluation is the ResourceVersion last evaluated",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- "state": {
- SchemaProps: spec.SchemaProps{
- Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.",
- Default: "",
- Type: []string{"string"},
- Format: "",
- },
- },
- },
- Required: []string{"lastEvaluation", "state"},
- },
- },
- }
-}
diff --git a/apps/investigation/pkg/apis/manifest.go b/apps/investigation/pkg/apis/manifest.go
deleted file mode 100644
index 2812059e14a..00000000000
--- a/apps/investigation/pkg/apis/manifest.go
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// This file is generated by grafana-app-sdk
-// DO NOT EDIT
-//
-
-package apis
-
-import (
- "encoding/json"
-
- "github.com/grafana/grafana-app-sdk/app"
-)
-
-var (
- rawSchemaInvestigationv1alpha1 = []byte(`{"spec":{"description":"spec is the schema of our resource. The spec should include all the user-ediable information for the kind.","properties":{"items":{"items":{"properties":{"dataQuery":{"description":"dataQuery contains the query used to generate this item.","oneOf":[{"allOf":[{"required":["refId","datasource","expr"]},{"not":{"anyOf":[{"required":["refId","datasource","expr"]}]}}]},{"allOf":[{"required":["refId","datasource","expr"]},{"not":{"anyOf":[{"required":["refId","datasource","expr"]}]}}]}],"properties":{"datasource":{"description":"datasource is the datasource of the query.","properties":{"apiVersion":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"uid":{"type":"string"}},"required":["uid","type","apiVersion","name"],"type":"object"},"expr":{"description":"expr is the expression of the query.","type":"string"},"maxLines":{"description":"maxLines (optional) is used to limit the number of log rows returned.","format":"int64","type":"integer"},"refId":{"description":"refId is the reference ID of the query.","type":"string"}},"type":"object"},"iconPath":{"description":"iconPath (optional) is the path to the icon for the item.","type":"string"},"id":{"type":"string"},"note":{"description":"note (optional) is a comment on the item.","items":{"properties":{"authorUserID":{"type":"string"},"bodyMarkdown":{"type":"string"}},"required":["authorUserID","bodyMarkdown"],"type":"object"},"type":"array"},"origin":{"description":"origin is where the item was created from.","type":"string"},"queryType":{"description":"queryType is the type of the query used to generate this item.","enum":["logs","metrics"],"type":"string"},"timeRange":{"description":"timeRange (optional) is the time range of the item.","properties":{"from":{"type":"number"},"to":{"type":"number"}},"required":["from","to"],"type":"object"},"title":{"type":"string"},"type":{"description":"type is the type of the item \"timeseries\", \"heatmap\", \"log-table\" (not an enum to allow for future extensions).","type":"string"},"url":{"description":"url is the URL to the item.","type":"string"}},"required":["id","title","type","url","origin","timeRange","queryType","dataQuery"],"type":"object"},"type":"array"},"status":{"enum":["open","closed"],"type":"string"},"title":{"type":"string"}},"required":["title","status","items"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
- versionSchemaInvestigationv1alpha1 app.VersionSchema
- _ = json.Unmarshal(rawSchemaInvestigationv1alpha1, &versionSchemaInvestigationv1alpha1)
-)
-
-var appManifestData = app.ManifestData{
- AppName: "investigation",
- Group: "investigation.grafana.app",
- Kinds: []app.ManifestKind{
- {
- Kind: "Investigation",
- Scope: "Namespaced",
- Conversion: false,
- Versions: []app.ManifestKindVersion{
- {
- Name: "v1alpha1",
- Schema: &versionSchemaInvestigationv1alpha1,
- },
- },
- },
- },
-}
-
-func jsonToMap(j string) map[string]any {
- m := make(map[string]any)
- json.Unmarshal([]byte(j), &j)
- return m
-}
-
-func LocalManifest() app.Manifest {
- return app.NewEmbeddedManifest(appManifestData)
-}
-
-func RemoteManifest() app.Manifest {
- return app.NewAPIServerManifest("investigation")
-}
diff --git a/apps/investigations/Makefile b/apps/investigations/Makefile
new file mode 100644
index 00000000000..7a5f1c99365
--- /dev/null
+++ b/apps/investigations/Makefile
@@ -0,0 +1,3 @@
+.PHONY: generate
+generate:
+ @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none
diff --git a/apps/investigations/example.json b/apps/investigations/example.json
new file mode 100644
index 00000000000..0a390fb074f
--- /dev/null
+++ b/apps/investigations/example.json
@@ -0,0 +1,152 @@
+[
+ {
+ "id": "896312ce-65b0-4b50-ade1-e7f04fa22c66",
+ "title": "Thursday morning investigation",
+ "hasCustomName": false,
+ "isFavorite": false,
+ "collectables": [
+ {
+ "origin": "Explore Logs",
+ "type": "timeseries",
+ "queries": [
+ {
+ "refId": "LABEL_BREAKDOWN_VALUES",
+ "queryType": "range",
+ "editorMode": "code",
+ "supportingQueryType": "grafana-lokiexplore-app",
+ "legendFormat": "{{detected_level}}",
+ "expr": "sum(count_over_time({service_name=\"web_app_1\"} | detected_level != \"\"[$__auto])) by (detected_level)"
+ }
+ ],
+ "timeRange": {
+ "to": "2025-02-13T11:31:20.536Z",
+ "from": "2025-02-13T11:16:20.536Z",
+ "raw": {
+ "from": "now-15m",
+ "to": "now"
+ }
+ },
+ "datasource": {
+ "uid": "fe9k7u07b1a0wc"
+ },
+ "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
+ "id": "LABEL_BREAKDOWN_VALUES_detected_level",
+ "title": "detected_level",
+ "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
+ "createdAt": "2025-02-13T11:31:23.637Z"
+ }
+ ],
+ "createdAt": "2025-02-13T11:31:23.636Z",
+ "updatedAt": "2025-02-13T11:31:23.637Z",
+ "viewMode": {
+ "mode": "compact",
+ "showComments": true,
+ "showTooltips": false
+ }
+ },
+ {
+ "id": "e9cf1958-d0ed-46b7-b597-9052c7648656",
+ "title": "Thursday morning investigation",
+ "hasCustomName": false,
+ "isFavorite": false,
+ "collectables": [
+ {
+ "origin": "Explore Logs",
+ "type": "timeseries",
+ "queries": [
+ {
+ "refId": "LABEL_BREAKDOWN_VALUES",
+ "queryType": "range",
+ "editorMode": "code",
+ "supportingQueryType": "grafana-lokiexplore-app",
+ "legendFormat": "{{detected_level}}",
+ "expr": "sum(count_over_time({service_name=\"web_app_1\"} | detected_level != \"\"[$__auto])) by (detected_level)"
+ }
+ ],
+ "timeRange": {
+ "to": "2025-02-13T11:31:20.536Z",
+ "from": "2025-02-13T11:16:20.536Z",
+ "raw": {
+ "from": "now-15m",
+ "to": "now"
+ }
+ },
+ "datasource": {
+ "uid": "fe9k7u07b1a0wc"
+ },
+ "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
+ "id": "LABEL_BREAKDOWN_VALUES_detected_level",
+ "title": "detected_level",
+ "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
+ "createdAt": "2025-02-13T11:31:23.638Z"
+ },
+ {
+ "origin": "Explore Logs",
+ "type": "timeseries",
+ "queries": [
+ {
+ "refId": "LABEL_BREAKDOWN_VALUES",
+ "queryType": "range",
+ "editorMode": "code",
+ "supportingQueryType": "grafana-lokiexplore-app",
+ "legendFormat": "{{service_name}}",
+ "expr": "sum(count_over_time({service_name=\"web_app_1\",service_name != \"\"} [$__auto])) by (service_name)"
+ }
+ ],
+ "timeRange": {
+ "to": "2025-02-13T11:31:20.536Z",
+ "from": "2025-02-13T11:16:20.536Z",
+ "raw": {
+ "from": "now-15m",
+ "to": "now"
+ }
+ },
+ "datasource": {
+ "uid": "fe9k7u07b1a0wc"
+ },
+ "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
+ "id": "LABEL_BREAKDOWN_VALUES_service_name",
+ "title": "service_name",
+ "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
+ "createdAt": "2025-02-13T11:31:41.507Z"
+ },
+ {
+ "origin": "Explore Logs",
+ "type": "timeseries",
+ "queries": [
+ {
+ "refId": "LABEL_BREAKDOWN_VALUES",
+ "queryType": "range",
+ "editorMode": "code",
+ "supportingQueryType": "grafana-lokiexplore-app",
+ "legendFormat": "{{service}}",
+ "expr": "sum(count_over_time({service_name=\"web_app_1\",service != \"\"} [$__auto])) by (service)"
+ }
+ ],
+ "timeRange": {
+ "to": "2025-02-13T11:31:20.536Z",
+ "from": "2025-02-13T11:16:20.536Z",
+ "raw": {
+ "from": "now-15m",
+ "to": "now"
+ }
+ },
+ "datasource": {
+ "uid": "fe9k7u07b1a0wc"
+ },
+ "url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
+ "id": "LABEL_BREAKDOWN_VALUES_service",
+ "title": "service",
+ "logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
+ "createdAt": "2025-02-13T11:31:43.698Z"
+ }
+ ],
+ "createdAt": "2025-02-13T11:31:23.637Z",
+ "updatedAt": "2025-02-13T11:31:43.698Z",
+ "viewMode": {
+ "mode": "compact",
+ "showComments": true,
+ "showTooltips": false
+ }
+ }
+]
diff --git a/apps/investigation/go.mod b/apps/investigations/go.mod
similarity index 67%
rename from apps/investigation/go.mod
rename to apps/investigations/go.mod
index de0d3fa2621..12da166af27 100644
--- a/apps/investigation/go.mod
+++ b/apps/investigations/go.mod
@@ -1,10 +1,10 @@
-module github.com/grafana/grafana/apps/investigation
+module github.com/grafana/grafana/apps/investigations
go 1.23.4
require (
- github.com/grafana/grafana-app-sdk v0.30.0
- k8s.io/apimachinery v0.32.0
+ github.com/grafana/grafana-app-sdk v0.31.0
+ k8s.io/apimachinery v0.32.1
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
)
@@ -18,7 +18,7 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
- github.com/getkin/kin-openapi v0.128.0 // indirect
+ github.com/getkin/kin-openapi v0.129.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
@@ -31,11 +31,10 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect
+ github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
@@ -44,40 +43,42 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
+ github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.61.0 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/otel v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
- go.opentelemetry.io/otel/metric v1.33.0 // indirect
- go.opentelemetry.io/otel/sdk v1.33.0 // indirect
- go.opentelemetry.io/otel/trace v1.33.0 // indirect
+ go.opentelemetry.io/otel v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.34.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
- golang.org/x/net v0.34.0 // indirect
- golang.org/x/oauth2 v0.25.0 // indirect
- golang.org/x/sync v0.10.0 // indirect
- golang.org/x/sys v0.29.0 // indirect
- golang.org/x/term v0.28.0 // indirect
- golang.org/x/text v0.21.0 // indirect
+ golang.org/x/net v0.35.0 // indirect
+ golang.org/x/oauth2 v0.27.0 // indirect
+ golang.org/x/sync v0.11.0 // indirect
+ golang.org/x/sys v0.30.0 // indirect
+ golang.org/x/term v0.29.0 // indirect
+ golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.9.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
- google.golang.org/grpc v1.69.4 // indirect
- google.golang.org/protobuf v1.36.1 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+ google.golang.org/grpc v1.70.0 // indirect
+ google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/api v0.32.0 // indirect
- k8s.io/apiextensions-apiserver v0.32.0 // indirect
- k8s.io/client-go v0.32.0 // indirect
+ k8s.io/api v0.32.1 // indirect
+ k8s.io/apiextensions-apiserver v0.32.1 // indirect
+ k8s.io/client-go v0.32.1 // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
diff --git a/apps/investigation/go.sum b/apps/investigations/go.sum
similarity index 77%
rename from apps/investigation/go.sum
rename to apps/investigations/go.sum
index 858c73dd4b4..d3caa3d08e3 100644
--- a/apps/investigation/go.sum
+++ b/apps/investigations/go.sum
@@ -16,8 +16,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
-github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
+github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30=
+github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -49,10 +49,10 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw=
-github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk=
-github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
-github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
+github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk=
+github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs=
+github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME=
+github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -60,8 +60,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
-github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -87,6 +85,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU=
+github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8=
+github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc=
+github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
@@ -102,8 +104,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
@@ -124,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -153,26 +155,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
-golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
-golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
-golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
-golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
-golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -187,14 +189,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
-google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -204,14 +206,14 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
-k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
-k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
-k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
-k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
-k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
-k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
+k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
+k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
+k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
+k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
+k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
+k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
+k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
diff --git a/apps/investigations/kinds/collectable.cue b/apps/investigations/kinds/collectable.cue
new file mode 100644
index 00000000000..6844c0a8cae
--- /dev/null
+++ b/apps/investigations/kinds/collectable.cue
@@ -0,0 +1,51 @@
+package investigations
+
+// Collectable represents an item collected during investigation
+#Collectable: {
+ id: string
+ createdAt: string
+
+ title: string
+ origin: string
+ type: string
+ queries: [...#Query] // +listType=atomic
+ timeRange: #TimeRange
+ datasource: #DatasourceRef
+ url: string
+ logoPath?: string
+
+ note: string
+ noteUpdatedAt: string
+}
+
+#CollectableSummary: {
+ id: string
+ title: string
+ logoPath: string
+ origin: string
+}
+
+// Query represents a data query
+#Query: {
+ refId: string
+ queryType: string
+ editorMode: string
+ supportingQueryType: string
+ legendFormat: string
+ expr: string
+}
+
+// TimeRange represents a time range with both absolute and relative values
+#TimeRange: {
+ from: string
+ to: string
+ raw: {
+ from: string
+ to: string
+ }
+}
+
+// DatasourceRef is a reference to a datasource
+#DatasourceRef: {
+ uid: string
+}
diff --git a/apps/investigations/kinds/cue.mod/module.cue b/apps/investigations/kinds/cue.mod/module.cue
new file mode 100644
index 00000000000..55fe474ed5f
--- /dev/null
+++ b/apps/investigations/kinds/cue.mod/module.cue
@@ -0,0 +1 @@
+module: "github.com/grafana/grafana/apps/investigations"
diff --git a/apps/investigations/kinds/investigation.cue b/apps/investigations/kinds/investigation.cue
new file mode 100644
index 00000000000..f1e9a69027f
--- /dev/null
+++ b/apps/investigations/kinds/investigation.cue
@@ -0,0 +1,64 @@
+package investigations
+
+// This is our Investigation definition, which contains metadata about the kind, and the kind's schema
+investigation: {
+ kind: "Investigation"
+ group: "investigations.grafana.app"
+ apiResource: {
+ groupOverride: "investigations.grafana.app"
+ }
+ pluralName: "Investigations"
+ current: "v0alpha1"
+ versions: {
+ "v0alpha1": {
+ codegen: {
+ frontend: true
+ backend: true
+ options: {
+ generateObjectMeta: true
+ generateClient: true
+ k8sLike: true
+ package: "github.com/grafana/grafana/apps/investigations"
+ }
+ }
+ schema: {
+ // spec is the schema of our resource
+ spec: {
+ title: string
+ createdByProfile: #Person
+ hasCustomName: bool
+ isFavorite: bool
+ overviewNote: string
+ overviewNoteUpdatedAt: string
+ collectables: [...#Collectable] // +listType=atomic
+ viewMode: #ViewMode
+ }
+ }
+ }
+ }
+}
+
+// Type definition for investigation summaries
+#InvestigationSummary: {
+ title: string
+ createdByProfile: #Person
+ hasCustomName: bool
+ isFavorite: bool
+ overviewNote: string
+ overviewNoteUpdatedAt: string
+ viewMode: #ViewMode
+ collectableSummaries: [...#CollectableSummary] // +listType=atomic
+}
+
+// Person represents a user profile with basic information
+#Person: {
+ uid: string // Unique identifier for the user
+ name: string // Display name of the user
+ gravatarUrl: string // URL to user's Gravatar image
+}
+
+#ViewMode: {
+ mode: "compact" | "full"
+ showComments: bool
+ showTooltips: bool
+}
diff --git a/apps/investigations/kinds/investigationindex.cue b/apps/investigations/kinds/investigationindex.cue
new file mode 100644
index 00000000000..447e156be4f
--- /dev/null
+++ b/apps/investigations/kinds/investigationindex.cue
@@ -0,0 +1,37 @@
+package investigations
+
+investigationIndex: {
+ kind: "InvestigationIndex"
+ group: "investigations.grafana.app"
+ apiResource: {
+ groupOverride: "investigations.grafana.app"
+ }
+ pluralName: "InvestigationIndexes"
+ current: "v0alpha1"
+ versions: {
+ "v0alpha1": {
+ codegen: {
+ frontend: true
+ backend: true
+ options: {
+ generateObjectMeta: true
+ generateClient: true
+ k8sLike: true
+ package: "github.com/grafana/grafana/apps/investigations"
+ }
+ }
+ schema: {
+ spec: {
+ // Title of the index, e.g. 'Favorites' or 'My Investigations'
+ title: string
+
+ // The Person who owns this investigation index
+ owner: #Person
+
+ // Array of investigation summaries
+ investigationSummaries: [...#InvestigationSummary] // +listType=atomic
+ }
+ }
+ }
+ }
+}
diff --git a/apps/investigations/kinds/manifest.cue b/apps/investigations/kinds/manifest.cue
new file mode 100644
index 00000000000..d9e93b00061
--- /dev/null
+++ b/apps/investigations/kinds/manifest.cue
@@ -0,0 +1,10 @@
+package investigations
+
+manifest: {
+ appName: "investigations"
+ groupOverride: "investigations.grafana.app"
+ kinds: [
+ investigation,
+ investigationIndex,
+ ]
+}
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/constants.go b/apps/investigations/pkg/apis/investigations/v0alpha1/constants.go
new file mode 100644
index 00000000000..288c15885ce
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/constants.go
@@ -0,0 +1,18 @@
+package v0alpha1
+
+import "k8s.io/apimachinery/pkg/runtime/schema"
+
+const (
+ // Group is the API group used by all kinds in this package
+ Group = "investigations.grafana.app"
+ // Version is the API version used by all kinds in this package
+ Version = "v0alpha1"
+)
+
+var (
+ // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package
+ GroupVersion = schema.GroupVersion{
+ Group: Group,
+ Version: Version,
+ }
+)
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_codec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_codec_gen.go
similarity index 97%
rename from apps/investigation/pkg/apis/investigation/v1alpha1/investigation_codec_gen.go
rename to apps/investigations/pkg/apis/investigations/v0alpha1/investigation_codec_gen.go
index 73e2232d045..6bde81b50d3 100644
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_codec_gen.go
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_codec_gen.go
@@ -2,7 +2,7 @@
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
-package v1alpha1
+package v0alpha1
import (
"encoding/json"
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_metadata_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_metadata_gen.go
new file mode 100644
index 00000000000..e0d2756f194
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_metadata_gen.go
@@ -0,0 +1,28 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+import (
+ time "time"
+)
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+type InvestigationMetadata struct {
+ UpdateTimestamp time.Time `json:"updateTimestamp"`
+ CreatedBy string `json:"createdBy"`
+ Uid string `json:"uid"`
+ CreationTimestamp time.Time `json:"creationTimestamp"`
+ DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
+ Finalizers []string `json:"finalizers"`
+ ResourceVersion string `json:"resourceVersion"`
+ Generation int64 `json:"generation"`
+ UpdatedBy string `json:"updatedBy"`
+ Labels map[string]string `json:"labels"`
+}
+
+// NewInvestigationMetadata creates a new InvestigationMetadata object.
+func NewInvestigationMetadata() *InvestigationMetadata {
+ return &InvestigationMetadata{}
+}
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_object_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_object_gen.go
similarity index 94%
rename from apps/investigation/pkg/apis/investigation/v1alpha1/investigation_object_gen.go
rename to apps/investigations/pkg/apis/investigations/v0alpha1/investigation_object_gen.go
index c97fc75eec1..21c8d760fe6 100644
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_object_gen.go
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_object_gen.go
@@ -2,7 +2,7 @@
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
-package v1alpha1
+package v0alpha1
import (
"fmt"
@@ -16,10 +16,10 @@ import (
// +k8s:openapi-gen=true
type Investigation struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata"`
- Spec InvestigationSpec `json:"spec"`
- InvestigationStatus InvestigationStatus `json:"status"`
+ metav1.TypeMeta `json:",inline" yaml:",inline"`
+ metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
+ Spec InvestigationSpec `json:"spec" yaml:"spec"`
+ InvestigationStatus InvestigationStatus `json:"status" yaml:"status"`
}
func (o *Investigation) GetSpec() any {
@@ -224,9 +224,9 @@ var _ resource.Object = &Investigation{}
// +k8s:openapi-gen=true
type InvestigationList struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata"`
- Items []Investigation `json:"items"`
+ metav1.TypeMeta `json:",inline" yaml:",inline"`
+ metav1.ListMeta `json:"metadata" yaml:"metadata"`
+ Items []Investigation `json:"items" yaml:"items"`
}
func (o *InvestigationList) DeepCopyObject() runtime.Object {
diff --git a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_schema_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_schema_gen.go
similarity index 81%
rename from apps/investigation/pkg/apis/investigation/v1alpha1/investigation_schema_gen.go
rename to apps/investigations/pkg/apis/investigations/v0alpha1/investigation_schema_gen.go
index c20995f0981..2a1f03c884a 100644
--- a/apps/investigation/pkg/apis/investigation/v1alpha1/investigation_schema_gen.go
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_schema_gen.go
@@ -2,7 +2,7 @@
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
-package v1alpha1
+package v0alpha1
import (
"github.com/grafana/grafana-app-sdk/resource"
@@ -10,7 +10,7 @@ import (
// schema is unexported to prevent accidental overwrites
var (
- schemaInvestigation = resource.NewSimpleSchema("investigation.grafana.app", "v1alpha1", &Investigation{}, &InvestigationList{}, resource.WithKind("Investigation"),
+ schemaInvestigation = resource.NewSimpleSchema("investigations.grafana.app", "v0alpha1", &Investigation{}, &InvestigationList{}, resource.WithKind("Investigation"),
resource.WithPlural("investigations"), resource.WithScope(resource.NamespacedScope))
kindInvestigation = resource.Kind{
Schema: schemaInvestigation,
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_spec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_spec_gen.go
new file mode 100644
index 00000000000..3bca90e4579
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_spec_gen.go
@@ -0,0 +1,140 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+// Person represents a user profile with basic information
+// +k8s:openapi-gen=true
+type InvestigationPerson struct {
+ // Unique identifier for the user
+ Uid string `json:"uid"`
+ // Display name of the user
+ Name string `json:"name"`
+ // URL to user's Gravatar image
+ GravatarUrl string `json:"gravatarUrl"`
+}
+
+// NewInvestigationPerson creates a new InvestigationPerson object.
+func NewInvestigationPerson() *InvestigationPerson {
+ return &InvestigationPerson{}
+}
+
+// Collectable represents an item collected during investigation
+// +k8s:openapi-gen=true
+type InvestigationCollectable struct {
+ Id string `json:"id"`
+ CreatedAt string `json:"createdAt"`
+ Title string `json:"title"`
+ Origin string `json:"origin"`
+ Type string `json:"type"`
+ // +listType=atomic
+ Queries []InvestigationQuery `json:"queries"`
+ TimeRange InvestigationTimeRange `json:"timeRange"`
+ Datasource InvestigationDatasourceRef `json:"datasource"`
+ Url string `json:"url"`
+ LogoPath *string `json:"logoPath,omitempty"`
+ Note string `json:"note"`
+ NoteUpdatedAt string `json:"noteUpdatedAt"`
+}
+
+// NewInvestigationCollectable creates a new InvestigationCollectable object.
+func NewInvestigationCollectable() *InvestigationCollectable {
+ return &InvestigationCollectable{
+ TimeRange: *NewInvestigationTimeRange(),
+ Datasource: *NewInvestigationDatasourceRef(),
+ }
+}
+
+// Query represents a data query
+// +k8s:openapi-gen=true
+type InvestigationQuery struct {
+ RefId string `json:"refId"`
+ QueryType string `json:"queryType"`
+ EditorMode string `json:"editorMode"`
+ SupportingQueryType string `json:"supportingQueryType"`
+ LegendFormat string `json:"legendFormat"`
+ Expr string `json:"expr"`
+}
+
+// NewInvestigationQuery creates a new InvestigationQuery object.
+func NewInvestigationQuery() *InvestigationQuery {
+ return &InvestigationQuery{}
+}
+
+// TimeRange represents a time range with both absolute and relative values
+// +k8s:openapi-gen=true
+type InvestigationTimeRange struct {
+ From string `json:"from"`
+ To string `json:"to"`
+ Raw InvestigationV0alpha1TimeRangeRaw `json:"raw"`
+}
+
+// NewInvestigationTimeRange creates a new InvestigationTimeRange object.
+func NewInvestigationTimeRange() *InvestigationTimeRange {
+ return &InvestigationTimeRange{
+ Raw: *NewInvestigationV0alpha1TimeRangeRaw(),
+ }
+}
+
+// DatasourceRef is a reference to a datasource
+// +k8s:openapi-gen=true
+type InvestigationDatasourceRef struct {
+ Uid string `json:"uid"`
+}
+
+// NewInvestigationDatasourceRef creates a new InvestigationDatasourceRef object.
+func NewInvestigationDatasourceRef() *InvestigationDatasourceRef {
+ return &InvestigationDatasourceRef{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationViewMode struct {
+ Mode InvestigationViewModeMode `json:"mode"`
+ ShowComments bool `json:"showComments"`
+ ShowTooltips bool `json:"showTooltips"`
+}
+
+// NewInvestigationViewMode creates a new InvestigationViewMode object.
+func NewInvestigationViewMode() *InvestigationViewMode {
+ return &InvestigationViewMode{}
+}
+
+// spec is the schema of our resource
+// +k8s:openapi-gen=true
+type InvestigationSpec struct {
+ Title string `json:"title"`
+ CreatedByProfile InvestigationPerson `json:"createdByProfile"`
+ HasCustomName bool `json:"hasCustomName"`
+ IsFavorite bool `json:"isFavorite"`
+ OverviewNote string `json:"overviewNote"`
+ OverviewNoteUpdatedAt string `json:"overviewNoteUpdatedAt"`
+ // +listType=atomic
+ Collectables []InvestigationCollectable `json:"collectables"`
+ ViewMode InvestigationViewMode `json:"viewMode"`
+}
+
+// NewInvestigationSpec creates a new InvestigationSpec object.
+func NewInvestigationSpec() *InvestigationSpec {
+ return &InvestigationSpec{
+ CreatedByProfile: *NewInvestigationPerson(),
+ ViewMode: *NewInvestigationViewMode(),
+ }
+}
+
+// +k8s:openapi-gen=true
+type InvestigationV0alpha1TimeRangeRaw struct {
+ From string `json:"from"`
+ To string `json:"to"`
+}
+
+// NewInvestigationV0alpha1TimeRangeRaw creates a new InvestigationV0alpha1TimeRangeRaw object.
+func NewInvestigationV0alpha1TimeRangeRaw() *InvestigationV0alpha1TimeRangeRaw {
+ return &InvestigationV0alpha1TimeRangeRaw{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationViewModeMode string
+
+const (
+ InvestigationViewModeModeCompact InvestigationViewModeMode = "compact"
+ InvestigationViewModeModeFull InvestigationViewModeMode = "full"
+)
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_status_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_status_gen.go
new file mode 100644
index 00000000000..c70224b96e4
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigation_status_gen.go
@@ -0,0 +1,44 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+// +k8s:openapi-gen=true
+type InvestigationstatusOperatorState struct {
+ // lastEvaluation is the ResourceVersion last evaluated
+ LastEvaluation string `json:"lastEvaluation"`
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ State InvestigationStatusOperatorStateState `json:"state"`
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ DescriptiveState *string `json:"descriptiveState,omitempty"`
+ // details contains any extra information that is operator-specific
+ Details map[string]interface{} `json:"details,omitempty"`
+}
+
+// NewInvestigationstatusOperatorState creates a new InvestigationstatusOperatorState object.
+func NewInvestigationstatusOperatorState() *InvestigationstatusOperatorState {
+ return &InvestigationstatusOperatorState{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationStatus struct {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ OperatorStates map[string]InvestigationstatusOperatorState `json:"operatorStates,omitempty"`
+ // additionalFields is reserved for future use
+ AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
+}
+
+// NewInvestigationStatus creates a new InvestigationStatus object.
+func NewInvestigationStatus() *InvestigationStatus {
+ return &InvestigationStatus{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationStatusOperatorStateState string
+
+const (
+ InvestigationStatusOperatorStateStateSuccess InvestigationStatusOperatorStateState = "success"
+ InvestigationStatusOperatorStateStateInProgress InvestigationStatusOperatorStateState = "in_progress"
+ InvestigationStatusOperatorStateStateFailed InvestigationStatusOperatorStateState = "failed"
+)
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_codec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_codec_gen.go
new file mode 100644
index 00000000000..0496ce4af6f
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_codec_gen.go
@@ -0,0 +1,28 @@
+//
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+//
+
+package v0alpha1
+
+import (
+ "encoding/json"
+ "io"
+
+ "github.com/grafana/grafana-app-sdk/resource"
+)
+
+// InvestigationIndexJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
+type InvestigationIndexJSONCodec struct{}
+
+// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
+func (*InvestigationIndexJSONCodec) Read(reader io.Reader, into resource.Object) error {
+ return json.NewDecoder(reader).Decode(into)
+}
+
+// Write writes JSON-encoded bytes into `writer` marshaled from `from`
+func (*InvestigationIndexJSONCodec) Write(writer io.Writer, from resource.Object) error {
+ return json.NewEncoder(writer).Encode(from)
+}
+
+// Interface compliance checks
+var _ resource.Codec = &InvestigationIndexJSONCodec{}
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_metadata_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_metadata_gen.go
new file mode 100644
index 00000000000..67e97598a93
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_metadata_gen.go
@@ -0,0 +1,28 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+import (
+ time "time"
+)
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+type InvestigationIndexMetadata struct {
+ UpdateTimestamp time.Time `json:"updateTimestamp"`
+ CreatedBy string `json:"createdBy"`
+ Uid string `json:"uid"`
+ CreationTimestamp time.Time `json:"creationTimestamp"`
+ DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
+ Finalizers []string `json:"finalizers"`
+ ResourceVersion string `json:"resourceVersion"`
+ Generation int64 `json:"generation"`
+ UpdatedBy string `json:"updatedBy"`
+ Labels map[string]string `json:"labels"`
+}
+
+// NewInvestigationIndexMetadata creates a new InvestigationIndexMetadata object.
+func NewInvestigationIndexMetadata() *InvestigationIndexMetadata {
+ return &InvestigationIndexMetadata{}
+}
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_object_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_object_gen.go
new file mode 100644
index 00000000000..cc3c33964ed
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_object_gen.go
@@ -0,0 +1,266 @@
+//
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+//
+
+package v0alpha1
+
+import (
+ "fmt"
+ "github.com/grafana/grafana-app-sdk/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
+ "time"
+)
+
+// +k8s:openapi-gen=true
+type InvestigationIndex struct {
+ metav1.TypeMeta `json:",inline" yaml:",inline"`
+ metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
+ Spec InvestigationIndexSpec `json:"spec" yaml:"spec"`
+ InvestigationIndexStatus InvestigationIndexStatus `json:"status" yaml:"status"`
+}
+
+func (o *InvestigationIndex) GetSpec() any {
+ return o.Spec
+}
+
+func (o *InvestigationIndex) SetSpec(spec any) error {
+ cast, ok := spec.(InvestigationIndexSpec)
+ if !ok {
+ return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec)
+ }
+ o.Spec = cast
+ return nil
+}
+
+func (o *InvestigationIndex) GetSubresources() map[string]any {
+ return map[string]any{
+ "status": o.InvestigationIndexStatus,
+ }
+}
+
+func (o *InvestigationIndex) GetSubresource(name string) (any, bool) {
+ switch name {
+ case "status":
+ return o.InvestigationIndexStatus, true
+ default:
+ return nil, false
+ }
+}
+
+func (o *InvestigationIndex) SetSubresource(name string, value any) error {
+ switch name {
+ case "status":
+ cast, ok := value.(InvestigationIndexStatus)
+ if !ok {
+ return fmt.Errorf("cannot set status type %#v, not of type InvestigationIndexStatus", value)
+ }
+ o.InvestigationIndexStatus = cast
+ return nil
+ default:
+ return fmt.Errorf("subresource '%s' does not exist", name)
+ }
+}
+
+func (o *InvestigationIndex) GetStaticMetadata() resource.StaticMetadata {
+ gvk := o.GroupVersionKind()
+ return resource.StaticMetadata{
+ Name: o.ObjectMeta.Name,
+ Namespace: o.ObjectMeta.Namespace,
+ Group: gvk.Group,
+ Version: gvk.Version,
+ Kind: gvk.Kind,
+ }
+}
+
+func (o *InvestigationIndex) SetStaticMetadata(metadata resource.StaticMetadata) {
+ o.Name = metadata.Name
+ o.Namespace = metadata.Namespace
+ o.SetGroupVersionKind(schema.GroupVersionKind{
+ Group: metadata.Group,
+ Version: metadata.Version,
+ Kind: metadata.Kind,
+ })
+}
+
+func (o *InvestigationIndex) GetCommonMetadata() resource.CommonMetadata {
+ dt := o.DeletionTimestamp
+ var deletionTimestamp *time.Time
+ if dt != nil {
+ deletionTimestamp = &dt.Time
+ }
+ // Legacy ExtraFields support
+ extraFields := make(map[string]any)
+ if o.Annotations != nil {
+ extraFields["annotations"] = o.Annotations
+ }
+ if o.ManagedFields != nil {
+ extraFields["managedFields"] = o.ManagedFields
+ }
+ if o.OwnerReferences != nil {
+ extraFields["ownerReferences"] = o.OwnerReferences
+ }
+ return resource.CommonMetadata{
+ UID: string(o.UID),
+ ResourceVersion: o.ResourceVersion,
+ Generation: o.Generation,
+ Labels: o.Labels,
+ CreationTimestamp: o.CreationTimestamp.Time,
+ DeletionTimestamp: deletionTimestamp,
+ Finalizers: o.Finalizers,
+ UpdateTimestamp: o.GetUpdateTimestamp(),
+ CreatedBy: o.GetCreatedBy(),
+ UpdatedBy: o.GetUpdatedBy(),
+ ExtraFields: extraFields,
+ }
+}
+
+func (o *InvestigationIndex) SetCommonMetadata(metadata resource.CommonMetadata) {
+ o.UID = types.UID(metadata.UID)
+ o.ResourceVersion = metadata.ResourceVersion
+ o.Generation = metadata.Generation
+ o.Labels = metadata.Labels
+ o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp)
+ if metadata.DeletionTimestamp != nil {
+ dt := metav1.NewTime(*metadata.DeletionTimestamp)
+ o.DeletionTimestamp = &dt
+ } else {
+ o.DeletionTimestamp = nil
+ }
+ o.Finalizers = metadata.Finalizers
+ if o.Annotations == nil {
+ o.Annotations = make(map[string]string)
+ }
+ if !metadata.UpdateTimestamp.IsZero() {
+ o.SetUpdateTimestamp(metadata.UpdateTimestamp)
+ }
+ if metadata.CreatedBy != "" {
+ o.SetCreatedBy(metadata.CreatedBy)
+ }
+ if metadata.UpdatedBy != "" {
+ o.SetUpdatedBy(metadata.UpdatedBy)
+ }
+ // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields
+ if metadata.ExtraFields != nil {
+ if annotations, ok := metadata.ExtraFields["annotations"]; ok {
+ if cast, ok := annotations.(map[string]string); ok {
+ o.Annotations = cast
+ }
+ }
+ if managedFields, ok := metadata.ExtraFields["managedFields"]; ok {
+ if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok {
+ o.ManagedFields = cast
+ }
+ }
+ if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok {
+ if cast, ok := ownerReferences.([]metav1.OwnerReference); ok {
+ o.OwnerReferences = cast
+ }
+ }
+ }
+}
+
+func (o *InvestigationIndex) GetCreatedBy() string {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ return o.ObjectMeta.Annotations["grafana.com/createdBy"]
+}
+
+func (o *InvestigationIndex) SetCreatedBy(createdBy string) {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy
+}
+
+func (o *InvestigationIndex) GetUpdateTimestamp() time.Time {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"])
+ return parsed
+}
+
+func (o *InvestigationIndex) SetUpdateTimestamp(updateTimestamp time.Time) {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
+}
+
+func (o *InvestigationIndex) GetUpdatedBy() string {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ return o.ObjectMeta.Annotations["grafana.com/updatedBy"]
+}
+
+func (o *InvestigationIndex) SetUpdatedBy(updatedBy string) {
+ if o.ObjectMeta.Annotations == nil {
+ o.ObjectMeta.Annotations = make(map[string]string)
+ }
+
+ o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy
+}
+
+func (o *InvestigationIndex) Copy() resource.Object {
+ return resource.CopyObject(o)
+}
+
+func (o *InvestigationIndex) DeepCopyObject() runtime.Object {
+ return o.Copy()
+}
+
+// Interface compliance compile-time check
+var _ resource.Object = &InvestigationIndex{}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexList struct {
+ metav1.TypeMeta `json:",inline" yaml:",inline"`
+ metav1.ListMeta `json:"metadata" yaml:"metadata"`
+ Items []InvestigationIndex `json:"items" yaml:"items"`
+}
+
+func (o *InvestigationIndexList) DeepCopyObject() runtime.Object {
+ return o.Copy()
+}
+
+func (o *InvestigationIndexList) Copy() resource.ListObject {
+ cpy := &InvestigationIndexList{
+ TypeMeta: o.TypeMeta,
+ Items: make([]InvestigationIndex, len(o.Items)),
+ }
+ o.ListMeta.DeepCopyInto(&cpy.ListMeta)
+ for i := 0; i < len(o.Items); i++ {
+ if item, ok := o.Items[i].Copy().(*InvestigationIndex); ok {
+ cpy.Items[i] = *item
+ }
+ }
+ return cpy
+}
+
+func (o *InvestigationIndexList) GetItems() []resource.Object {
+ items := make([]resource.Object, len(o.Items))
+ for i := 0; i < len(o.Items); i++ {
+ items[i] = &o.Items[i]
+ }
+ return items
+}
+
+func (o *InvestigationIndexList) SetItems(items []resource.Object) {
+ o.Items = make([]InvestigationIndex, len(items))
+ for i := 0; i < len(items); i++ {
+ o.Items[i] = *items[i].(*InvestigationIndex)
+ }
+}
+
+// Interface compliance compile-time check
+var _ resource.ListObject = &InvestigationIndexList{}
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_schema_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_schema_gen.go
new file mode 100644
index 00000000000..3a0041dc6b8
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_schema_gen.go
@@ -0,0 +1,34 @@
+//
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+//
+
+package v0alpha1
+
+import (
+ "github.com/grafana/grafana-app-sdk/resource"
+)
+
+// schema is unexported to prevent accidental overwrites
+var (
+ schemaInvestigationIndex = resource.NewSimpleSchema("investigations.grafana.app", "v0alpha1", &InvestigationIndex{}, &InvestigationIndexList{}, resource.WithKind("InvestigationIndex"),
+ resource.WithPlural("investigationindexes"), resource.WithScope(resource.NamespacedScope))
+ kindInvestigationIndex = resource.Kind{
+ Schema: schemaInvestigationIndex,
+ Codecs: map[resource.KindEncoding]resource.Codec{
+ resource.KindEncodingJSON: &InvestigationIndexJSONCodec{},
+ },
+ }
+)
+
+// Kind returns a resource.Kind for this Schema with a JSON codec
+func InvestigationIndexKind() resource.Kind {
+ return kindInvestigationIndex
+}
+
+// Schema returns a resource.SimpleSchema representation of InvestigationIndex
+func InvestigationIndexSchema() *resource.SimpleSchema {
+ return schemaInvestigationIndex
+}
+
+// Interface compliance checks
+var _ resource.Schema = kindInvestigationIndex
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_spec_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_spec_gen.go
new file mode 100644
index 00000000000..4180a63a326
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_spec_gen.go
@@ -0,0 +1,92 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+// Person represents a user profile with basic information
+// +k8s:openapi-gen=true
+type InvestigationIndexPerson struct {
+ // Unique identifier for the user
+ Uid string `json:"uid"`
+ // Display name of the user
+ Name string `json:"name"`
+ // URL to user's Gravatar image
+ GravatarUrl string `json:"gravatarUrl"`
+}
+
+// NewInvestigationIndexPerson creates a new InvestigationIndexPerson object.
+func NewInvestigationIndexPerson() *InvestigationIndexPerson {
+ return &InvestigationIndexPerson{}
+}
+
+// Type definition for investigation summaries
+// +k8s:openapi-gen=true
+type InvestigationIndexInvestigationSummary struct {
+ Title string `json:"title"`
+ CreatedByProfile InvestigationIndexPerson `json:"createdByProfile"`
+ HasCustomName bool `json:"hasCustomName"`
+ IsFavorite bool `json:"isFavorite"`
+ OverviewNote string `json:"overviewNote"`
+ OverviewNoteUpdatedAt string `json:"overviewNoteUpdatedAt"`
+ ViewMode InvestigationIndexViewMode `json:"viewMode"`
+ // +listType=atomic
+ CollectableSummaries []InvestigationIndexCollectableSummary `json:"collectableSummaries"`
+}
+
+// NewInvestigationIndexInvestigationSummary creates a new InvestigationIndexInvestigationSummary object.
+func NewInvestigationIndexInvestigationSummary() *InvestigationIndexInvestigationSummary {
+ return &InvestigationIndexInvestigationSummary{
+ CreatedByProfile: *NewInvestigationIndexPerson(),
+ ViewMode: *NewInvestigationIndexViewMode(),
+ }
+}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexViewMode struct {
+ Mode InvestigationIndexViewModeMode `json:"mode"`
+ ShowComments bool `json:"showComments"`
+ ShowTooltips bool `json:"showTooltips"`
+}
+
+// NewInvestigationIndexViewMode creates a new InvestigationIndexViewMode object.
+func NewInvestigationIndexViewMode() *InvestigationIndexViewMode {
+ return &InvestigationIndexViewMode{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexCollectableSummary struct {
+ Id string `json:"id"`
+ Title string `json:"title"`
+ LogoPath string `json:"logoPath"`
+ Origin string `json:"origin"`
+}
+
+// NewInvestigationIndexCollectableSummary creates a new InvestigationIndexCollectableSummary object.
+func NewInvestigationIndexCollectableSummary() *InvestigationIndexCollectableSummary {
+ return &InvestigationIndexCollectableSummary{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexSpec struct {
+ // Title of the index, e.g. 'Favorites' or 'My Investigations'
+ Title string `json:"title"`
+ // The Person who owns this investigation index
+ Owner InvestigationIndexPerson `json:"owner"`
+ // Array of investigation summaries
+ // +listType=atomic
+ InvestigationSummaries []InvestigationIndexInvestigationSummary `json:"investigationSummaries"`
+}
+
+// NewInvestigationIndexSpec creates a new InvestigationIndexSpec object.
+func NewInvestigationIndexSpec() *InvestigationIndexSpec {
+ return &InvestigationIndexSpec{
+ Owner: *NewInvestigationIndexPerson(),
+ }
+}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexViewModeMode string
+
+const (
+ InvestigationIndexViewModeModeCompact InvestigationIndexViewModeMode = "compact"
+ InvestigationIndexViewModeModeFull InvestigationIndexViewModeMode = "full"
+)
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_status_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_status_gen.go
new file mode 100644
index 00000000000..455d39bdb48
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/investigationindex_status_gen.go
@@ -0,0 +1,44 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+package v0alpha1
+
+// +k8s:openapi-gen=true
+type InvestigationIndexstatusOperatorState struct {
+ // lastEvaluation is the ResourceVersion last evaluated
+ LastEvaluation string `json:"lastEvaluation"`
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ State InvestigationIndexStatusOperatorStateState `json:"state"`
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ DescriptiveState *string `json:"descriptiveState,omitempty"`
+ // details contains any extra information that is operator-specific
+ Details map[string]interface{} `json:"details,omitempty"`
+}
+
+// NewInvestigationIndexstatusOperatorState creates a new InvestigationIndexstatusOperatorState object.
+func NewInvestigationIndexstatusOperatorState() *InvestigationIndexstatusOperatorState {
+ return &InvestigationIndexstatusOperatorState{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexStatus struct {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ OperatorStates map[string]InvestigationIndexstatusOperatorState `json:"operatorStates,omitempty"`
+ // additionalFields is reserved for future use
+ AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
+}
+
+// NewInvestigationIndexStatus creates a new InvestigationIndexStatus object.
+func NewInvestigationIndexStatus() *InvestigationIndexStatus {
+ return &InvestigationIndexStatus{}
+}
+
+// +k8s:openapi-gen=true
+type InvestigationIndexStatusOperatorStateState string
+
+const (
+ InvestigationIndexStatusOperatorStateStateSuccess InvestigationIndexStatusOperatorStateState = "success"
+ InvestigationIndexStatusOperatorStateStateInProgress InvestigationIndexStatusOperatorStateState = "in_progress"
+ InvestigationIndexStatusOperatorStateStateFailed InvestigationIndexStatusOperatorStateState = "failed"
+)
diff --git a/apps/investigations/pkg/apis/investigations/v0alpha1/zz_openapi_gen.go b/apps/investigations/pkg/apis/investigations/v0alpha1/zz_openapi_gen.go
new file mode 100644
index 00000000000..36648cfcd11
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations/v0alpha1/zz_openapi_gen.go
@@ -0,0 +1,1061 @@
+//go:build !ignore_autogenerated
+// +build !ignore_autogenerated
+
+// Code generated by grafana-app-sdk. DO NOT EDIT.
+
+package v0alpha1
+
+import (
+ common "k8s.io/kube-openapi/pkg/common"
+ spec "k8s.io/kube-openapi/pkg/validation/spec"
+)
+
+func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
+ return map[string]common.OpenAPIDefinition{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.Investigation": schema_pkg_apis_investigations_v0alpha1_Investigation(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationCollectable": schema_pkg_apis_investigations_v0alpha1_InvestigationCollectable(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationDatasourceRef": schema_pkg_apis_investigations_v0alpha1_InvestigationDatasourceRef(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndex": schema_pkg_apis_investigations_v0alpha1_InvestigationIndex(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexCollectableSummary": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexCollectableSummary(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexInvestigationSummary": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexInvestigationSummary(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexList": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexList(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexPerson(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexSpec": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexSpec(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexStatus": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexStatus(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexViewMode": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexViewMode(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexstatusOperatorState": schema_pkg_apis_investigations_v0alpha1_InvestigationIndexstatusOperatorState(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationList": schema_pkg_apis_investigations_v0alpha1_InvestigationList(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationPerson": schema_pkg_apis_investigations_v0alpha1_InvestigationPerson(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationQuery": schema_pkg_apis_investigations_v0alpha1_InvestigationQuery(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationSpec": schema_pkg_apis_investigations_v0alpha1_InvestigationSpec(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationStatus": schema_pkg_apis_investigations_v0alpha1_InvestigationStatus(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationTimeRange": schema_pkg_apis_investigations_v0alpha1_InvestigationTimeRange(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationV0alpha1TimeRangeRaw": schema_pkg_apis_investigations_v0alpha1_InvestigationV0alpha1TimeRangeRaw(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationViewMode": schema_pkg_apis_investigations_v0alpha1_InvestigationViewMode(ref),
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationstatusOperatorState": schema_pkg_apis_investigations_v0alpha1_InvestigationstatusOperatorState(ref),
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_Investigation(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
+ },
+ },
+ "spec": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationSpec"),
+ },
+ },
+ "status": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationStatus"),
+ },
+ },
+ },
+ Required: []string{"metadata", "spec", "status"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationSpec", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationCollectable(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Collectable represents an item collected during investigation",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "id": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "createdAt": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "title": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "origin": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "type": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "queries": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationQuery"),
+ },
+ },
+ },
+ },
+ },
+ "timeRange": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationTimeRange"),
+ },
+ },
+ "datasource": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationDatasourceRef"),
+ },
+ },
+ "url": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "logoPath": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "note": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "noteUpdatedAt": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"id", "createdAt", "title", "origin", "type", "queries", "timeRange", "datasource", "url", "note", "noteUpdatedAt"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationDatasourceRef", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationQuery", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationTimeRange"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationDatasourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "DatasourceRef is a reference to a datasource",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "uid": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"uid"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndex(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
+ },
+ },
+ "spec": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexSpec"),
+ },
+ },
+ "status": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexStatus"),
+ },
+ },
+ },
+ Required: []string{"metadata", "spec", "status"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexSpec", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexCollectableSummary(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "id": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "title": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "logoPath": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "origin": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"id", "title", "logoPath", "origin"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexInvestigationSummary(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Type definition for investigation summaries",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "title": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "createdByProfile": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson"),
+ },
+ },
+ "hasCustomName": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "isFavorite": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "overviewNote": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "overviewNoteUpdatedAt": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "viewMode": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexViewMode"),
+ },
+ },
+ "collectableSummaries": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexCollectableSummary"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"title", "createdByProfile", "hasCustomName", "isFavorite", "overviewNote", "overviewNoteUpdatedAt", "viewMode", "collectableSummaries"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexCollectableSummary", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexViewMode"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexList(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
+ },
+ },
+ "items": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndex"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"metadata", "items"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndex", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexPerson(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Person represents a user profile with basic information",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "uid": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Unique identifier for the user",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Display name of the user",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "gravatarUrl": {
+ SchemaProps: spec.SchemaProps{
+ Description: "URL to user's Gravatar image",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"uid", "name", "gravatarUrl"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "title": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Title of the index, e.g. 'Favorites' or 'My Investigations'",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "owner": {
+ SchemaProps: spec.SchemaProps{
+ Description: "The Person who owns this investigation index",
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson"),
+ },
+ },
+ "investigationSummaries": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "Array of investigation summaries",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexInvestigationSummary"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"title", "owner", "investigationSummaries"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexInvestigationSummary", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexPerson"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "operatorStates": {
+ SchemaProps: spec.SchemaProps{
+ Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexstatusOperatorState"),
+ },
+ },
+ },
+ },
+ },
+ "additionalFields": {
+ SchemaProps: spec.SchemaProps{
+ Description: "additionalFields is reserved for future use",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationIndexstatusOperatorState"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexViewMode(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "mode": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "showComments": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "showTooltips": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"mode", "showComments", "showTooltips"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationIndexstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "lastEvaluation": {
+ SchemaProps: spec.SchemaProps{
+ Description: "lastEvaluation is the ResourceVersion last evaluated",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "state": {
+ SchemaProps: spec.SchemaProps{
+ Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "descriptiveState": {
+ SchemaProps: spec.SchemaProps{
+ Description: "descriptiveState is an optional more descriptive state field which has no requirements on format",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "details": {
+ SchemaProps: spec.SchemaProps{
+ Description: "details contains any extra information that is operator-specific",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"lastEvaluation", "state"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationList(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "kind": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "apiVersion": {
+ SchemaProps: spec.SchemaProps{
+ Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "metadata": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
+ },
+ },
+ "items": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.Investigation"),
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"metadata", "items"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.Investigation", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationPerson(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Person represents a user profile with basic information",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "uid": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Unique identifier for the user",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Display name of the user",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "gravatarUrl": {
+ SchemaProps: spec.SchemaProps{
+ Description: "URL to user's Gravatar image",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"uid", "name", "gravatarUrl"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationQuery(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Query represents a data query",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "refId": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "queryType": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "editorMode": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "supportingQueryType": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "legendFormat": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "expr": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"refId", "queryType", "editorMode", "supportingQueryType", "legendFormat", "expr"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "spec is the schema of our resource",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "title": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "createdByProfile": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationPerson"),
+ },
+ },
+ "hasCustomName": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "isFavorite": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "overviewNote": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "overviewNoteUpdatedAt": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "collectables": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationCollectable"),
+ },
+ },
+ },
+ },
+ },
+ "viewMode": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationViewMode"),
+ },
+ },
+ },
+ Required: []string{"title", "createdByProfile", "hasCustomName", "isFavorite", "overviewNote", "overviewNoteUpdatedAt", "collectables", "viewMode"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationCollectable", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationPerson", "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationViewMode"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "operatorStates": {
+ SchemaProps: spec.SchemaProps{
+ Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationstatusOperatorState"),
+ },
+ },
+ },
+ },
+ },
+ "additionalFields": {
+ SchemaProps: spec.SchemaProps{
+ Description: "additionalFields is reserved for future use",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationstatusOperatorState"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationTimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "TimeRange represents a time range with both absolute and relative values",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "from": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "to": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "raw": {
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationV0alpha1TimeRangeRaw"),
+ },
+ },
+ },
+ Required: []string{"from", "to", "raw"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1.InvestigationV0alpha1TimeRangeRaw"},
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationV0alpha1TimeRangeRaw(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "from": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "to": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"from", "to"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationViewMode(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "mode": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "showComments": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "showTooltips": {
+ SchemaProps: spec.SchemaProps{
+ Default: false,
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"mode", "showComments", "showTooltips"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_investigations_v0alpha1_InvestigationstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "lastEvaluation": {
+ SchemaProps: spec.SchemaProps{
+ Description: "lastEvaluation is the ResourceVersion last evaluated",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "state": {
+ SchemaProps: spec.SchemaProps{
+ Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "descriptiveState": {
+ SchemaProps: spec.SchemaProps{
+ Description: "descriptiveState is an optional more descriptive state field which has no requirements on format",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "details": {
+ SchemaProps: spec.SchemaProps{
+ Description: "details contains any extra information that is operator-specific",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"lastEvaluation", "state"},
+ },
+ },
+ }
+}
diff --git a/apps/investigations/pkg/apis/investigations_manifest.go b/apps/investigations/pkg/apis/investigations_manifest.go
new file mode 100644
index 00000000000..eed596424bc
--- /dev/null
+++ b/apps/investigations/pkg/apis/investigations_manifest.go
@@ -0,0 +1,65 @@
+//
+// This file is generated by grafana-app-sdk
+// DO NOT EDIT
+//
+
+package apis
+
+import (
+ "encoding/json"
+
+ "github.com/grafana/grafana-app-sdk/app"
+)
+
+var (
+ rawSchemaInvestigationv0alpha1 = []byte(`{"spec":{"description":"spec is the schema of our resource","properties":{"collectables":{"items":{"properties":{"createdAt":{"type":"string"},"datasource":{"properties":{"uid":{"type":"string"}},"required":["uid"],"type":"object"},"id":{"type":"string"},"logoPath":{"type":"string"},"note":{"type":"string"},"noteUpdatedAt":{"type":"string"},"origin":{"type":"string"},"queries":{"items":{"properties":{"editorMode":{"type":"string"},"expr":{"type":"string"},"legendFormat":{"type":"string"},"queryType":{"type":"string"},"refId":{"type":"string"},"supportingQueryType":{"type":"string"}},"required":["refId","queryType","editorMode","supportingQueryType","legendFormat","expr"],"type":"object"},"type":"array"},"timeRange":{"properties":{"from":{"type":"string"},"raw":{"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"to":{"type":"string"}},"required":["from","to","raw"],"type":"object"},"title":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["id","createdAt","title","origin","type","queries","timeRange","datasource","url","note","noteUpdatedAt"],"type":"object"},"type":"array"},"createdByProfile":{"properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","collectables","viewMode"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
+ versionSchemaInvestigationv0alpha1 app.VersionSchema
+ _ = json.Unmarshal(rawSchemaInvestigationv0alpha1, &versionSchemaInvestigationv0alpha1)
+ rawSchemaInvestigationIndexv0alpha1 = []byte(`{"spec":{"properties":{"investigationSummaries":{"description":"Array of investigation summaries","items":{"properties":{"collectableSummaries":{"items":{"properties":{"id":{"type":"string"},"logoPath":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"}},"required":["id","title","logoPath","origin"],"type":"object"},"type":"array"},"createdByProfile":{"properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","viewMode","collectableSummaries"],"type":"object"},"type":"array"},"owner":{"description":"The Person who owns this investigation index","properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"title":{"description":"Title of the index, e.g. 'Favorites' or 'My Investigations'","type":"string"}},"required":["title","owner","investigationSummaries"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
+ versionSchemaInvestigationIndexv0alpha1 app.VersionSchema
+ _ = json.Unmarshal(rawSchemaInvestigationIndexv0alpha1, &versionSchemaInvestigationIndexv0alpha1)
+)
+
+var appManifestData = app.ManifestData{
+ AppName: "investigations",
+ Group: "investigations.grafana.app",
+ Kinds: []app.ManifestKind{
+ {
+ Kind: "Investigation",
+ Scope: "Namespaced",
+ Conversion: false,
+ Versions: []app.ManifestKindVersion{
+ {
+ Name: "v0alpha1",
+ Schema: &versionSchemaInvestigationv0alpha1,
+ },
+ },
+ },
+
+ {
+ Kind: "InvestigationIndex",
+ Scope: "Namespaced",
+ Conversion: false,
+ Versions: []app.ManifestKindVersion{
+ {
+ Name: "v0alpha1",
+ Schema: &versionSchemaInvestigationIndexv0alpha1,
+ },
+ },
+ },
+ },
+}
+
+func jsonToMap(j string) map[string]any {
+ m := make(map[string]any)
+ json.Unmarshal([]byte(j), &j)
+ return m
+}
+
+func LocalManifest() app.Manifest {
+ return app.NewEmbeddedManifest(appManifestData)
+}
+
+func RemoteManifest() app.Manifest {
+ return app.NewAPIServerManifest("investigations")
+}
diff --git a/apps/investigation/pkg/app/app.go b/apps/investigations/pkg/app/investigations_app.go
similarity index 66%
rename from apps/investigation/pkg/app/app.go
rename to apps/investigations/pkg/app/investigations_app.go
index 539b7241540..8ec35c1ed19 100644
--- a/apps/investigation/pkg/app/app.go
+++ b/apps/investigations/pkg/app/investigations_app.go
@@ -9,7 +9,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
- investigationv1alpha1 "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1"
+ investigationsv0alpha1 "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1"
)
func New(cfg app.Config) (app.App, error) {
@@ -24,7 +24,10 @@ func New(cfg app.Config) (app.App, error) {
},
ManagedKinds: []simple.AppManagedKind{
{
- Kind: investigationv1alpha1.InvestigationKind(),
+ Kind: investigationsv0alpha1.InvestigationKind(),
+ },
+ {
+ Kind: investigationsv0alpha1.InvestigationIndexKind(),
},
},
}
@@ -44,10 +47,13 @@ func New(cfg app.Config) (app.App, error) {
func GetKinds() map[schema.GroupVersion][]resource.Kind {
gv := schema.GroupVersion{
- Group: investigationv1alpha1.InvestigationKind().Group(),
- Version: investigationv1alpha1.InvestigationKind().Version(),
+ Group: investigationsv0alpha1.InvestigationKind().Group(),
+ Version: investigationsv0alpha1.InvestigationKind().Version(),
}
return map[schema.GroupVersion][]resource.Kind{
- gv: {investigationv1alpha1.InvestigationKind()},
+ gv: {
+ investigationsv0alpha1.InvestigationKind(),
+ investigationsv0alpha1.InvestigationIndexKind(),
+ },
}
}
diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/investigation_object_gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/investigation_object_gen.ts
new file mode 100644
index 00000000000..b355ebaca05
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/investigation_object_gen.ts
@@ -0,0 +1,49 @@
+/*
+ * This file was generated by grafana-app-sdk. DO NOT EDIT.
+ */
+import { Spec } from './types.spec.gen';
+import { Status } from './types.status.gen';
+
+export interface Metadata {
+ name: string;
+ namespace: string;
+ generateName?: string;
+ selfLink?: string;
+ uid?: string;
+ resourceVersion?: string;
+ generation?: number;
+ creationTimestamp?: string;
+ deletionTimestamp?: string;
+ deletionGracePeriodSeconds?: number;
+ labels?: Record;
+ annotations?: Record;
+ ownerReferences?: OwnerReference[];
+ finalizers?: string[];
+ managedFields?: ManagedFieldsEntry[];
+}
+
+export interface OwnerReference {
+ apiVersion: string;
+ kind: string;
+ name: string;
+ uid: string;
+ controller?: boolean;
+ blockOwnerDeletion?: boolean;
+}
+
+export interface ManagedFieldsEntry {
+ manager?: string;
+ operation?: string;
+ apiVersion?: string;
+ time?: string;
+ fieldsType?: string;
+ subresource?: string;
+}
+
+export interface Investigation {
+ kind: string;
+ apiVersion: string;
+ metadata: Metadata;
+ spec: Spec;
+ status: Status;
+}
diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.metadata.gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.metadata.gen.ts
new file mode 100644
index 00000000000..4377f3c1d08
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.metadata.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+export interface Metadata {
+ updateTimestamp: string;
+ createdBy: string;
+ uid: string;
+ creationTimestamp: string;
+ deletionTimestamp?: string;
+ finalizers: string[];
+ resourceVersion: string;
+ generation: number;
+ updatedBy: string;
+ labels: Record;
+}
+
+export const defaultMetadata = (): Metadata => ({
+ updateTimestamp: "",
+ createdBy: "",
+ uid: "",
+ creationTimestamp: "",
+ finalizers: [],
+ resourceVersion: "",
+ generation: 0,
+ updatedBy: "",
+ labels: {},
+});
+
diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.spec.gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.spec.gen.ts
new file mode 100644
index 00000000000..c2a128284bc
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.spec.gen.ts
@@ -0,0 +1,132 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// Person represents a user profile with basic information
+export interface Person {
+ // Unique identifier for the user
+ uid: string;
+ // Display name of the user
+ name: string;
+ // URL to user's Gravatar image
+ gravatarUrl: string;
+}
+
+export const defaultPerson = (): Person => ({
+ uid: "",
+ name: "",
+ gravatarUrl: "",
+});
+
+// Collectable represents an item collected during investigation
+export interface Collectable {
+ id: string;
+ createdAt: string;
+ title: string;
+ origin: string;
+ type: string;
+ // +listType=atomic
+ queries: Query[];
+ timeRange: TimeRange;
+ datasource: DatasourceRef;
+ url: string;
+ logoPath?: string;
+ note: string;
+ noteUpdatedAt: string;
+}
+
+export const defaultCollectable = (): Collectable => ({
+ id: "",
+ createdAt: "",
+ title: "",
+ origin: "",
+ type: "",
+ queries: [],
+ timeRange: defaultTimeRange(),
+ datasource: defaultDatasourceRef(),
+ url: "",
+ note: "",
+ noteUpdatedAt: "",
+});
+
+// Query represents a data query
+export interface Query {
+ refId: string;
+ queryType: string;
+ editorMode: string;
+ supportingQueryType: string;
+ legendFormat: string;
+ expr: string;
+}
+
+export const defaultQuery = (): Query => ({
+ refId: "",
+ queryType: "",
+ editorMode: "",
+ supportingQueryType: "",
+ legendFormat: "",
+ expr: "",
+});
+
+// TimeRange represents a time range with both absolute and relative values
+export interface TimeRange {
+ from: string;
+ to: string;
+ raw: {
+ from: string;
+ to: string;
+ };
+}
+
+export const defaultTimeRange = (): TimeRange => ({
+ from: "",
+ to: "",
+ raw: {
+ from: "",
+ to: "",
+},
+});
+
+// DatasourceRef is a reference to a datasource
+export interface DatasourceRef {
+ uid: string;
+}
+
+export const defaultDatasourceRef = (): DatasourceRef => ({
+ uid: "",
+});
+
+export interface ViewMode {
+ mode: "compact" | "full";
+ showComments: boolean;
+ showTooltips: boolean;
+}
+
+export const defaultViewMode = (): ViewMode => ({
+ mode: "compact",
+ showComments: false,
+ showTooltips: false,
+});
+
+// spec is the schema of our resource
+export interface Spec {
+ title: string;
+ createdByProfile: Person;
+ hasCustomName: boolean;
+ isFavorite: boolean;
+ overviewNote: string;
+ overviewNoteUpdatedAt: string;
+ // +listType=atomic
+ collectables: Collectable[];
+ viewMode: ViewMode;
+}
+
+export const defaultSpec = (): Spec => ({
+ title: "",
+ createdByProfile: defaultPerson(),
+ hasCustomName: false,
+ isFavorite: false,
+ overviewNote: "",
+ overviewNoteUpdatedAt: "",
+ collectables: [],
+ viewMode: defaultViewMode(),
+});
+
diff --git a/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.status.gen.ts b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.status.gen.ts
new file mode 100644
index 00000000000..01be8df7961
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigation/v0alpha1/types.status.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+export interface OperatorState {
+ // lastEvaluation is the ResourceVersion last evaluated
+ lastEvaluation: string;
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ state: "success" | "in_progress" | "failed";
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ descriptiveState?: string;
+ // details contains any extra information that is operator-specific
+ details?: Record;
+}
+
+export const defaultOperatorState = (): OperatorState => ({
+ lastEvaluation: "",
+ state: "success",
+});
+
+export interface Status {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ operatorStates?: Record;
+ // additionalFields is reserved for future use
+ additionalFields?: Record;
+}
+
+export const defaultStatus = (): Status => ({
+});
+
diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/investigationindex_object_gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/investigationindex_object_gen.ts
new file mode 100644
index 00000000000..399b25f3878
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/investigationindex_object_gen.ts
@@ -0,0 +1,49 @@
+/*
+ * This file was generated by grafana-app-sdk. DO NOT EDIT.
+ */
+import { Spec } from './types.spec.gen';
+import { Status } from './types.status.gen';
+
+export interface Metadata {
+ name: string;
+ namespace: string;
+ generateName?: string;
+ selfLink?: string;
+ uid?: string;
+ resourceVersion?: string;
+ generation?: number;
+ creationTimestamp?: string;
+ deletionTimestamp?: string;
+ deletionGracePeriodSeconds?: number;
+ labels?: Record;
+ annotations?: Record;
+ ownerReferences?: OwnerReference[];
+ finalizers?: string[];
+ managedFields?: ManagedFieldsEntry[];
+}
+
+export interface OwnerReference {
+ apiVersion: string;
+ kind: string;
+ name: string;
+ uid: string;
+ controller?: boolean;
+ blockOwnerDeletion?: boolean;
+}
+
+export interface ManagedFieldsEntry {
+ manager?: string;
+ operation?: string;
+ apiVersion?: string;
+ time?: string;
+ fieldsType?: string;
+ subresource?: string;
+}
+
+export interface InvestigationIndex {
+ kind: string;
+ apiVersion: string;
+ metadata: Metadata;
+ spec: Spec;
+ status: Status;
+}
diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.metadata.gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.metadata.gen.ts
new file mode 100644
index 00000000000..4377f3c1d08
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.metadata.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+export interface Metadata {
+ updateTimestamp: string;
+ createdBy: string;
+ uid: string;
+ creationTimestamp: string;
+ deletionTimestamp?: string;
+ finalizers: string[];
+ resourceVersion: string;
+ generation: number;
+ updatedBy: string;
+ labels: Record;
+}
+
+export const defaultMetadata = (): Metadata => ({
+ updateTimestamp: "",
+ createdBy: "",
+ uid: "",
+ creationTimestamp: "",
+ finalizers: [],
+ resourceVersion: "",
+ generation: 0,
+ updatedBy: "",
+ labels: {},
+});
+
diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.spec.gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.spec.gen.ts
new file mode 100644
index 00000000000..f0d2f25d96b
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.spec.gen.ts
@@ -0,0 +1,84 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// Person represents a user profile with basic information
+export interface Person {
+ // Unique identifier for the user
+ uid: string;
+ // Display name of the user
+ name: string;
+ // URL to user's Gravatar image
+ gravatarUrl: string;
+}
+
+export const defaultPerson = (): Person => ({
+ uid: "",
+ name: "",
+ gravatarUrl: "",
+});
+
+// Type definition for investigation summaries
+export interface InvestigationSummary {
+ title: string;
+ createdByProfile: Person;
+ hasCustomName: boolean;
+ isFavorite: boolean;
+ overviewNote: string;
+ overviewNoteUpdatedAt: string;
+ viewMode: ViewMode;
+ // +listType=atomic
+ collectableSummaries: CollectableSummary[];
+}
+
+export const defaultInvestigationSummary = (): InvestigationSummary => ({
+ title: "",
+ createdByProfile: defaultPerson(),
+ hasCustomName: false,
+ isFavorite: false,
+ overviewNote: "",
+ overviewNoteUpdatedAt: "",
+ viewMode: defaultViewMode(),
+ collectableSummaries: [],
+});
+
+export interface ViewMode {
+ mode: "compact" | "full";
+ showComments: boolean;
+ showTooltips: boolean;
+}
+
+export const defaultViewMode = (): ViewMode => ({
+ mode: "compact",
+ showComments: false,
+ showTooltips: false,
+});
+
+export interface CollectableSummary {
+ id: string;
+ title: string;
+ logoPath: string;
+ origin: string;
+}
+
+export const defaultCollectableSummary = (): CollectableSummary => ({
+ id: "",
+ title: "",
+ logoPath: "",
+ origin: "",
+});
+
+export interface Spec {
+ // Title of the index, e.g. 'Favorites' or 'My Investigations'
+ title: string;
+ // The Person who owns this investigation index
+ owner: Person;
+ // Array of investigation summaries
+ // +listType=atomic
+ investigationSummaries: InvestigationSummary[];
+}
+
+export const defaultSpec = (): Spec => ({
+ title: "",
+ owner: defaultPerson(),
+ investigationSummaries: [],
+});
+
diff --git a/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.status.gen.ts b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.status.gen.ts
new file mode 100644
index 00000000000..01be8df7961
--- /dev/null
+++ b/apps/investigations/plugin/src/generated/investigationindex/v0alpha1/types.status.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+export interface OperatorState {
+ // lastEvaluation is the ResourceVersion last evaluated
+ lastEvaluation: string;
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ state: "success" | "in_progress" | "failed";
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ descriptiveState?: string;
+ // details contains any extra information that is operator-specific
+ details?: Record;
+}
+
+export const defaultOperatorState = (): OperatorState => ({
+ lastEvaluation: "",
+ state: "success",
+});
+
+export interface Status {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ operatorStates?: Record;
+ // additionalFields is reserved for future use
+ additionalFields?: Record;
+}
+
+export const defaultStatus = (): Status => ({
+});
+
diff --git a/apps/playlist/Makefile b/apps/playlist/Makefile
index 926f5a2840f..7a5f1c99365 100644
--- a/apps/playlist/Makefile
+++ b/apps/playlist/Makefile
@@ -1,8 +1,3 @@
.PHONY: generate
generate:
- @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess
- # HACK: Clean up generated CRD files.
- # TODO: The SDK currently omits generating the manifest Go file with `--defencoding=none`,
- # which we would normally use here to skip generating the CRD files.
- # This needs to be addressed.
- @rm -rf definitions
+ @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none
diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod
index f7304ea395c..cd07f8c199e 100644
--- a/apps/playlist/go.mod
+++ b/apps/playlist/go.mod
@@ -3,9 +3,9 @@ module github.com/grafana/grafana/apps/playlist
go 1.23.4
require (
- github.com/grafana/grafana-app-sdk v0.30.0
- k8s.io/apimachinery v0.32.0
- k8s.io/client-go v0.32.0
+ github.com/grafana/grafana-app-sdk v0.31.0
+ k8s.io/apimachinery v0.32.1
+ k8s.io/client-go v0.32.1
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
)
@@ -19,7 +19,7 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
- github.com/getkin/kin-openapi v0.128.0 // indirect
+ github.com/getkin/kin-openapi v0.129.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
@@ -32,11 +32,10 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect
+ github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
@@ -45,39 +44,41 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
+ github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.61.0 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/otel v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
- go.opentelemetry.io/otel/metric v1.33.0 // indirect
- go.opentelemetry.io/otel/sdk v1.33.0 // indirect
- go.opentelemetry.io/otel/trace v1.33.0 // indirect
+ go.opentelemetry.io/otel v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.34.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
- golang.org/x/net v0.34.0 // indirect
- golang.org/x/oauth2 v0.25.0 // indirect
- golang.org/x/sync v0.10.0 // indirect
- golang.org/x/sys v0.29.0 // indirect
- golang.org/x/term v0.28.0 // indirect
- golang.org/x/text v0.21.0 // indirect
+ golang.org/x/net v0.35.0 // indirect
+ golang.org/x/oauth2 v0.27.0 // indirect
+ golang.org/x/sync v0.11.0 // indirect
+ golang.org/x/sys v0.30.0 // indirect
+ golang.org/x/term v0.29.0 // indirect
+ golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.9.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
- google.golang.org/grpc v1.69.4 // indirect
- google.golang.org/protobuf v1.36.1 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
+ google.golang.org/grpc v1.70.0 // indirect
+ google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/api v0.32.0 // indirect
- k8s.io/apiextensions-apiserver v0.32.0 // indirect
+ k8s.io/api v0.32.1 // indirect
+ k8s.io/apiextensions-apiserver v0.32.1 // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum
index 858c73dd4b4..d3caa3d08e3 100644
--- a/apps/playlist/go.sum
+++ b/apps/playlist/go.sum
@@ -16,8 +16,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
-github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
+github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30=
+github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -49,10 +49,10 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw=
-github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk=
-github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
-github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
+github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk=
+github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs=
+github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME=
+github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -60,8 +60,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
-github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -87,6 +85,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU=
+github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8=
+github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc=
+github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
@@ -102,8 +104,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
@@ -124,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
-go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
-go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
-go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
-go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
-go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
-go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -153,26 +155,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
-golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
-golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
-golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
-golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
-golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -187,14 +189,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
-google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
-google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
-google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
+google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -204,14 +206,14 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
-k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
-k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
-k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
-k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
-k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
-k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
-k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
+k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
+k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
+k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
+k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
+k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
+k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
+k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
diff --git a/conf/defaults.ini b/conf/defaults.ini
index 91e7af25442..bf0bf994b56 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -130,6 +130,13 @@ password =
# Example: mysql://user:secret@host:port/database
url =
+# Set to true or false to enable or disable high availability mode.
+# When it's set to false some functions will be simplified and only run in-process
+# instead of relying on the database.
+#
+# Only set it to false if you run only a single instance of Grafana.
+high_availability = true
+
# Max idle conn setting default is 2
max_idle_conn = 2
@@ -354,6 +361,9 @@ disable_brute_force_login_protection = false
# max number of failed login attempts before user gets locked
brute_force_login_protection_max_attempts = 5
+# disable protection against brute force login attempts by IP address
+disable_ip_address_login_protection = true
+
# set to true if you host Grafana behind HTTPS. default is false.
cookie_secure = false
@@ -452,6 +462,9 @@ min_refresh_interval = 5s
# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json"
default_home_dashboard_path =
+# Dashboards UIDs to report performance metrics for. * can be used to report metrics for all dashboards
+dashboard_performance_metrics =
+
################################### Data sources #########################
[datasources]
# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API.
@@ -521,9 +534,11 @@ external_manage_link_url =
external_manage_link_name =
external_manage_info =
+# Deprecated: Assign your viewers to editors.
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
viewers_can_edit = false
+# Deprecated: Assign your editors to admins.
# Editors can administrate dashboard, folders and teams they create
editors_can_admin = false
@@ -1013,6 +1028,10 @@ user_identity_fallback_credentials_enabled = true
# By default is the same as token URL configured for AAD authentication settings
user_identity_token_url =
+# Override client authentication method for Azure Active Directory
+# By default is the same as client authentication method configured for AAD authentication settings
+user_identity_client_authentication =
+
# Override ADD application ID which would be used to exchange users token to an access token for the datasource
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
user_identity_client_id =
@@ -1021,6 +1040,14 @@ user_identity_client_id =
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
user_identity_client_secret =
+# Override the AAD managed identity client ID
+# By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow)
+user_identity_managed_identity_client_id =
+
+# Override the AAD federated credential audience
+# By default is the same as used in AAD authentication or can be set to another audience (for OBO flow)
+user_identity_federated_credential_audience =
+
# Allows the usage of a custom token request assertion when Grafana is behind an authentication proxy
# In most cases this will not need to be used. To enable this set the value to "username"
# The default is empty and any other value will not enable this functionality
@@ -1559,6 +1586,9 @@ enabled = true
# set the default offset for the time picker
defaultTimeOffset = 1h
+# hides the download logs button in Explore
+hide_logs_download = false
+
#################################### Help #############################
[help]
# Enable the Help section
@@ -1723,6 +1753,7 @@ sas_token_expiration_days =
# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
server_url =
# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
+# The `callback_url` can also be configured to support usage of the image renderer running as a plugin with support for SSL / HTTPS. For example https://localhost:3000/.
callback_url =
# An auth token that will be sent to and verified by the renderer. The renderer will deny any request without an auth token matching the one configured on the renderer side.
renderer_token = -
@@ -1785,6 +1816,10 @@ preinstall_disabled = false
# tuning. 0 disables Live, -1 means unlimited connections.
max_connections = 100
+# message_size_limit is the maximum size in bytes of Websocket messages from clients. Defaults to 64KB.
+# The limit can be disabled by setting it to -1.
+message_size_limit = 65536
+
# allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live.
# If not set then origin will be matched over root_url. Supports wildcard symbol "*".
allowed_origins =
diff --git a/conf/sample.ini b/conf/sample.ini
index d1a06d9c331..da688f03078 100644
--- a/conf/sample.ini
+++ b/conf/sample.ini
@@ -129,6 +129,13 @@
# Example: mysql://user:secret@host:port/database
;url =
+# Set to true or false to enable or disable high availability mode.
+# When it's set to false some functions will be simplified and only run in-process
+# instead of relying on the database.
+#
+# Only set it to false if you run only a single instance of Grafana.
+;high_availability = true
+
# Max idle conn setting default is 2
;max_idle_conn = 2
@@ -353,6 +360,9 @@
# max number of failed login attempts before user gets locked
;brute_force_login_protection_max_attempts = 5
+# disable protection against brute force login attempts by IP address
+; disable_ip_address_login_protection = true
+
# set to true if you host Grafana behind HTTPS. default is false.
;cookie_secure = false
@@ -525,9 +535,12 @@
;external_manage_link_name =
;external_manage_info =
+
+# Deprecated: Assign your viewers to editors.
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
;viewers_can_edit = false
+# Deprecated: Assign your editors to admins.
# Editors can administrate dashboard, folders and teams they create
;editors_can_admin = false
@@ -998,6 +1011,10 @@
# By default is the same as token URL configured for AAD authentication settings
;user_identity_token_url =
+# Override client authentication method for Azure Active Directory
+# By default is the same as client authentication method configured for AAD authentication settings
+;user_identity_client_authentication =
+
# Override ADD application ID which would be used to exchange users token to an access token for the datasource
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
;user_identity_client_id =
@@ -1006,6 +1023,14 @@
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
;user_identity_client_secret =
+# Override the AAD managed identity client ID
+# By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow)
+;user_identity_managed_identity_client_id =
+
+# Override the AAD federated credential audience
+# By default is the same as used in AAD authentication or can be set to another audience (for OBO flow)
+;user_identity_federated_credential_audience =
+
# Allows the usage of a custom token request assertion when Grafana is behind an authentication proxy
# In most cases this will not need to be used. To enable this set the value to "username"
# The default is empty and any other value will not enable this functionality
@@ -1391,12 +1416,6 @@
# For example: `disabled_labels=grafana_folder`
disabled_labels =
-
-[unified_alerting.reserved_labels]
-# Comma-separated list of reserved labels added by the Grafana Alerting engine that should be disabled.
-# For example: `disabled_labels=grafana_folder`
-;disabled_labels =
-
[unified_alerting.state_history]
# Enable the state history functionality in Unified Alerting. The previous states of alert rules will be visible in panels and in the UI.
; enabled = true
diff --git a/contribute/developer-guide.md b/contribute/developer-guide.md
index 9a4d98d8003..da3669615ab 100644
--- a/contribute/developer-guide.md
+++ b/contribute/developer-guide.md
@@ -318,7 +318,7 @@ Previously, Grafana used Yarn PnP to install frontend dependencies, which requir
### Too many open files when running `make run`
-Depending on your environment, you may have to increase the maximum number of open files allowed. For the rest of this section, we will assume you are on a UNIX-like OS (for example, Linux or macOS), where you can control the maximum number of open files through the [ulimit](https://ss64.com/bash/ulimit.html) shell command.
+Depending on your environment, you may need to increase the maximum number of open files allowed. For the rest of this section, we will assume you are on a UNIX-like OS (for example, Linux or macOS), where you can control the maximum number of open files through the [ulimit](https://ss64.com/bash/ulimit.html) shell command.
To see how many open files are allowed, run:
@@ -361,6 +361,58 @@ ulimit: open files: cannot modify limit: Operation not permitted
If that happens to you, chances are you've already set a lower limit and your shell won't let you set a higher one. Try looking in your shell initialization files (`~/.bashrc`, typically), to see if there's already an `ulimit` command that you can tweak.
+### System limit for number of file watchers reached while running `yarn start`
+
+Depending on your environment, you may need to increase the number of file watchers allowed by `inotify` package to monitor filesystem changes. You may encounter an error `Error: ENOSPC: System limit for number of file watchers reached` otherwise.
+
+Edit the system config file to insert the new value for file watchers limit:
+
+On Linux:
+
+```bash
+echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
+```
+
+On macOS:
+
+```bash
+sudo sysctl -w kern.maxfiles=524288
+```
+
+Check if the new value was applied. It must output `524288`:
+
+On Linux:
+
+```bash
+cat /proc/sys/fs/inotify/max_user_watches
+```
+
+On macOS:
+
+```bash
+sysctl kern.maxfiles
+```
+
+### JavaScript heap out of memory while running `yarn start`
+
+Running `yarn start` requires a substantial amount of memory space. You may check the currently allocated heap space to `node` by running the command:
+
+```bash
+node -e 'console.log(v8.getHeapStatistics().heap_size_limit/(1024*1024))'
+```
+
+Increase the default heap memory to something greater than the currently allocated memory. Make sure the value is a multiple of `1024`.
+
+```bash
+export NODE_OPTIONS="--max-old-space-size=8192"
+```
+
+Or on Windows:
+
+```
+Set NODE_OPTIONS="--max-old-space-size=8192"
+```
+
### Getting `AggregateError` when building frontend tests
If you encounter an `AggregateError` when building new tests, this is probably due to a call to our client [backend service](https://github.com/grafana/grafana/blob/main/public/app/core/services/backend_srv.ts) not being mocked. Our backend service anticipates multiple responses being returned and was built to return errors as an array. A test encountering errors from the service will group those errors as an `AggregateError` without breaking down the individual errors within. `backend_srv.processRequestError` is called once per error and is a great place to return information on what the individual errors might contain.
diff --git a/contribute/style-guides/themes.md b/contribute/style-guides/themes.md
index 61c01764429..e4608ec55f1 100644
--- a/contribute/style-guides/themes.md
+++ b/contribute/style-guides/themes.md
@@ -96,11 +96,12 @@ Example use cases:
### Background colors
-| Property | When to use |
-| --------------------------------- | ------------------------------------------------------------------------------------------------ |
-| theme.colors.background.canvas | Dashboard background. A background surface for panels and panes that use primary background |
-| theme.colors.background.primary | The default content background for content panes and panels |
-| theme.colors.background.secondary | For cards and other surfaces that need to stand out when placed on top of the primary background |
+| Property | When to use |
+| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| theme.colors.background.canvas | Dashboard background. A background surface for panels and panes that use primary background |
+| theme.colors.background.primary | The default content background for content panes and panels |
+| theme.colors.background.secondary | For cards and other surfaces that need to stand out when placed on top of the primary background |
+| theme.colors.background.elevated | For popovers and menu backgrounds. This is the same color as primary in most light themes but in dark themes it has a brighter shade to help give it contrast against the primary background |
### Borders
diff --git a/devenv/bulk-dashboards/bulkdash.jsonnet b/devenv/bulk-dashboards/bulkdash.jsonnet
index 1a77d8abd70..05e396df2d6 100644
--- a/devenv/bulk-dashboards/bulkdash.jsonnet
+++ b/devenv/bulk-dashboards/bulkdash.jsonnet
@@ -1118,18 +1118,6 @@
"1d"
],
"status": "Stable",
- "time_options": [
- "5m",
- "15m",
- "1h",
- "2h",
- " 6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
- ],
"type": "timepicker"
},
"timezone": "browser",
diff --git a/devenv/datasources.yaml b/devenv/datasources.yaml
index 452d2d3d1bc..00efa243c1c 100644
--- a/devenv/datasources.yaml
+++ b/devenv/datasources.yaml
@@ -136,6 +136,14 @@ datasources:
tsdbResolution: 1
tsdbVersion: 3
+ - name: gdev-opentsdb-v2.4
+ type: opentsdb
+ access: proxy
+ url: http://localhost:4242
+ jsonData:
+ tsdbResolution: 1
+ tsdbVersion: 4
+
- name: gdev-elasticsearch
type: elasticsearch
uid: gdev-elasticsearch
diff --git a/devenv/dev-dashboards-without-uid/panel_tests_graph.json b/devenv/dev-dashboards-without-uid/panel_tests_graph.json
index b5d50f4f7b7..6bba1d9ae02 100644
--- a/devenv/dev-dashboards-without-uid/panel_tests_graph.json
+++ b/devenv/dev-dashboards-without-uid/panel_tests_graph.json
@@ -1639,8 +1639,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "browser",
"title": "Panel Tests - Graph",
diff --git a/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json b/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json
index 3ff76d12df2..98d49958aaf 100644
--- a/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json
+++ b/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json
@@ -490,17 +490,6 @@
"1h",
"2h",
"1d"
- ],
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
]
},
"timezone": "browser",
diff --git a/devenv/dev-dashboards-without-uid/panel_tests_polystat.json b/devenv/dev-dashboards-without-uid/panel_tests_polystat.json
index 951bb780017..25b1f7154e5 100644
--- a/devenv/dev-dashboards-without-uid/panel_tests_polystat.json
+++ b/devenv/dev-dashboards-without-uid/panel_tests_polystat.json
@@ -3408,8 +3408,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Polystat",
diff --git a/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json b/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json
index 97719e82251..f46ccc2042f 100644
--- a/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json
+++ b/devenv/dev-dashboards/datasource-influxdb/influxdb-templated.json
@@ -312,7 +312,6 @@
"now": true,
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"status": "Stable",
- "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"],
"type": "timepicker"
},
"timezone": "browser",
diff --git a/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json b/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json
index 19c5e1d0718..8fda3e8e714 100644
--- a/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json
+++ b/devenv/dev-dashboards/datasource-mssql/mssql_fakedata.json
@@ -537,8 +537,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Datasource tests - MSSQL",
diff --git a/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json b/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json
index 0137001067c..40f9d55e5ca 100644
--- a/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json
+++ b/devenv/dev-dashboards/datasource-mssql/mssql_unittest.json
@@ -2831,8 +2831,7 @@
"to": "2018-03-15T13:55:01.000Z"
},
"timepicker": {
- "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
- "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Datasource tests - MSSQL (unit test)",
diff --git a/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json b/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json
index 96e4688f7bd..cd1a24fb5bd 100644
--- a/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json
+++ b/devenv/dev-dashboards/datasource-mysql/mysql_fakedata.json
@@ -541,8 +541,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Datasource tests - MySQL",
diff --git a/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json b/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json
index b68c9db97ec..2f2a42c1175 100644
--- a/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json
+++ b/devenv/dev-dashboards/datasource-mysql/mysql_unittest.json
@@ -2643,8 +2643,7 @@
"to": "2018-03-15T13:55:01.000Z"
},
"timepicker": {
- "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
- "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Datasource tests - MySQL (unittest)",
diff --git a/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json b/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json
index d7d9514e639..750b3284517 100644
--- a/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json
+++ b/devenv/dev-dashboards/datasource-postgres/postgres_fakedata.json
@@ -577,8 +577,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Datasource tests - Postgres",
diff --git a/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json b/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json
index a114ed1b7ef..acec0d08a44 100644
--- a/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json
+++ b/devenv/dev-dashboards/datasource-postgres/postgres_unittest.json
@@ -2621,8 +2621,7 @@
"to": "2018-03-15T13:55:01.000Z"
},
"timepicker": {
- "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
- "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Datasource tests - Postgres (unittest)",
diff --git a/devenv/dev-dashboards/datasource-testdata/demo1.json b/devenv/dev-dashboards/datasource-testdata/demo1.json
index abe39ffeb55..6d8034f81e6 100644
--- a/devenv/dev-dashboards/datasource-testdata/demo1.json
+++ b/devenv/dev-dashboards/datasource-testdata/demo1.json
@@ -1092,7 +1092,6 @@
"now": true,
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"status": "Stable",
- "time_options": ["5m", "15m", "1h", "2h", " 6h", "12h", "24h", "2d", "7d", "30d"],
"type": "timepicker"
},
"timezone": "browser",
diff --git a/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json b/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json
index 4b9535ecbbd..a002a208f85 100644
--- a/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json
+++ b/devenv/dev-dashboards/datasource-testdata/new_features_in_v62.json
@@ -1326,8 +1326,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "New Features in v6.2",
diff --git a/devenv/dev-dashboards/home.json b/devenv/dev-dashboards/home.json
index 9c3d65d4add..840d32919ea 100644
--- a/devenv/dev-dashboards/home.json
+++ b/devenv/dev-dashboards/home.json
@@ -240,17 +240,6 @@
"1h",
"2h",
"1d"
- ],
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
]
},
"timezone": "",
diff --git a/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json b/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json
index a708467d7be..eda6ccfe996 100644
--- a/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json
+++ b/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json
@@ -654,8 +654,7 @@
"to": "now"
},
"timepicker": {
- "refresh_intervals": ["2s", "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
- "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ "refresh_intervals": ["2s", "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Bar Gauge Demo",
diff --git a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json
index 29a6929cf8f..3dfa360c740 100644
--- a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json
+++ b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json
@@ -1423,17 +1423,6 @@
"1h",
"2h",
"1d"
- ],
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
]
},
"timezone": "",
diff --git a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json
index 0f36c203cd0..06fc26e382d 100644
--- a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json
+++ b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge2.json
@@ -519,8 +519,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Bar Gauge 2",
diff --git a/devenv/dev-dashboards/panel-common/lazy_loading.json b/devenv/dev-dashboards/panel-common/lazy_loading.json
index 859eede4b4f..960c466124c 100644
--- a/devenv/dev-dashboards/panel-common/lazy_loading.json
+++ b/devenv/dev-dashboards/panel-common/lazy_loading.json
@@ -2202,8 +2202,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Lazy Loading",
diff --git a/devenv/dev-dashboards/panel-common/panels_without_title.json b/devenv/dev-dashboards/panel-common/panels_without_title.json
index 44bd210e73b..8f62c9cca5e 100644
--- a/devenv/dev-dashboards/panel-common/panels_without_title.json
+++ b/devenv/dev-dashboards/panel-common/panels_without_title.json
@@ -893,8 +893,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - With & Without title",
diff --git a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json
index 09b72e5c030..f7ff80edc12 100644
--- a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json
+++ b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json
@@ -254,8 +254,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Gauge Multi Series",
diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests.json b/devenv/dev-dashboards/panel-gauge/gauge_tests.json
index 458f53dbc08..309a255fcc7 100644
--- a/devenv/dev-dashboards/panel-gauge/gauge_tests.json
+++ b/devenv/dev-dashboards/panel-gauge/gauge_tests.json
@@ -1319,8 +1319,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Gauge",
diff --git a/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json b/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json
index 01e9e8c2f43..c2d27efd469 100644
--- a/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json
+++ b/devenv/dev-dashboards/panel-graph/graph-gradient-area-fills.json
@@ -372,8 +372,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Graph - Gradient Area Fills",
diff --git a/devenv/dev-dashboards/panel-graph/graph-time-regions.json b/devenv/dev-dashboards/panel-graph/graph-time-regions.json
index 2031788ae3f..a4e03148536 100644
--- a/devenv/dev-dashboards/panel-graph/graph-time-regions.json
+++ b/devenv/dev-dashboards/panel-graph/graph-time-regions.json
@@ -569,8 +569,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "browser",
"title": "Panel Tests - Graph Time Regions",
diff --git a/devenv/dev-dashboards/panel-graph/graph_tests.json b/devenv/dev-dashboards/panel-graph/graph_tests.json
index 4d01c2cb534..bd1fc95d3d6 100644
--- a/devenv/dev-dashboards/panel-graph/graph_tests.json
+++ b/devenv/dev-dashboards/panel-graph/graph_tests.json
@@ -1639,8 +1639,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "browser",
"title": "Panel Tests - Graph",
diff --git a/devenv/dev-dashboards/panel-polystat/polystat_test.json b/devenv/dev-dashboards/panel-polystat/polystat_test.json
index 6be355ebd99..faa84463019 100644
--- a/devenv/dev-dashboards/panel-polystat/polystat_test.json
+++ b/devenv/dev-dashboards/panel-polystat/polystat_test.json
@@ -3408,8 +3408,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Polystat",
diff --git a/devenv/dev-dashboards/panel-table/table_tests.json b/devenv/dev-dashboards/panel-table/table_tests.json
index 8582ef068d7..b8ca436e110 100644
--- a/devenv/dev-dashboards/panel-table/table_tests.json
+++ b/devenv/dev-dashboards/panel-table/table_tests.json
@@ -440,8 +440,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "browser",
"title": "Panel Tests - Table",
diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json b/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json
index a7389cc6ed6..8f3267a62d7 100644
--- a/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json
+++ b/devenv/dev-dashboards/panel-timeseries/timeseries-gradient-area.json
@@ -562,8 +562,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel Tests - Graph NG - Gradient Area Fills",
diff --git a/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json b/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json
index a7cc41acd8f..8966137b55e 100644
--- a/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json
+++ b/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json
@@ -1132,8 +1132,7 @@
"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"]
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "",
"title": "Panel tests - Slow Queries & Annotations",
diff --git a/devenv/dev-dashboards/scenarios/time_zone_support.json b/devenv/dev-dashboards/scenarios/time_zone_support.json
index feb317f917e..cc1f81ee221 100644
--- a/devenv/dev-dashboards/scenarios/time_zone_support.json
+++ b/devenv/dev-dashboards/scenarios/time_zone_support.json
@@ -684,8 +684,7 @@
"to": "now"
},
"timepicker": {
- "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
- "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+ "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
},
"timezone": "utc",
"title": "Panel Tests - Time zone support",
diff --git a/devenv/docker/blocks/elasticstack/docker-compose.yaml b/devenv/docker/blocks/elasticstack/docker-compose.yaml
index ad8a0d5e7f0..35b4254ac63 100644
--- a/devenv/docker/blocks/elasticstack/docker-compose.yaml
+++ b/devenv/docker/blocks/elasticstack/docker-compose.yaml
@@ -5,6 +5,8 @@
- "discovery.type=single-node"
- "xpack.license.self_generated.type=basic"
- "xpack.security.enabled=false"
+ - 'ES_JAVA_OPTS=-XX:UseSVE=0'
+ - 'CLI_JAVA_OPTS=-XX:UseSVE=0'
ports:
- 9200:9200
diff --git a/devenv/docker/blocks/prometheus/recording.yml b/devenv/docker/blocks/prometheus/recording.yml
index f45cdbeddf2..2b8ae3e477a 100644
--- a/devenv/docker/blocks/prometheus/recording.yml
+++ b/devenv/docker/blocks/prometheus/recording.yml
@@ -1,4 +1,18 @@
groups:
+ - name: TESTING_RULES
+ rules:
+ - record: test:rate5m
+ expr: rate(ismail_prometheus_tsdb_reloads_total{job="prometheus"}[5m])
+ labels:
+ identifier: special_idx
+ - record: agent:test:rate5m
+ expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m])
+ labels:
+ identifier: special_idx
+ - record: agent:test:rate5m_agg_
+ expr: rate(prometheus_tsdb_reloads_total_agg_{job="aggregated"}[5m])
+ labels:
+ identifier: special_idx
- name: RECORDING_RULES
rules:
- record: instance_path:requests:rate5m
@@ -9,6 +23,14 @@ groups:
expr: rate(prometheus_tsdb_reloads_failures_total{job="prometheus"}[5m])
- record: instance_path:reloads:rate5m
expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m])
+ - record: test:rate5m
+ expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m])
+ - record: test:rate5m_agg_
+ expr: rate(prometheus_tsdb_reloads_total_agg_{job="aggregated"}[5m])
+ - record: agent:test:rate5m
+ expr: rate(prometheus_tsdb_reloads_total{job="prometheus"}[5m])
+ - record: agent:test:rate5m_agg_
+ expr: rate(prometheus_tsdb_reloads_total_agg_{job="aggregated"}[5m])
- record: instance_path:request_failures_per_requests:ratio_rate5m
expr: |2
instance_path:reloads_failures:rate5m{job="prometheus"}
diff --git a/devenv/docker/blocks/prometheus_random_data/Dockerfile b/devenv/docker/blocks/prometheus_random_data/Dockerfile
index c4ebc8cb0ec..41f90677409 100644
--- a/devenv/docker/blocks/prometheus_random_data/Dockerfile
+++ b/devenv/docker/blocks/prometheus_random_data/Dockerfile
@@ -7,7 +7,7 @@ RUN CGO_ENABLED=0 GOOS=linux go install -tags netgo -ldflags '-w' github.com/pro
# Final image.
FROM scratch
-LABEL maintainer "The Prometheus Authors "
+LABEL maintainer="The Prometheus Authors "
COPY --from=builder /go/bin/random .
EXPOSE 8080
ENTRYPOINT ["/random"]
diff --git a/devenv/docker/blocks/stateful_webhook/Dockerfile b/devenv/docker/blocks/stateful_webhook/Dockerfile
new file mode 100644
index 00000000000..03b50db2135
--- /dev/null
+++ b/devenv/docker/blocks/stateful_webhook/Dockerfile
@@ -0,0 +1,12 @@
+FROM golang:1.23.5
+
+ADD main.go /go/src/webhook/main.go
+
+WORKDIR /go/src/webhook
+
+RUN mkdir /tmp/logs
+RUN go build -o /bin main.go
+
+ENV PORT=8080
+
+ENTRYPOINT [ "/bin/main" ]
diff --git a/devenv/docker/blocks/stateful_webhook/docker-compose.yaml b/devenv/docker/blocks/stateful_webhook/docker-compose.yaml
new file mode 100644
index 00000000000..7217516c4e9
--- /dev/null
+++ b/devenv/docker/blocks/stateful_webhook/docker-compose.yaml
@@ -0,0 +1,5 @@
+ stateful_webhook:
+ build:
+ context: docker/blocks/stateful_webhook
+ ports:
+ - "8080:8080"
diff --git a/devenv/docker/blocks/stateful_webhook/main.go b/devenv/docker/blocks/stateful_webhook/main.go
new file mode 100644
index 00000000000..926cf4e593e
--- /dev/null
+++ b/devenv/docker/blocks/stateful_webhook/main.go
@@ -0,0 +1,149 @@
+package main
+
+import (
+ "encoding/json"
+ "io"
+ "log"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+)
+
+type Event struct {
+ Status string `json:"status"`
+ TimeNow time.Time `json:"timeNow"`
+ StartsAt time.Time `json:"startsAt"`
+ Node string `json:"node"`
+ DeltaLastSeconds float64 `json:"deltaLastSeconds"`
+ DeltaStartSeconds float64 `json:"deltaStartSeconds"`
+}
+
+type Notification struct {
+ Alerts []Alert `json:"alerts"`
+ CommonAnnotations map[string]string `json:"commonAnnotations"`
+ CommonLabels map[string]string `json:"commonLabels"`
+ ExternalURL string `json:"externalURL"`
+ GroupKey string `json:"groupKey"`
+ GroupLabels map[string]string `json:"groupLabels"`
+ Message string `json:"message"`
+ OrgID int `json:"orgId"`
+ Receiver string `json:"receiver"`
+ State string `json:"state"`
+ Status string `json:"status"`
+ Title string `json:"title"`
+ TruncatedAlerts int `json:"truncatedAlerts"`
+ Version string `json:"version"`
+}
+
+type Alert struct {
+ Annotations map[string]string `json:"annotations"`
+ DashboardURL string `json:"dashboardURL"`
+ StartsAt time.Time `json:"startsAt"`
+ EndsAt time.Time `json:"endsAt"`
+ Fingerprint string `json:"fingerprint"`
+ GeneratorURL string `json:"generatorURL"`
+ Labels map[string]string `json:"labels"`
+ PanelURL string `json:"panelURL"`
+ SilenceURL string `json:"silenceURL"`
+ Status string `json:"status"`
+ ValueString string `json:"valueString"`
+ Values map[string]any `json:"values"`
+}
+
+type NotificationHandler struct {
+ startedAt time.Time
+ stats map[string]int
+ hist []Event
+ m sync.Mutex
+}
+
+func NewNotificationHandler() *NotificationHandler {
+ return &NotificationHandler{
+ startedAt: time.Now(),
+ stats: make(map[string]int),
+ hist: make([]Event, 0),
+ }
+}
+
+func (ah *NotificationHandler) Notify(w http.ResponseWriter, r *http.Request) {
+ b, err := io.ReadAll(r.Body)
+ if err != nil {
+ log.Println(err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+ n := Notification{}
+ if err := json.Unmarshal(b, &n); err != nil {
+ log.Println(err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+ log.Printf("got notification from: %s. a: %v", r.RemoteAddr, n)
+
+ ah.m.Lock()
+ defer ah.m.Unlock()
+
+ addr := r.RemoteAddr
+ if split := strings.Split(r.RemoteAddr, ":"); len(split) > 0 {
+ addr = split[0]
+ }
+
+ a := n.Alerts[0]
+
+ timeNow := time.Now()
+
+ ah.stats[n.Status]++
+
+ var d time.Duration
+ if len(ah.hist) > 0 {
+ last := ah.hist[len(ah.hist)-1]
+ d = timeNow.Sub(last.TimeNow)
+ }
+
+ ah.hist = append(ah.hist, Event{
+ Status: n.Status,
+ StartsAt: a.StartsAt,
+ TimeNow: timeNow,
+ Node: addr,
+ DeltaLastSeconds: d.Seconds(),
+ DeltaStartSeconds: timeNow.Sub(ah.startedAt).Seconds(),
+ })
+}
+
+func (ah *NotificationHandler) GetNotifications(w http.ResponseWriter, _ *http.Request) {
+ ah.m.Lock()
+ defer ah.m.Unlock()
+ w.Header().Set("Content-Type", "application/json")
+
+ res, err := json.MarshalIndent(map[string]any{"stats": ah.stats, "history": ah.hist}, "", "\t")
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ //nolint:errcheck
+ w.Write([]byte(`{"error":"failed to marshal alerts"}`))
+ log.Printf("failed to marshal alerts: %v\n", err)
+ return
+ }
+
+ log.Printf("requested current state\n%v\n", string(res))
+
+ _, err = w.Write(res)
+ if err != nil {
+ log.Printf("failed to write response: %v\n", err)
+ }
+}
+
+func main() {
+ ah := NewNotificationHandler()
+
+ http.HandleFunc("/ready", func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ })
+
+ http.HandleFunc("/notify", ah.Notify)
+ http.HandleFunc("/notifications", ah.GetNotifications)
+
+ log.Println("Listening")
+ //nolint:errcheck
+ http.ListenAndServe("0.0.0.0:8080", nil)
+}
diff --git a/devenv/docker/blocks/tempo/tempo.yaml b/devenv/docker/blocks/tempo/tempo.yaml
index 7cfb1230a3f..b1130a6e27c 100644
--- a/devenv/docker/blocks/tempo/tempo.yaml
+++ b/devenv/docker/blocks/tempo/tempo.yaml
@@ -6,15 +6,23 @@ distributor:
jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can
protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver
thrift_http: #
- grpc: # for a production deployment you should only enable the receivers you need!
+ endpoint: "tempo:14268" # for a production deployment you should only enable the receivers you need!
+ grpc:
+ endpoint: "tempo:14250"
thrift_binary:
+ endpoint: "tempo:6832"
thrift_compact:
+ endpoint: "tempo:6831"
zipkin:
+ endpoint: "tempo:9411"
otlp:
protocols:
- http:
grpc:
+ endpoint: "tempo:4317"
+ http:
+ endpoint: "tempo:4318"
opencensus:
+ endpoint: "tempo:55678"
compactor:
compaction:
diff --git a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json
index b4946cfb6b3..0357c3d10f1 100644
--- a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json
+++ b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json
@@ -151,17 +151,6 @@
"1h",
"2h",
"1d"
- ],
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
]
},
"timezone": "",
diff --git a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json
index 7643250ec28..2bf789366cc 100644
--- a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json
+++ b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json
@@ -5376,17 +5376,6 @@
"1d"
],
"status": "Stable",
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
- ],
"type": "timepicker"
},
"timezone": "browser",
diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json
index b4946cfb6b3..0357c3d10f1 100644
--- a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json
+++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json
@@ -151,17 +151,6 @@
"1h",
"2h",
"1d"
- ],
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
]
},
"timezone": "",
diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json
index 7643250ec28..2bf789366cc 100644
--- a/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json
+++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json
@@ -5376,17 +5376,6 @@
"1d"
],
"status": "Stable",
- "time_options": [
- "5m",
- "15m",
- "1h",
- "6h",
- "12h",
- "24h",
- "2d",
- "7d",
- "30d"
- ],
"type": "timepicker"
},
"timezone": "browser",
diff --git a/docs/docs.mk b/docs/docs.mk
index c0aae10ba50..dfb6e9e501c 100644
--- a/docs/docs.mk
+++ b/docs/docs.mk
@@ -120,3 +120,12 @@ update: ## Fetch the latest version of this Makefile and the `make-docs` script
curl -s -LO https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/docs.mk
curl -s -LO https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs
chmod +x make-docs
+
+# ls static/templates/ | sed 's/-template\.md//' | xargs
+TOPIC_TYPES := concept multiple-tasks reference section task tutorial visualization
+.PHONY: $(patsubst %,topic/%,$(TOPIC_TYPES))
+topic/%: ## Create a topic from the Writers' Toolkit template. Specify the topic type as the target, for example, `make topic/task TOPIC_PATH=sources/my-new-topic.md`.
+topic/%:
+ $(if $(TOPIC_PATH),,$(error "You must set the TOPIC_PATH variable to the path where the $(@F) topic will be created. For example: make $(@) TOPIC_PATH=sources/my-new-topic.md"))
+ mkdir -p $(dir $(TOPIC_PATH))
+ curl -s -o $(TOPIC_PATH) https://raw.githubusercontent.com/grafana/writers-toolkit/refs/heads/main/docs/static/templates/$(@F)-template.md
diff --git a/docs/make-docs b/docs/make-docs
index ba448ce0727..80c89aea7ce 100755
--- a/docs/make-docs
+++ b/docs/make-docs
@@ -1,4 +1,6 @@
#!/bin/sh
+# shellcheck disable=SC2034
+#
# The source of this file is https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs.
# # `make-docs` procedure changelog
#
@@ -6,6 +8,24 @@
# [Semantic versioning](https://semver.org/) is used to help the reader identify the significance of changes.
# Changes are relevant to this script and the support docs.mk GNU Make interface.
#
+# ## 8.5.1 (2025-02-18)
+#
+# ### Fixed
+#
+# - PHONY declaration for topic/ targets.
+#
+# ## 8.5.0 (2025-02-13)
+#
+# ### Added
+#
+# - make topic/ TOPIC_PATH= target to create a new topic from the Writers' Toolkit templates.
+#
+# ## 8.4.0 (2025-01-27)
+#
+# ### Fixed
+#
+# - Correct mount for the /docs/grafana-cloud/send-data/fleet-management/ project.
+#
# ## 8.3.0 (2024-12-27)
#
# ### Added
@@ -304,6 +324,7 @@ PODMAN="$(if command -v podman >/dev/null 2>&1; then echo podman; else echo dock
if ! command -v curl >/dev/null 2>&1; then
if ! command -v wget >/dev/null 2>&1; then
+ # shellcheck disable=SC2016
errr 'either `curl` or `wget` must be installed for this script to work.'
exit 1
@@ -311,6 +332,7 @@ if ! command -v curl >/dev/null 2>&1; then
fi
if ! command -v "${PODMAN}" >/dev/null 2>&1; then
+ # shellcheck disable=SC2016
errr 'either `podman` or `docker` must be installed for this script to work.'
exit 1
@@ -357,6 +379,10 @@ EOF
exit 1
fi
+# The following variables comprise a pseudo associative array of project names to source repositories.
+# You only need to set a SOURCES variable if the project name does not match the source repository name.
+# You can get a key identifier using the `identifier` function.
+# To look up the value of any pseudo associative array, use the `aget` function.
SOURCES_as_code='as-code-docs'
SOURCES_enterprise_metrics='backend-enterprise'
SOURCES_enterprise_metrics_='backend-enterprise'
@@ -366,11 +392,16 @@ SOURCES_grafana_cloud_alerting_and_irm_slo='slo'
SOURCES_grafana_cloud_k6='k6-docs'
SOURCES_grafana_cloud_data_configuration_integrations='cloud-onboarding'
SOURCES_grafana_cloud_frontend_observability_faro_web_sdk='faro-web-sdk'
+SOURCES_grafana_cloud_send_data_fleet_management='fleet-management'
SOURCES_helm_charts_mimir_distributed='mimir'
SOURCES_helm_charts_tempo_distributed='tempo'
SOURCES_opentelemetry='opentelemetry-docs'
SOURCES_resources='website'
+# The following variables comprise a pseudo associative array of project names to versions.
+# You only need to set a VERSIONS variable if it is not the default of 'latest'.
+# You can get a key identifier using the `identifier` function.
+# To look up the value of any pseudo associative array, use the `aget` function.
VERSIONS_as_code='UNVERSIONED'
VERSIONS_grafana_cloud='UNVERSIONED'
VERSIONS_grafana_cloud_alerting_and_irm_machine_learning='UNVERSIONED'
@@ -378,12 +409,17 @@ VERSIONS_grafana_cloud_alerting_and_irm_slo='UNVERSIONED'
VERSIONS_grafana_cloud_k6='UNVERSIONED'
VERSIONS_grafana_cloud_data_configuration_integrations='UNVERSIONED'
VERSIONS_grafana_cloud_frontend_observability_faro_web_sdk='UNVERSIONED'
+VERSIONS_grafana_cloud_send_data_fleet_management='UNVERSIONED'
VERSIONS_opentelemetry='UNVERSIONED'
VERSIONS_resources='UNVERSIONED'
VERSIONS_technical_documentation='UNVERSIONED'
VERSIONS_website='UNVERSIONED'
VERSIONS_writers_toolkit='UNVERSIONED'
+# The following variables comprise a pseudo associative array of project names to source repository paths.
+# You only need to set a PATHS variable if it is not the default of 'docs/sources'.
+# You can get a key identifier using the `identifier` function.
+# To look up the value of any pseudo associative array, use the `aget` function.
PATHS_grafana_cloud='content/docs/grafana-cloud'
PATHS_helm_charts_mimir_distributed='docs/sources/helm-charts/mimir-distributed'
PATHS_helm_charts_tempo_distributed='docs/sources/helm-charts/tempo-distributed'
@@ -816,7 +852,9 @@ EOF
case "${OUTPUT_FORMAT}" in
human)
if ! command -v jq >/dev/null 2>&1; then
+ # shellcheck disable=SC2016
errr '`jq` must be installed for the `doc-validator` target to work.'
+ # shellcheck disable=SC2016
note 'To install `jq`, refer to https://jqlang.github.io/jq/download/,'
exit 1
diff --git a/docs/sources/administration/data-source-management/_index.md b/docs/sources/administration/data-source-management/_index.md
index 71c5d87d625..ce91fc46eae 100644
--- a/docs/sources/administration/data-source-management/_index.md
+++ b/docs/sources/administration/data-source-management/_index.md
@@ -110,6 +110,10 @@ By reducing the number of queries and requests sent to data sources, caching can
Query caching works for Grafana's [built-in data sources]({{< relref "../../datasources/#built-in-core-data-sources" >}}), and [backend data source plugins](https://grafana.com/grafana/plugins/?type=datasource) that extend the `DataSourceWithBackend` class in the plugins SDK.
+{{% admonition type="note" %}}
+Logs Insights for the CloudWatch data source does not support query caching due to the way logs are requested from AWS.
+{{% /admonition %}}
+
To verify that a data source works with query caching, follow the [instructions below](#enable-and-configure-query-caching) to **Enable and Configure query caching**. If caching is enabled in Grafana but the Caching tab is not visible for the given data source, then query caching is not available for that data source.
{{% admonition type="note" %}}
diff --git a/docs/sources/administration/data-source-management/teamlbac/_index.md b/docs/sources/administration/data-source-management/teamlbac/_index.md
index ac801cf9d18..74470c792bf 100644
--- a/docs/sources/administration/data-source-management/teamlbac/_index.md
+++ b/docs/sources/administration/data-source-management/teamlbac/_index.md
@@ -20,7 +20,7 @@ Label-Based Access Control (LBAC) allows fine-grained access control to data sou
## Supported Data Sources
-LBAC for data sources is currently available for `Loki, Prometheus` with basic authentication. Support for additional data sources may be added in future updates.
+LBAC for data sources is currently generally available for `Loki` and in **experimental** for `Prometheus`. Support for additional data sources may be added in future updates.
**LBAC for data sources offers:**
@@ -28,11 +28,6 @@ LBAC for data sources is currently available for `Loki, Prometheus` with basic a
- Simplified data source management by consolidating multiple sources into one.
- Dashboard reuse across teams with tailored access.
-{{< admonition type="note" >}}
-LBAC rules is available for **private preview** in Grafana Cloud.
-Report any unexpected behavior to the Grafana Support team.
-{{< /admonition >}}
-
You can configure user access based upon team memberships using `LogQL`.
LBAC for data sources controls access to logs or metrics depending on the rules set for each team.
@@ -57,7 +52,7 @@ This flexibility allows teams to use the same data source for multiple use cases
## Before you begin
-To be able to use LBAC for data sources, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance.
+To be able to use LBAC for data sources metrics, you need to enable the feature toggle `teamHttpHeadersMimir` on your Grafana instance.
## Limitations
@@ -95,4 +90,4 @@ To set up LBAC for data sources for a data source, refer to [Configure LBAC for
## Provisioning of LBAC rules
-We recommend using our Terraform provider to set up provisioning for [Resource data source config LBAC rules] (https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source_config_lbac_rules). Refer to our provider documentation to learn how to configure rules for a data source.
+We recommend using our Terraform provider to set up provisioning for [Resource data source config LBAC rules](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source_config_lbac_rules). Refer to our provider documentation to learn how to configure rules for a data source.
diff --git a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md
index 0e5875eb26c..18a21b24ee8 100644
--- a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md
+++ b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/index.md
@@ -19,8 +19,6 @@ You cannot configure LBAC rules for Grafana-provisioned data sources from the UI
## Before you begin
-To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you.
-
- Be sure that you have the permission setup to create a Loki tenant in Grafana Cloud
- Be sure that you have admin data source permissions for Grafana.
diff --git a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md
index aa54d970e6f..29f8467ad6d 100644
--- a/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md
+++ b/docs/sources/administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/index.md
@@ -18,20 +18,26 @@ weight: 250
LBAC for data sources is available in private preview on Grafana Cloud for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created, provisioning is currently not available.
-You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-Prometheus/#task-1-lbac-configuration-for-new-Prometheus-data-source) and then add the LBAC configuration to the new data source.
+You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](#task-1-lbac-configuration-for-new-prometheus-data-source) and then add the LBAC configuration to the new data source.
## Before you begin
-To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you.
+To be able to use LBAC for Prometheus data sources, you need to enable the feature toggle `teamHttpHeadersMimir` on your Grafana instance. Contact support to enable the feature toggle for you.
- Be sure that you have the permission setup to create a Prometheus tenant in Grafana Cloud
- Be sure that you have admin data source permissions for Grafana.
+## Grafana Cloud
+
+LBAC for data sources is available in private preview on Grafana Cloud for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created, provisioning is currently not available.
+
+You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. We recommend that you replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-Prometheus/#task-1-lbac-configuration-for-new-Prometheus-data-source) and then add the LBAC configuration to the new data source.
+
### Permissions
We recommend that you remove all permissions for roles and teams that are not required to access the data source. This will help to ensure that only the required teams have access to the data source. The recommended permissions are `Admin` permission and only add the teams `Query` permissions that you want to add LBAC for data sources rules for.
-## Task 1: LBAC Configuration for New Prometheus Data Source
+## Task 1: LBAC Configuration for new Prometheus data source
1. Access Prometheus data sources details for your stack through grafana.com
1. Copy Prometheus details and create a CAP
@@ -53,11 +59,11 @@ For more information on how to setup LBAC for data sources rules for a Prometheu
LBAC for data sources is available in Grafana Enterprise for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created.
-You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-Prometheus/#task-1-lbac-configuration-for-new-Prometheus-data-source) and then add the LBAC configuration to the new data source.
+You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for new Prometheus data source](https://grafana.com/docs/grafana//administration/data-source-management/teamlbac/configure-teamlbac-for-prometheus/#task-1-lbac-configuration-for-new-prometheus-data-source) and then add the LBAC configuration to the new data source.
## Before you begin
-To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you.
+To be able to use LBAC for Prometheus data sources, you need to enable the feature toggle `teamHttpHeadersMimir` on your Grafana instance. Contact support to enable the feature toggle for you.
- Be sure that you have the permission setup to create a cluster in your Grafana
- Be sure that you have admin plugins permissions for Grafana.
@@ -79,7 +85,7 @@ We recommend that you remove all permissions for roles and teams that are not re
- Ensure the CAP does not include `labels` rules.
- Store the `token` to be used as password for authentication.
-## Task 1: LBAC Configuration for New Prometheus Data Source
+## Task 1: LBAC Configuration for new Prometheus data source
1. Create a new Prometheus data source
- In Grafana, proceed to add a new data source and select Prometheus as the type.
diff --git a/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md b/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md
index 3942490c0ff..f76dd3d64bb 100644
--- a/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md
+++ b/docs/sources/administration/data-source-management/teamlbac/create-teamlbac-rules/index.md
@@ -17,8 +17,6 @@ LBAC for data sources is available for LBAC-supported data sources created with
## Before you begin
-To be able to use LBAC for data sources rules, you need to enable the feature toggle `teamHttpHeaders` on your Grafana instance. Contact support to enable the feature toggle for you.
-
- Be sure that you have the permission setup to create a Loki tenant in Grafana Cloud.
- Be sure that you have admin data source permissions for Grafana.
- Be sure that you have a team setup in Grafana.
diff --git a/docs/sources/administration/enterprise-licensing/_index.md b/docs/sources/administration/enterprise-licensing/_index.md
index 131ec8d0f79..16d7e9899f1 100644
--- a/docs/sources/administration/enterprise-licensing/_index.md
+++ b/docs/sources/administration/enterprise-licensing/_index.md
@@ -246,6 +246,14 @@ Your license is controlled by the following rules:
As the license expiration date approaches, you will see a banner in Grafana that encourages you to renew. To learn about how to renew your license and what happens in Grafana when a license expires, refer to [License expiration]({{< relref "#license-expiration" >}}).
+**License token expiration:** Grafana Enterprise requires a valid token, which is automatically renewed.
+
+A license token is a digital key that activates your license. By default, the license token is renewed every 24 hours by calling the Grafana API. Short-lived license tokens enable more frequent validation that licenses are compliant, and allow for more frequent license updates - for example, adding users or invalidating a compromised license.
+
+To view the details of your license token, sign in to Grafana Enterprise as a Server Administrator and visit **Administration** > **General** > **Statistics and licensing**. Token details are in the Token section under License Details.
+
+License token renewal requires internet access, and requires that the `auto_refresh_license` [configuration setting](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/enterprise-configuration/#auto_refresh_license) be set to `true`. If your Grafana Enterprise instance cannot connect to the internet, contact your Grafana Labs account team for additional options for token renewal and license audit.
+
**Grafana License URL:** Your license does not work with an instance of Grafana with a different root URL.
The License URL is the complete URL of your Grafana instance, for example `https://grafana.your-company.com/`. It is defined in the [root_url]({{< relref "../../setup-grafana/configure-grafana/#root_url" >}}) configuration setting.
@@ -256,14 +264,24 @@ The system creates a session when a user signs in to Grafana from a new device,
When a user reaches the session limit, the fourth connection succeeds and the longest inactive session is signed out.
-### Request usage billing
-
-You can request Grafana Labs to activate usage billing which allows an unlimited number of active users. When usage billing is enabled, Grafana does not enforce active user limits or display warning banners. Instead, you are charged for active users that exceed the limit, according to your customer contract.
-
-Usage billing involves a contractual agreement between you and Grafana Labs, and it is only available if Grafana Enterprise is configured to [automatically refresh its license token]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#auto_refresh_license" >}}).
-
### Request a change to your license
To increase the number of licensed users within Grafana, extend a license, or change your licensed URL, contact [Grafana support](/profile/org#support) or your Grafana Labs account team. They will update your license, which you can activate from within Grafana.
For instructions about how to activate your license after it is updated, refer to [Activate an Enterprise license]({{< relref "#activate-an-enterprise-license" >}}).
+
+## Usage billing
+
+Standard Grafana Enterprise licenses include a certain number of seats that can be used, and prevent more users logging into Grafana than have been licensed. This makes sense if you prefer a predictable bill. It can however be a problem if you anticipate uneven usage patterns over time or when it's critical that no user ever be prevented from logging in to Grafana due to capacity constraints.
+
+For those use-cases we support usage-based billing, where your license includes a certain number of included users and you are billed on a monthly basis for any excess active users during the month.
+
+Usage billing involves a contractual agreement between you and Grafana Labs and an update to your license, and it is only available if Grafana Enterprise version 10.0.0 or higher is configured to [automatically refresh its license token]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#auto_refresh_license" >}}).
+
+### User deduplication
+
+If your organization has multiple Grafana Enterprise instances with usage billing enabled, then each active user counts only once toward your license, regardless of how many instances that user signs into. Each Grafana Enterprise instance submits a hashed list of users to Grafana Labs via API every day. Each user email address or anonymous device ID is hashed using a one-way sha256 algorithm, and submitted to Grafana where the hashed users are deduplicated across instances.
+
+### Request usage billing
+
+To request usage billing, contact your Grafana Labs account team or [submit a support ticket](https://grafana.com/profile/org#support).
diff --git a/docs/sources/administration/migration-guide/_index.md b/docs/sources/administration/migration-guide/_index.md
new file mode 100644
index 00000000000..deb7de8c533
--- /dev/null
+++ b/docs/sources/administration/migration-guide/_index.md
@@ -0,0 +1,23 @@
+---
+aliases:
+ - /docs/grafana-cloud/account-management/e2c-guide/
+ - /docs/grafana-cloud/account-management/migration-guide/
+description: Migrate from Grafana OSS/Enterprise to Grafana Cloud
+keywords:
+ - Grafana Cloud
+ - Grafana Enterprise
+ - Grafana OSS
+menuTitle: Migrate from Grafana OSS/Enterprise to Grafana Cloud
+title: Migrate from Grafana OSS/Enterprise to Grafana Cloud
+---
+
+# Migrate from Grafana OSS/Enterprise to Grafana Cloud
+
+When you decide to migrate from your self-managed Grafana instance to Grafana Cloud, you can benefit from the convenience of a managed observability platform, additional cloud-only features, and robust security. There are a couple of key approaches to help you transition to Grafana Cloud.
+
+| Migration type | Tools used | Availability | Migratable resources |
+| :------------- | :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Manual | - Command line utilities
- The Grafana HTTP API
| Generally available in all versions of Grafana OSS/Enterprise | The entire Grafana instance |
+| Automated | The Grafana Cloud Migration Assistant | Available in public preview from Grafana v11.2 using the `onPremToCloudMigrations` feature toggle. This toggle is enabled by default in Grafana v11.5 and later. | - Dashboards
- Folders
- Data sources
- App Plugins
- Panel Plugins
- Library Panels
- Grafana Alerting resources
|
+
+Our detailed [migration guide](https://www.grafana.com/docs/grafana-cloud/account-management/migration-guide/manually-migrate-to-grafana-cloud/) explains the key steps and scripts to manually migrate your resources to Grafana Cloud, covering a comprehensive set of resources in your Grafana instance. Alternatively, the [Grafana Cloud Migration Assistant](https://www.grafana.com/docs/grafana-cloud/account-management/migration-guide/cloud-migration-assistant/), available in public preview in Grafana v11.2 and later, automates the migration process across a broad range of Grafana resources. You can use the migration assistant to migrate a large proportion of your Grafana resources and then, if needed, leverage the migration guide to migrate the rest.
diff --git a/docs/sources/administration/migration-guide/cloud-migration-assistant.md b/docs/sources/administration/migration-guide/cloud-migration-assistant.md
new file mode 100644
index 00000000000..87a5a47cb5a
--- /dev/null
+++ b/docs/sources/administration/migration-guide/cloud-migration-assistant.md
@@ -0,0 +1,195 @@
+---
+description: Migrate from Grafana OSS/Enterprise to Grafana Cloud using the Grafana Cloud Migration Assistant
+keywords:
+ - Grafana Cloud
+ - Grafana Enterprise
+ - Grafana OSS
+menuTitle: Migrate to Grafana Cloud using the Grafana Cloud Migration Assistant
+title: Migrate from Grafana OSS/Enterprise to Grafana Cloud using the Grafana Cloud Migration Assistant
+weight: 400
+---
+
+# Grafana Cloud Migration Assistant
+
+The Grafana Cloud Migration Assistant is available in Grafana 11.2+ as a [public preview feature](https://grafana.com/docs/release-life-cycle/#public-preview) that automatically migrates resources from your Grafana OSS/Enterprise instance to Grafana Cloud. It provides the following functionalities:
+
+- Securely connect your self-managed instance to a Grafana Cloud instance.
+- Seamlessly migrate resources such as dashboards, data sources, and folders to your cloud instance in a few easy steps.
+- View the migration status of your resources in real-time.
+
+Some of the benefits of the migration assistant are:
+
+Ease of use
+: Follow the steps provided by the UI to easily migrate all your resources to Grafana Cloud without using Grafana APIs or scripts.
+
+Security
+: Encrypt and securely migrate your resources to your connected Grafana Cloud instance.
+
+Speed
+: Migrate all of your resources in minutes and accelerate your transition to Grafana Cloud.
+
+## Supported resources
+
+The following resources are supported by the migration assistant:
+
+- Dashboards
+- Folders
+- Data sources
+- App Plugins
+- Panel Plugins
+- Library Panels
+- Grafana Alerting resources
+
+## Before you begin
+
+To use the Grafana migration assistant, you need:
+
+- Grafana v11.2 or above with the `onPremToCloudMigrations` feature toggle enabled. In Grafana 11.5, this is enabled by default. For more information on how to enable a feature toggle, refer to [Configure feature toggles](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles/#configure-feature-toggles).
+- A [Grafana Cloud Stack](https://grafana.com/docs/grafana-cloud/get-started/) you intend to migrate your resources to.
+- [`Admin`](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/) access to the Grafana Cloud Stack. To check your access level, go to `https://grafana.com/orgs//members`.
+- [Grafana server administrator](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/#grafana-server-administrators) access to your existing Grafana OSS/Enterprise instance. To check your access level, go to `https:///admin/users`.
+- Internet access from your existing Grafana OSS/Enterprise instance.
+
+## Access the migration assistant
+
+In Grafana OSS, access to the migration assistant is limited to the server administrator.
+
+In Grafana Enterprise, the server administrator has access to the migration assistant by default. It is also possible to grant access to other Admins using a role-based access control (RBAC) role that enables other admins on the Grafana instance to view, build snapshots, and upload resources to Grafana Cloud.
+
+### Grant access in Grafana Enterprise
+
+{{< admonition type="important">}}
+You must [configure RBAC](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/configure-rbac/) before you can grant other administrators access to the Grafana Migration Assistant.
+{{< /admonition >}}
+
+To grant other Admins access to the migration assistant in Grafana Enterprise:
+
+1. Sign in to Grafana as a server administrator.
+1. Navigate to **Home** > **Administration** > **Users and access** > **Users** in the Grafana sidebar.
+1. Click an Admin.
+1. In the **Organizations** section, click **Change role**.
+1. Select **Organization resource migrator** from the role selector menu under **Migration Assistant**.
+
+ 
+
+1. Click **Apply**.
+
+## Use the migration assistant
+
+You can use the migration assistant to generate a migration token on your Grafana Cloud instance, use that token to connect your self-managed Grafana instance to your Grafana Cloud instance, build snapshots of your self-managed Grafana instance, and upload these snapshots to Grafana Cloud.
+
+### Generate a migration token on the destination cloud instance:
+
+1. Navigate to **Home** > **Administration** > **General** > **Migrate to Grafana Cloud** in the cloud instance where you intend to migrate your resources.
+1. Click on the **Generate a migration token** button.
+
+ 
+
+1. Make a copy of the migration token by copying to clipboard. The token is required to authenticate your self-managed instance with the Grafana Cloud Stack.
+
+### Connect your self-managed Grafana instance to the Grafana Cloud Stack
+
+1. On your self-managed Grafana instance, navigate to **Home** > **Administration** > **General** > **Migrate to Grafana Cloud**.
+
+1. Click the **Migrate this instance to Cloud** button.
+
+ 
+
+1. Enter your token and click **Connect to this Stack**.
+
+ 
+
+### Build a snapshot
+
+After connecting to the cloud stack, this is the empty state of the migration assistant. You need to create a snapshot of the self-managed Grafana instance to upload it to the cloud stack.
+
+- Click **Build snapshot**
+
+ 
+
+### Upload resources to the cloud
+
+After a snapshot is created, a list of resources appears with resource Type and Status populated with **Not yet uploaded**.
+
+
+
+1. Click on **Upload snapshot** to copy the resources to the Grafana Cloud instance. This also updates statuses for the list of resources. The status changes to 'Uploaded to cloud' for resources successfully copied to the cloud.
+
+ The Snapshot information also updates to inform the user of total resources, errors, and total number of successfully migrated resources.
+
+ 
+
+1. Use the assistant's real-time progress tracking to monitor the migration.
+
+1. Review error details for any issues that need manual resolution.
+
+## Snapshots created by the migration assistant
+
+The migration assistant currently supports a subset of all resources available in Grafana. Refer to [Supported Resources](https://wwww.grafana.com/docs/grafana-cloud/account-management/cloud-migration-assistant/#supported-resources) for more details.
+
+When you create a snapshot, the migration assistant makes a copy of all supported resources and saves them in the snapshot. The snapshot reflects the current state of the resources when the snapshot is built and is stored locally on your instance, ready to be uploaded in the last stage. It is currently not possible to select specific resources to include in the snapshot, such as only dashboards. All supported resources are included by default.
+
+Resources saved in the snapshot are strictly limited to the resources stored within an organization. This is important to note if there are multiple organizations used in your Grafana instance. If you want to migrate multiple organizations, refer to [Migrate multiple organizations](https://wwww.grafana.com/docs/grafana-cloud/account-management/cloud-migration-assistant/#migrate-multiple-organizations) for more information and guidance.
+
+## Resource migration details
+
+During a migration, resource UIDs are preserved, allowing you to correlate your local and cloud resources. If you perform the same migration multiple times, resources in your Grafana Cloud stack that were previously migrated are updated. The assistant never modifies your self-managed resources or cloud resources that didn't come from a snapshot.
+
+### Dashboards and folders
+
+Dashboard names and UIDs are preserved along with references to data sources. Folder hierarchy is also preserved, so you can find your dashboards and other resources saved in identical folder locations.
+
+### Data sources
+
+Your data sources, including credentials, are migrated securely and seamlessly to your Grafana Cloud instance, so you don't need to find and enter all your data source credentials again.
+
+### Plugins
+
+The migration assistant supports any plugins found in the plugins catalog. As long as the plugin is signed or is a core plugin built into Grafana, it is eligible for migration. Due to security reasons, unsigned plugins are not supported in Grafana Cloud. If you are using any unsigned private plugins, Grafana recommends you seek an alternative plugin for the catalog or work on a strategy to deprecate certain functionality from your self-managed instance.
+
+### Grafana Alerting resources
+
+The migration assistant can migrate the majority of Grafana Alerting resources to your Grafana Cloud instance. These include:
+
+- Alert rules
+- Notifications
+- Contact points
+- Mute timings
+- Notification policy tree
+- Notification templates
+
+This is sufficient to have your Alerting configuration up and running in Grafana Cloud with minimal effort.
+
+Migration of Silences is not supported by the migration assistant and needs to be configured manually. Alert History is also not available for migration.
+
+Successfully migrating Alerting resources to your Grafana Cloud instance could result in 2 sets of notifications being generated; one from your OSS/Enterprise instance and another from the newly migrated alerts in your Grafana Cloud instance. To avoid double notifications, a new `alert_rules_state` configuration option in the `custom.ini` or `grafana.ini` file controls how Alert Rules are migrated to the Grafana Cloud instance and is set to `paused` by default so you can review and test your Alerting resources in your Grafana Cloud instance without duplicate notifications.
+
+The available options for `alert_rule_state` are:
+
+`paused`
+: Creates all Alert rules in paused state on the Cloud instance. This is helpful to avoid double notifications.
+
+`unchanged`
+: The Alert rules maintain their original state coming from the source instance.
+
+When you are ready to start using your alert rules and notifications from your Grafana Cloud instance, run the migration again with `alert_rules_state = unchanged`.
+
+### Resource permissions
+
+Because the migration assistant does not yet migrate teams or RBAC permissions, your resources are migrated with default permissions. Ensure that you reconfigure permissions in your cloud stack as needed following a migration. For more information, refer to [Grafana Cloud user roles and permissions](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/).
+
+## Migrate multiple organizations
+
+If you are using the [organizations](https://grafana.com/docs/grafana/latest/administration/organization-management/#about-organizations) feature on your Grafana Instance and intend to migrate to Grafana Cloud, you need to plan this aspect of the migration carefully.
+
+The organizations feature is not supported in Grafana Cloud, but folders and RBAC can be used to protect and grant permissions to resources instead. The recommended path is to migrate multiple organizations to a single cloud stack. This is the simplest option and provides the best user experience.
+
+The migration assistant creates and uploads snapshots based on the resources within a specific organization. There is no option to migrate an entire Grafana instance with multiple organizations at once. You need to run the migration process for each organization you want to migrate.
+
+The Grafana server administrator is granted access to the migration assistant by default. The server administrator can perform the migration by switching organizations and running the migration assistant each time. The Grafana server administrator can also grant access to the migration assistant to organization administrators who are members using the RBAC **Migration Assistant:Organization resource migrator** role. This allows those organization administrators to run the migration process for their respective organizations.
+
+### Access Control and managing resources in the Cloud Instance
+
+The main driver for setting up organizations in the first place is resource isolation. In order to achieve this in Grafana Cloud, you can organize resources into folders and set up teams and permissions that correspond to your organizations.
+
+For more information about configuring teams and permissions, refer to [Configure Grafana Teams](https://grafana.com/docs/grafana/latest/administration/team-management/configure-grafana-teams/).
diff --git a/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md b/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md
new file mode 100644
index 00000000000..6049846c108
--- /dev/null
+++ b/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md
@@ -0,0 +1,312 @@
+---
+description: Migrate from Grafana OSS/Enterprise to Grafana Cloud manually
+keywords:
+ - Grafana Cloud
+ - Grafana Enterprise
+ - Grafana OSS
+menuTitle: Manually migrate to Grafana Cloud
+title: Migrate from Grafana OSS/Enterprise to Grafana Cloud manually
+weight: 300
+---
+
+# Migrate from Grafana OSS/Enterprise to Grafana Cloud manually
+
+This migration guide is designed to assist Grafana OSS/Enterprise users in seamlessly transitioning manually to Grafana Cloud.
+
+{{< admonition type="note" >}}
+There isn't yet a standard method for importing existing data into Grafana Cloud from self-managed databases.
+{{< /admonition >}}
+
+## Plan and perform a manual migration
+
+If you need to migrate resources beyond what is supported by the Grafana Cloud Migration Assistant, you can migrate them manually with this guide. Moving your team from Grafana OSS/Enterprise to Grafana Cloud manually involves some coordination and communication in addition to the technical migration in the following documentation.
+
+If you are an existing Grafana OSS/Enterprise customer, contact your account team at Grafana Labs to plan a transition period, arrange licenses, and learn how much your Grafana Cloud subscription costs in comparison to Grafana OSS/Enterprise. The account team can also offer specific guidance and arrange professional services to assist with your migration if needed.
+
+Evaluate Grafana Cloud's security and compliance policies at the [Grafana Labs Trust Center](https://trust.grafana.com/).
+
+You may choose to test Grafana Cloud for some time before migrating your entire organization. To do so, set up a “test” stack in Cloud and migrate resources there first. If you use Grafana Alerting, make sure to set up a different contact point so that alerts do not fire twice.
+
+When you decide to migrate, set aside a day of cutover during which users should not create new dashboards or alerts. Migrate any newly-created resources, turn on your production Alerting contact points and notification policies in Cloud and turn them off in Grafana OSS/Enterprise, and notify your users. You may also choose to redirect the Grafana OSS/Enterprise URL to your Grafana Cloud URL.
+
+| Component | Migration Effort | Notes |
+| ------------ | ---------------- | -------------------------------------------------------------------------- |
+| Folders | Low | |
+| Dashboards | Low | Data source references might need to be renamed |
+| Alerts | Medium | Data source based alerts might need to be adapted |
+| Plugins | Medium | Depends on the feature set of the plugin |
+| Data sources | High | If the data sources references any secrets, you need to provide them again |
+
+## Before you begin
+
+Ensure you have the following:
+
+- A [Grafana Cloud Stack](https://grafana.com/docs/grafana-cloud/get-started/) and access to a Linux Machine (or a working WSL2 installation) to run the code snippets in this guide.
+- Administrator access to a Grafana Cloud stack. To check you access level, Go to `https://grafana.com/orgs//members`
+- Administrator access to your existing Grafana OSS/Enterprise instance. To check your access level, Go to `https:///admin/users`
+- Access to the credentials used to connect to your data sources. For example, API keys or usernames and passwords. Since this information is encrypted, it cannot be copied from one instance to the other.
+- If some of your data sources are only available from inside your network, refer to the requirements for [Private Data Source Connect](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/)
+- For Plugins, Reports and Playlists only: The [curl](https://github.com/curl/curl) and [jq](https://jqlang.github.io/jq/download/) command line tools
+
+## Upgrade Grafana OSS/Enterprise to the latest version
+
+Grafana Cloud stacks generally run the latest version of Grafana. In order to avoid issues during migration, upgrade Grafana by following our guides [here](https://grafana.com/docs/grafana/latest/upgrade-guide/).
+
+## Migrate Grafana resources
+
+In this guide, the term **"resources"** refers to things you create in Grafana, like dashboards, folders, alerts, data sources, and permissions.
+
+The process of migration works by pulling the existing resources (like dashboards and folders) from the old Grafana instance, modifying them if necessary, and then pushing them to the new Grafana Cloud instance.
+
+In the provided code snippets throughout this migration guide, you need to substitute specific placeholders with your actual credentials and instance URLs. Make the following replacements before executing the scripts:
+
+- `$GRAFANA_SOURCE_TOKEN` with the access token from your Grafana OSS/Enterprise instance.
+- `$GRAFANA_DEST_TOKEN` with the access token from your Grafana Cloud instance.
+- `$GRAFANA_ONPREM_INSTANCE_URL` with the URL of your Grafana OSS/Enterprise instance. For example: `https://grafana.mydomain.com`
+- `$GRAFANA_CLOUD_INSTANCE_URL` with the URL of your Grafana Cloud instance. For example: `https://myorganization.grafana.net`
+
+### Migrate Grafana Plugins
+
+Migration of plugins is the first step when transitioning from Grafana OSS/Enterprise to Grafana Cloud, given that plugins are integral components that influence the functionality and display of other Grafana resources, such as dashboards.
+
+1. To retrieve the Plugins installed in your Grafana OSS/Enterprise instance, issue an HTTP GET request to the `/api/plugins` endpoint. Use the following shell command:
+
+ ```shell
+ response=$(curl -s -H "Accept: application/json" -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" "${GRAFANA_ONPREM_INSTANCE_URL}/api/plugins")
+
+ plugins=$(echo $response | jq '[.[] | select(.signatureType == "community" or (.signatureType != "internal" and .signatureType != "")) | {name: .id, version: .info.version}]')
+
+ echo "$plugins" > plugins.json
+ ```
+
+ The command provided above will carry out an HTTP request to this endpoint and accomplish several tasks:
+
+ - It issues a GET request to the `/api/plugins` endpoint of your Grafana OSS/Enterprise instance to retrieve a list of installed plugins.
+ - It filters out the list to only include community plugins and those signed by external parties.
+ - It extracts the plugin ID and version before storing them in a `plugins.json` file.
+
+1. To import the plugins in your Grafana Cloud Instance, execute the following command. This command constructs an HTTP POST request to `https://grafana.com/api/instances//plugins`
+
+ ```shell
+ CLOUD_INSTANCE=$GRAFANA_CLOUD_INSTANCE_URL
+
+ stack_slug="${CLOUD_INSTANCE#*//}"
+ stack_slug="${stack_slug%%.*}"
+ jq -c '.[]' plugins.json | while IFS= read -r plugin; do
+ name=$(echo "$plugin" | jq -r '.name')
+ version=$(echo "$plugin" | jq -r '.version')
+ echo "Adding plugin $name with version $version to stack $stack_slug"
+ response=$(curl -s -X POST "https://grafana.com/api/instances/$stack_slug/plugins" \
+ -H "Authorization: Bearer " \
+ -H "Content-Type: application/json" \
+ -d "{\"plugin\": \"$name\", \"version\": \"$version\"}")
+ echo "POST response for plugin $name version $version: $response"
+ done
+ ```
+
+ Replace `` with your Grafana Cloud Access Policy Token. To create a new one, refer to Grafana Cloud [access policies documentation](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/access-policies/)
+
+ This script iterates through each plugin listed in the `plugins.json` file:
+
+ - It constructs a POST request for each plugin to add it to the specified Grafana Cloud instance.
+ - It reports back the response for each POST request to give you confirmation or information about any issues that occurred.
+
+### Migrate resources that are already provisioned as-code
+
+If you already use tools like [Terraform](https://grafana.com/docs/grafana-cloud/developer-resources/infrastructure-as-code/terraform/), [Ansible](https://grafana.com/docs/grafana-cloud/developer-resources/infrastructure-as-code/ansible/), or [Grafana’s HTTP API](https://grafana.com/docs/grafana-cloud/developer-resources/api-reference/http-api/) to provision resources to Grafana, redirect those to the new Grafana Cloud instance by replacing the Grafana URL and credentials.
+
+### Migrate dashboards, folders, data sources, library panels, and alert rules using Grizzly
+
+Grizzly is a command line tool that streamlines working with Grafana resources. Use it to migrate most of the content in your Grafana instance. Follow these steps in your terminal to install Grizzly. If you need to change the os or the architecture, Refer to the Grizzly [releases](https://github.com/grafana/grizzly/releases) and use the binary according to your needs.
+
+```shell
+# download the binary (adapt os and arch as needed)
+$ curl -fSL -o "/usr/local/bin/grr" "https://github.com/grafana/grizzly/releases/download/v0.3.1/grr-linux-amd64"
+
+# make it executable
+$ chmod a+x "/usr/local/bin/grr"
+
+# have fun :)
+$ grr --help
+```
+
+First, create a new folder on your computer and navigate to it to keep your work organized.
+
+```shell
+mkdir grafana-migration
+cd grafana-migration
+```
+
+To give grizzly access to your Grafana OSS/Enterprise instance and the Grafana Cloud Instance, you need to create a [service account](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/) and a corresponding [access token](https://www.grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/#service-account-tokens) on each instance. You can use these tokens to authenticate requests to pull and push resources. Follow these steps on your Grafana OSS/Enterprise instance:
+
+- Navigate to the **Administration -> Users and access -> Service Accounts** Page within the Grafana OSS/Enterprise instance.
+- Click on **Add Service Account**
+- Give the Service account a descriptive name like “grizzly-migration” and apply the **Admin** role.
+- After creating the account, click on **Add Service Account Token**
+- Enter a name for the token
+- Select **Set expiration date** and enter an expiry date for the token
+- Click **Generate Token** and save this token in a password manager or other secure place.
+
+Complete the service account creation and token generation process for your Grafana Cloud Instance by following the same steps outlined above for your Grafana OSS/Enterprise instance. This ensures that Grizzly has the necessary access token for both platforms.
+
+Next, to tell grizzly which instances you’re going to work on, use the following commands:
+
+```shell
+grr config create-context grafana-onprem
+grr config use-context grafana-onprem
+grr config set output-format json
+grr config set grafana.url $GRAFANA_ENT_INSTANCE_URL
+grr config set grafana.token $GRAFANA_SOURCE_TOKEN
+
+grr config create-context grafana-cloud
+grr config use-context grafana-cloud
+grr config set output-format json
+grr config set grafana.url $GRAFANA_CLOUD_INSTANCE_URL
+grr config set grafana.token $GRAFANA_DEST_TOKEN
+```
+
+Afterward, you will have two contexts set up; one for your local Grafana OSS/Enterprise installation and one for Grafana Cloud. The `grr config use-context` command allows you to switch between the two instances while using Grizzly.
+
+#### Export existing resources
+
+Switch to the `grafana-onprem` context and use the pull command to fetch the resources you want to migrate:
+
+```shell
+grr config use-context grafana-onprem
+grr pull . \
+ -t 'Dashboard/*' \
+ -t 'Datasource/*' \
+ -t 'DashboardFolder/*' \
+ -t 'LibraryElement/*' \
+ -t 'AlertRuleGroup/*' \
+ -t 'AlertContactPoint/*' \
+ -t 'AlertNotificationPolicy/*'
+```
+
+This will fetch the specified resources from Grafana and store them in the current directory.
+
+#### Push the resources to your Grafana Cloud stack
+
+With everything in place, switch to the Grafana cloud context and use the following commands to apply the resources to the configured instance:
+
+```shell
+grr config use-context grafana-cloud
+
+grr apply . -t 'DashboardFolder/*'
+grr apply . -t 'LibraryElement/*'
+grr apply . -t 'Datasource/*'
+grr apply . -t 'Dashboard/*'
+grr apply . -t 'AlertRuleGroup/*'
+grr apply . -t 'AlertContactPoint/*'
+grr apply . -t 'AlertNotificationPolicy/*'
+```
+
+#### Fill in data source credentials
+
+After migrating your data sources, you must fill in their credentials, like tokens, usernames, or passwords. For security reasons, grizzly cannot read encrypted data source credentials from the existing Grafana instance.
+
+To fill in the missing authentication information, go to the **Connections -> Datasources** page in your new Grafana Cloud instance and verify that credentials for all data sources are set. You can skip data sources starting with `grafanacloud` - These are managed by Grafana Cloud directly and provide access to Grafana Cloud databases.
+
+If one of your data sources can only be accessed from your internal network, take a look at the [Private Data Source Connect documentation](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/).
+
+After you have configured the data sources, all your dashboards should be available as they were before.
+
+##### (Optional) Configure Private Data Source Connect (PDC)
+
+This step only applies if you use Grafana OSS/Enterprise to access network-secured data sources.
+
+Some data sources, like Prometheus or SQL databases, live on private networks or behind fire wall rules that are not accessible by Grafana Cloud. If your Grafana OSS/Enterprise instance was hosted on the same network as your data source, you might find that Grafana Cloud cannot connect to all of the same data sources that Grafana OSS/Enterprise could access.
+
+To access these data sources from Grafana Cloud, follow our guide to [configure PDC in your network](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/), and then configure the applicable Grafana data sources to [connect using PDC](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-a-data-source-to-use-private-data-source-connect-pdc). Note that PDC is only needed for your network-secured data sources, not for data sources like Splunk or CloudWatch that are accessible over the public internet.
+
+For more information on how PDC works, see our [overview document]({{< relref "../connect-externally-hosted/private-data-source-connect" >}}).
+
+### Migrate reports and playlists using Grafana’s HTTP API
+
+Grizzly does not currently support Reports and Playlists as a resource, so you can perform this migration using Grafana’s HTTP API using the `curl` command.
+
+#### Reports (For Grafana Enterprise only)
+
+1. To export your Reports, you will need to invoke the `api/reports` endpoint of your Grafana OSS/Enterprise instance. The below shell command accomplishes this by using `curl` to send a request to the endpoint and then stores the retrieved report configuration into a file named `reports.json`.
+
+ ```shell
+ curl ${GRAFANA_ONPREM_INSTANCE_URL}/api/reports -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" > reports.json
+ ```
+
+2. To upload the configuration data you have saved in the `reports.json` file to your new Grafana Cloud instance, run the below command. The command will take the local file `reports.json` and push its contents to the `api/reports` endpoint of your Grafana Cloud instance.
+
+ ```shell
+ jq -M -r -c '.[]' < reports.json | while read -r json; do curl -XPOST ${GRAFANA_CLOUD_INSTANCE_URL}/api/reports -H"Authorization: Bearer $GRAFANA_DEST_TOKEN" -d"$json" -H 'Content-Type: application/json'; done
+ ```
+
+#### Playlists
+
+1. To retrieve the Playlists from your Grafana OSS/Enterprise instance, issue an HTTP GET request to the `/api/playlists` endpoint. Use the following shell command:
+
+ ```shell
+ mkdir playlists
+ curl "${GRAFANA_ONPREM_INSTANCE_URL}/api/playlists" \
+ -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" \
+ | jq -M -r -c '.[] | .uid' \
+ | while read -r uid; do \
+ curl "${GRAFANA_ONPREM_INSTANCE_URL}/api/playlists/$uid" \
+ -H "Authorization: Bearer $GRAFANA_SOURCE_TOKEN" \
+ > playlists/$uid.json; \
+ done
+ ```
+
+ The command provided above will carry out an HTTP request to this endpoint and accomplish several tasks:
+
+ - It fetches an array of all the playlists available in the Grafana OSS/Enterprise instance.
+ - It then iterates through each playlist to obtain the complete set of details.
+ - Finally, it stores each playlist's specification as separate JSON files within a directory named `playlists`
+
+2. To import the playlists, execute the following command. This command constructs an HTTP POST request targeting the `/api/playlists` endpoint of your Grafana Cloud Instance.
+
+ ```shell
+ for playlist in playlists/*; do
+ curl -XPOST "${GRAFANA_CLOUD_INSTANCE_URL}/api/playlists" \
+ -H "Authorization: Bearer $GRAFANA_DEST_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d $playlist > /dev/null;
+ done
+ ```
+
+### Migrate single sign-on configuration
+
+Grafana Cloud stacks support all of the same authentication and authorization options as Grafana OSS/Enterprise, except for [anonymous authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/anonymous-auth/) and use of the [Auth proxy](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/auth-proxy/). However, single sign-on settings cannot be exported and imported like dashboards, alerts, and other resources.
+
+To set up SAML authentication from scratch using Grafana’s UI or API, follow [these instructions](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/saml-ui/) to Configure SAML authentication in Grafana.
+
+LDAP and OIDC/OAuth2 can only be configured in Grafana Cloud by the Grafana Labs support team. Follow [these instructions](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/) to request SSO configuration from the support team.
+
+### Migrate custom Grafana configuration
+
+You may have customized the [configuration](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/) of your Grafana OSS/Enterprise instance, for example with feature toggles, custom auth, or embedding options. Since Grafana configuration is stored in environment variables or the filesystem where Grafana runs, Grafana Cloud users do not have access to it. However, you can open a support ticket to ask a Grafana Labs support engineer for customizations.
+
+The following customizations are available via support:
+
+- Enabling [feature toggles](http://www.grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles).
+- [Single sign-on and team sync using SAML, LDAP, or OAuth](http://www.grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication).
+- Enable [embedding Grafana dashboards in other applications](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#allow_embedding) for Grafana Cloud contracted customers.
+- [Audit logging](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/audit-grafana/) ([Usage insights logs and dashboards](https://grafana.com/docs/grafana-cloud/account-management/usage-insights/) are available in Grafana Cloud Pro and Advanced by default).
+
+Note that the following custom configurations are not supported in Grafana Cloud:
+
+- [Anonymous user access](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/anonymous-auth/).
+- [Auth proxy](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/auth-proxy/).
+- [Third-party database encryption](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-database-encryption/) and the [Hashicorp Vault](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-database-encryption/encrypt-secrets-using-hashicorp-key-vault/) integration.
+- Running self-signed plugins, like custom-built data sources or visualizations. For more information on plugin signing, refer to our [developer documentation](https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin).
+
+If you have a custom configuration in Grafana OSS/Enterprise that is not listed here, reach out to our support team to find out whether they can help you set it up.
+
+## Next steps
+
+After you have successfully migrated resources and configuration from Grafana OSS/Enterprise, consider the following steps to enhance your monitoring experience:
+
+- **Get started with Grafana Cloud**: learn more about the functionality available in Grafana Cloud, which is not available in the open source or Enterprise editions. Read more in [Get started with Grafana Cloud](https://grafana.com/docs/grafana-cloud/get-started/)
+- **AWS PrivateLink for Grafana Cloud**: securely transmit telemetry data from your AWS Virtual Private Cloud (VPC) to Grafana Cloud, entirely within the AWS network.
+ Learn how to set this up with [AWS PrivateLink Integration](https://grafana.com/docs/grafana-cloud/send-data/aws-privatelink/).
+- **Azure PrivateLink for Grafana Cloud**, securely transmit telemetry from your Azure Virtual Network to Grafana Cloud while staying on the Azure network, and avoid exposing your traffic to the public internet.
+ Learn how to set this up with [AWS PrivateLink Integration](https://grafana.com/docs/grafana-cloud/send-data/azure-privatelink/).
+- **[Grafana Integrations](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/)**: ready-made integrations to make monitoring your infrastructure and applications more straightforward.
diff --git a/docs/sources/administration/plugin-management/index.md b/docs/sources/administration/plugin-management/index.md
index 09566ccf250..21e048ed9ea 100644
--- a/docs/sources/administration/plugin-management/index.md
+++ b/docs/sources/administration/plugin-management/index.md
@@ -66,7 +66,7 @@ To prevent users from seeing an app plugin, refer to [these permissions scenario
## Plugin catalog
-The Grafana plugin catalog allows you to browse and manage plugins from within Grafana. Only Grafana server administrators and Organization administrators can access and use the plugin catalog. For more information about Grafana roles and permissions, refer to [Roles and permissions]({{< relref "../administration/roles-and-permissions" >}}).
+The Grafana plugin catalog allows you to browse and manage plugins from within Grafana. Only Grafana server administrators and Organization administrators can access and use the plugin catalog. For more information about Grafana roles and permissions, refer to [Roles and permissions]({{< relref "../roles-and-permissions" >}}).
The following access rules apply depending on the user role:
@@ -236,6 +236,32 @@ WARN[06-01|16:45:59] Running an unsigned plugin pluginID=
If you're developing a plugin, then you can enable development mode to allow all unsigned plugins.
{{% /admonition %}}
+## Integrate plugins
+
+You can configure your Grafana instance to let the frontends of installed plugins directly communicate locally with the backends of other installed plugins. By default, you can only communicate with plugin backends remotely. You can use this configuration to, for example, enable a [canvas panel](https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/canvas/) to call an application resource API that is permitted by the `actions_allow_post_url` option.
+
+To enable backend communication between plugins:
+
+1. Set the plugins you want to communicate with. In your configuration file (`grafana.ini` or `custom.ini` depending on your operating system) remove the semicolon to enable and then set the following configuration option:
+
+ ```
+ actions_allow_post_url=
+ ```
+
+ This is a comma-separated list that uses glob matching.
+
+ - To allow access to all plugins that have a backend:
+
+ ```
+ actions_allow_post_url=/api/plugins/*
+ ```
+
+ - To access to the backend of only one plugin:
+
+ ```
+ actions_allow_post_url=/api/plugins/
+ ```
+
## Plugin Frontend Sandbox
{{% admonition type="caution" %}}
diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md
index 5c164a708aa..b06979b4880 100644
--- a/docs/sources/administration/provisioning/index.md
+++ b/docs/sources/administration/provisioning/index.md
@@ -479,6 +479,7 @@ The following sections detail the supported settings and secure settings for eac
| mentionGroups | |
| mentionChannel | |
| token | yes |
+| color | |
#### Alert notification `victorops`
diff --git a/docs/sources/administration/roles-and-permissions/_index.md b/docs/sources/administration/roles-and-permissions/_index.md
index 134482723b4..87457d1d0fa 100644
--- a/docs/sources/administration/roles-and-permissions/_index.md
+++ b/docs/sources/administration/roles-and-permissions/_index.md
@@ -121,29 +121,6 @@ For more information about assigning dashboard folder permissions, refer to [Gra
For more information about assigning dashboard permissions, refer to [Grant dashboard permissions]({{< relref "../user-management/manage-dashboard-permissions/#grant-dashboard-permissions" >}}).
-## Editors with administrator permissions
-
-If you have access to the Grafana server, you can modify the default editor role so that editors can use administrator permissions to manage dashboard folders, dashboards, and teams that they create.
-
-{{% admonition type="note" %}}
-This permission does not allow editors to manage folders, dashboards, and teams that they do not create.
-{{% /admonition %}}
-
-This setting can be used to enable self-organizing teams to administer their own dashboards.
-
-For more information about assigning administrator permissions to editors, refer to [Grant editors administrator permissions]({{< relref "../user-management/server-user-management/grant-editor-admin-permissions/" >}}).
-
-## Viewers with dashboard preview and Explore permissions
-
-If you have access to the Grafana server, you can modify the default viewer role so that viewers can:
-
-- Edit and preview dashboards, but cannot save their changes or create new dashboards.
-- Access and use [Explore]({{< relref "../../explore" >}}).
-
-Extending the viewer role is useful for public Grafana installations where you want anonymous users to be able to edit panels and queries, but not be able to save or create new dashboards.
-
-For more information about assigning dashboard preview permissions to viewers, refer to [Enable viewers to preview dashboards and use Explore]({{< relref "../user-management/manage-dashboard-permissions/#enable-viewers-to-edit-but-not-save-dashboards-and-use-explore" >}}).
-
## Teams and permissions
A team is a group of users within an organization that have common dashboard and data source permission needs. For example, instead of assigning five users access to the same dashboard, you can create a team that consists of those users and assign dashboard permissions to the team. A user can belong to multiple teams.
@@ -153,7 +130,7 @@ You can assign a team member one of the following permissions:
- **Member**: Includes the user as a member of the team. Members do not have team administrator privileges.
- **Admin**: Administrators have permission to manage various aspects of the team, including team membership, permissions, and settings.
-Because teams exist inside an organization, the organization administrator can manage all teams. When the `editors_can_admin` setting is enabled, editors can create teams and manage teams that they create. For more information about the `editors_can_admin` setting, refer to [Grant editors administrator permissions]({{< relref "../user-management/server-user-management/grant-editor-admin-permissions/" >}}).
+Because teams exist inside an organization, the organization administrator can manage all teams.
For details on managing teams, see [Team management]({{< relref "../team-management/" >}}).
diff --git a/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md b/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md
index 09c2fc4c3d4..e04c2fcecf2 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md
@@ -369,9 +369,11 @@ Here are two ways to achieve this:
# Update the role
curl -H 'Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt' -H 'Content-Type: application/json' \
- -X PUT-d @/tmp/basic_viewer.json '/api/access-control/roles/basic_viewer'
+ -X PUT -d @/tmp/basic_viewer.json '/api/access-control/roles/basic_viewer'
```
+ The token that is used in this request is the [service account token](ref:service-accounts).
+
- Or use the `role > from` list and `permission > state` option of your provisioning file:
```yaml
@@ -394,6 +396,20 @@ Here are two ways to achieve this:
state: 'present'
```
+ If your goal is to remove an access to an app you should remove it from the role and update it. For example:
+
+ ```bash
+ # Fetch the role, modify it to remove permissions to kentik-connect-app and increment role version
+ curl -H 'Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt' \
+ -X GET '/api/access-control/roles/basic_viewer' | \
+ jq 'del(.created)| del(.updated) | del(.permissions[].created) | del(.permissions[].updated) | .version += 1' | \
+ jq 'del(.permissions[] | select (.action == "plugins.app:access" and .scope == "plugins:id:kentik-connect-app"))'
+
+ # Update the role
+ curl -H 'Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt' -H 'Content-Type: application/json' \
+ -X PUT -d @/tmp/basic_viewer.json '/api/access-control/roles/basic_viewer'
+ ```
+
### Manage user permissions through teams
In the scenario where you want users to grant access by the team they belong to, we recommend to set users role to `No Basic Role` and let the team assignment assign the role instead.
diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
index f651becaa4e..2c328c749b8 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
@@ -58,8 +58,8 @@ The following tables list permissions associated with basic and fixed roles.
| ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`
`fixed:roles:writer`
`fixed:users:reader`
`fixed:users:writer`
`fixed:org.users:reader`
`fixed:org.users:writer`
`fixed:ldap:reader`
`fixed:ldap:writer`
`fixed:stats:reader`
`fixed:settings:reader`
`fixed:settings:writer`
`fixed:provisioning:writer`
`fixed:organization:reader`
`fixed:organization:maintainer`
`fixed:licensing:reader`
`fixed:licensing:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:maintainer`
`fixed:authentication.config:writer`
`fixed:library.panels:creator`
`fixed:library.panels:reader`
`fixed:library.panels:general.reader`
`fixed:library.panels:writer`
`fixed:library.panels:general.writer`
`fixed:groupsync:writer`
`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. |
| Admin | `basic_admin` | `fixed:reports:reader`
`fixed:reports:writer`
`fixed:datasources:reader`
`fixed:datasources:writer`
`fixed:organization:writer`
`fixed:datasources.permissions:reader`
`fixed:datasources.permissions:writer`
`fixed:teams:writer`
`fixed:dashboards:reader`
`fixed:dashboards:writer`
`fixed:dashboards.permissions:reader`
`fixed:dashboards.permissions:writer`
`fixed:dashboards.public:writer`
`fixed:folders:reader`
`fixed:folders:writer`
`fixed:folders.permissions:reader`
`fixed:folders.permissions:writer`
`fixed:alerting:writer`
`fixed:apikeys:reader`
`fixed:apikeys:writer`
`fixed:alerting.provisioning.secrets:reader`
`fixed:alerting.provisioning:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:writer`
`fixed:library.panels:creator`
`fixed:library.panels:reader`
`fixed:library.panels:general.reader`
`fixed:library.panels:writer`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer`
`fixed:groupsync:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
-| Editor | `basic_editor` | `fixed:datasources:explorer`
`fixed:dashboards:creator`
`fixed:folders:creator`
`fixed:annotations:writer`
`fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled
`fixed:alerting:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:creator`
`fixed:library.panels:general.reader`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
-| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
`fixed:organization:reader`
`fixed:annotations:reader`
`fixed:annotations.dashboard:writer`
`fixed:alerting:reader`
`fixed:plugins.app:reader`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:general.reader`
`fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. |
+| Editor | `basic_editor` | `fixed:datasources:explorer`
`fixed:dashboards:creator`
`fixed:folders:creator`
`fixed:annotations:writer`
`fixed:alerting:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:creator`
`fixed:library.panels:general.reader`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
+| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
`fixed:organization:reader`
`fixed:annotations:reader`
`fixed:annotations.dashboard:writer`
`fixed:alerting:reader`
`fixed:plugins.app:reader`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:general.reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
## Fixed role definitions
diff --git a/docs/sources/administration/user-management/manage-dashboard-permissions/index.md b/docs/sources/administration/user-management/manage-dashboard-permissions/index.md
index e87d0193fcc..b75f675cc5e 100644
--- a/docs/sources/administration/user-management/manage-dashboard-permissions/index.md
+++ b/docs/sources/administration/user-management/manage-dashboard-permissions/index.md
@@ -63,27 +63,7 @@ Grant dashboard permissions when you want to restrict or enhance dashboard acces
1. Select the user, service account, team, or role.
1. Select the permission and click **Save**.
-## Enable viewers to edit (but not save) dashboards and use Explore
-
-By default, the viewer organization role does not allow viewers to create dashboards or use the Explore feature. However, by modifying a configuration setting, you can allow viewers to edit a panel and make changes to a dashboard but not save those changes. This setting also enables viewers to use the Explore feature.
-
-This modification is useful for public Grafana installations where you want anonymous users to be able to edit panels and queries but not save or create new dashboards.
-
-### Before you begin
-
-- Ensure that you have access to the Grafana server
-
-**To enable viewers to preview dashboards and use Explore**:
-
-1. Open the Grafana configuration file.
-
- For more information about the Grafana configuration file and its location, refer to [Configuration]({{< relref "../../../setup-grafana/configure-grafana/" >}}).
-
-1. Locate the `viewers_can_edit` parameter.
-1. Set the `viewers_can_edit` value to `true`.
-1. Save your changes and restart Grafana.
-
-## Edit dashboard permissions
+# Edit dashboard permissions
Edit dashboard permissions when you are want to enhance or restrict a user's access to a dashboard. For more information about dashboard permissions, refer to [Dashboard permissions]({{< relref "../../roles-and-permissions/#dashboard-permissions" >}}).
diff --git a/docs/sources/administration/user-management/server-user-management/grant-editor-admin-permissions/index.md b/docs/sources/administration/user-management/server-user-management/grant-editor-admin-permissions/index.md
deleted file mode 100644
index cfefd775975..00000000000
--- a/docs/sources/administration/user-management/server-user-management/grant-editor-admin-permissions/index.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-aliases:
- - ../../manage-users-and-permissions/manage-server-users/grant-editor-admin-permissions/
-labels:
- products:
- - enterprise
- - oss
-title: Grant editors team creator permissions
-weight: 60
----
-
-# Grant editors team creator permissions
-
-By default, the editor organization role does not allow editors to creator and manage teams. You can allow them to do so using the `editors_can_admin` configuration option.
-
-When `editors_can_admin` is enabled, users with the Editor role in an organization can create teams, and they are Administrators of the teams they create. To learn more about team permissions, refer to [Team management]({{< relref "../../../team-management/" >}}).
-
-## Before you begin
-
-- Ensure that you have access to the Grafana server
-
-**To enable editors with team creator permissions**:
-
-1. Log in to the Grafana server and open the Grafana configuration file.
-
- For more information about the Grafana configuration file and its location, refer to [Configuration]({{< relref "../../../../setup-grafana/configure-grafana/" >}}).
-
-1. Locate the `editors_can_admin` parameter.
-1. Set the `editors_can_admin` value to `true`.
-1. Save your changes and restart the Grafana server.
diff --git a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md
index 2ad18a60ab6..41c9150b52c 100644
--- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md
+++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md
@@ -148,8 +148,6 @@ You can toggle between the two options. Once you have created an alert rule, the
Switching from advanced to default may result in queries and expressions that cannot be converted. In this case, a warning message asks if you want to continue to reset to default settings.
-Default and advanced options are enabled by default for Grafana Cloud users and this feature is being rolled out progressively. OSS users can enable them via the [`alertingQueryAndExpressionsStepMode` feature toggle](/setup-grafana/configure-grafana/feature-toggles/).
-
{{< docs/shared lookup="alerts/configure-alert-rule-name.md" source="grafana" version="" >}}
## Define query and condition
@@ -259,16 +257,11 @@ Complete the following steps to set up notifications.
1. You can also optionally select a mute timing as well as groupings and timings to define when not to send notifications.
- {{< admonition type="note" >}}
- An auto-generated notification policy is generated. Only admins can view these auto-generated policies from the **Notification policies** list view. Any changes have to be made in the alert rules form. {{< /admonition >}}
-
**Use notification policy**
- 1. Choose this option to use the [notification policy tree](ref:notification-policies) to direct your notifications.
+ 1. Choose this option to use the [notification policy tree](ref:notification-policies) to handle alert notifications.
- {{< admonition type="note" >}}
- All alert rules and instances, irrespective of their labels, match the default notification policy. If there are no nested policies, or no nested policies match the labels in the alert rule or alert instance, then the default notification policy is the matching policy.
- {{< /admonition >}}
+ All notifications for this alert rule are managed by the notification policy tree, which routes alerts based on their labels. If an alert does not match a specific policy, the default notification policy applies, ensuring all alerts are handled.
1. Preview your alert instance routing set up.
diff --git a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md
index aeaddf1e405..746cb778052 100644
--- a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md
+++ b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md
@@ -44,6 +44,10 @@ Grafana allows you to link an alert rule to a dashboard panel. This can help you
An alert rule is linked to a panel by setting the [`dashboardUId` and `panelId` annotations](ref:annotations). Both annotations must be set together.
+{{% admonition type="tutorial" %}}
+For a hands-on example of integrating alert rules with dashboards, check out [Part 5 of our Get Started with Grafana Alerting tutorial](http://www.grafana.com/tutorials/alerting-get-started-pt5/).
+{{% /admonition %}}
+
## Link alert rules to panels
When configuring the alert rule, you can set the dashboard and panel annotations as shown in this [video](https://youtu.be/ClLp-iSoaSY?si=qKWnvSVaQuvYcuw9&t=170).
diff --git a/docs/sources/alerting/configure-notifications/create-silence.md b/docs/sources/alerting/configure-notifications/create-silence.md
index 6f781a60ce3..5cf6f4a9c04 100644
--- a/docs/sources/alerting/configure-notifications/create-silence.md
+++ b/docs/sources/alerting/configure-notifications/create-silence.md
@@ -61,7 +61,7 @@ refs:
# Configure silences
-Silences stop notifications from getting created and last for only a specified window of time. Use them to temporarily prevent alert notifications, such as during incident response or a maintenance window.
+Silences stop notifications from being created for a specified time window but do not interrupt alert evaluation. Use them to temporarily prevent alert notifications, such as during incident response or a maintenance window.
{{< admonition type="note" >}}
Silences are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and only suppress notifications for alerts managed by that Alertmanager.
diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md b/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md
index ce35c4c75e8..215ed1926dc 100644
--- a/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md
+++ b/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md
@@ -86,12 +86,7 @@ refs:
destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt/
- alertmanager-architecture:
- - pattern: /docs/grafana/
- destination: /docs/grafana//alerting/configure-notifications/#alertmanager-architecture
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/#alertmanager-architecture
- external-alertmanager:
+ configure-alertmanager:
- pattern: /docs/grafana/
destination: /docs/grafana//alerting/set-up/configure-alertmanager/
- pattern: /docs/grafana-cloud/
@@ -101,13 +96,23 @@ refs:
destination: /docs/grafana//alerting/configure-notifications/template-notifications/manage-notification-templates/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/manage-notification-templates/
+ configure-grafana-alerts:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/alerting-rules/create-grafana-managed-rule/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/
+ configure-contact-points:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/
---
# Configure contact points
Use contact points to specify where to receive alert notifications. Contact points contain the configuration for sending alert notifications, including destinations like email, Slack, OnCall, webhooks, and their notification messages.
-A contact point can have one or multiple destinations, known as [contact point integrations](#list-of-supported-integrations). Alert notifications are sent to each integration within the chosen contact point.
+A contact point can have one or multiple destinations, known as [contact point integrations](#supported-contact-point-integrations). Alert notifications are sent to each integration within the chosen contact point.
On the **Contact Points** tab, you can:
@@ -119,9 +124,42 @@ On the **Contact Points** tab, you can:
- Delete contact points. Note that you cannot delete contact points that are in use by a notification policy. To proceed, either delete the notification policy or update it to use another contact point.
{{% admonition type="note" %}}
-Contact points are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and cannot be used by notification policies in other Alertmanagers.
+Contact points are assigned to a [specific Alertmanager](ref:configure-alertmanager) and cannot be used by notification policies in other Alertmanagers.
{{% /admonition %}}
+## Supported contact point integrations
+
+Each contact point integration has its own configuration options and setup process. The following list shows the contact point integrations supported by Grafana.
+
+{{< column-list >}}
+
+- Alertmanager
+- [AWS SNS](ref:sns)
+- Cisco Webex Teams
+- DingDing
+- [Discord](ref:discord)
+- [Email](ref:email)
+- [Google Chat](ref:gchat)
+- [Grafana OnCall](ref:oncall)
+- Kafka REST Proxy
+- Line
+- [Microsoft Teams](ref:teams)
+- [MQTT](ref:mqtt)
+- [Opsgenie](ref:opsgenie)
+- [PagerDuty](ref:pagerduty)
+- Pushover
+- Sensu Go
+- [Slack](ref:slack)
+- [Telegram](ref:telegram)
+- Threema Gateway
+- VictorOps
+- [Webhook](ref:webhook)
+- WeCom
+
+{{< /column-list >}}
+
+Some of the integrations above are not supported by Prometheus Alertmanager. For the list of supported integrations, refer to the [Prometheus Alertmanager receiver settings](https://prometheus.io/docs/alerting/latest/configuration/#receiver-integration-settings).
+
## Add a contact point
Complete the following steps to add a contact point.
@@ -132,7 +170,7 @@ Complete the following steps to add a contact point.
1. On the **Contact Points** tab, click **+ Add contact point**.
1. Enter a descriptive name for the contact point.
1. From **Integration**, select a type and fill out mandatory fields. For example, if you choose email, enter the email addresses. Or if you choose Slack, enter the Slack channel and users who should be contacted.
-1. Some contact point integrations, like email or Webhook, have optional settings. In **Optional settings**, specify additional settings for the selected contact point integration.
+1. Some [contact point integrations](#supported-contact-point-integrations), like email or Webhook, have optional settings. In **Optional settings**, specify additional settings for the selected contact point integration.
1. In Notification settings, optionally select **Disable resolved message** if you do not want to be notified when an alert resolves.
1. Save your changes.
@@ -148,6 +186,16 @@ To add another integration to a contact point, complete the following steps.
- In **Optional settings**, specify additional settings for the selected contact point integration.
1. Save your changes.
+## Customize notification messages
+
+In contact points, you can also customize notification messages. For example, when setting up an email contact point integration, click **Message** or **Subject** to modify it.
+
+By default, notification messages include common alert details, which are usually sufficient for most cases.
+
+If necessary, you can customize the content and format of notification messages. You can create a custom notification template, which can then be applied to one or more contact points.
+
+On the **Notification templates** tab, you can view, edit, copy or delete notification templates. Refer to [manage notification templates](ref:manage-notification-templates) for instructions on selecting or creating a template for a contact point.
+
## Test a contact point
Testing a contact point is only available for Grafana Alertmanager. Complete the following steps to test a contact point.
@@ -159,45 +207,9 @@ Testing a contact point is only available for Grafana Alertmanager. Complete the
1. Choose whether to send a predefined test notification or choose custom to add your own custom annotations and labels to include in the notification.
1. Click **Send test notification** to fire the alert.
-## Customize notification messages
+## Enable notifications for a contact point
-In contact points, you can also customize notification messages. For example, when setting up an email contact point integration, click **Message** or **Subject** to modify it.
+After creating a contact point, you can enable it to receive alert notifications using one of the following methods:
-By default, notification messages include common alert details, which are usually sufficient for most cases.
-
-If necessary, you can customize the content and format of notification messages. You can create a custom notification template, which can then be applied to one or more contact points.
-
-On the **Notification templates** tab, you can view, edit, copy or delete notification templates. Refer to [manage notification templates](ref:manage-notification-templates) for instructions on selecting or creating a template for a contact point.
-
-## List of supported integrations
-
-Each contact point integration has its own configuration options and setup process. In most cases, this involves providing an API key or a Webhook URL.
-
-The following table lists the contact point integrations supported by Grafana.
-
-| Name | Type |
-| ---------------------------- | ------------------------- |
-| Alertmanager | `prometheus-alertmanager` |
-| [Amazon SNS](ref:sns) | `sns` |
-| Cisco Webex Teams | `webex` |
-| DingDing | `dingding` |
-| [Discord](ref:discord) | `discord` |
-| [Email](ref:email) | `email` |
-| [Google Chat](ref:gchat) | `googlechat` |
-| [Grafana Oncall](ref:oncall) | `oncall` |
-| Kafka REST Proxy | `kafka` |
-| Line | `line` |
-| [Microsoft Teams](ref:teams) | `teams` |
-| [MQTT](ref:mqtt) | `mqtt` |
-| [Opsgenie](ref:opsgenie) | `opsgenie` |
-| [Pagerduty](ref:pagerduty) | `pagerduty` |
-| Pushover | `pushover` |
-| Sensu Go | `sensugo` |
-| [Slack](ref:slack) | `slack` |
-| [Telegram](ref:telegram) | `telegram` |
-| Threema Gateway | `threema` |
-| VictorOps | `victorops` |
-| [Webhook](ref:webhook) | `webhook` |
-| WeCom | `wecom` |
-
-Some of these integrations are not compatible with [external Alertmanagers](ref:external-alertmanager). For the list of Prometheus Alertmanager integrations, refer to the [Prometheus Alertmanager receiver settings](https://prometheus.io/docs/alerting/latest/configuration/#receiver-integration-settings).
+- **Assign it to alert rules** – Select the contact point in the [notifications options for Grafana-managed alert rules](ref:configure-grafana-alerts) to directly associate it with specific alerts.
+- **Assign it to notification policies** – Add the contact point to one or more [notification policies](ref:configure-contact-points), which manage the alert notifications you want the contact point to receive.
diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md
index 84274e64043..9c449640511 100644
--- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md
+++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns.md
@@ -14,11 +14,32 @@ labels:
menuTitle: Amazon SNS
title: Configure Amazon SNS for Alerting
weight: 100
+refs:
+ notification-templates:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/configure-notifications/template-notifications/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/
+ configure-contact-points:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/
+ test-contact-point:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/#test-a-contact-point
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/#test-a-contact-point
+ enable-contact-point-notifications:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/#enable-notifications-for-a-contact-point
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/#enable-notifications-for-a-contact-point
---
-# Configure Amazon SNS for Alerting
+# Configure Amazon SNS notifications
-Use the Grafana Alerting - Amazon SNS integration to send notifications to Amazon SNS when your alerts are firing. You can receive notifications via the various subscriber channels supported by SNS.
+Use the Amazon SNS integration in a contact point to send alert notifications to a SNS topic. Then, configure the SNS topic to forward notifications to distinct subscriber channels used in your SNS account.
## Before you begin
@@ -27,71 +48,51 @@ Before you begin, ensure you have the following:
- **AWS SNS Topic**: An SNS topic to send notifications to.
- **AWS IAM Identity with necessary access**: An IAM identity (e.g. user, role) with the necessary permissions to publish messages to the SNS topic.
-For an example setup, see [Example Minimal Setup Using Assumed IAM Role]({{< relref "#example-minimal-setup-using-assumed-iam-role" >}}).
+For a minimal setup, refer to [Example using an Access Key](#example-using-an-access-key).
-## Adding the SNS Contact Point in Grafana
+## Configure Amazon SNS for a contact point
-With AWS resources configured, proceed to add SNS as a contact point in Grafana.
+To create a contact point with a SNS integration, complete the following steps.
-- Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**.
-- Click on **"Add contact point"**.
-- **Name**: Enter a descriptive name (e.g., `AWS SNS`).
-- Choose **"AWS SNS"** from the list of contact point types.
+1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**.
+1. Click **+ Add contact point**.
+1. Enter a name for the contact point.
+1. From the **Integration** list, select **AWS SNS**.
+1. Set up the required [settings](#sns-settings) for your SNS configuration.
+1. Click **Save contact point**.
-### 2. Configure SNS Settings
+For more details on contact points, including how to test them and enable notifications, refer to [Configure contact points](ref:configure-contact-points).
-#### SNS Settings
+## SNS Settings
- **The Amazon SNS API URL**: (Optional) The SNS API URL, e.g., `https://sns.us-east-2.amazonaws.com`. If not specified, the SNS API URL from the SNS SDK will be used.
- **Signature Version (sigv4)**: Configures AWS's Signature Verification 4 signing process to sign requests.
- **Region**: (Optional) The AWS region. If blank, the region from the default credentials chain is used.
- - **Access Key**: The AWS API access key.
- - **Secret Key**: The AWS API secret key.
+ - **Access Key** : (Optional) The AWS API access key.
+ - **Secret Key**: (Optional) The AWS API secret key.
+ > Both `Access Key` and `Secret Key` must be provided together or left blank together.
+ >
+ > If left blank, Grafana searches for credentials using the default credentials chain, including environment variables (`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`), the shared credential file, and EC2 instance roles.
- **Profile**: (Optional) Named AWS profile used to authenticate.
- **Role ARN**: (Optional) The ARN of an AWS IAM role to assume for authentication, serving as an alternative to using AWS API keys.
- **SNS topic ARN**: (Optional) If you don't specify this value, you must specify a value for the `Phone number` or `Target ARN`. If you are using a FIFO SNS topic you should set a message group interval longer than 5 minutes to prevent messages with the same group key being deduplicated by the SNS default deduplication window.
- **Phone number**: (Optional) Phone number if message is delivered via SMS in E.164 format. If you don't specify this value, you must specify a value for the `SNS topic ARN` or `Target ARN`.
- **Target ARN**: (Optional) The mobile platform endpoint ARN if message is delivered via mobile notifications. If you don't specify this value, you must specify a value for the `SNS topic ARN` or `Phone number`.
-- **Subject**: (Optional) Customize the subject line or use the default template. This field is templateable.
-- **Message**: (Optional) Customize the message content or use the default template. This field is templateable.
+- **Subject**: (Optional) Customize the subject. This field supports [notification templates](ref:notification-templates) and, by default, uses the default title template (`default.title`). It **cannot be an empty string**.
+- **Message**: (Optional) Customize the message. This field supports [notification templates](ref:notification-templates) and, by default, uses the default message template (`default.message`).
- **Attributes**: (Optional) Add any SNS message attributes.
-{{< admonition type="note" >}}
-Both `Access Key` and `Secret Key` must be provided together or left blank together. If blank it defaults to a chain of credential
-providers to search for credentials in environment variables, shared credential file, and EC2 Instance Roles.
+## Example using an Access Key
-Environment variables: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
-{{< /admonition >}}
+This section outlines a minimal setup to configure Amazon SNS with Alerting.
-### 3. Test & Save the Contact Point
-
-- Click **"Test"** to verify that the SNS configuration is working correctly.
-- After the test is successful, click **"Save contact point"** to add the SNS contact point.
-
-### 4. Next steps
-
-The SNS contact point is ready to receive alert notifications.
-
-To add this contact point to your alert, complete the following steps.
-
-1. In Grafana, navigate to **Alerting** > **Alert rules**.
-1. Edit or create a new alert rule.
-1. Scroll down to the **Configure labels and notifications** section.
-1. Under Notifications click **Select contact point**.
-1. From the drop-down menu, select the previously created contact point.
-1. **Click Save rule and exit**.
-
-## Example Minimal Setup Using Assumed IAM Role
-
-This section outlines a minimal setup to configure SNS with Grafana using an assumed IAM Role.
-
-### 1. Create an SNS Topic
+### 1. Create an SNS Topic and Email Subscriber
1. **Navigate to SNS in AWS Console**:
- Go to the [Amazon SNS Console](https://console.aws.amazon.com/sns/v3/home).
-2. **Create a new topic** [[AWS Documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html)]:
+2. **Create a new topic**:
- On the **Topics** page, choose **"Create topic"**.
- Select **"Standard"** as the type.
@@ -99,105 +100,70 @@ This section outlines a minimal setup to configure SNS with Grafana using an ass
- **Encryption**: Leave disabled for this minimal setup.
- Click **"Create topic"**.
-3. (Optional) **Add an email subscriber to help test** [[AWS Documentation](https://docs.aws.amazon.com/sns/latest/dg/sns-email-notifications.html)]:
+3. (Optional) **Add an email subscriber to help test**:
- Within your newly created topic, click on **"Create subscription"**.
- **Protocol**: Choose `Email`.
- **Endpoint**: Enter your email address to receive test notifications.
- Click **"Create subscription"**.
- **Confirm Subscription**: Check your email and confirm the subscription by clicking the provided link.
-### 2. Create an IAM Role
+### 2. Create an IAM Policy, User, and Access Key
1. **Navigate to IAM in AWS Console**:
- Go to the [IAM Console](https://console.aws.amazon.com/iam/home).
-2. **Create a new role** [[AWS Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html)]:
+2. **Create a new policy**:
- - On the **Roles** page, choose **"Create role"**.
- - **Trusted Entity**: Select **"This account"**.
- - Click **"Next"** until the end, name it (e.g., `GrafanaSNSRole`), and click **"Create role"**.
-
-3. **Attach Inline Policy**:
-
- - After creating the role, select it and navigate to the **"Permissions"** tab.
- - Click on **"Add permission"** > **"Create inline policy"**.
+ - On the **Policies** page, choose **"Create policy"**.
- Switch to the **"JSON"** tab and paste the following policy, replacing `Resource` with your SNS topic ARN:
- ```json
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": ["sns:Publish", "sns:GetTopicAttributes"],
- "Resource": "arn:aws:sns:::"
- }
- ]
- }
- ```
+ ```json
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": ["sns:Publish", "sns:GetTopicAttributes"],
+ "Resource": "arn:aws:sns:::"
+ }
+ ]
+ }
+ ```
- Click **"Next"**, name it (e.g., `SNSPublishPolicy`), and click **"Create policy"**.
-### 3. Create an IAM Policy
-
-1. **Create a new policy to allow assuming the above IAM role** [[AWS Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html)]:
-
- - In the IAM Console, on the **Policies** page, choose **"Create policy"**.
- - Switch to the **"JSON"** tab and paste the following policy, replacing `Resource` with the ARN of the role you created earlier:
-
- ```json
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": "sts:AssumeRole",
- "Resource": "arn:aws:iam:::role/GrafanaSNSRole"
- }
- ]
- }
- ```
-
-2. **Review and Create**:
- - Click **"Next"**, name it (e.g., `AssumeSNSRolePolicy`), and click **"Create policy"**.
-
-### 4. Create an IAM User
-
-1. **Create a new IAM user to assume the above role** [[AWS Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html)]:
+3. **Create a new IAM user and assign the policy**
- In the IAM Console, on the **Users** page, choose **"Create user"**.
- - Enter a **User name**, e.g., `grafana-sns-user`.
+ - Enter a **User name**, e.g., `alerting-sns-user`.
- Click **"Next"**.
- - Select **"Attach policies directly"**.
- - Search for the policy you created earlier (`AssumeSNSRolePolicy`) and select it.
+ - In **Set permissions**, select **"Attach policies directly"**.
+ - Search for the policy you created earlier (`SNSPublishPolicy`) and select it.
- Click **"Next"** , and click **"Create user"**.
-2. **Generate credentials**:
+4. **Create an Access Key**:
- Within your newly created user, click on **"Create access key"**.
- Select an appropriate use-case, e.g., `Application running outside AWS`.
- Click **"Next"** , and click **"Create access key"**.
- - **Save Credentials**: Note the **Access key ID** and **Secret access key**. You'll need these for Grafana's configuration.
+ - **Save Credentials**: Note the **Access key ID** and **Secret access key** that are required in the next step.
-### 5. Add the SNS Contact Point in Grafana
+### 3. Configure the SNS Contact Point in Grafana
-After creating the IAM user and obtaining the necessary credentials, proceed to [configure the SNS contact point in Grafana]({{< relref "#adding-the-sns-contact-point-in-grafana" >}}) using the following details:
+Follow the steps in [configure Amazon SNS for a contact point](#configure-amazon-sns-for-a-contact-point), using the settings below and replacing the placeholders with the SNS and IAM values created in the previous steps.
-- **The Amazon SNS API URL**: `https://sns.us-east-1.amazonaws.com`
+- **The Amazon SNS API URL**: `https://sns..amazonaws.com`
- **Signature Version (sigv4)**:
- - **Region**: `us-east-1`
+ - **Region**: ``
- **Access Key**: ``.
- **Secret Key**: ``
- - **Role ARN**: `arn:aws:iam:::role/GrafanaSNSRole`
-- **SNS topic ARN**: `arn:aws:sns:::My-Topic`
+- **SNS topic ARN**: `arn:aws:sns:::`
-{{< admonition type="note" >}}
-Replace the placeholder values (`https://sns.us-east-1.amazonaws.com`, `us-east-1`, ``, ``, `arn:aws:iam:::role/GrafanaSNSRole`, `arn:aws:sns:::My-Topic`) with your actual AWS credentials and ARNs.
-{{< /admonition >}}
+[Test the contact point](ref:test-contact-point) to ensure it's working, or [enable notifications](ref:enable-contact-point-notifications) for it.
## Additional Resources
+- [Configure contact points](ref:configure-contact-points)
- [Amazon SNS Documentation](https://docs.aws.amazon.com/sns/index.html)
-- [AWS IAM Documentation](https://docs.aws.amazon.com/iam/index.html)
-- [Prometheus Alertmanager SNS Integration](https://prometheus.io/docs/alerting/configuration/#sns_config)
-- [Cloudwatch AWS Authentication]({{< relref "../../../../datasources/aws-cloudwatch/aws-authentication" >}})
+- [Amazon IAM Documentation](https://docs.aws.amazon.com/iam/index.html)
+- [Prometheus Alertmanager SNS Configuration](https://prometheus.io/docs/alerting/configuration/#sns_config)
diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md
index 6562b948095..e7741ac3b56 100644
--- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md
+++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier.md
@@ -28,13 +28,83 @@ refs:
destination: /docs/grafana//alerting/configure-notifications/template-notifications/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/
+ configure-contact-points:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/
---
-# Configure the webhook notifier for Alerting
+# Configure webhook notifications
-The webhook notification is a simple way to send information about a state change over HTTP to a custom endpoint. Using this notification you could integrate Grafana into a system of your choosing.
+Use the webhook integration in contact points to send alert notifications to your webhook.
-## Webhook JSON payload
+The webhook integration is a flexible way to integrate alerts into your system. When a notification is triggered, it sends a JSON request with alert details and additional data to the webhook endpoint.
+
+## Configure webhook for a contact point
+
+To create a contact point with webhook integration, complete the following steps.
+
+1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**.
+1. Click **+ Add contact point**.
+1. Enter a name for the contact point.
+1. From the **Integration** list, select **Webhook**.
+1. In the **URL** field, copy in your Webhook URL.
+1. (Optional) Configure [additional settings](#webhook-settings).
+1. Click **Save contact point**.
+
+For more details on contact points, including how to test them and enable notifications, refer to [Configure contact points](ref:configure-contact-points).
+
+## Webhook settings
+
+| Option | Description |
+| ------ | ---------------- |
+| URL | The Webhook URL. |
+
+#### Optional settings
+
+| Option | Description |
+| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
+| HTTP Method | Specifies the HTTP method to use: `POST` or `PUT`. |
+| Basic Authentication Username | Username for HTTP Basic Authentication. |
+| Basic Authentication Password | Password for HTTP Basic Authentication. |
+| Authentication Header Scheme | Scheme for the `Authorization` Request Header. Default is `Bearer`. |
+| Authentication Header Credentials | Credentials for the `Authorization` Request header. |
+| Max Alerts | Maximum number of alerts to include in a notification. Any alerts exceeding this limit are ignored. `0` means no limit. |
+| TLS | TLS configuration options, including CA certificate, client certificate, and client key. |
+
+{{< admonition type="note" >}}
+
+You can configure either HTTP Basic Authentication or the Authorization request header, but not both.
+
+{{< /admonition >}}
+
+#### Optional settings using templates
+
+Use the following settings to include custom data within the [JSON payload](#body). Both options support using [notification templates](ref:notification-templates).
+
+| Option | Description |
+| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
+| Title | Sends the value as a string in the `title` field of the [JSON payload](#body). Supports [notification templates](ref:notification-templates). |
+| Message | Sends the value as a string in the `message` field of the [JSON payload](#body). Supports [notification templates](ref:notification-templates). |
+
+{{< admonition type="note" >}}
+You can customize the `title` and `message` options to include custom messages and notification data using notification templates. These fields are always sent as strings in the JSON payload.
+
+However, you cannot customize the webhook data structure, such as adding or changing other JSON fields and HTTP headers, or sending data in a different format like XML.
+
+If you need to format these fields as JSON or modify other webhook request options, consider sending webhook notifications to a proxy server that adjusts the webhook request before forwarding it to the final destination.
+{{< /admonition >}}
+
+#### Optional notification settings
+
+| Option | Description |
+| ------------------------ | ------------------------------------------------------------------- |
+| Disable resolved message | Enable this option to prevent notifications when an alert resolves. |
+
+## JSON payload
+
+The following example shows the payload of a webhook notification containing information about two firing alerts:
```json
{
@@ -106,76 +176,47 @@ The webhook notification is a simple way to send information about a state chang
}
```
-## Webhook fields
-
### Body
-| Key | Type | Description |
-| ----------------- | ------------------------- | ------------------------------------------------------------------------------- |
-| receiver | string | Name of the webhook |
-| status | string | Current status of the alert, `firing` or `resolved` |
-| orgId | number | ID of the organization related to the payload |
-| alerts | array of [alerts](#alert) | Alerts that are triggering |
-| groupLabels | object | Labels that are used for grouping, map of string keys to string values |
-| commonLabels | object | Labels that all alarms have in common, map of string keys to string values |
-| commonAnnotations | object | Annotations that all alarms have in common, map of string keys to string values |
-| externalURL | string | External URL to the Grafana instance sending this webhook |
-| version | string | Version of the payload |
-| groupKey | string | Key that is used for grouping |
-| truncatedAlerts | number | Number of alerts that were truncated |
-| title | string | Custom title |
-| state | string | State of the alert group (either `alerting` or `ok`) |
-| message | string | Custom message |
+The JSON payload of webhook notifications includes the following key-value pairs:
+
+| Key | Type | Description |
+| ------------------- | ------------------------- | -------------------------------------------------------------------------------- |
+| `receiver` | string | Name of the contact point. |
+| `status` | string | Current status of the alert, `firing` or `resolved`. |
+| `orgId` | number | ID of the organization related to the payload. |
+| `alerts` | array of [alerts](#alert) | Alerts that are triggering. |
+| `groupLabels` | object | Labels that are used for grouping, map of string keys to string values. |
+| `commonLabels` | object | Labels that all alarms have in common, map of string keys to string values. |
+| `commonAnnotations` | object | Annotations that all alarms have in common, map of string keys to string values. |
+| `externalURL` | string | External URL to the Grafana instance sending this webhook. |
+| `version` | string | Version of the payload structure. |
+| `groupKey` | string | Key that is used for grouping. |
+| `truncatedAlerts` | number | Number of alerts that were truncated. |
+| `state` | string | State of the alert group (either `alerting` or `ok`). |
+
+The following key-value pairs are also included in the JSON payload and can be configured in the [webhook settings using notification templates](#optional-settings-using-templates).
+
+| Key | Type | Description |
+| --------- | ------ | -------------------------------------------------------------------------------------------------------------------- |
+| `title` | string | Custom title. Configurable in [webhook settings using notification templates](#optional-settings-using-templates). |
+| `message` | string | Custom message. Configurable in [webhook settings using notification templates](#optional-settings-using-templates). |
### Alert
-| Key | Type | Description |
-| ------------ | ------ | ---------------------------------------------------------------------------------- |
-| status | string | Current status of the alert, `firing` or `resolved` |
-| labels | object | Labels that are part of this alert, map of string keys to string values |
-| annotations | object | Annotations that are part of this alert, map of string keys to string values |
-| startsAt | string | Start time of the alert |
-| endsAt | string | End time of the alert, default value when not resolved is `0001-01-01T00:00:00Z` |
-| values | object | Values that triggered the current status |
-| generatorURL | string | URL of the alert rule in the Grafana UI |
-| fingerprint | string | The labels fingerprint, alarms with the same labels will have the same fingerprint |
-| silenceURL | string | URL to silence the alert rule in the Grafana UI |
-| dashboardURL | string | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation |
-| panelURL | string | A link to the panel if the alert has a Panel ID annotation |
-| imageURL | string | URL of a screenshot of a panel assigned to the rule that created this notification |
+The Alert object represents an alert included in the notification group, as provided by the [`alerts` field](#body).
-{{< admonition type="note" >}}
-
-You can customize the `title` and `message` fields using [notification templates](ref:notification-templates).
-
-However, you cannot customize webhook data structure or format, including JSON fields or sending data in XML, nor can you change the webhook HTTP headers.
-
-{{< /admonition >}}
-
-## Procedure
-
-To create your Webhook integration in Grafana Alerting, complete the following steps.
-
-1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**.
-1. Click **+ Add contact point**.
-1. Enter a contact point name.
-1. From the Integration list, select **Webhook**.
-1. In the **URL** field, copy in your Webhook URL.
-1. Click **Test** to check that your integration works.
-
- ** For Grafana Alertmanager only.**
-
-1. Click **Save contact point**.
-
-## Next steps
-
-The Webhook contact point is ready to receive alert notifications.
-
-To add this contact point to your alert, complete the following steps.
-
-1. In Grafana, navigate to **Alerting** > **Alert rules**.
-1. Edit or create a new alert rule.
-1. Scroll down to the **Configure labels and notifications** section.
-1. Under Notifications, click **Select contact point**.
-1. From the drop-down menu, select the previously created contact point.
-1. **Click Save rule and exit**.
+| Key | Type | Description |
+| -------------- | ------ | ----------------------------------------------------------------------------------- |
+| `status` | string | Current status of the alert, `firing` or `resolved`. |
+| `labels` | object | Labels that are part of this alert, map of string keys to string values. |
+| `annotations` | object | Annotations that are part of this alert, map of string keys to string values. |
+| `startsAt` | string | Start time of the alert. |
+| `endsAt` | string | End time of the alert, default value when not resolved is `0001-01-01T00:00:00Z`. |
+| `values` | object | Values that triggered the current status. |
+| `generatorURL` | string | URL of the alert rule in the Grafana UI. |
+| `fingerprint` | string | The labels fingerprint, alarms with the same labels will have the same fingerprint. |
+| `silenceURL` | string | URL to silence the alert rule in the Grafana UI. |
+| `dashboardURL` | string | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation. |
+| `panelURL` | string | A link to the panel if the alert has a Panel ID annotation. |
+| `imageURL` | string | URL of a screenshot of a panel assigned to the rule that created this notification. |
diff --git a/docs/sources/alerting/configure-notifications/mute-timings.md b/docs/sources/alerting/configure-notifications/mute-timings.md
index 1ef726267e3..79be30654e0 100644
--- a/docs/sources/alerting/configure-notifications/mute-timings.md
+++ b/docs/sources/alerting/configure-notifications/mute-timings.md
@@ -39,7 +39,9 @@ refs:
# Configure mute timings
-A mute timing is a recurring interval of time when no new notifications for a policy are generated or sent. Use them to prevent alerts from firing a specific and reoccurring period, for example, a regular maintenance period or weekends.
+A mute timing is a recurring interval that stops notifications for one or multiple notification policies during a specified period. It suppresses notifications but does not interrupt alert evaluation.
+
+Use mute timings to temporarily pause notifications for a specific recurring period, such as a regular maintenance window or weekends.
{{< admonition type="note" >}}
Mute timings are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and only suppress notifications for alerts managed by that Alertmanager.
diff --git a/docs/sources/alerting/fundamentals/_index.md b/docs/sources/alerting/fundamentals/_index.md
index 934f019cfd3..147a5e36d2d 100644
--- a/docs/sources/alerting/fundamentals/_index.md
+++ b/docs/sources/alerting/fundamentals/_index.md
@@ -132,7 +132,7 @@ Each notification policy decides where to send the alert (contact point) and whe
### Silences and mute timings
-[Silences](ref:silences) and [mute timings](ref:mute-timings) allow you to pause notifications for specific alerts or even entire notification policies. Use a silence to pause notifications on an ad-hoc basis, such as during a maintenance window; and use mute timings to pause notifications at regular intervals, such as evenings and weekends.
+[Silences](ref:silences) and [mute timings](ref:mute-timings) allow you to pause notifications without interrupting alert rule evaluation. Use a silence to pause notifications on a one-time basis, such as during a maintenance window; and use mute timings to pause notifications at regular intervals, such as evenings and weekends.
### Architecture
diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md
index cb4f932a5f6..bcfc5b27769 100644
--- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md
+++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md
@@ -87,7 +87,7 @@ In [Configure no data and error handling](ref:no-data-and-error-handling), you c
Note that `No Data` and `Error` states are supported only for Grafana-managed alert rules.
-{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}}
+{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}}
{{< docs/shared lookup="alerts/table-configure-no-data-and-error.md" source="grafana" version="" >}}
diff --git a/docs/sources/alerting/fundamentals/alert-rules/_index.md b/docs/sources/alerting/fundamentals/alert-rules/_index.md
index 47e410b8564..439c56b130c 100644
--- a/docs/sources/alerting/fundamentals/alert-rules/_index.md
+++ b/docs/sources/alerting/fundamentals/alert-rules/_index.md
@@ -25,9 +25,17 @@ refs:
destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure-prometheus-data-source/#alerting
queries-and-conditions:
- pattern: /docs/grafana/
- destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/
+ destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#data-source-queries
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#data-source-queries
+ alert-condition:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
+ recorded-queries:
+ - pattern: /docs/
+ destination: /docs/grafana//administration/recorded-queries/
notification-images:
- pattern: /docs/grafana/
destination: /docs/grafana//alerting/configure-notifications/template-notifications/images-in-notifications/
@@ -45,14 +53,9 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/
expression-queries:
- pattern: /docs/grafana/
- destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#expression-queries
+ destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#expression-queries
- alert-condition:
- - pattern: /docs/grafana/
- destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions
alert-rule-evaluation:
- pattern: /docs/grafana/
destination: /docs/grafana//alerting/fundamentals/alert-rules/rule-evaluation/
@@ -64,8 +67,8 @@ refs:
An alert rule is a set of evaluation criteria for when an alert rule should fire. An alert rule consists of:
-- Queries and expressions that select the data set to evaluate.
-- A condition (the threshold) that the query must meet or exceed to trigger the alert instance.
+- [Queries](ref:queries-and-conditions) that select the dataset to evaluate.
+- An [alert condition](ref:alert-condition) (the threshold) that the query must meet or exceed to trigger the alert instance.
- An interval that specifies the frequency of [alert rule evaluation](ref:alert-rule-evaluation) and a duration indicating how long the condition must be met to trigger the alert instance.
- Other customizable options, for example, setting what should happen in the absence of data, notification messages, and more.
diff --git a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md
index 229816db306..175306e8ef1 100644
--- a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md
+++ b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md
@@ -17,21 +17,16 @@ labels:
title: Queries and conditions
weight: 104
refs:
- data-sources:
- - pattern: /docs/grafana/
- destination: /docs/grafana//datasources/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/
data-source-alerting:
- pattern: /docs/grafana/
destination: /docs/grafana//alerting/fundamentals/alert-rules/#supported-data-sources
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/#supported-data-sources
- alert-rule-evaluation:
+ state-and-health:
- pattern: /docs/grafana/
- destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/
+ destination: /docs/grafana//alerting/fundamentals/state-and-health/
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/state-and-health/
query-transform-data:
- pattern: /docs/grafana/
destination: /docs/grafana//panels-visualizations/query-transform-data/
@@ -41,71 +36,122 @@ refs:
# Queries and conditions
-In Grafana, queries fetch and transform data from [data sources,](ref:data-sources) which include databases like MySQL or PostgreSQL, time series databases like Prometheus or InfluxDB, and services like Amazon CloudWatch or Azure Monitor.
+In Grafana, queries fetch and transform data from data sources, which include databases like MySQL or PostgreSQL, time series databases like Prometheus or InfluxDB, and services like Amazon CloudWatch or Azure Monitor.
-A query specifies the data to extract from a data source, with the syntax varying based on the type of data source used.
+An alert rule defines the following components:
-In Alerting, an alert rule defines of one or more queries and expressions that select the data you want to measure and a [condition](#alert-condition) that needs to be met before an alert rule fires.
+- A [query](#data-source-queries) that specifies the data to retrieve from a data source, with the syntax depending on the type of data source used.
+- A [condition](#alert-condition) that must be met before the alert rule fires.
+- Optional [expressions](#advanced-options-expressions) to perform transformations on the retrieved data.
+
+Alerting periodically runs the queries and expressions, evaluating the condition. If the condition is breached, an alert instance is triggered for each time series.
## Data source queries
-Alerting queries are the same type of queries available in Grafana panels. Queries in Grafana can be applied in various ways, depending on the data source and query language being used. However, not all [data sources support Alerting](ref:data-source-alerting).
+Alerting queries are the same as the queries used in Grafana panels, but Grafana-managed alerts are limited to querying [data sources that have Alerting enabled](ref:data-source-alerting).
-Each data source’s query editor provides a customized user interface to help you write queries that take advantage of its unique capabilities. For additional information about queries in Grafana, refer to [Query and transform data](ref:query-transform-data).
+Queries in Grafana can be applied in various ways, depending on the data source and query language being used. Each data source’s query editor provides a customized user interface to help you write queries that take advantage of its unique capabilities.
-Some common types of query components include:
+For more details about queries in Grafana, refer to [Query and transform data](ref:query-transform-data).
-**Metrics or data fields**: Specify the specific metrics or data fields you want to retrieve, such as CPU usage, network traffic, or sensor readings.
+{{< figure src="/media/docs/alerting/alerting-query-conditions-default-options.png" max-width="750px" caption="Define alert query and alert condition" >}}
-**Time range**: Define the time range for which you want to fetch data, such as the last hour, a specific day, or a custom time range.
+## Alert condition
-**Filters**: Apply filters to narrow down the data based on specific criteria, such as filtering data by a specific tag, host, or application.
+The alert condition is the query or expression that determines whether the alert fires or not depending whether the value satisfies the specified comparison. There can be only one condition which determines the triggering of the alert.
-**Aggregations**: Perform aggregations on the data to calculate metrics like averages, sums, or counts over a given time period.
+If the queried data meets the defined condition, Grafana fires the alert.
-**Grouping**: Group the data by specific dimensions or tags to create aggregated views or breakdowns.
+When using **Default options**, the `When` input [reduces the query data](#reduce), and the last input defines the threshold condition.
-{{% admonition type="note" %}}
-Grafana doesn't support alert queries with template variables. More details [here](https://community.grafana.com/t/template-variables-are-not-supported-in-alert-queries-while-setting-up-alert/2514).
-{{% /admonition %}}
+When using **Advanced options**, you have to choose one of your queries or expressions as the alert condition.
-## Expression queries
+## Advanced options: Expressions
-In Grafana, an expression is used to perform calculations, transformations, or aggregations on the data source queried data. It allows you to create custom metrics or modify existing metrics based on mathematical operations, functions, or logical expressions.
+Expressions are only available for Grafana-managed alerts and when the **Advanced options** are enabled.
-By leveraging expression queries, users can perform tasks such as calculating the percentage change between two values, applying functions like logarithmic or trigonometric functions, aggregating data over specific time ranges or dimensions, and implementing conditional logic to handle different scenarios.
+In Grafana, expressions allow you to perform calculations, transformations, or aggregations on queried data. They modify existing metrics through mathematical operations, functions, or logical expressions.
-In Alerting, you can only use expressions for Grafana-managed alert rules. For each expression, you can choose from the math, reduce, and resample expressions. These are called multi-dimensional rules, because they generate an alert instance for each series.
+With expression queries, you can perform tasks such as calculating the percentage change between two values, applying functions like logarithmic or trigonometric functions, aggregating data over specific time ranges or dimensions, and implementing conditional logic to handle different scenarios.
-**Reduce**
+{{< figure src="/media/docs/alerting/alert-rule-expressions.png" max-width="750px" caption="Alert rule expressions" >}}
-Aggregates time series values in the selected time range into a single value. It's not necessary for [rules using numeric data](#alert-on-numeric-data).
+The following expressions are available:
-**Math**
+### Reduce
-Performs free-form math functions/operations on time series and number data. Can be used to preprocess time series data or to define an alert condition for number data. For example:
+Aggregates time series values within the selected time range into a single number.
+
+Reduce takes one or more time series and transform each series into a single number, which can then be compared in the alert condition.
+
+The following aggregations functions are included: `Min`, `Max`, `Mean`, `Mediam`, `Sum`, `Count`, and `Last`.
+
+### Math
+
+Performs free-form math functions/operations on time series data and numbers. For instance, `$A + 1` or `$A * 100`.
+
+You can also use a Math expression to define the alert condition for numbers. For example:
- `$B > 70` should fire if the value of B (query or expression) is more than 70.
- `$B < $C * 100` should fire if the value of B is less than the value of C multiplied by 100.
If queries being compared have multiple series in their results, series from different queries are matched if they have the same labels or one is a subset of the other.
-**Resample**
+### Resample
Realigns a time range to a new set of timestamps, this is useful when comparing time series data from different data sources where the timestamps would otherwise not align.
-**Threshold**
+### Threshold
-Checks if any time series data matches the threshold condition.
+Compares single numbers from previous queries or expressions (e.g., `$A`, `$B`) to a specified condition. It's often used to define the alert condition.
-The threshold expression allows you to compare two single values. It returns `0` when the condition is false and `1` if the condition is true. The following threshold functions are available:
+The threshold expression allows the comparison between two single values. Available threshold functions are:
-- Is above (x > y)
-- Is below (x < y)
-- Is within range (x > y1 AND x < y2)
-- Is outside range (x < y1 AND x > y2)
+- **Is above**: `$A > 5`
+- **Is below**: `$B < 3`
+- **Is equal to**: `$A == 2`
+- **Is not equal to**: `$B =! 4`
+- **Is above or equal to**: `$A >= 8`
+- **Is below or equal to**: `$B <= 16`
+- **Is within range**: `$A > 0 AND $A < 10`
+- **Is outside range**: `$B < 0 OR $B > 100`
+- **Is within range included**: `$A >= 0 AND $A <= 10`
+- **Is outside range included**: `$B <= 0 OR $B >= 100`
-**Classic condition (legacy)**
+A threshold returns `0` when the condition is false and `1` when true.
+
+If the threshold is set as the alert condition, the alert fires when the threshold returns `1`.
+
+#### Recovery threshold
+
+To reduce the noise from flapping alerts, you can set a recovery threshold different to the alert threshold.
+
+Flapping alerts occur when a metric hovers around the alert threshold condition and may lead to frequent state changes, resulting in too many notifications.
+
+The value of a flapping metric can continually go above and below a threshold, resulting in a series of firing-resolved-firing notifications and a noisy alert state history.
+
+For example, if you have an alert for latency with a threshold of 1000ms and the number fluctuates around 1000 (say 980 -> 1010 -> 990 -> 1020, and so on), then each of those might trigger a notification:
+
+- 980 -> 1010 triggers a firing alert.
+- 1010 -> 990 triggers a resolving alert.
+- 990 -> 1020 triggers a firing alert again.
+
+To prevent this, you can set a recovery threshold to define two thresholds instead of one:
+
+1. An alert is triggered when the first threshold is crossed.
+1. An alert is resolved only when the second (recovery) threshold is crossed.
+
+In the previous example, setting the recovery threshold to 900ms means the alert only resolves when the latency falls below 900ms:
+
+- 980 -> 1010 triggers a firing alert.
+- 1010 -> 990 does not resolve the alert, keeping it in the firing state.
+- 990 -> 1020 keeps the alert in the firing state.
+
+The recovery threshold mitigates unnecessary alert state changes and reduces alert noise.
+
+{{< collapse title="Classic condition (legacy)" >}}
+
+#### Classic condition (legacy)
Classic conditions exist mainly for compatibility reasons and should be avoided if possible.
@@ -113,66 +159,35 @@ Classic condition checks if any time series data matches the alert condition. It
| Condition operators | How it works |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| and | Two conditions before and after must be true for the overall condition to be true. |
-| or | If one of conditions before and after are true, the overall condition is true. |
-| logic-or | If the condition before `logic-or` is true, the overall condition is immediately true, without evaluating subsequent conditions. For instance, `TRUE and TRUE logic-or FALSE and FALSE` evaluate to `TRUE`, because the preceding condition returns `TRUE`. |
+| `and` | Two conditions before and after must be true for the overall condition to be true. |
+| `or` | If one of conditions before and after are true, the overall condition is true. |
+| `logic-or` | If the condition before `logic-or` is true, the overall condition is immediately true, without evaluating subsequent conditions. For instance, `TRUE and TRUE logic-or FALSE and FALSE` evaluate to `TRUE`, because the preceding condition returns `TRUE`. |
-## Aggregations
+The following aggregation functions are also available to further refine your query.
-Grafana Alerting provides the following aggregation functions to enable you to further refine your query.
+| Function | What it does |
+| ------------------ | ------------------------------------------------------------------------------- |
+| `avg` | Displays the average of the values |
+| `min` | Displays the lowest value |
+| `max` | Displays the highest value |
+| `sum` | Displays the sum of all values |
+| `count` | Counts the number of values in the result |
+| `last` | Displays the last value |
+| `median` | Displays the median value |
+| `diff` | Displays the difference between the newest and oldest value |
+| `diff_abs` | Displays the absolute value of diff |
+| `percent_diff` | Displays the percentage value of the difference between newest and oldest value |
+| `percent_diff_abs` | Displays the absolute value of `percent_diff` |
+| `count_non_null` | Displays a count of values in the result set that aren't `null` |
-These functions are available for **Reduce** and **Classic condition** expressions only.
-
-| Function | Expression | What it does |
-| ---------------- | ---------------- | ------------------------------------------------------------------------------- |
-| avg | Reduce / Classic | Displays the average of the values |
-| min | Reduce / Classic | Displays the lowest value |
-| max | Reduce / Classic | Displays the highest value |
-| sum | Reduce / Classic | Displays the sum of all values |
-| count | Reduce / Classic | Counts the number of values in the result |
-| last | Reduce / Classic | Displays the last value |
-| median | Reduce / Classic | Displays the median value |
-| diff | Classic | Displays the difference between the newest and oldest value |
-| diff_abs | Classic | Displays the absolute value of diff |
-| percent_diff | Classic | Displays the percentage value of the difference between newest and oldest value |
-| percent_diff_abs | Classic | Displays the absolute value of percent_diff |
-| count_non_null | Classic | Displays a count of values in the result set that aren't `null` |
-
-## Alert condition
-
-An alert condition is the query or expression that determines whether the alert fires or not depending on the value it yields. There can be only one condition which determines the triggering of the alert.
-
-After you have defined your queries and expressions, choose one of them as the alert rule condition. By default, the last expression added is used as the alert condition.
-
-When the queried data satisfies the defined condition, Grafana triggers the associated alert, which can be configured to send notifications through various channels like email, Slack, or PagerDuty.
-
-For details about how the alert evaluation triggers notifications, refer to [Alert rule evaluation](ref:alert-rule-evaluation).
-
-## Recovery threshold
-
-To reduce the noise of flapping alerts, you can set a recovery threshold different to the alert threshold.
-
-Flapping alerts occur when a metric hovers around the alert threshold condition and may lead to frequent state changes, resulting in too many notifications being generated.
-
-It can be tricky to create an alert rule for a noisy metric. That is, when the value of a metric continually goes above and below a threshold. This is called flapping and results in a series of firing - resolved - firing notifications and a noisy alert state history.
-
-For example, if you have an alert for latency with a threshold of 1000ms and the number fluctuates around 1000 (say 980 ->1010 -> 990 -> 1020, and so on) then each of those triggers a notification.
-
-To solve this problem, you can set a (custom) recovery threshold, which basically means having two thresholds instead of one:
-
-1. An alert is triggered when the first threshold is crossed.
-2. An alert is resolved only when the second threshold is crossed.
-
-For example, you could set a threshold of 1000ms and a recovery threshold of 900ms. This way, an alert rule only stops firing when it goes under 900ms and flapping is reduced.
-
-For details about how the alert evaluation triggers notifications, refer to [Alert rule evaluation](ref:alert-rule-evaluation).
+{{< /collapse >}}
## Alert on numeric data
Among certain data sources numeric data that is not time series can be directly alerted on, or passed into Server Side Expressions (SSE). This allows for more processing and resulting efficiency within the data source, and it can also simplify alert rules.
-When alerting on numeric data instead of time series data, there is no need to reduce each labeled time series into a single number. Instead labeled numbers are returned to Grafana instead.
+When alerting on numeric data instead of time series data, there is no need to [reduce](#reduce) each labeled time series into a single number. Instead labeled numbers are returned to Grafana instead.
-### Tabular Data
+#### Tabular Data
This feature is supported with backend data sources that query tabular data:
diff --git a/docs/sources/alerting/fundamentals/notifications/_index.md b/docs/sources/alerting/fundamentals/notifications/_index.md
index 538eaa0d00a..4c85da09ef6 100644
--- a/docs/sources/alerting/fundamentals/notifications/_index.md
+++ b/docs/sources/alerting/fundamentals/notifications/_index.md
@@ -126,7 +126,7 @@ Grafana Alerting provides advanced notification capabilities that you’ll find
For instance, you can customize notifications with shared [templates](ref:templates) that provide actionable alert information and can be reused for multiple notifications.
-Additionally, you can use [silences](ref:silences) and [mute timings](ref:mute-timings) to pause notifications for a given time window or at regular intervals, respectively.
+Additionally, you can use [silences](ref:silences) and [mute timings](ref:mute-timings) to pause or suppress notifications without interrupting alert evaluation.
## Architecture
diff --git a/docs/sources/alerting/fundamentals/notifications/notification-policies.md b/docs/sources/alerting/fundamentals/notifications/notification-policies.md
index 48a4bd70e37..ae8d53f1520 100644
--- a/docs/sources/alerting/fundamentals/notifications/notification-policies.md
+++ b/docs/sources/alerting/fundamentals/notifications/notification-policies.md
@@ -76,7 +76,7 @@ Each policy consists of a set of label matchers (0 or more) that specify which a
{{< figure src="/media/docs/alerting/notification-routing.png" max-width="750px" caption="Matching alert instances with notification policies" alt="Example of a notification policy tree" >}}
{{< admonition type="tip" >}}
-For a practical example of routing with notification policies, refer to our [Getting Started with Alert Instances and Notification Routing tutorial](https://grafana.com/tutorials/alerting-get-started-pt4/).
+For a practical example of routing with notification policies, refer to our [Getting Started with Alert Instances and Notification Routing tutorial](https://grafana.com/tutorials/alerting-get-started-pt2/).
{{< /admonition >}}
## Routing
diff --git a/docs/sources/alerting/set-up/performance-limitations/index.md b/docs/sources/alerting/set-up/performance-limitations/index.md
index 69becbad450..b8524e4c8a7 100644
--- a/docs/sources/alerting/set-up/performance-limitations/index.md
+++ b/docs/sources/alerting/set-up/performance-limitations/index.md
@@ -61,7 +61,13 @@ For more information, refer to [this GitHub issue](https://github.com/grafana/gr
If you have a high number of alert instances, it can happen that the load on the database gets very high, as each state
transition of an alert instance is saved in the database.
-This can be prevented by writing to the database periodically. For this the feature flag `alertingSaveStatePeriodic` needs
+### Compressed alert state
+
+When the `alertingSaveStateCompressed` feature toggle is enabled, Grafana saves the alert rule state in a compressed form, reducing database overhead for alerts with many instances.
+
+### Save state periodically
+
+High load can be also prevented by writing to the database periodically. For this the feature flag `alertingSaveStatePeriodic` needs
to be enabled. By default, it saves the states every 5 minutes to the database and on each shutdown. The periodic interval
can also be configured using the `state_periodic_save_interval` configuration flag. During this process, Grafana deletes all existing alert instances from the
database and then writes the entire current set of instances back in batches in a single transacton.
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 b7eb4b841fa..850600cc074 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
@@ -511,6 +511,8 @@ settings:
#
endpointUrl: https://custom_url/api/chat.postMessage
#
+ color: {{ if eq .Status "firing" }}#D63232{{ else }}#36a64f{{ end }}
+ #
title: |
{{ template "slack.default.title" . }}
text: |
diff --git a/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md b/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md
index 13220b2f56a..610f74585d1 100644
--- a/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md
+++ b/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md
@@ -209,3 +209,7 @@ When adding or editing an annotation, you can define a repeating time region by
The above configuration produces the following result in the Time series panel:
{{< figure src="/media/docs/grafana/screenshot-grafana-10-0-timeseries-time-regions.png" max-width="600px" alt="Time series visualization with time regions business hours" >}}
+
+Toggle the **Advanced** switch and use [Cron syntax](https://crontab.run/) to set more granular time region controls. The following example sets a time region of 9:00 AM, Monday to Friday:
+
+{{< figure src="/media/docs/grafana/dashboards/screenshot-annotations-cron-option-v11.6.png" max-width="600px" alt="Time region query with cron syntax" >}}
diff --git a/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md b/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md
index 474320e361d..0fff2d65d9f 100644
--- a/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md
+++ b/docs/sources/dashboards/build-dashboards/view-dashboard-json-model/index.md
@@ -136,6 +136,18 @@ The grid has a negative gravity that moves panels up if there is empty space abo
"now": true,
"hidden": false,
"nowDelay": "",
+ "quick_ranges": [
+ {
+ "display": "Last 6 hours",
+ "from": "now-6h",
+ "to": "now"
+ },
+ {
+ "display": "Last 7 days",
+ "from": "now-7d",
+ "to": "now"
+ }
+ ],
"refresh_intervals": [
"5s",
"10s",
@@ -163,6 +175,7 @@ Usage of the fields is explained below:
| **now** | |
| **hidden** | whether timepicker is hidden or not |
| **nowDelay** | override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
+| **quick_ranges** | custom quick ranges |
| **refresh_intervals** | interval options available in the refresh picker dropdown |
| **status** | |
| **type** | |
diff --git a/docs/sources/dashboards/create-reports/_index.md b/docs/sources/dashboards/create-reports/_index.md
index b29c4fe6f72..0b1de37d290 100644
--- a/docs/sources/dashboards/create-reports/_index.md
+++ b/docs/sources/dashboards/create-reports/_index.md
@@ -19,66 +19,36 @@ title: Create and manage reports
description: Generate and share PDF reports from your Grafana dashboards
weight: 600
refs:
- repeat-panels-or-rows:
- - pattern: /docs/grafana/
- destination: /docs/grafana//panels-visualizations/configure-panel-options/#configure-repeating-panels
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-panel-options/#configure-repeating-panels
- http-apis:
- - pattern: /docs/grafana/
- destination: /docs/grafana//developers/http_api/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//developers/http_api/
- image-rendering:
- - pattern: /docs/grafana/
- destination: /docs/grafana//setup-grafana/image-rendering/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//setup-grafana/image-rendering/
- rbac:
- - pattern: /docs/grafana/
- destination: /docs/grafana//administration/roles-and-permissions/access-control/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//administration/roles-and-permissions/access-control/
- permission:
- - pattern: /docs/grafana/
- destination: /docs/grafana//administration/roles-and-permissions/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//administration/roles-and-permissions/
- role-based-access-control:
- - pattern: /docs/grafana/
- destination: /docs/grafana//administration/roles-and-permissions/access-control/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//administration/roles-and-permissions/access-control/
- configuration:
- - pattern: /docs/grafana/
- destination: /docs/grafana//setup-grafana/configure-grafana/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//setup-grafana/configure-grafana/
- image-rendering:
- - pattern: /docs/grafana/
- destination: /docs/grafana//setup-grafana/image-rendering/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//setup-grafana/image-rendering/
- templates-and-variables:
- - pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/variables/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/visualizations/dashboards/variables/
grafana-enterprise:
- pattern: /docs/grafana/
destination: /docs/grafana//introduction/grafana-enterprise/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana//introduction/grafana-enterprise/
- configuration:
+ image-rendering:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//setup-grafana/image-rendering/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//setup-grafana/image-rendering/
+ max-size-configuration:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#max_attachment_size_mb
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#max_attachment_size_mb
+ log-filters:
- pattern: /docs/grafana/
destination: /docs/grafana//setup-grafana/configure-grafana/#filters
- pattern: /docs/grafana-cloud/
destination: /docs/grafana//setup-grafana/configure-grafana/#filters
- time-range-controls:
+ permission:
- pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/use-dashboards/#set-dashboard-time-range
+ destination: /docs/grafana//administration/roles-and-permissions/
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/visualizations/dashboards/use-dashboards/#set-dashboard-time-range
+ destination: /docs/grafana//administration/roles-and-permissions/
+ rbac:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//administration/roles-and-permissions/access-control/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//administration/roles-and-permissions/access-control/
send-report:
- pattern: /docs/grafana/
destination: /docs/grafana//developers/http_api/reporting/#send-a-report
@@ -94,62 +64,61 @@ refs:
destination: /docs/grafana//setup-grafana/configure-grafana/#temp_data_lifetime
- pattern: /docs/grafana-cloud/
destination: /docs/grafana//setup-grafana/configure-grafana/#temp_data_lifetime
- rendering-configuration:
+ templates-and-variables:
- pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/create-reports/report-settings/#rendering-configuration
+ destination: /docs/grafana//dashboards/variables/
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana//dashboards/create-reports/report-settings/#rendering-configuration
- reporting-configuration:
+ destination: /docs/grafana-cloud/visualizations/dashboards/variables/
+ time-range-controls:
- pattern: /docs/grafana/
- destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#reporting
+ destination: /docs/grafana//dashboards/use-dashboards/#set-dashboard-time-range
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#reporting
-
+ destination: /docs/grafana-cloud/visualizations/dashboards/use-dashboards/#set-dashboard-time-range
---
# Create and manage reports
-**Reporting** allows you to automatically generate PDFs from any of your dashboards and have Grafana email them to interested parties on a schedule. This is available in Grafana Cloud and in Grafana Enterprise.
-
-{{< admonition type="note" >}}
-If you have [Role-based access control](ref:role-based-access-control) enabled, for some actions you would need to have relevant permissions.
-Refer to specific guides to understand what permissions are required.
-{{< /admonition >}}
-
-Any changes you make to a dashboard used in a report are reflected the next time the report is sent. For example, if you change the time range in the dashboard, then the time range in the report also changes, unless you've configured a custom time range.
+**Reporting** allows you to send automated and scheduled emails from any of your dashboards.
+You can configure several elements of these reports and generate PDFs, CSV files, and embedded images.
+Any changes you make to a dashboard used in a report are reflected the next time the report is sent.
## Requirements
-- SMTP must be configured for reports to be sent. Refer to [SMTP](ref:smtp) in [Configuration](ref:configuration) for more information.
-- The Image Renderer plugin must be installed or the remote rendering service must be set up. Refer to [Image rendering](ref:image-rendering) for more information.
+For Grafana Enterprise, the Reporting feature has the following requirements:
+
+- SMTP must be configured for reports to be sent. Refer to [SMTP configuration documentation](ref:smtp) for more information.
+- The [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) (v3.10+) must be installed or the remote rendering service must be set up. Refer to [Image rendering](ref:image-rendering) for more information.
### Rendering configuration
-When generating reports, each panel renders separately before being collected in a PDF. You can configure the per-panel rendering timeout and number of concurrently rendered panels.
+By default, attachments (PDFs, CSV files, and embedded images) larger than 10 MB are not sent, which keeps email servers from rejecting the email.
+You can increase or decrease this limit in the [reporting configuration](ref:max-size-configuration).
-To make a panel more legible, you can set a scale factor for the rendered images. However, a higher scale factor increases the file size of the generated PDF.
-
-You can also specify custom fonts that support different Unicode scripts. The DejaVu font is the default used for PDF rendering.
-
-These options are available in the [reporting configuration](ref:reporting-configuration) of the `ini` file for Enterprise Grafana.
+When a report file is generated, it's temporarily written to the corresponding folder (`csv`, `pdf`, `png`) in the Grafana `data` folder.
+A background job runs every 10 minutes and removes temporary files.
+You can set how long a file should be stored before being removed by configuring the [`temp_data_lifetime`](ref:temp-data-lifetime) setting in your `ini` file.
## Access control
-When [RBAC](ref:rbac) is enabled, you need to have the relevant [Permissions](ref:permission) to create and manage reports.
+Only organization administrators can create reports by default.
+You can customize who can create reports with [role-based access control (RBAC)](ref:rbac).
+
+When [RBAC](ref:rbac) is enabled, you need to have the relevant [permissions](ref:permission) to create and manage reports.
+Refer to specific guides to understand what permissions are required.
## Create a report
-Only organization administrators can create reports by default. You can customize who can create reports with [Role-based access control](ref:role-based-access-control).
+The report creation process is multi-step, but you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page.
-The report creation process is multi-step, but you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page:
+You can also save the report as a draft at any step in the process:

To create a report, follow these steps:
-1. Click **Dashboards > Reporting** in the main menu.
+1. In the main menu, click **Dashboards > Reporting**.
1. Click **+ Create a new report**.
-1. Complete the report steps, as needed; you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page:
+1. Complete the report steps, as needed:
- [Select dashboard](#1-select-dashboard)
- [Format report](#2-format-report)
- [Schedule](#3-schedule)
@@ -162,16 +131,17 @@ To create a report, follow these steps:
### 1. Select dashboard
-At this step, select the dashboard or dashboards on which the report is based, as well as the variables and time range for those dashboards. The options are:
+At this step, select the dashboard or dashboards on which the report is based, as well as the variables and time ranges for those dashboards.
+The options are:
| Option | Description |
| ------ | ----------- |
-| Source dashboard | Select the dashboard from which you want to generate the report. |
-| [Template variables (optional)](#template-variables) | Select the variable values for the selected dashboard. This option is only displayed if the dashboard has variables. |
-| [Time range (optional)](#time-range) | By default, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report. |
-| Add another dashboard | Add more dashboards to the report. Additional dashboards will be rendered as new pages in the same PDF file, or additional images if you chose to embed images in your report email. You can't add the same dashboard to a report multiple times. |
+| Source dashboard (required) | Select the dashboard from which you want to generate the report. |
+| [Template variables](#template-variables) | Select the variable values for the selected dashboard. This option is only displayed if the dashboard has variables. |
+| [Time range](#time-range) | If you leave the field empty, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report. |
+| Add another dashboard | Add more dashboards to the report. |
@@ -179,30 +149,39 @@ At this step, select the dashboard or dashboards on which the report is based, a
This option is only displayed if the dashboard has variables.
-You can configure report-specific template variables for the dashboard on the report page. The variables that you select will override the variables from the dashboard, and they are used when rendering a PDF file of the report. For detailed information about using template variables, refer to the [Templates and variables](ref:templates-and-variables) section.
+You can configure report-specific template variables for the dashboard on the report page.
+The variables that you select override the variables from the dashboard.
+For detailed information about using template variables, refer to [Variables](ref:templates-and-variables).
-The query variables saved with a report might become out of date if the results of that query change. For example, if your template variable queries for a list of hostnames and a new hostname is added, then it will not be included in the report. If that occurs, the selected variables must be manually updated in the report. If you select the **All** value for the template variable or if you keep the dashboard's original variable selection, then the report stays up-to-date as new values are added.
+The query variables saved with a report might become out-of-date if the results of that query change.
+For example, if your template variable queries for a list of hostnames and a new hostname is added, then it won't be included in the report.
+If that occurs, the selected variables must be manually updated in the report.
+If you select the **All** value for the template variable or if you keep the dashboard's original variable selection, then the report stays up-to-date as new values are added.
#### Time range
-By default, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report by:
-
-- Saving a modified time range to the dashboard. Changing the dashboard time range without saving it doesn't change the time zone of the report.
-- Setting a time range via the **Time range** field in the report form. If specified, the custom time range overrides the time range from the report's dashboard.
+If you leave the **Time range** field empty, reports use the saved time range of the dashboard.
+Optionally, you can change the time range of the report by setting it in the **Time range** field.
+If specified, the custom time range overrides the time range from the report's dashboard.
The page header of the report displays the time range for the dashboard's data queries.
##### Report time zones
-Reports use the time zone of the dashboard from which they’re generated. You can control the time zone for your reports by setting the dashboard to a specific time zone. Note that this affects the display of the dashboard for all users.
+Reports use the time zone of the dashboard from which they're generated.
+You can control the time zone for your reports by setting the dashboard to a specific time zone.
+Note that this affects the display of the dashboard for all users.
-If a dashboard has the **Browser Time** setting, the reports generated from that dashboard use the time zone of the Grafana server. As a result, this time zone might not match the time zone of users creating or receiving the report.
+If a dashboard has the **Browser Time** setting, the reports generated from that dashboard use the time zone of the Grafana server.
+As a result, this time zone might not match the time zone of users creating or receiving the report.
+If you want to use a specific time zone, save the dashboard with a fixed time zone instead of **Browser Time**
-If the time zone is set differently between your Grafana server and its remote image renderer, then the time ranges in the report might be different between the page header and the time axes in the panels. To avoid this, set the time zone to UTC for dashboards when using a remote renderer. Each dashboard's time zone setting is visible in the [time range controls](ref:time-range-controls).
+Each dashboard's time zone setting is visible in the [time range controls](ref:time-range-controls).
### 2. Format report
-At this step, set the report formatting options. You can select multiple options:
+At this step, select one or more report formatting options.
+You can select multiple options, but you must select _at least one_:
- [Attach the report as a PDF](#attach-the-report-as-a-pdf)
- [Include table data as PDF appendix](#table-data-in-pdf) (Public preview only)
@@ -212,13 +191,18 @@ At this step, set the report formatting options. You can select multiple options
#### Attach the report as a PDF
-If you selected the PDF format option, under the **Style the PDF** section, set the following options:
+If you selected the PDF format option under the **Style the PDF** section, you can configure the following options:
-- **Show template variables** - Click the checkbox to select this option.
+- **Configure multiple PDFs** - Click the **Combine all dashboard PDFs in one file** checkbox if you want to generate one PDF file for all the dashboards included in the report. This option is only displayed if your report includes multiple dashboards.
+- **Configure report header** - Click the **Show template variables** checkbox to show dashboard variables.
- **Orientation** - Set the report orientation in **Portrait** or **Landscape**. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples.
- **Layout** - Select one of the following:
- - **Simple** - Renders each panel as full-width across the PDF. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples.
- - **Grid** - Renders the PDF with the same panel arrangement and width as the source dashboard. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples.
+
+ - **Simple** - Renders each panel as full-width across the PDF.
+ - **Grid** - Renders the PDF with the same panel arrangement and width as the source dashboard.
+
+ Refer to the [Layout and orientation table](#layout-and-orientation) to see examples.
+
- **Zoom** - Zoom in to enlarge text in your PDF, or zoom out to see more data (like table columns) per panel.
Click **Preview PDF** in the top-right corner of the screen to view a rendered PDF with the options you selected.
@@ -238,50 +222,40 @@ Click **Preview PDF** in the top-right corner of the screen to view a rendered P
#### Embed a dashboard as an image in the email
-You can send a report email with an image of the dashboard embedded in the email instead of attached as a PDF. In this case, the email recipients can see the dashboard at a glance instead of having to open the PDF.
+You can send a report email with an image of the dashboard embedded in the email.
+This lets the email recipients see the dashboard at a glance.
#### Attach a CSV file of the table panel data
-{{< admonition type="note" >}}
-To use this feature in Grafana Enterprise, you must have [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0.
-{{< /admonition >}}
+You can attach a CSV file to the report email for each table panel on the selected dashboard.
-You can attach a CSV file to the report email for each table panel on the selected dashboard, along with the PDF report. By default, CSV files larger than 10Mb are not sent, which keeps email servers from rejecting the email. You can increase or decrease this limit in the [reporting configuration](ref:rendering-configuration).
-
-This feature relies on the same plugin that supports the [image rendering](ref:image-rendering) features.
-
-When the CSV file is generated, it is temporarily written to the `csv` folder in the Grafana `data` folder.
-
-A background job runs every 10 minutes and removes temporary CSV files. You can configure how long a CSV file should be stored before being removed by configuring the [temp-data-lifetime](ref:temp-data-lifetime) setting. This setting also affects how long a renderer PNG file should be stored.
-
-Click **Download CSV** in the top-right corner of the screen to see the file.
+Click **Download CSV** in the top-right corner of the screen to download a zipped file of the CSV files for your selected dashboard.
#### Table data in PDF
-{{% admonition type="note" %}}
-Available in public preview (`pdfTables` feature toggle) in [Grafana Enterprise](ref:grafana-enterprise) v10.3+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, as well as [Grafana Cloud](/docs/grafana-cloud/).
-{{% /admonition %}}
+{{< admonition type="note" >}}
+Available in public preview (`pdfTables` feature toggle) in [Grafana Enterprise](ref:grafana-enterprise) v10.3+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, as well as in [Grafana Cloud](/docs/grafana-cloud/).
+{{< /admonition >}}
When there's more data in your table visualizations than can be shown in the dashboard PDF, you can select one of these two options to access all table visualization data as PDF in your reports:
-- **Include table data as PDF appendix** - Adds an appendix to the main dashboard PDF.
+- **Include table data as PDF appendix** - Adds an appendix to the dashboard PDF.
- **Attach a separate PDF of table data** - Generates a separate PDF file.
-This feature relies on the same plugin that supports the [image rendering](ref:image-rendering) features.
-
### 3. Schedule
-At this step, set scheduling information. Options vary depending on the frequency you select.
+At this step, set scheduling information.
+Options vary depending on the frequency you select.
| Option | Description |
| ------ | ----------- |
-| Frequency | Scheduled reports can be sent once, or repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. You can also disable scheduling by selecting **Never**, for example to send the report using the API. |
+| Frequency | You can schedule reports to be sent once, or repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. You can also disable scheduling by selecting **Never**. For example, you might want to [send the report using the API](#send-a-report-using-the-api). |
| Time | Choose one of the following:- **Send now** sends the report immediately after you save it. To stop sending the report at some point in the future, add an end date.
- **Send later** schedules a report for a later date. When you select this option, the required **Start date**, **Start time**, and **Time zone** options are displayed.
|
-| End date | If you leave the end date empty, the report is sent out indefinitely. |
+| End date | If you leave this field empty, the report is sent out indefinitely. |
| Send only from Monday to Friday | For reports that have an hourly or daily frequency, you can choose to send them only from Monday to Friday. |
-| Send on the last day of the month | When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select this option instead. This way, the report is sent on the last day of every month regardless of how many days there are in any given month. |
+| Send on the last day of the month | When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select the **Send on the last day of the month** option. This way, the report is sent on the last day of every month regardless of how many days there are in the month. |
@@ -289,23 +263,29 @@ At this step, set scheduling information. Options vary depending on the frequenc
At this step, enter information related to sharing the report:
+
+
| Option | Description |
| ------ | ----------- |
-| Report name | Name of the report as you want it to appear in the **Reports** list. The report name also populates the email subject line. |
-| Recipients | Enter the email addresses of the people or teams that you want to receive the report, separated by commas or semicolons. |
+| Report name (required) | The name of the report as you want it to appear in the **Reports** list. The report name also populates the email subject line. |
+| Recipients (required) | Enter the email addresses of the people or teams that you want to receive the report, separated by commas or semicolons. |
| Reply-to email address | The address that appears in the **Reply to** field of the email. |
-| Message | Message body in the email with the report. |
-| Include a dashboard link | Include a link to the dashboard from within the report email. |
+| Message | The body of the message in the email with the report. |
+| Include a dashboard link | Include a links to the dashboards in the report email. |
-Click **Send test email** in the top-right corner of the screen to verify that the configuration works as expected and to verify that emails are working. You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing. The last saved version of the report will be sent to selected emails.
+
+
+Click **Send test email** in the top-right corner of the screen to verify that the configuration works as expected and to verify that emails are working.
+You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing.
### 5. Confirm
-At this step, the confirmation page displays all your the settings. Review them and confirm that they're correct or click the provided **Edit** links for each section to make updates.
+At this step, the confirmation page displays all the report settings.
+Review them and confirm that they're correct or click the provided **Edit** links for each section to make updates.
Then, click **Send now** or **Schedule send**.
@@ -313,7 +293,7 @@ You can also save the report as a draft or discard it. Discarding the report is
## Send a report using the API
-You can send reports programmatically with the [send report](ref:send-report) endpoint in the [HTTP APIs](ref:http-apis).
+You can send reports programmatically with the [send report](ref:send-report) endpoint using the HTTP API.
## Manage reports
@@ -321,7 +301,7 @@ On the **Reports** page, you can view and manage your existing reports or create

-### Edit
+### Edit reports
To edit a report, follow these steps:
@@ -331,9 +311,10 @@ To edit a report, follow these steps:
1. When you've finished making changes, click **Confirm** at the top of the screen to go to the last step.
1. Click **Update report**.
-### Pause or resume a report
+### Pause or resume reports {#pause-a-report}
-You can pause and resume sending reports from the report list view. To do this, follow these steps:
+You can pause and resume sending reports from the report list view.
+To do this, follow these steps:
1. In the main menu, click **Dashboards > Reporting**.
1. On the row of the report you want to update, do one of the following:
@@ -341,11 +322,22 @@ You can pause and resume sending reports from the report list view. To do this,
- Click the pause icon - The report won't be sent according to its schedule until it's resumed.
- Click the resume icon - The report resumes on its previous schedule.
+### Delete reports
+
+To delete a report, follow these steps:
+
+1. In the main menu, click **Dashboards > Reporting**.
+1. On the row of the report you want to update, click the trash can icon.
+1. Click **Delete** to confirm.
+
+Deleting a report is irreversible.
+
## Troubleshoot Reporting
-To troubleshoot and get more log information, enable debug logging in the configuration file. Refer to [Configuration](ref:configuration) for more information.
+To troubleshoot and get more log information, enable debug logging in the configuration file.
+Refer to the [log filters configuration documentation](ref:log-filters) for more information.
```bash
[log]
-filters = report:debug
+filters = rendering:debug,report.api:debug,report.render:debug,report.scheduler:debug,report.sender:debug,report.service:debug
```
diff --git a/docs/sources/dashboards/create-reports/report-settings/index.md b/docs/sources/dashboards/create-reports/report-settings/index.md
index c442a4346a8..b878953b71a 100644
--- a/docs/sources/dashboards/create-reports/report-settings/index.md
+++ b/docs/sources/dashboards/create-reports/report-settings/index.md
@@ -12,21 +12,17 @@ title: Reporting settings
description: Manage organizational Reporting settings
weight: 700
refs:
- grafana-enterprise:
+ change-ui-theme:
- pattern: /docs/grafana/
- destination: /docs/grafana//introduction/grafana-enterprise/
+ destination: /docs/grafana//administration/organization-preferences/#change-grafana-ui-theme
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana//introduction/grafana-enterprise/
- configuration:
- - pattern: /docs/grafana/
- destination: /docs/grafana//setup-grafana/configure-grafana/#filters
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//setup-grafana/configure-grafana/#filters
+ destination: /docs/grafana//administration/organization-preferences/#change-grafana-ui-theme
---
# Reporting settings
-You can configure organization-wide report settings and branding options in **Dashboards > Reporting > Settings**. Settings are applied to all the reports for the current organization.
+You can configure organization-wide report settings and branding options in **Dashboards > Reporting > Settings**.
+Settings are applied to all the reports for the current organization.
## Attachment settings
@@ -34,13 +30,22 @@ The options in this section control the branding and theming of the report attac
### PDF
-- **Company logo** - Company logo displayed in the report PDF. It can be configured by specifying a URL, or by uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo.
+- **Company logo** - Company logo displayed in the report PDF.
+ Configure it by specifying a URL or uploading a file.
+ The maximum file size is 16 MB.
+ Defaults to the Grafana logo.
-- **Theme** - Theme of the PDF attached to the report. Defaults to **Light**. The selected theme is also applied to the PDFs generated when you click **Preview PDF** during report creation or select the **Export as PDF** option on a dashboard. If **Current** is selected, the PDF in the report will be in the instance theme of the Admin, but the preview and exported PDFs will be in the user's instance theme.
+- **Theme** - Theme of the PDF attached to the report.
+ The selected theme is also applied to the PDFs generated when you click **Preview PDF** during report creation or select the **Export as PDF** option on a dashboard.
+
+ If **Current** is selected, the PDF in the report is in the instance theme of the report creator, but the preview and exported PDFs are in the user's instance theme.
+ Defaults to **Light**.
### Embedded Image
-- **Theme** - Theme of the dashboard image embedded in the email. Defaults to **Dark**.
+- **Theme** - Theme of the dashboard image embedded in the email.
+ If **Current** is selected, the image in the report is in the instance theme of the report creator. If the report creator doesn't have a theme set, then the team, organization, or server theme is used. For more information refer to [Change Grafana UI theme](ref:change-ui-theme).
+ Defaults to **Dark**.
@@ -48,9 +53,10 @@ The options in this section control the branding and theming of the report attac
-- **Company logo** - Company logo displayed in the report email. It can be configured by specifying a URL, or by uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo.
+- **Company logo** - Company logo displayed in the report email. Configure it by specifying a URL or uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo.
- **Email footer** - Toggle to enable the report email footer. Select **Sent by** or **None**.
- **Footer link text** - Text of the link in the report email footer. Defaults to `Grafana`.
- **Footer link URL** - Link of the report email footer.
-Currently, the API does not allow for the simultaneous upload of files with identical names for both the email logo and report logo. You can still upload the same file for each logo separately in two distinct steps.
+Currently, the API does not allow for the simultaneous upload of files with identical names for both the email logo and report logo.
+You can still upload the same file for each logo separately in two distinct steps.
diff --git a/docs/sources/dashboards/share-dashboards-panels/_index.md b/docs/sources/dashboards/share-dashboards-panels/_index.md
index b9d84b58ab6..5885b9b8e86 100644
--- a/docs/sources/dashboards/share-dashboards-panels/_index.md
+++ b/docs/sources/dashboards/share-dashboards-panels/_index.md
@@ -44,9 +44,9 @@ refs:
destination: /docs/grafana/grafana-cloud/visualizations/dashboards/share-dashboards-panels/shared-dashboards/
configure-report:
- pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/create-reports/#create-or-update-a-report
+ destination: /docs/grafana//dashboards/create-reports/#create-a-report
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana-cloud/visualizations/dashboards/create-reports/#create-or-update-a-report
+ destination: /docs/grafana-cloud/visualizations/dashboards/create-reports/#create-a-report
image-rendering-config:
- pattern: /docs/grafana/
destination: /docs/grafana//setup-grafana/image-rendering/#configuration
diff --git a/docs/sources/dashboards/variables/add-template-variables/index.md b/docs/sources/dashboards/variables/add-template-variables/index.md
index fa4e49ef5a4..54dc20e2d47 100644
--- a/docs/sources/dashboards/variables/add-template-variables/index.md
+++ b/docs/sources/dashboards/variables/add-template-variables/index.md
@@ -89,6 +89,8 @@ refs:
# Add variables
+
+
The following table lists the types of variables shipped with Grafana.
| Variable type | Description |
@@ -134,11 +136,13 @@ To create a variable, follow these steps:
- [Interval](#add-an-interval-variable)
- [Ad hoc filters](#add-ad-hoc-filters)
+
+
## Add a query variable
Query variables enable you to write a data source query that can return a list of metric names, tag values, or keys. For example, a query variable might return a list of server names, sensor IDs, or data centers. The variable values change as they dynamically fetch options with a data source query.
-Query variables are generally only supported for strings. If your query returns numbers or any other data type, you might need to convert them to strings in order to use them as variables. For the Azure data source, for example, you can use the [tostring](https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/tostringfunction) function for this purpose.
+Query variables are generally only supported for strings. If your query returns numbers or any other data type, you might need to convert them to strings to use them as variables. For the Azure data source, for example, you can use the [`tostring`](https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/tostringfunction) function for this purpose.
Query expressions can contain references to other variables and in effect create linked variables. Grafana detects this and automatically refreshes a variable when one of its linked variables change.
@@ -168,7 +172,7 @@ Query expressions are different for each data source. For more information, refe
- Some data sources let you provide custom "display names" for the values. For instance, the PostgreSQL, MySQL, and Microsoft SQL Server plugins handle this by looking for fields named `__text` and `__value` in the result. Other data sources may look for `text` and `value` or use a different approach. Always remember to double-check the documentation for the data source.
- If you need more room in a single input field query editor, then hover your cursor over the lines in the lower right corner of the field and drag downward to expand.
-1. (Optional) In the **Regex** field, type a regex expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with regex](#filter-variables-with-regex).
+1. (Optional) In the **Regex** field, type a regular expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with a regular expression](#filter-variables-with-regex).
1. In the **Sort** drop-down list, select the sort order for values to be displayed in the dropdown list. The default option, **Disabled**, means that the order of options returned by your data source query is used.
1. Under **Refresh**, select when the variable should update options:
@@ -240,7 +244,7 @@ _Data source_ variables enable you to quickly change the data source for an enti
1. [Enter general options](#enter-general-options).
1. Under the **Data source options** section of the page, in the **Type** drop-down list, select the target data source for the variable.
-1. (Optional) In **Instance name filter**, enter a regex filter for which data source instances to choose from in the variable value drop-down list.
+1. (Optional) In **Instance name filter**, enter a regular expression filter for which data source instances to choose from in the variable value drop-down list.
Leave this field empty to display all instances.
@@ -289,6 +293,9 @@ The following example shows a more complex Graphite example, from the [Graphite
groupByNode(summarize(movingAverage(apps.$app.$server.counters.requests.count, 5), '$interval', 'sum', false), 2, 'sum')
```
+
+
+
## Add ad hoc filters
_Ad hoc filters_ are one of the most complex and flexible variable options available.
@@ -298,10 +305,10 @@ Ad hoc filters let you add label/value filters that are automatically added to a
Unlike other variables, you don't use ad hoc filters in queries.
Instead, you use ad hoc filters to write filters for existing queries.
-{{% admonition type="note" %}}
+{{< admonition type="note" >}}
Not all data sources support ad hoc filters.
Examples of those that do include Prometheus, Loki, InfluxDB, and Elasticsearch.
-{{% /admonition %}}
+{{< /admonition >}}
To create an ad hoc filter, follow these steps:
@@ -318,6 +325,9 @@ To create an ad hoc filter, follow these steps:
Now you can [filter data on the dashboard](ref:filter-dashboard).
+
+
+
## Configure variable selection options
**Selection Options** are a feature you can use to manage variable option selections. All selection options are optional, and they are off by default.
@@ -326,9 +336,9 @@ Now you can [filter data on the dashboard](ref:filter-dashboard).
Interpolating a variable with multiple values selected is tricky as it is not straight forward how to format the multiple values into a string that is valid in the given context where the variable is used. Grafana tries to solve this by allowing each data source plugin to inform the templating interpolation engine what format to use for multiple values.
-{{% admonition type="note" %}}
+{{< admonition type="note" >}}
The **Custom all value** option on the variable must be blank for Grafana to format all values into a single string. If it is left blank, then Grafana concatenates (adds together) all the values in the query. Something like `value1,value2,value3`. If a custom `all` value is used, then instead the value is something like `*` or `all`.
-{{% /admonition %}}
+{{< /admonition >}}
#### Multi-value variables with a Graphite data source
@@ -336,17 +346,17 @@ Graphite uses glob expressions. A variable with multiple values would, in this c
#### Multi-value variables with a Prometheus or InfluxDB data source
-InfluxDB and Prometheus use regex expressions, so the same variable would be interpolated as `(host1|host2|host3)`. Every value would also be regex escaped. If not, a value with a regex control character would break the regex expression.
+InfluxDB and Prometheus use regular expressions, so the same variable would be interpolated as `(host1|host2|host3)`. Every value would also be regular expression escaped. If not, a value with a regular expression control character would break the regular expression.
#### Multi-value variables with an Elastic data source
-Elasticsearch uses lucene query syntax, so the same variable would be formatted as `("host1" OR "host2" OR "host3")`. In this case, every value must be escaped so that the value only contains lucene control words and quotation marks.
+Elasticsearch uses Lucene query syntax, so the same variable would be formatted as `("host1" OR "host2" OR "host3")`. In this case, every value must be escaped so that the value only contains Lucene control words and quotation marks.
#### Troubleshoot multi-value variables
-Automatic escaping and formatting can cause problems and it can be tricky to grasp the logic behind it. Especially for InfluxDB and Prometheus where the use of regex syntax requires that the variable is used in regex operator context.
+Automatic escaping and formatting can cause problems and it can be tricky to grasp the logic behind it. Especially for InfluxDB and Prometheus where the use of regular expression syntax requires that the variable is used in regular expression operator context.
-If you do not want Grafana to do this automatic regex escaping and formatting, then you must do one of the following:
+If you do not want Grafana to do this automatic regular expression escaping and formatting, then you must do one of the following:
- Turn off the **Multi-value** or **Include All option** options.
- Use the [raw variable format](ref:raw-variable-format).
@@ -359,28 +369,28 @@ Grafana adds an `All` option to the variable dropdown list. If a user selects th
This option is only visible if the **Include All option** is selected.
-Enter regex, globs, or lucene syntax in the **Custom all value** field to define the value of the `All` option.
+Enter regular expressions, globs, or Lucene syntax in the **Custom all value** field to define the value of the `All` option.
-By default the `All` value includes all options in combined expression. This can become very long and can have performance problems. Sometimes it can be better to specify a custom all value, like a wildcard regex.
+By default the `All` value includes all options in combined expression. This can become very long and can have performance problems. Sometimes it can be better to specify a custom all value, like a wildcard regular expression.
-In order to have custom regex, globs, or lucene syntax in the **Custom all value** option, it is never escaped so you have to think about what is a valid value for your data source.
+In order to have custom regular expression, globs, or Lucene syntax in the **Custom all value** option, it is never escaped so you have to think about what is a valid value for your data source.
## Global variables
Grafana has global built-in variables that can be used in expressions in the query editor. This topic lists them in alphabetical order and defines them. These variables are useful in queries, dashboard links, panel links, and data links.
-### $\_\_dashboard
+### `$__dashboard`
This variable is the name of the current dashboard.
-### $\_\_from and $\_\_to
+### `$__from` and `$__to`
Grafana has two built-in time range variables: `$__from` and `$__to`. They are currently always interpolated as epoch milliseconds by default, but you can control date formatting.
| Syntax | Example result | Description |
| ------------------------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `${__from}` | 1594671549254 | Unix millisecond epoch |
-| `${__from:date}` | 2020-07-13T20:19:09.254Z | No args, defaults to ISO 8601/RFC 3339 |
+| `${__from:date}` | 2020-07-13T20:19:09.254Z | No arguments, defaults to ISO 8601/RFC 3339 |
| `${__from:date:iso}` | 2020-07-13T20:19:09.254Z | ISO 8601/RFC 3339 |
| `${__from:date:seconds}` | 1594671549 | Unix seconds epoch |
| `${__from:date:YYYY-MM}` | 2020-07 | Any custom [date format](https://momentjs.com/docs/#/displaying/) that does not include the `:` character. Uses browser time. Use `:date` or `:date:iso` for UTC |
@@ -389,7 +399,7 @@ The syntax above also works with `${__to}`.
You can use this variable in URLs, as well. For example, you can send a user to a dashboard that shows a time range from six hours ago until now: https://play.grafana.org/d/000000012/grafana-play-home?viewPanel=2&orgId=1?from=now-6h&to=now
-### $\_\_interval
+### `$__interval`
You can use the `$__interval` variable as a parameter to group by time (for InfluxDB, MySQL, Postgres, MSSQL), Date histogram interval (for Elasticsearch), or as a _summarize_ function parameter (for Graphite).
@@ -403,42 +413,42 @@ In the InfluxDB data source, the legacy variable `$interval` is the same variabl
The InfluxDB and Elasticsearch data sources have `Group by time interval` fields that are used to hard code the interval or to set the minimum limit for the `$__interval` variable (by using the `>` syntax -> `>10m`).
-### $\_\_interval_ms
+### `$__interval_ms`
This variable is the `$__interval` variable in milliseconds, not a time interval formatted string. For example, if the `$__interval` is `20m` then the `$__interval_ms` is `1200000`.
-### $\_\_name
+### `$__name`
-This variable is only available in the Singlestat panel and can be used in the prefix or suffix fields on the Options tab. The variable is replaced with the series name or alias.
+This variable is only available in the **Singlestat** panel and can be used in the prefix or suffix fields on the Options tab. The variable is replaced with the series name or alias.
-{{% admonition type="note" %}}
-The Singlestat panel is no longer available from Grafana 8.0.
-{{% /admonition %}}
+{{< admonition type="note" >}}
+The **Singlestat** panel is no longer available from Grafana 8.0.
+{{< /admonition >}}
-### $\_\_org
+### `$__org`
This variable is the ID of the current organization.
`${__org.name}` is the name of the current organization.
-### $\_\_user
+### `$__user`
`${__user.id}` is the ID of the current user.
`${__user.login}` is the login handle of the current user.
`${__user.email}` is the email for the current user.
-### $\_\_range
+### `$__range`
Currently only supported for Prometheus and Loki data sources. This variable represents the range for the current dashboard. It is calculated by `to - from`. It has a millisecond and a second representation called `$__range_ms` and `$__range_s`.
-### $\_\_rate_interval
+### `$__rate_interval`
Currently only supported for Prometheus data sources. The `$__rate_interval` variable is meant to be used in the rate function. Refer to [Prometheus query variables](ref:prometheus-query-variables) for details.
-### $\_\_rate_interval_ms
+### `$__rate_interval_ms`
This variable is the `$__rate_interval` variable in milliseconds, not a time-interval-formatted string. For example, if the `$__rate_interval` is `20m` then the `$__rate_interval_ms` is `1200000`.
-### $timeFilter or $\_\_timeFilter
+### `$timeFilter` or `$__timeFilter`
The `$timeFilter` variable returns the currently selected time range as an expression. For example, the time range interval `Last 7 days` expression is `time > now() - 7d`.
@@ -449,7 +459,7 @@ This is used in several places, including:
- SQL queries in MySQL, Postgres, and MSSQL.
- The `$__timeFilter` variable is used in the MySQL data source.
-### $\_\_timezone
+### `$__timezone`
The `$__timezone` variable returns the currently selected time zone, either `utc` or an entry of the IANA time zone database (for example, `America/New_York`).
@@ -485,9 +495,9 @@ In this example, there are several applications. Each application has a differen
Now, you could make separate variables for each metric source, but then you have to know which server goes with which app. A better solution is to use one variable to filter another. In this example, when the user changes the value of the `app` variable, it changes the dropdown options returned by the `server` variable. Both variables use the **Multi-value** option and **Include all option**, enabling users to select some or all options presented at any time.
-##### app variable
+##### `app` variable
-The query for this variable basically says, "Give me all the applications that exist."
+The query for this variable basically says, "Find all the applications that exist."
```
apps.*
@@ -495,9 +505,9 @@ apps.*
The values returned are `backend`, `country`, `fakesite`, and `All`.
-##### server variable
+##### `server` variable
-The query for this variable basically says, "Give me all servers for the currently chosen application."
+The query for this variable basically says, "Find all servers for the currently chosen application."
```
apps.$app.*
@@ -521,9 +531,9 @@ The query returns all servers associated with `fakesite`, including `web_server_
##### More variables
-{{% admonition type="note" %}}
+{{< admonition type="note" >}}
This example is theoretical. The Graphite server used in the example does not contain CPU metrics.
-{{% /admonition %}}
+{{< /admonition >}}
The dashboard stops at two levels, but you could keep going. For example, if you wanted to get CPU metrics for selected servers, you could copy the `server` variable and extend the query so that it reads:
@@ -531,7 +541,7 @@ The dashboard stops at two levels, but you could keep going. For example, if you
apps.$app.$server.cpu.*
```
-This query basically says, "Show me the CPU metrics for the selected server."
+This query basically says, "Find the CPU metrics for the selected server."
Depending on what variable options the user selects, you could get queries like:
@@ -547,9 +557,9 @@ In this example, you have several data centers. Each data center has a different
In this example, when the user changes the value of the `datacenter` variable, it changes the dropdown options returned by the `host` variable. The `host` variable uses the **Multi-value** option and **Include all option**, allowing users to select some or all options presented at any time. The `datacenter` does not use either option, so you can only select one data center at a time.
-##### datacenter variable
+##### `datacenter` variable
-The query for this variable basically says, "Give me all the data centers that exist."
+The query for this variable basically says, "Find all the data centers that exist."
```
SHOW TAG VALUES WITH KEY = "datacenter"
@@ -557,9 +567,9 @@ SHOW TAG VALUES WITH KEY = "datacenter"
The values returned are `America`, `Africa`, `Asia`, and `Europe`.
-##### host variable
+##### `host` variable
-The query for this variable basically says, "Give me all hosts for the currently chosen data center."
+The query for this variable basically says, "Find all hosts for the currently chosen data center."
```
SHOW TAG VALUES WITH KEY = "hostname" WHERE "datacenter" =~ /^$datacenter$/
@@ -583,9 +593,9 @@ The query returns all servers associated with `Europe`, including `server3`, `se
##### More variables
-{{% admonition type="note" %}}
+{{< admonition type="note" >}}
This example is theoretical. The InfluxDB server used in the example does not contain CPU metrics.
-{{% /admonition %}}
+{{< /admonition >}}
The dashboard stops at two levels, but you could keep going. For example, if you wanted to get CPU metrics for selected hosts, you could copy the `host` variable and extend the query so that it reads:
@@ -593,7 +603,7 @@ The dashboard stops at two levels, but you could keep going. For example, if you
SHOW TAG VALUES WITH KEY = "cpu" WHERE "datacenter" =~ /^$datacenter$/ AND "host" =~ /^$host$/
```
-This query basically says, "Show me the CPU metrics for the selected host."
+This query basically says, "Find the CPU metrics for the selected host."
Depending on what variable options the user selects, you could get queries like:
@@ -607,7 +617,7 @@ SHOW TAG VALUES WITH KEY = "cpu" WHERE "datacenter" =~ /^Europe/ AND "host" =~ /
The following practices make your dashboards and variables easier to use.
-#### Creating new linked variables
+#### New linked variables creation
- Chaining variables create parent/child dependencies. You can envision them as a ladder or a tree.
- The easiest way to create a new chained variable is to copy the variable that you want to base the new one on. In the variable list, click the **Duplicate variable** icon to the right of the variable entry to create a copy. You can then add on to the query for the parent variable.
@@ -627,13 +637,17 @@ The more layers of dependency you have in variables, the longer it takes to upda
For example, if you have a series of four linked variables (country, region, server, metric) and you change a root variable value (country), then Grafana must run queries for all the dependent variables before it updates the visualizations in the dashboard.
-## Filter variables with regex
+
-Using the Regex Query option, you filter the list of options returned by the variable query or modify the options returned.
+## Filter variables with regular expressions {#filter-variables-with-regex}
-This page shows how to use regex to filter/modify values in the variable dropdown.
+
-Using the Regex Query Option, you filter the list of options returned by the Variable query or modify the options returned. For more information, refer to the Mozilla guide on [Regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
+Using the **Regex** query option, you filter the list of options returned by the variable query or modify the options returned.
+
+This page shows how to use a regular expression to filter/modify values in the variable dropdown.
+
+Using the **Regex** query option, you filter the list of options returned by the Variable query or modify the options returned. For more information, refer to the Mozilla guide on [Regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
Examples of filtering on the following list of options:
@@ -646,7 +660,7 @@ backend_04
### Filter so that only the options that end with `01` or `02` are returned:
-Regex:
+**Regex**:
```regex
/(01|02)$/
@@ -659,9 +673,9 @@ backend_01
backend_02
```
-### Filter and modify the options using a regex capture group to return part of the text:
+### Filter and modify the options using a regular expression to capture group to return part of the text:
-Regex:
+**Regex**:
```regex
/.*(01|02)/
@@ -684,7 +698,7 @@ up{instance="demo.robustperception.io:9093",job="alertmanager"} 1 1521630638000
up{instance="demo.robustperception.io:9100",job="node"} 1 1521630638000
```
-Regex:
+**Regex**:
```regex
/.*instance="([^"]*).*/
@@ -711,7 +725,7 @@ node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_2",chip_name="enp216s0f0np2"
node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_3",chip_name="enp216s0f0np3"} 1
```
-Passed through the following Regex:
+Passed through the following regular expression:
```regex
/chip_name="(?[^"]+)|chip="(?[^"]+)/g
diff --git a/docs/sources/datasources/aws-cloudwatch/_index.md b/docs/sources/datasources/aws-cloudwatch/_index.md
index 6e5d3a0618e..414e770d2bc 100644
--- a/docs/sources/datasources/aws-cloudwatch/_index.md
+++ b/docs/sources/datasources/aws-cloudwatch/_index.md
@@ -356,6 +356,10 @@ The CloudWatch data source can query data from both CloudWatch metrics and Cloud
For details, see the [query editor documentation]({{< relref "./query-editor" >}}).
+## Query caching
+
+When you enable [query and resource caching]({{< relref "/administration/data-source-management/#query-and-resource-caching" >}}), Grafana temporarily stores the results of data source queries and resource requests. Query caching is available in CloudWatch Metrics in Grafana Cloud and Grafana Enterprise. It is not available in CloudWatch Logs Insights due to how query results are polled from AWS.
+
## Use template variables
Instead of hard-coding details such as server, application, and sensor names in metric queries, you can use variables.
@@ -438,7 +442,7 @@ For more information, refer to the AWS documentation for [Service Quotas](https:
The CloudWatch plugin enables you to monitor and troubleshoot applications across multiple regional accounts. Using cross-account observability, you can seamlessly search, visualize and analyze metrics and logs without worrying about account boundaries.
-To use this feature, configure in the [AWS console under Cloudwatch Settings](https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-cross-account-observability/), a monitoring and source account, and then add the necessary IAM permissions as described above.
+To use this feature, configure in the [AWS console under CloudWatch Settings](https://aws.amazon.com/blogs/aws/new-amazon-cloudwatch-cross-account-observability/), a monitoring and source account, and then add the necessary IAM permissions as described above.
## CloudWatch Logs data protection
diff --git a/docs/sources/datasources/aws-cloudwatch/query-editor/index.md b/docs/sources/datasources/aws-cloudwatch/query-editor/index.md
index b65bd308e77..05b5eb3a7f4 100644
--- a/docs/sources/datasources/aws-cloudwatch/query-editor/index.md
+++ b/docs/sources/datasources/aws-cloudwatch/query-editor/index.md
@@ -226,7 +226,7 @@ The label field allows you to override the default name of the metric legend usi
## Query CloudWatch Logs
The logs query editor helps you write CloudWatch Logs Query Language queries across defined regions and log groups.
-It supports querying Cloudwatch logs with Logs Insights Query Language, OpenSearch PPL and OpenSearch SQL.
+It supports querying CloudWatch logs with Logs Insights Query Language, OpenSearch PPL and OpenSearch SQL.
### Create a CloudWatch Logs query
@@ -237,7 +237,7 @@ It supports querying Cloudwatch logs with Logs Insights Query Language, OpenSear
Region and log groups are mandatory fields when querying with Logs Insights QL and OpenSearch PPL. Log group selection is not necessary when querying with OpenSearch SQL. However, selecting log groups simplifies writing logs queries by populating syntax suggestions with discovered log group fields.
{{< /admonition >}}
-1. Use the main input area to write your logs query. AWS Cloudwatch only supports a subset of OpenSearch SQL and PPL commands. To find out more about the syntax supported, consult [Amazon CloudWatch Logs documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData_Languages.html)
+1. Use the main input area to write your logs query. AWS CloudWatch only supports a subset of OpenSearch SQL and PPL commands. To find out more about the syntax supported, consult [Amazon CloudWatch Logs documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData_Languages.html)
#### Querying Log groups with OpenSearch SQL
diff --git a/docs/sources/datasources/azure-monitor/template-variables/index.md b/docs/sources/datasources/azure-monitor/template-variables/index.md
index 5eb2d9f983c..73711ee701a 100644
--- a/docs/sources/datasources/azure-monitor/template-variables/index.md
+++ b/docs/sources/datasources/azure-monitor/template-variables/index.md
@@ -48,19 +48,24 @@ For an introduction to templating and template variables, refer to the [Templati
You can specify these Azure Monitor data source queries in the Variable edit view's **Query Type** field.
-| Name | Description |
-| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
-| **Subscriptions** | Returns subscriptions. |
-| **Resource Groups** | Returns resource groups for a specified. Supports multi-value. subscription. |
-| **Namespaces** | Returns metric namespaces for the specified subscription and resource group. |
-| **Regions** | Returns regions for the specified subscription |
-| **Resource Names** | Returns a list of resource names for a specified subscription, resource group and namespace. Supports multi-value. |
-| **Metric Names** | Returns a list of metric names for a resource. |
-| **Workspaces** | Returns a list of workspaces for the specified subscription. |
-| **Logs** | Use a KQL query to return values. |
-| **Resource Graph** | Use an ARG query to return values. |
+| Name | Description |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
+| **Subscriptions** | Returns subscriptions. |
+| **Resource Groups** | Returns resource groups for a specified. Supports multi-value. subscription. |
+| **Namespaces** | Returns metric namespaces for the specified subscription and resource group. |
+| **Regions** | Returns regions for the specified subscription |
+| **Resource Names** | Returns a list of resource names for a specified subscription, resource group and namespace. Supports multi-value. |
+| **Metric Names** | Returns a list of metric names for a resource. |
+| **Workspaces** | Returns a list of workspaces for the specified subscription. |
+| **Logs** | Use a KQL query to return values. |
+| **Custom Namespaces** | Returns metric namespaces for the specified resource. |
+| **Custom Metric Names** | Returns a list of custom metric names for the specified resource. |
-Any Log Analytics Kusto Query Language (KQL) query that returns a single list of values can also be used in the Query field.
+{{< admonition type="note" >}}
+Custom metrics cannot be emitted against a subscription or resource group. Select resources only when you need to retrieve custom metric namespaces or custom metric names associated with a specific resource.
+{{< /admonition >}}
+
+You can use any Log Analytics Kusto Query Language (KQL) query that returns a single list of values in the `Query` field.
For example:
| Query | List of values returned |
diff --git a/docs/sources/datasources/opentsdb/_index.md b/docs/sources/datasources/opentsdb/_index.md
index 560ba731bb5..07a5a66add0 100644
--- a/docs/sources/datasources/opentsdb/_index.md
+++ b/docs/sources/datasources/opentsdb/_index.md
@@ -62,7 +62,7 @@ To configure basic settings for the data source, complete the following steps:
| **Default** | Default data source that will be be pre-selected for new panels. |
| **URL** | The HTTP protocol, IP, and port of your OpenTSDB server (default port is usually 4242). |
| **Allowed cookies** | Listing of cookies to forward to the data source. |
-| **Version** | The OpenTSDB version. |
+| **Version** | The OpenTSDB version (supported versions are: 2.4, 2.3, 2.2 and versions less than 2.1). |
| **Resolution** | Metrics from OpenTSDB may have data points with either second or millisecond resolution. |
| **Lookup limit** | Default is 1000. |
@@ -98,9 +98,13 @@ can be used to query OpenTSDB. Fill Policy is also introduced in OpenTSDB 2.2.
While using OpenTSDB 2.2 data source, make sure you use either Filters or Tags as they are mutually exclusive. If used together, might give you weird results.
{{% /admonition %}}
+{{% admonition type="note" %}}
+When using OpenTSDB 2.4 with alerting, queries are executed with the parameter `arrays=true`. This causes OpenTSDB to return data points as an array of arrays instead of a map of key-value pairs. Grafana then converts this data into the appropriate data frame format.
+{{% /admonition %}}
+
### Auto complete suggestions
-As soon as you start typing metric names, tag names and tag values , you should see highlighted auto complete suggestions for them.
+As you begin typing metric names, tag names, or tag values, highlighted autocomplete suggestions will appear.
The autocomplete only works if the OpenTSDB suggest API is enabled.
## Templating queries
diff --git a/docs/sources/datasources/postgres/_index.md b/docs/sources/datasources/postgres/_index.md
index 3489b760f70..868a2bb7d9e 100644
--- a/docs/sources/datasources/postgres/_index.md
+++ b/docs/sources/datasources/postgres/_index.md
@@ -2,7 +2,7 @@
aliases:
- ../data-sources/postgres/
- ../features/datasources/postgres/
-description: Guide for using PostgreSQL in Grafana
+description: Introduction to the PostgreSQL data source in Grafana.
keywords:
- grafana
- postgresql
@@ -16,506 +16,58 @@ menuTitle: PostgreSQL
title: PostgreSQL data source
weight: 1200
refs:
- provisioning-data-sources:
- - pattern: /docs/grafana/
- destination: /docs/grafana//administration/provisioning/#datasources
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//administration/provisioning/#datasources
- variables:
- - pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/variables/
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//dashboards/variables/
- add-template-variables-interval-ms:
- - pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms
- add-template-variables-interval:
- - pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval
- - pattern: /docs/grafana-cloud/
- destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval
annotate-visualizations:
- pattern: /docs/grafana/
destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/
- configure-standard-options-display-name:
+ configure-postgres-data-source:
- pattern: /docs/grafana/
- destination: /docs/grafana//panels-visualizations/configure-standard-options/#display-name
+ destination: /docs/grafana//datasources/postgres/configure/
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana//panels-visualizations/configure-standard-options/#display-name
- data-source-management:
+ destination: /docs/grafana//datasources/postgres/configure/
+ postgres-query-editor:
- pattern: /docs/grafana/
- destination: /docs/grafana//administration/data-source-management/
+ destination: /docs/grafana//datasources/postgres/query-editor/
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana//administration/data-source-management/
- variable-syntax-advanced-variable-format-options:
+ destination: /docs/grafana//datasources/postgres/query-editor/
+ alerting:
- pattern: /docs/grafana/
- destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options
+ destination: /docs/grafana//alerting/
- pattern: /docs/grafana-cloud/
- destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/
+ transformations:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//panels-visualizations/query-transform-data/transform-data/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/transform-data/
+ visualizations:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//panels-visualizations/visualizations/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/
---
# PostgreSQL data source
-Grafana ships with a built-in PostgreSQL data source plugin that allows you to query and visualize data from a PostgreSQL compatible database.
+Grafana includes a built-in PostgreSQL data source plugin, enabling you to query and visualize data from any PostgreSQL-compatible database. You don't need to install a plugin to add the PostgreSQL data source to your Grafana instance.
-For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:data-source-management).
-Only users with the organization administrator role can add data sources.
-Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system.
+Grafana offers several configuration options for this data source as well as a visual and code-based query editor.
+
+## Get started with the PostgreSQL data source
+
+The following documents will help you get started with the PostgreSQL data source in Grafana:
+
+- [Configure the PostgreSQL data source](ref:configure-postgres-data-source)
+- [PostgreSQL query editor](ref:postgres-query-editor)
+
+After you have configured the data source you can:
+
+- Create a variety of [visualizations](ref:visualizations)
+- Add [annotations](ref:annotate-visualizations)
+- Set up [alerting](ref:alerting)
+- Add [transformations](ref:transformations)
+
+View a PostgreSQL overview on Grafana Play:
{{< docs/play title="PostgreSQL Overview" url="https://play.grafana.org/d/ddvpgdhiwjvuod/postgresql-overview" >}}
-
-## PostgreSQL settings
-
-To configure basic settings for the data source, complete the following steps:
-
-1. Click **Connections** in the left-side menu.
-1. Under Your connections, click **Data sources**.
-1. Enter `PostgreSQL` in the search bar.
-1. Select **PostgreSQL**.
-
- The **Settings** tab of the data source is displayed.
-
-1. Set the data source's basic configuration options:
-
-| Name | Description |
-| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| **Name** | The data source name. This is how you refer to the data source in panels and queries. |
-| **Default** | Default data source means that it will be pre-selected for new panels. |
-| **Host** | The IP address/hostname and optional port of your PostgreSQL instance. _Do not_ include the database name. The connection string for connecting to Postgres will not be correct and it may cause errors. |
-| **Database** | Name of your PostgreSQL database. |
-| **User** | Database user's login/username |
-| **Password** | Database user's password |
-| **SSL Mode** | Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. When SSL Mode is disabled, SSL Method and Auth Details would not be visible. |
-| **SSL Auth Details Method** | Determines whether the SSL Auth details will be configured as a file path or file content. |
-| **SSL Auth Details Value** | File path or file content of SSL root certificate, client certificate and client key |
-| **Max open** | The maximum number of open connections to the database, default `100`. |
-| **Max idle** | The maximum number of connections in the idle connection pool, default `100`. |
-| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections. Default is `true`. |
-| **Max lifetime** | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours. |
-| **Version** | Determines which functions are available in the query builder. |
-| **TimescaleDB** | A time-series database built as a PostgreSQL extension. When enabled, Grafana uses `time_bucket` in the `$__timeGroup` macro to display TimescaleDB specific aggregate functions in the query builder. For more information, see [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana). |
-
-### Min time interval
-
-A lower limit for the [`$__interval`](ref:add-template-variables-interval) and [`$__interval_ms`](ref:add-template-variables-interval-ms) variables.
-Recommended to be set to write frequency, for example `1m` if your data is written every minute.
-This option can also be overridden/configured in a dashboard panel under data source options. It's important to note that this value **needs** to be formatted as a
-number followed by a valid time identifier, e.g. `1m` (1 minute) or `30s` (30 seconds). The following time identifiers are supported:
-
-| Identifier | Description |
-| ---------- | ----------- |
-| `y` | year |
-| `M` | month |
-| `w` | week |
-| `d` | day |
-| `h` | hour |
-| `m` | minute |
-| `s` | second |
-| `ms` | millisecond |
-
-### Database user permissions (Important!)
-
-The database user you specify when you add the data source should only be granted SELECT permissions on
-the specified database and tables you want to query. Grafana does not validate that the query is safe. The query
-could include any SQL statement. For example, statements like `DELETE FROM user;` and `DROP TABLE user;` would be
-executed. To protect against this we **highly** recommend you create a specific PostgreSQL user with restricted permissions.
-
-Example:
-
-```sql
- CREATE USER grafanareader WITH PASSWORD 'password';
- GRANT USAGE ON SCHEMA schema TO grafanareader;
- GRANT SELECT ON schema.table TO grafanareader;
-```
-
-Make sure the user does not get any unwanted privileges from the public role.
-
-## Query builder
-
-{{< figure src="/static/img/docs/screenshot-postgres-query-editor.png" class="docs-image--no-shadow" caption="PostgreSQL query builder" >}}
-
-The PostgreSQL query builder is available when editing a panel using a PostgreSQL data source. The built query can be run by pressing the `Run query` button in the top right corner of the editor.
-
-### Format
-
-The response from PostgreSQL can be formatted as either a table or as a time series. To use the time series format one of the columns must be named `time`.
-
-### Dataset and table selection
-
-The dataset dropdown will be populated with the configured database to which the user has access.
-The table dropdown is populated with the tables that are available within that database.
-
-### Columns and Aggregation functions (SELECT)
-
-Using the dropdown, select a column to include in the data. You can also specify an optional aggregation function.
-
-Add further value columns by clicking the plus button and another column dropdown appears.
-
-{{< docs/shared source="grafana" lookup="datasources/sql-query-builder-macros.md" version="" >}}
-
-### Filter data (WHERE)
-
-To add a filter, toggle the **Filter** switch at the top of the editor.
-This reveals a **Filter by column value** section with two dropdown selectors.
-
-Use the first dropdown to choose whether all of the filters need to match (`AND`), or if only one of the filters needs to match (`OR`).
-Use the second dropdown to choose a filter.
-
-To filter on more columns, click the plus (`+`) button to the right of the condition dropdown.
-
-To remove a filter, click the `x` button next to that filter's dropdown.
-
-After selecting a date type column, you can choose Macros from the operators list and select timeFilter which will add the $\_\_timeFilter macro to the query with the selected date column.
-
-### Group By
-
-To group the results by column, flip the group switch at the top of the editor. You can then choose which column to group the results by. The group by clause can be removed by pressing the X button.
-
-### Preview
-
-By flipping the preview switch at the top of the editor, you can get a preview of the SQL query generated by the query builder.
-
-### Provision the data source
-
-It's now possible to configure data sources using config files with Grafana's provisioning system. You can read more about how it works and all the settings you can set for data sources on the [provisioning docs page](ref:provisioning-data-sources).
-
-#### Provisioning example
-
-```yaml
-apiVersion: 1
-
-datasources:
- - name: Postgres
- type: postgres
- url: localhost:5432
- user: grafana
- secureJsonData:
- password: 'Password!'
- jsonData:
- database: grafana
- sslmode: 'disable' # disable/require/verify-ca/verify-full
- maxOpenConns: 100
- maxIdleConns: 100
- maxIdleConnsAuto: true
- connMaxLifetime: 14400
- postgresVersion: 903 # 903=9.3, 904=9.4, 905=9.5, 906=9.6, 1000=10
- timescaledb: false
-```
-
-{{% admonition type="note" %}}
-In the above code, the `postgresVersion` value of `10` refers to version PostgreSQL 10 and above.
-{{% /admonition %}}
-
-#### Troubleshoot provisioning
-
-If you encounter metric request errors or other issues:
-
-- Make sure your data source YAML file parameters exactly match the example. This includes parameter names and use of quotation marks.
-- Make sure the `database` name is not included in the `url`.
-
-## Code editor
-
-{{< figure src="/static/img/docs/v92/sql_code_editor.png" class="docs-image--no-shadow" >}}
-
-To make advanced queries, switch to the code editor by clicking `code` in the top right corner of the editor. The code editor support autocompletion of tables, columns, SQL keywords, standard sql functions, Grafana template variables and Grafana macros. Columns cannot be completed before a table has been specified.
-
-You can expand the code editor by pressing the `chevron` pointing downwards in the lower right corner of the code editor.
-
-`CTRL/CMD + Return` works as a keyboard shortcut to run the query.
-
-## Macros
-
-Macros can be used within a query to simplify syntax and allow for dynamic parts.
-
-| Macro example | Description |
-| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `$__time(dateColumn)` | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, _UNIX_TIMESTAMP(dateColumn) as time_sec_ |
-| `$__timeEpoch(dateColumn)` | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, _UNIX_TIMESTAMP(dateColumn) as time_sec_ |
-| `$__timeFilter(dateColumn)` | Will be replaced by a time range filter using the specified column name. For example, _dateColumn BETWEEN FROM_UNIXTIME(1494410783) AND FROM_UNIXTIME(1494410983)_ |
-| `$__timeFrom()` | Will be replaced by the start of the currently active time selection. For example, _FROM_UNIXTIME(1494410783)_ |
-| `$__timeTo()` | Will be replaced by the end of the currently active time selection. For example, _FROM_UNIXTIME(1494410983)_ |
-| `$__timeGroup(dateColumn,'5m')` | Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed),\* |
-| `$__timeGroup(dateColumn,'5m', 0)` | Same as above but with a fill parameter so missing points in that series will be added by grafana and 0 will be used as value (only works with time series queries). |
-| `$__timeGroup(dateColumn,'5m', NULL)` | Same as above but NULL will be used as value for missing points (only works with time series queries). |
-| `$__timeGroup(dateColumn,'5m', previous)` | Same as above but the previous value in that series will be used as fill value if no value has been seen yet NULL will be used (only works with time series queries). |
-| `$__timeGroupAlias(dateColumn,'5m')` | Will be replaced identical to $\_\_timeGroup but with an added column alias. |
-| `$__unixEpochFilter(dateColumn)` | Will be replaced by a time range filter using the specified column name with times represented as Unix timestamp. For example, _dateColumn > 1494410783 AND dateColumn < 1494497183_ |
-| `$__unixEpochFrom()` | Will be replaced by the start of the currently active time selection as Unix timestamp. For example, _1494410783_ |
-| `$__unixEpochTo()` | Will be replaced by the end of the currently active time selection as Unix timestamp. For example, _1494497183_ |
-| `$__unixEpochNanoFilter(dateColumn)` | Will be replaced by a time range filter using the specified column name with times represented as nanosecond timestamp. For example, _dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872_ |
-| `$__unixEpochNanoFrom()` | Will be replaced by the start of the currently active time selection as nanosecond timestamp. For example, _1494410783152415214_ |
-| `$__unixEpochNanoTo()` | Will be replaced by the end of the currently active time selection as nanosecond timestamp. For example, _1494497183142514872_ |
-| `$__unixEpochGroup(dateColumn,'5m', [fillmode])` | Same as $\_\_timeGroup but for times stored as Unix timestamp (`fillMode` only works with time series queries). |
-| `$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])` | Same as above but also adds a column alias (`fillMode` only works with time series queries). |
-
-## Table queries
-
-If the `Format as` query option is set to `Table` then you can basically do any type of SQL query. The table panel will automatically show the results of whatever columns and rows your query returns.
-
-Query editor with example query:
-
-
-
-The query:
-
-```sql
-SELECT
- title as "Title",
- "user".login as "Created By",
- dashboard.created as "Created On"
-FROM dashboard
-INNER JOIN "user" on "user".id = dashboard.created_by
-WHERE $__timeFilter(dashboard.created)
-```
-
-You can control the name of the Table panel columns by using regular `as ` SQL column selection syntax.
-
-The resulting table panel:
-
-
-
-## Time series queries
-
-If you set Format as to _Time series_, then the query must have a column named time that returns either a SQL datetime or any numeric datatype representing Unix epoch in seconds. In addition, result sets of time series queries must be sorted by time for panels to properly visualize the result.
-
-A time series query result is returned in a [wide data frame format](https://grafana.com/developers/plugin-tools/key-concepts/data-frames#wide-format). Any column except time or of type string transforms into value fields in the data frame query result. Any string column transforms into field labels in the data frame query result.
-
-> For backward compatibility, there's an exception to the above rule for queries that return three columns including a string column named metric. Instead of transforming the metric column into field labels, it becomes the field name, and then the series name is formatted as the value of the metric column. See the example with the metric column below.
-
-To optionally customize the default series name formatting, refer to [Standard options definitions](ref:configure-standard-options-display-name).
-
-**Example with `metric` column:**
-
-```sql
-SELECT
- $__timeGroupAlias("time_date_time",'5m'),
- min("value_double"),
- 'min' as metric
-FROM test_data
-WHERE $__timeFilter("time_date_time")
-GROUP BY time
-ORDER BY time
-```
-
-Data frame result:
-
-```text
-+---------------------+-----------------+
-| Name: time | Name: min |
-| Labels: | Labels: |
-| Type: []time.Time | Type: []float64 |
-+---------------------+-----------------+
-| 2020-01-02 03:05:00 | 3 |
-| 2020-01-02 03:10:00 | 6 |
-+---------------------+-----------------+
-```
-
-**Example using the fill parameter in the $\_\_timeGroupAlias macro to convert null values to be zero instead:**
-
-```sql
-SELECT
- $__timeGroupAlias("createdAt",'5m',0),
- sum(value) as value,
- hostname
-FROM test_data
-WHERE
- $__timeFilter("createdAt")
-GROUP BY time, hostname
-ORDER BY time
-```
-
-Given the data frame result in the following example and using the graph panel, you will get two series named _value 10.0.1.1_ and _value 10.0.1.2_. To render the series with a name of _10.0.1.1_ and _10.0.1.2_ , use a [Standard options definitions](ref:configure-standard-options-display-name) display value of `${__field.labels.hostname}`.
-
-Data frame result:
-
-```text
-+---------------------+---------------------------+---------------------------+
-| Name: time | Name: value | Name: value |
-| Labels: | Labels: hostname=10.0.1.1 | Labels: hostname=10.0.1.2 |
-| Type: []time.Time | Type: []float64 | Type: []float64 |
-+---------------------+---------------------------+---------------------------+
-| 2020-01-02 03:05:00 | 3 | 4 |
-| 2020-01-02 03:10:00 | 6 | 7 |
-+---------------------+---------------------------+---------------------------+
-```
-
-**Example with multiple columns:**
-
-```sql
-SELECT
- $__timeGroupAlias("time_date_time",'5m'),
- min("value_double") as "min_value",
- max("value_double") as "max_value"
-FROM test_data
-WHERE $__timeFilter("time_date_time")
-GROUP BY time
-ORDER BY time
-```
-
-Data frame result:
-
-```text
-+---------------------+-----------------+-----------------+
-| Name: time | Name: min_value | Name: max_value |
-| Labels: | Labels: | Labels: |
-| Type: []time.Time | Type: []float64 | Type: []float64 |
-+---------------------+-----------------+-----------------+
-| 2020-01-02 03:04:00 | 3 | 4 |
-| 2020-01-02 03:05:00 | 6 | 7 |
-+---------------------+-----------------+-----------------+
-```
-
-## Templating
-
-Instead of hard-coding things like server, application and sensor name in your metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
-
-Refer to [Templates and variables](ref:variables) for an introduction to the templating feature and the different types of template variables.
-
-### Query variable
-
-If you add a template variable of the type `Query`, you can write a PostgreSQL query that can
-return things like measurement names, key names or key values that are shown as a dropdown select box.
-
-For example, you can have a variable that contains all values for the `hostname` column in a table if you specify a query like this in the templating variable _Query_ setting.
-
-```sql
-SELECT hostname FROM host
-```
-
-A query can return multiple columns and Grafana will automatically create a list from them. For example, the query below will return a list with values from `hostname` and `hostname2`.
-
-```sql
-SELECT host.hostname, other_host.hostname2 FROM host JOIN other_host ON host.city = other_host.city
-```
-
-To use time range dependent macros like `$__timeFilter(column)` in your query the refresh mode of the template variable needs to be set to _On Time Range Change_.
-
-```sql
-SELECT event_name FROM event_log WHERE $__timeFilter(time_column)
-```
-
-Another option is a query that can create a key/value variable. The query should return two columns that are named `__text` and `__value`. The `__text` column value should be unique (if it is not unique then the first value is used). The options in the dropdown will have a text and value that allows you to have a friendly name as text and an id as the value. An example query with `hostname` as the text and `id` as the value:
-
-```sql
-SELECT hostname AS __text, id AS __value FROM host
-```
-
-You can also create nested variables. Using a variable named `region`, you could have
-the hosts variable only show hosts from the current selected region with a query like this (if `region` is a multi-value variable then use the `IN` comparison operator rather than `=` to match against multiple values):
-
-```sql
-SELECT hostname FROM host WHERE region IN($region)
-```
-
-#### Using `__searchFilter` to filter results in Query Variable
-
-Using `__searchFilter` in the query field will filter the query result based on what the user types in the dropdown select box.
-When nothing has been entered by the user the default value for `__searchFilter` is `%`.
-
-> Important that you surround the `__searchFilter` expression with quotes as Grafana does not do this for you.
-
-The example below shows how to use `__searchFilter` as part of the query field to enable searching for `hostname` while the user types in the dropdown select box.
-
-Query
-
-```sql
-SELECT hostname FROM my_host WHERE hostname LIKE '$__searchFilter'
-```
-
-### Using Variables in Queries
-
-Template variable values are only quoted when the template variable is a `multi-value`.
-
-If the variable is a multi-value variable then use the `IN` comparison operator rather than `=` to match against multiple values.
-
-There are two syntaxes:
-
-`$` Example with a template variable named `hostname`:
-
-```sql
-SELECT
- atimestamp as time,
- aint as value
-FROM table
-WHERE $__timeFilter(atimestamp) and hostname in($hostname)
-ORDER BY atimestamp ASC
-```
-
-`[[varname]]` Example with a template variable named `hostname`:
-
-```sql
-SELECT
- atimestamp as time,
- aint as value
-FROM table
-WHERE $__timeFilter(atimestamp) and hostname in([[hostname]])
-ORDER BY atimestamp ASC
-```
-
-#### Disabling quoting for multi-value variables
-
-Grafana automatically creates a quoted, comma-separated string for multi-value variables. For example: if `server01` and `server02` are selected then it will be formatted as: `'server01', 'server02'`. To disable quoting, use the csv formatting option for variables:
-
-`${servers:csv}`
-
-Read more about variable formatting options in the [Variables](ref:variable-syntax-advanced-variable-format-options) documentation.
-
-## Annotations
-
-[Annotations](ref:annotate-visualizations) allow you to overlay rich event information on top of graphs. You add annotation queries via the Dashboard menu / Annotations view.
-
-**Example query using time column with epoch values:**
-
-```sql
-SELECT
- epoch_time as time,
- metric1 as text,
- concat_ws(', ', metric1::text, metric2::text) as tags
-FROM
- public.test_data
-WHERE
- $__unixEpochFilter(epoch_time)
-```
-
-**Example region query using time and timeend columns with epoch values:**
-
-```sql
-SELECT
- epoch_time as time,
- epoch_time_end as timeend,
- metric1 as text,
- concat_ws(', ', metric1::text, metric2::text) as tags
-FROM
- public.test_data
-WHERE
- $__unixEpochFilter(epoch_time)
-```
-
-**Example query using time column of native SQL date/time data type:**
-
-```sql
-SELECT
- native_date_time as time,
- metric1 as text,
- concat_ws(', ', metric1::text, metric2::text) as tags
-FROM
- public.test_data
-WHERE
- $__timeFilter(native_date_time)
-```
-
-| Name | Description |
-| --------- | ----------------------------------------------------------------------------------------------------------------- |
-| `time` | The name of the date/time field. Could be a column with a native SQL date/time data type or epoch value. |
-| `timeend` | Optional name of the end date/time field. Could be a column with a native SQL date/time data type or epoch value. |
-| `text` | Event description field. |
-| `tags` | Optional field name to use for event tags as a comma separated string. |
-
-## Alerting
-
-Time series queries should work in alerting conditions. Table formatted queries are not yet supported in alert rule
-conditions.
diff --git a/docs/sources/datasources/postgres/configure/_index.md b/docs/sources/datasources/postgres/configure/_index.md
new file mode 100644
index 00000000000..7b6a66c0a70
--- /dev/null
+++ b/docs/sources/datasources/postgres/configure/_index.md
@@ -0,0 +1,194 @@
+---
+description: This document provides instructions for configuring the PostgreSQL data source.
+keywords:
+ - grafana
+ - postgresql
+ - guide
+labels:
+ products:
+ - cloud
+ - enterprise
+ - oss
+menuTitle: Configure the PostgreSQL data source
+title: Configure the PostgreSQL data source
+weight: 10
+refs:
+ provisioning-data-sources:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//administration/provisioning/#datasources
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//administration/provisioning/#datasources
+ variables:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//dashboards/variables/
+ add-template-variables-interval-ms:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval_ms
+ add-template-variables-interval:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval
+ data-source-management:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//administration/data-source-management/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//administration/data-source-management/
+ variable-syntax-advanced-variable-format-options:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//dashboards/variables/variable-syntax/#advanced-variable-format-options
+---
+
+# Configure the PostgreSQL data source
+
+This document provides instructions for configuring the PostgreSQL data source and explains available configuration options. For general information on managing data sources refer to [Data source management](ref:data-source-management).
+
+## Before you begin
+
+You must have the `Organization administrator` role to configure the Postgres data source.
+Organization administrators can also [configure the data source via YAML](#provision-the-data-source) with the Grafana provisioning system.
+
+Grafana comes with a built-in PostgreSQL data source plugin, eliminating the need to install a plugin.
+
+{{< admonition type="note" >}}
+When adding a data source, the database user you specify should have only `SELECT` permissions on the relevant database and tables. Grafana does not validate the safety of queries, which means they can include potentially harmful SQL statements, such as `USE otherdb;` or `DROP TABLE user;`, that could be executed. To mitigate this risk, Grafana strongly recommends creating a dedicated PostgreSQL user with restricted permissions.
+{{< /admonition >}}
+
+Example:
+
+```sql
+ CREATE USER grafanareader WITH PASSWORD 'password';
+ GRANT USAGE ON SCHEMA schema TO grafanareader;
+ GRANT SELECT ON schema.table TO grafanareader;
+```
+
+## Add the PostgreSQL data source
+
+Complete the following steps to set up a new PostgreSQL data source:
+
+1. Click **Connections** in the left-side menu.
+1. Click **Add new connection**
+1. Type `PostgreSQL` in the search bar.
+1. Select the **PostgreSQL data source**.
+1. Click **Add new data source** in the upper right.
+
+You are taken to the **Settings** tab where you will configure the data source.
+
+## PostgreSQL configuration options
+
+Following is a list of PostgreSQL configuration options:
+
+- **Name** - Sets the name you use to refer to the data source in panels and queries. Examples: `PostgreSQL-DB-1`.
+- **Default** - Toggle to set this specific PostgreSQL data source as the default pre-selected data source in panels and visualizations.
+
+**Connection section:**
+
+- **Host URL** - The IP address/hostname and optional port of your PostgreSQL instance.
+- **Database name** - The name of your PostgreSQL database.
+
+**Authentication section:**
+
+- **Username** - Enter the username used to connect to your PostgreSQL database.
+- **Password** - Enter the password used to connect to the PostgreSQL database.
+- **TLS/SSL Mode** - Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. When **TLS/SSL Mode** is disabled, **TLS/SSL Method** and **TLS/SSL Auth Details** aren't visible options.
+- **TLS/SSL Method** - Determines how TLS/SSL certificates are configured.
+ - **File system path** - This option allows you to configure certificates by specifying paths to existing certificates on the local file system where Grafana is running. Ensure this file is readable by the user executing the Grafana process.
+ - **Certificate content** - This option allows you to configure certificate by specifying their content. The content is stored and encrypted in the Grafana database. When connecting to the database, the certificates are saved as files, on the local filesystem, in the Grafana data path.
+
+**TLS/SSL Auth Details**
+
+If you select the TLS/SSL Mode options **require**, **verify-ca** or **verify-full** and **file system path** the following are required:
+
+- **TLS/SSL Root Certificate** - Specify the path to the root certificate file.
+- **TLS/SSL Client Certificate** - Specify the path to the client certificate and ensure the file is accessible to the user running the Grafana process.
+- **TLS/SSL Client Key** - Specify the path to the client key file and ensure the file is accessible to the user running the Grafana process.
+
+If you select the TLS/SSL Mode option **require** and TLS/SSL Method certificate content the following are required:
+
+- **TLS/SSL Client Certificate** - Provide the client certificate.
+- **TLS/SSL Client Key** - Provide the client key.
+
+If you select the TLS/SSL Mode options **verify-ca** or **verify-full** with the TLS/SSL Method certificate content the following are required:
+
+- **TLS/SSL Client Certificate** - Provide the client certificate.
+- **TLS/SSL Root Certificate** - Provide the root certificate.
+- **TLS/SSL Client Key** - Provide the client key.
+
+**PostgreSQL Options:**
+
+- **Version** - Determines which functions are available in the query builder. The default is the current version.
+- **Min time interval** - Defines a lower limit for the auto group by by time interval. Grafana recommends aligning this setting with the data write frequency. For example, set it to `1m` if your data is written every minute. Refer to [Min time interval](#min-time-interval) for format examples.
+- **TimescaleDB** - A time-series database built as a PostgreSQL extension. When enabled, Grafana uses `time_bucket` in the `$__timeGroup` macro to display TimescaleDB specific aggregate functions in the query builder. For more information, refer to [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana).
+
+**Connection limits:**
+
+- **Max open** - The maximum number of open connections to the database. The default `100`.
+- **Auto max idle** - Toggle to set the maximum number of idle connections to the number of maximum open connections. This setting is toggled on by default.
+- **Max idle** - The maximum number of connections in the idle connection pool. The default `100`.
+- **Max lifetime** - The maximum amount of time in seconds a connection may be reused. The default is `14400`, or 4 hours.
+
+**Private data source connect** - _Only for Grafana Cloud users._ Private data source connect, or PDC, allows you to establish a private, secured connection between a Grafana Cloud instance, or stack, and data sources secured within a private network. Click the drop-down to locate the URL for PDC. For more information regarding Grafana PDC refer to [Private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/).
+
+Click **Manage private data source connect** to be taken to your PDC connection page, where you’ll find your PDC configuration details.
+
+After you have added your PostgreSQL connection settings, click **Save & test** to test and save the data source connection.
+
+### Min time interval
+
+The **Min time interval** setting defines a lower limit for the [`$__interval`](ref:add-template-variables-interval) and [`$__interval_ms`](ref:add-template-variables-interval-ms) variables.
+
+This option can also be configured or overridden in the dashboard panel under the data source settings.
+
+This value must be formatted as a number followed by a valid time identifier:
+
+| Identifier | Description |
+| ---------- | ----------- |
+| `y` | year |
+| `M` | month |
+| `w` | week |
+| `d` | day |
+| `h` | hour |
+| `m` | minute |
+| `s` | second |
+| `ms` | millisecond |
+
+## Provision the data source
+
+You can define and configure the data source in YAML files with [provisioning](/docs/grafana//administration/provisioning/#data-sources).
+For more information about provisioning, and available configuration options, refer to [Provision Grafana](ref:provisioning-data-sources).
+
+### PostgreSQL provisioning example
+
+```yaml
+apiVersion: 1
+
+datasources:
+ - name: Postgres
+ type: postgres
+ url: localhost:5432
+ user: grafana
+ secureJsonData:
+ password: 'Password!'
+ jsonData:
+ database: grafana
+ sslmode: 'disable' # disable/require/verify-ca/verify-full
+ maxOpenConns: 100
+ maxIdleConns: 100
+ maxIdleConnsAuto: true
+ connMaxLifetime: 14400
+ postgresVersion: 903 # 903=9.3, 904=9.4, 905=9.5, 906=9.6, 1000=10
+ timescaledb: false
+```
+
+#### Troubleshoot provisioning issues
+
+If you encounter metric request errors or other issues:
+
+- Ensure that the parameters in your data source YAML file precisely match the example provided, including parameter names and the correct use of quotation marks.
+- Verify that the database name _isn't_ included in the URL.
diff --git a/docs/sources/datasources/postgres/query-editor/_index.md b/docs/sources/datasources/postgres/query-editor/_index.md
new file mode 100644
index 00000000000..5c37f7047e7
--- /dev/null
+++ b/docs/sources/datasources/postgres/query-editor/_index.md
@@ -0,0 +1,410 @@
+---
+description: This document describes the PostgreSQL query editor in Grafana.
+keywords:
+ - grafana
+ - postgresql
+ - guide
+labels:
+ products:
+ - cloud
+ - enterprise
+ - oss
+menuTitle: PostgreSQL query editor
+title: PostgreSQL query editor
+weight: 20
+refs:
+ variables:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//dashboards/variables/
+ add-template-variables-interval:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval
+ explore:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//explore/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana//explore/
+ query-transform-data:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//panels-visualizations/query-transform-data/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/
+ query-editor:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//panels-visualizations/query-transform-data/#query-editors
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/#query-editors
+ alert-rules:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/fundamentals/alert-rules/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/
+ template-annotations-and-labels:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//alerting/alerting-rules/templates/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/
+ templates:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/variables/#templates
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/dashboards/variables/#templates
+---
+
+# PostgreSQL query editor
+
+Grafana query editors are unique for each data source.
+
+For general information on Grafana query editors, refer to [Query editors](ref:query-editor).
+
+For general information on querying data sources in Grafana, refer to [Query and transform data](ref:query-transform-data).
+
+The PostgreSQL query editor is located on the [Explore page](ref:explore). You can also access the PostgreSQL query editor from a dashboard panel. Click the ellipsis in the upper right of the panel and select **Edit**.
+
+{{< figure src="/static/img/docs/screenshot-postgres-query-editor.png" class="docs-image--no-shadow" caption="PostgreSQL query builder" >}}
+
+## PostgreSQL query editor components
+
+The PostgreSQL query editor has two modes: **Builder** and **Code**.
+
+Builder mode helps you build a query using a visual interface. Code mode allows for advanced querying and offers support for complex SQL query writing.
+
+### PostgreSQL Builder mode
+
+The following components will help you build a PostgreSQL query:
+
+- **Format** - Select a format response from the drop-down for the PostgreSQL query. The default is **Table**. If you use the **Time series** format option, one of the columns must be `time`. Refer to [Time series queries](#time-series-queries) for more information.
+- **Table** - Select a table from the drop-down. Tables correspond to the chosen database.
+- **Data operations** - _Optional_ Select an aggregation from the drop-down. You can add multiple data operations by clicking the **+ sign**. Click the **garbage can icon** to remove data operations.
+- **Column** - Select a column on which to run the aggregation.
+- **Alias** - _Optional_ Add an alias from the drop-down. You can also add your own alias by typing it in the box and clicking **Enter**. Remove an alias by clicking the **X**.
+- **Filter** - Toggle to add filters.
+- **Filter by column value** - _Optional_ If you toggle **Filter** you can add a column to filter by from the drop-down. To filter on more columns, click the **+ sign** to the right of the condition drop-down. You can choose a variety of operators from the drop-down next to the condition. When multiple filters are added you can add an `AND` operator to display all true conditions or an `OR` operator to display any true conditions. Use the second drop-down to choose a filter. To remove a filter, click the `X` button next to that filter's drop-down. After selecting a date type column, you can choose **Macros** from the operators list and select `timeFilter` which will add the `$\_\_timeFilter` macro to the query with the selected date column.
+- **Group** - Toggle to add **Group by column**.
+- **Group by column** - Select a column to filter by from the drop-down. Click the **+sign** to filter by multiple columns. Click the **X** to remove a filter.
+- **Order** - Toggle to add an `ORDER BY` statement.
+- **Order by** - Select a column to order by from the drop-down. Select ascending (`ASC`) or descending (`DESC`) order.
+- **Limit** - You can add an optional limit on the number of retrieved results. Default is 50.
+- **Preview** - Toggle for a preview of the SQL query generated by the query builder. Preview is toggled on by default.
+
+## PostgreSQL Code mode
+
+To create advanced queries, switch to **Code mode** by clicking **Code** in the upper right of the editor window. Code mode supports the auto-completion of tables, columns, SQL keywords, standard SQL functions, Grafana template variables, and Grafana macros. Columns cannot be completed before a table has been specified.
+
+{{< figure src="/static/img/docs/v92/sql_code_editor.png" class="docs-image--no-shadow" >}}
+
+Select **Table** or **Time Series** as the format. Click the **{}** in the bottom right to format the query. Click the **downward caret** to expand the Code mode editor. **CTRL/CMD + Return** serves as a keyboard shortcut to execute the query.
+
+{{< admonition type="warning" >}}
+Changes made to a query in Code mode will not transfer to Builder mode and will be discarded. You will be prompted to copy your code to the clipboard to save any changes.
+{{< /admonition >}}
+
+## Macros
+
+You can add macros to your queries to simplify the syntax and enable dynamic elements, such as date range filters.
+
+| Macro example | Description |
+| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `$__time(dateColumn)` | Replaces the value with an expression to convert to a UNIX timestamp and renames the column to `time_sec`. Example: `UNIX_TIMESTAMP(dateColumn) AS time_sec`. |
+| `$__timeEpoch(dateColumn)` | Replaces the value with an expression to convert to a UNIX Epoch timestamp and renames the column to `time_sec`. Example: `UNIX_TIMESTAMP(dateColumn) AS time_sec`. |
+| `$__timeFilter(dateColumn)` | Replaces the value a time range filter using the specified column name. Example: `dateColumn BETWEEN FROM_UNIXTIME(1494410783) AND FROM_UNIXTIME(1494410983)` |
+| `$__timeFrom()` | Replaces the value with the start of the currently active time selection. Example: `FROM_UNIXTIME(1494410783)` |
+| `$__timeTo()` | Replaces the value with the end of the currently active time selection. Example: `FROM_UNIXTIME(1494410983)` |
+| `$__timeGroup(dateColumn,'5m')` | Replaces the value with an expression suitable for use in a `GROUP BY` clause. Example: `cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) AS signed)*300 AS signed)` |
+| `$__timeGroup(dateColumn,'5m', 0)` | Same as the `$__timeGroup(dateColumn,'5m')` macro, but includes a fill parameter to ensure missing points in the series are added by Grafana, using 0 as the default value. **This applies only to time series queries.** |
+| `$__timeGroup(dateColumn,'5m', NULL)` | Same as the `$__timeGroup(dateColumn,'5m', 0)` but `NULL` is used as the value for missing points. _This applies only to time series queries._ |
+| `$__timeGroup(dateColumn,'5m', previous)` | Same as the `$__timeGroup(dateColumn,'5m', previous)` macro, but uses the previous value in the series as the fill value. If no previous value exists, it uses `NULL`. _This applies only to time series queries._ |
+| `$__timeGroupAlias(dateColumn,'5m')` | Replaces the value identical to `$__timeGroup` but with an added column alias. |
+| `$__unixEpochFilter(dateColumn)` | Replaces the value by a time range filter using the specified column name with times represented as a UNIX timestamp. Example: `dateColumn > 1494410783 AND dateColumn < 1494497183` |
+| `$__unixEpochFrom()` | Replaces the value with the start of the currently active time selection as a UNIX timestamp. Example: `1494410783` |
+| `$__unixEpochTo()` | Replaces the value with the end of the currently active time selection as a UNIX timestamp. Example: `1494497183` |
+| `$__unixEpochNanoFilter(dateColumn)` | Replaces the value with a time range filter using the specified column name with time represented as a nanosecond timestamp. Example: `dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872` |
+| `$__unixEpochNanoFrom()` | Replaces the value with the start of the currently active time selection as a nanosecond timestamp. Example: `1494410783152415214` |
+| `$__unixEpochNanoTo()` | Replaces the value with the end of the currently active time selection as nanosecond timestamp. Example: `1494497183142514872` |
+| `$__unixEpochGroup(dateColumn,'5m', [fillmode])` | Same as `$__timeGroup` but for times stored as Unix timestamp. `fillMode` only works with time series queries. |
+| `$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])` | Same as `$__timeGroup` but also adds a column alias. `fillMode` only works with time series queries. |
+
+## Table SQL queries
+
+If the **Format** option is set to **Table**, you can execute virtually any type of SQL query. The Table panel will automatically display the resulting columns and rows from your query.
+
+
+
+You can change or customize the name of a Table panel column by using the SQL keyword `AS` syntax.
+
+```sql
+SELECT
+ title as "Title",
+ "user".login as "Created By",
+ dashboard.created as "Created On"
+FROM dashboard
+INNER JOIN "user" on "user".id = dashboard.created_by
+WHERE $__timeFilter(dashboard.created)
+```
+
+## Time series queries
+
+Set the **Format** option to **Time series** to create and run time series queries.
+
+{{< admonition type="note" >}}
+To run a time series query you must include a column named `time` that returns either a SQL `datetime` value or a numeric datatype representing the UNIX epoch time in seconds. Additionally, the query results must be sorted by the `time` column for proper visualization in panels.
+{{< /admonition >}}
+
+The examples in this section refer to the data in the following table:
+
+```text
++---------------------+--------------+---------------------+----------+
+| time_date_time | value_double | CreatedAt | hostname |
++---------------------+--------------+---------------------+----------+
+| 2020-01-02 03:05:00 | 3.0 | 2020-01-02 03:05:00 | 10.0.1.1 |
+| 2020-01-02 03:06:00 | 4.0 | 2020-01-02 03:06:00 | 10.0.1.2 |
+| 2020-01-02 03:10:00 | 6.0 | 2020-01-02 03:10:00 | 10.0.1.1 |
+| 2020-01-02 03:11:00 | 7.0 | 2020-01-02 03:11:00 | 10.0.1.2 |
+| 2020-01-02 03:20:00 | 5.0 | 2020-01-02 03:20:00 | 10.0.1.2 |
++---------------------+--------------+---------------------+----------+
+```
+
+Time series query results are returned in [wide data frame format](https://grafana.com/developers/plugin-tools/key-concepts/data-frames#wide-format). In the data frame query result, any column, except for time or string-type columns, transforms into value fields. String columns, on the other hand, become field labels.
+
+{{< admonition type="note" >}}
+For backward compatibility, an exception to this rule applies to queries that return three columns, one of which is a string column named `metric`. Instead of converting the metric column into field labels, it is used as the field name, while the series name is set to its value. See the following example for reference.
+{{< /admonition >}}
+
+**Example with `metric` column:**
+
+```sql
+SELECT
+ $__timeGroupAlias("time_date_time",'5m'),
+ min("value_double"),
+ 'min' as metric
+FROM test_data
+WHERE $__timeFilter("time_date_time")
+GROUP BY time
+ORDER BY time
+```
+
+Data frame result:
+
+```text
++---------------------+-----------------+
+| Name: time | Name: min |
+| Labels: | Labels: |
+| Type: []time.Time | Type: []float64 |
++---------------------+-----------------+
+| 2020-01-02 03:05:00 | 3 |
+| 2020-01-02 03:10:00 | 6 |
++---------------------+-----------------+
+```
+
+To customize default series name formatting, refer to [Standard options definitions](ref:configure-standard-options-display-name).
+
+Following are time series query examples.
+
+**Example using the fill parameter in the $\_\_timeGroupAlias macro to convert null values to be zero instead:**
+
+```sql
+SELECT
+ $__timeGroupAlias("createdAt",'5m',0),
+ sum(value) as value,
+ hostname
+FROM test_data
+WHERE
+ $__timeFilter("createdAt")
+GROUP BY time, hostname
+ORDER BY time
+```
+
+Based on the data frame result in the following example, the time series panel will generate two series named _value 10.0.1.1_ and _value 10.0.1.2_. To display the series names as _10.0.1.1_ and _10.0.1.2_, use the [Standard options definitions](ref:configure-standard-options-display-name) display value `${__field.labels.hostname}`.
+
+Data frame result:
+
+```text
++---------------------+---------------------------+---------------------------+
+| Name: time | Name: value | Name: value |
+| Labels: | Labels: hostname=10.0.1.1 | Labels: hostname=10.0.1.2 |
+| Type: []time.Time | Type: []float64 | Type: []float64 |
++---------------------+---------------------------+---------------------------+
+| 2020-01-02 03:05:00 | 3 | 4 |
+| 2020-01-02 03:10:00 | 6 | 7 |
++---------------------+---------------------------+---------------------------+
+```
+
+**Example with multiple columns:**
+
+```sql
+SELECT
+ $__timeGroupAlias("time_date_time",'5m'),
+ min("value_double") as "min_value",
+ max("value_double") as "max_value"
+FROM test_data
+WHERE $__timeFilter("time_date_time")
+GROUP BY time
+ORDER BY time
+```
+
+Data frame result:
+
+```text
++---------------------+-----------------+-----------------+
+| Name: time | Name: min_value | Name: max_value |
+| Labels: | Labels: | Labels: |
+| Type: []time.Time | Type: []float64 | Type: []float64 |
++---------------------+-----------------+-----------------+
+| 2020-01-02 03:04:00 | 3 | 4 |
+| 2020-01-02 03:05:00 | 6 | 7 |
++---------------------+-----------------+-----------------+
+```
+
+## Templating
+
+Instead of hard coding values like server, application, or sensor names in your metric queries, you can use variables. Variables appear as drop-down select boxes at the top of the dashboard. These drop-downs make it easy to change the data being displayed in your dashboard.
+
+Refer to [Templates](ref:templates) for an introduction to creating template variables as well as the different types.
+
+### Query variable
+
+If you add a `Query` template variable you can write a PostgreSQL query to retrieve items such as measurement names, key names, or key values, which will be displayed in the drop-down menu.
+
+For example, you can use a variable to retrieve all the values from the `hostname` column in a table by creating the following query in the templating variable _Query_ setting.
+
+```sql
+SELECT hostname FROM host
+```
+
+A query can return multiple columns, and Grafana will automatically generate a list based on the query results. For example, the following query returns a list with values from `hostname` and `hostname2`.
+
+```sql
+SELECT host.hostname, other_host.hostname2 FROM host JOIN other_host ON host.city = other_host.city
+```
+
+To use time range dependent macros like `$__timeFilter(column)` in your query, you must set the template variable's refresh mode to _On Time Range Change_.
+
+```sql
+SELECT event_name FROM event_log WHERE $__timeFilter(time_column)
+```
+
+Another option is a query that can create a key/value variable. The query should return two columns that are named `__text` and `__value`. The `__text` column must contain unique values (if not, only the first value is used). This allows the drop-down options to display a text-friendly name as the text while using an ID as the value. For example, a query could use `hostname` as the text and `id` as the value:
+
+```sql
+SELECT hostname AS __text, id AS __value FROM host
+```
+
+You can also create nested variables. For example, if you have a variable named `region`, you can configure the `hosts` variable to display only the hosts within the currently selected region as shown in the following example. If `region` is a multi-value variable, use the `IN` operator instead of `=` to match multiple values.
+
+```sql
+SELECT hostname FROM host WHERE region IN($region)
+```
+
+#### Using `__searchFilter` to filter results in Query Variable
+
+Using `__searchFilter` in the query field allows the query results to be filtered based on the user’s input in the drop-down selection box. If you do not enter anything, the default value for `__searchFilter` is `%`.
+
+Note that you must enclose the `__searchFilter` expression in quotes as Grafana does not add them automatically.
+
+The following example demonstrates how to use `__searchFilter` in the query field to enable real-time searching for `hostname` as the user type in the drop-down selection box.
+
+```sql
+SELECT hostname FROM my_host WHERE hostname LIKE '$__searchFilter'
+```
+
+### Using Variables in Queries
+
+Template variable values are only quoted when the template variable is a `multi-value`.
+
+If the variable is a multi-value variable, use the `IN` comparison operator instead of `=` to match against multiple values.
+
+You can use two different syntaxes:
+
+`$` Example with a template variable named `hostname`:
+
+```sql
+SELECT
+ atimestamp as time,
+ aint as value
+FROM table
+WHERE $__timeFilter(atimestamp) and hostname in($hostname)
+ORDER BY atimestamp ASC
+```
+
+`[[varname]]` Example with a template variable named `hostname`:
+
+```sql
+SELECT
+ atimestamp as time,
+ aint as value
+FROM table
+WHERE $__timeFilter(atimestamp) and hostname in([[hostname]])
+ORDER BY atimestamp ASC
+```
+
+#### Disabling quoting for multi-value variables
+
+Grafana automatically formats multi-value variables as a quoted, comma-separated string. For example, if `server01` and `server02` are selected, they are formatted as `'server01'`, `'server02'`. To remove the quotes, enable the CSV formatting option for the variables:
+
+`${servers:csv}`
+
+Read more about variable formatting options in the [Variables](ref:variable-syntax-advanced-variable-format-options) documentation.
+
+## Annotations
+
+[Annotations](ref:annotate-visualizations) allow you to overlay rich event information on top of graphs. Add annotation queries via the **Dashboard settings > Annotations view**.
+
+**Example query using a `time` column with epoch values:**
+
+```sql
+SELECT
+ epoch_time as time,
+ metric1 as text,
+ concat_ws(', ', metric1::text, metric2::text) as tags
+FROM
+ public.test_data
+WHERE
+ $__unixEpochFilter(epoch_time)
+```
+
+**Example region query using `time` and `timeend` columns with epoch values:**
+
+```sql
+SELECT
+ epoch_time as time,
+ epoch_time_end as timeend,
+ metric1 as text,
+ concat_ws(', ', metric1::text, metric2::text) as tags
+FROM
+ public.test_data
+WHERE
+ $__unixEpochFilter(epoch_time)
+```
+
+**Example query using a `time` column with a native SQL date/time data type:**
+
+```sql
+SELECT
+ native_date_time as time,
+ metric1 as text,
+ concat_ws(', ', metric1::text, metric2::text) as tags
+FROM
+ public.test_data
+WHERE
+ $__timeFilter(native_date_time)
+```
+
+| Name | Description |
+| --------- | --------------------------------------------------------------------------------------------------------------------- |
+| `time` | The name of the date/time field, which can be a column with a native SQL date/time data type or epoch value. |
+| `timeend` | Optional name of the end date/time field, which can be a column with a native SQL date/time data type or epoch value. |
+| `text` | Event description field. |
+| `tags` | Optional field name to use for event tags as a comma-separated string. |
+
+## Alerting
+
+Use time series queries to create alerts. Table formatted queries aren't yet supported in alert rule conditions.
+
+For more information regarding alerting refer to the following:
+
+- [Alert rules](ref:alert-rules)
+- [Template annotations and labels](ref:template-annotations-and-labels)
diff --git a/docs/sources/datasources/prometheus/configure-prometheus-data-source.md b/docs/sources/datasources/prometheus/configure-prometheus-data-source.md
index 077b6aeff04..98ef1608f60 100644
--- a/docs/sources/datasources/prometheus/configure-prometheus-data-source.md
+++ b/docs/sources/datasources/prometheus/configure-prometheus-data-source.md
@@ -48,13 +48,18 @@ Grafana ships with built-in support for Prometheus. If you are new to Prometheus
## Configure the data source
+{{< shared id="add-prom-data-source" >}}
+
To add the Prometheus data source, complete the following steps:
1. Click **Connections** in the left-side menu.
1. Under **Connections**, click **Add new connection**.
1. Enter `Prometheus` in the search bar.
-1. Select **Prometheus**.
+1. Click **Prometheus data source**.
1. Click **Add new data source** in the upper right.
+1. Enter a name for the data source.
+
+{{< /shared >}}
You will be taken to the **Settings** tab where you will set up your Prometheus configuration.
@@ -70,7 +75,8 @@ The first option to configure is the name of your connection:
### Connection section
-- **Prometheus server URL** - The URL of your Prometheus server. If your Prometheus server is local, use `http://localhost:9090`. If it is on a server within a network, this is the URL with port where you are running Prometheus. Example: `http://prometheus.example.orgname:9090`.
+- **Prometheus server URL** - The URL of your Prometheus server. {{< shared id="prom-data-source-url" >}}
+ If your Prometheus server is local, use `http://localhost:9090`. If it's on a server within a network, this is the URL with the port where you are running Prometheus. Example: `http://prometheus.example.orgname:9090`.
{{< admonition type="note" >}}
@@ -80,6 +86,8 @@ You should use the IP address of the Prometheus container, or the hostname if yo
{{< /admonition >}}
+{{< /shared >}}
+
### Authentication section
There are several authentication methods you can choose in the Authentication section.
@@ -171,3 +179,22 @@ Support for exemplars is available only for the Prometheus data source. If this
- **Label name** - The name of the field in the `labels` object used to obtain the traceID property.
- **Remove exemplar link** - Click to remove existing links.
+
+### Troubleshooting
+
+Refer to the following troubleshooting information, as required.
+
+#### Data doesn't appear in Explore metrics
+
+
+
+If metric data doesn't appear in Explore after you've successfully tested a connection to a Prometheus data source or sent
+metrics to Grafana Cloud, ensure that you've selected the correct data source in the **Data source** drop-down menu. If
+you've used remote_write to send metrics to Grafana Cloud, the data source name follows the convention
+`grafanacloud-stackname-prom`.
+
+
+
+The following image shows the **Data source** field in Explore metrics.
+
+
diff --git a/docs/sources/datasources/pyroscope/_index.md b/docs/sources/datasources/pyroscope/_index.md
index 069902bfc0a..60efe8f3f33 100644
--- a/docs/sources/datasources/pyroscope/_index.md
+++ b/docs/sources/datasources/pyroscope/_index.md
@@ -56,7 +56,7 @@ To use profiling data, you should:
- [Configure your application to send profiles](/docs/pyroscope//configure-client/)
- [Configure the Grafana Pyroscope data source](./configure-pyroscope-data-source/).
-- [View and query profiling data using Explore Profiles or the query editor ](./query-profile-data/)
+- [View and query profiling data using Profiles Drilldown or the query editor ](./query-profile-data/)
## Continuous profiling
diff --git a/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md b/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md
index 8a344f6a7e6..0acfd01df3c 100644
--- a/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md
+++ b/docs/sources/datasources/pyroscope/configure-pyroscope-data-source.md
@@ -33,11 +33,11 @@ refs:
destination: /docs/grafana//datasources/tempo/configure-tempo-data-source/
- pattern: /docs/grafana-cloud/
destination: docs/grafana-cloud/connect-externally-hosted/data-sources/tempo/configure-tempo-data-source/
- provisioning-data-sources:
+ explore-profiles:
- pattern: /docs/grafana/
- destination: /docs/grafana//administration/provisioning/#data-sources
- - pattern: /docs/grafana-cloud/provision
- destination: /docs/grafana//administration/provisioning/#data-sources
+ destination: /docs/grafana//explore/simplified-exploration/profiles/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/simplified-exploration/profiles/
---
# Configure the Grafana Pyroscope data source
@@ -73,7 +73,7 @@ To configure basic settings for the data source, complete the following steps:
1. On the **Settings** tab, complete the **Name**, **Connection**, and **Authentication** sections.
- Use the **Name** field to specify the name used for the data source in panels, queries, and Explore. Toggle the **Default** switch for the data source to be pre-selected for new panels.
-- Under **Connection**, enter the **URL** of the Pyroscope instance. For example, `https://example.com:4100`.
+- Under **Connection**, enter the **URL** of the Pyroscope instance. For example, `https://example.com:4100`. Refer to [Connection URL](#connection-url) for more information.
- Complete the [**Authentication** section](#authentication).
1. Optional: Use **Additional settings** to configure other options.
@@ -89,6 +89,24 @@ To modify an existing Pyroscope data source:
1. Optional: Use **Additional settings** to configure or modify other options.
1. After completing your updates, select **Save & test**.
+#### Connection URL
+
+The data source connection URL should point to a location of a running Pyroscope backend.
+
+**Grafana Cloud Profiles**
+
+Your Grafana Cloud instance automatically includes a fully provisioned data source.
+
+If you are running a self-managed Grafana instance or need to configure an additional Pyroscope data source pointing to Grafana Cloud Profiles, you can find the Pyroscope URL under the **Manage your stack** section for your organization.
+
+**Self-managed Pyroscope backend**
+
+The connection URL for a self-managed Pyroscope backend depends on how Pyroscope is deployed.
+Refer to the steps under [Query profiles in Grafana](https://grafana.com/docs/pyroscope//deploy-kubernetes/helm/#query-profiles-in-grafana) for more information on how to configure the data source.
+
+If you plan to use the [Profiles Drilldown](ref:explore-profiles) application and you are running a self-managed Pyroscope backend in microservices mode, the data source connection URL should point to a gateway or proxy that routes requests to the corresponding Pyroscope service.
+Refer to the [Helm ingress configuration](https://github.com/grafana/pyroscope/blob/main/operations/pyroscope/helm/pyroscope/templates/ingress.yaml) for specific routing requirements.
+
## Authentication
Use this section to select an authentication method to access the data source.
diff --git a/docs/sources/datasources/pyroscope/query-profile-data.md b/docs/sources/datasources/pyroscope/query-profile-data.md
index d56a629d42d..d0917ccfe12 100644
--- a/docs/sources/datasources/pyroscope/query-profile-data.md
+++ b/docs/sources/datasources/pyroscope/query-profile-data.md
@@ -1,5 +1,5 @@
---
-description: Explore your profiling data using Explore Profiles or the Pyroscope query editor.
+description: Explore your profiling data using Profiles Drilldown or the Pyroscope query editor.
keywords:
- query
- profiling
@@ -33,7 +33,7 @@ refs:
destination: /docs/grafana//explore/simplified-exploration/profiles/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/simplified-exploration/profiles/
- explore-profile-install:
+ explore-profiles-install:
- pattern: /docs/grafana/
destination: /docs/grafana//explore/simplified-exploration/profiles/access/
- pattern: /docs/grafana-cloud/
@@ -54,22 +54,22 @@ https://grafana.com/docs/grafana-cloud/visualizations/panels-visualizations/visu
# Query profile data
-You can query your profile data using the open source Grafana Explore Profiles app or the built-in Grafana Pyroscope data source query editor.
+You can query your profile data using the open source Grafana Profiles Drilldown app or the built-in Grafana Pyroscope data source query editor.
-- Explore Profiles provides a queryless experience for inspecting your profiling data with opinionated workflows to assist your investigation.
+- Profiles Drilldown provides a queryless experience for inspecting your profiling data with opinionated workflows to assist your investigation.
- Data source query editor provides complete control over your data exploration and is recommended for experienced users.
In addition, you can embed flame graph panels in Grafana dashboards.
Refer to the [Flame graph panel](ref:flame-graph-panel) documentation for details.
-## Explore Profiles
+## Profiles Drilldown
-[Explore Profiles](ref:explore-profiles) is a native Grafana application designed to integrate seamlessly with Pyroscope, the open source continuous profiling platform, providing a smooth, queryless experience for browsing and analyzing profiling data.
+[Grafana Profiles Drilldown](ref:explore-profiles) is a native Grafana application designed to integrate seamlessly with Pyroscope, the open source continuous profiling platform, providing a smooth, queryless experience for browsing and analyzing profiling data.
-You can use Explore Profiles in Grafana Cloud or in your own Grafana instance.
-For more information, refer to [Access or install Explore Profiles](ref:explore-profiles-install).
+You can use Profiles Drilldown in Grafana Cloud or in your own Grafana instance.
+For more information, refer to [Access or install Profiles Drilldown](ref:explore-profiles-install).
-
+
### Use cases
@@ -80,10 +80,10 @@ The main use cases are the following:
- Proactive: Cutting costs, addressing latency issues, or optimizing memory usage for applications
- Reactive: Resolving incidents with line-level accuracy or debugging active latency/memory issues
-Explore Profiles provides an intuitive interface to specifically support proactivee and reactive use cases.
+Profiles Drilldown provides an intuitive interface to specifically support proactivee and reactive use cases.
You get a holistic view of all of your services and how they're functioning, but also the ability to drill down for more targeted root cause analysis.
-Explore Profiles offers a convenient platform to analyze profiles and get insights that are impossible to get from using other traditional signals like logs, metrics, or tracing.
+Profiles Drilldown offers a convenient platform to analyze profiles and get insights that are impossible to get from using other traditional signals like logs, metrics, or tracing.
{{< youtube id="x9aPw_CbIQc" >}}
@@ -93,7 +93,7 @@ Explore Profiles offers a convenient platform to analyze profiles and get insigh
The Pyroscope data source query editor gives you access to a profile type selector, a label selector, and collapsible options.
-Like Explore Profiles, the query editor also provides a flame graph to visualize data.
+Like Profiles Drilldown, the query editor also provides a flame graph to visualize data.

diff --git a/docs/sources/datasources/tempo/_index.md b/docs/sources/datasources/tempo/_index.md
index 8b2123b5cd4..836db6cac14 100644
--- a/docs/sources/datasources/tempo/_index.md
+++ b/docs/sources/datasources/tempo/_index.md
@@ -71,7 +71,14 @@ refs:
# Tempo data source
-Grafana ships with built-in support for [Tempo](https://grafana.com/docs/tempo/latest/), a high-volume, minimal-dependency trace storage, open source tracing solution from Grafana Labs. This topic explains configuration and queries specific to the Tempo data source.
+Grafana ships with built-in support for [Tempo](https://grafana.com/docs/tempo//), a high-volume, minimal-dependency trace storage, open source tracing solution from Grafana Labs.
+
+To learn more about traces, refer to [Introduction to tracing](https://grafana.com/docs/tempo//introduction/).
+
+To use traces, you need you have an application or service that is instrumented to emit traces.
+Refer to the [Instrument for tracing](https://grafana.com/docs/tempo//getting-started/instrumentation/) for more information.
+
+## Add a data source
For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:data-source-management).
Only users with the organization administrator role can add data sources.
@@ -81,6 +88,8 @@ This video explains how to add data sources, including Loki, Tempo, and Mimir, t
{{< youtube id="cqHO0oYW6Ic" start="298" >}}
-Once you've added the data source, you can [configure it]({{< relref "./configure-tempo-data-source/" >}}) so that your Grafana instance's users can create queries in its [query editor]({{< relref "./query-editor/" >}}) when they [build dashboards](ref:build-dashboards) and use [Explore](ref:explore).
+## Learn more
+
+After you've added the data source, you can [configure it](./configure-tempo-data-source/) so that your Grafana instance's users can create queries in its [query editor](./query-editor/) when they [build dashboards](ref:build-dashboards) and use [Explore](ref:explore).
{{< section withDescriptions="true">}}
diff --git a/docs/sources/datasources/tempo/configure-tempo-data-source.md b/docs/sources/datasources/tempo/configure-tempo-data-source.md
index 157238122f1..39d3cdf751a 100644
--- a/docs/sources/datasources/tempo/configure-tempo-data-source.md
+++ b/docs/sources/datasources/tempo/configure-tempo-data-source.md
@@ -131,7 +131,7 @@ To use streaming, you need to:
- Run Tempo version 2.2 or later, or Grafana Enterprise Traces (GET) version 2.2 or later, or use Grafana Cloud Traces.
- Tempo must have `stream_over_http_enabled: true` for streaming to work.
- For more information, refer to [Tempo GRPC API](https://grafana.com/docs/tempo//api_docs/#tempo-grpc-api).
+ For more information, refer to [Tempo gRPC API](https://grafana.com/docs/tempo//api_docs/#tempo-grpc-api).
- For self-managed Tempo or GET instances: If your Tempo or GET instance is behind a load balancer or proxy that doesn't supporting gRPC or HTTP2, streaming may not work and should be deactivated.
@@ -333,6 +333,10 @@ You can choose one of three options:
| **Duration** | _(Default)_ Displays the span duration on the span bar row. |
| **Tag** | Displays the span tag on the span bar row. You must also specify which tag key to use to get the tag value, such as `component`. |
+### Tag limit
+
+The **Tag limit** setting modifies the max number of tags and tag values to retrieve from Tempo. Default: 5000
+
### Private data source connect
[//]: # 'Shared content for authentication section procedure in data sources'
diff --git a/docs/sources/datasources/tempo/query-editor/_index.md b/docs/sources/datasources/tempo/query-editor/_index.md
index b561a54bdcd..a6b85cfd436 100644
--- a/docs/sources/datasources/tempo/query-editor/_index.md
+++ b/docs/sources/datasources/tempo/query-editor/_index.md
@@ -61,7 +61,7 @@ The queries use [TraceQL](/docs/tempo/latest/traceql), the query language design
For general documentation on querying data sources in Grafana, refer to [Query and transform data](ref:query-transform-data).
{{< admonition type="tip" >}}
-Don't know TraceQL? Try [Explore Traces](ref:explore-traces-app), an intuitive, queryless app that lets you explore your tracing data using RED metrics.
+Don't know TraceQL? Try [Grafana Traces Drilldown](ref:explore-traces-app), an intuitive, queryless app that lets you explore your tracing data using RED metrics.
{{< /admonition >}}
## Before you begin
@@ -105,7 +105,7 @@ Refer to the [Search using the TraceQL query builder documentation]({{< relref "
The **TraceQL** query editor lets you search by trace ID and write TraceQL queries using autocomplete.
-Refer to the [TraceQL query editor documentation]({{< relref "./traceql-editor" >}}) to learn more about constructing queries using a code-editor-like experience.
+Refer to the [TraceQL query editor documentation](./traceql-editor) to learn more about constructing queries using a code-editor-like experience.

@@ -113,8 +113,8 @@ You can also search for a trace ID by entering it into the query field.
### Service graph view
-Grafana’s **Service Graph** view uses metrics to display span request rates, error rates, and durations, as well as service graphs.
-Once the requirements are set up, this preconfigured view is immediately available.
+The **Service Graph** view uses metrics to display span request rates, error rates, and durations, as well as service graphs.
+After the requirements are set up, this preconfigured view is immediately available.
Using the service graph view, you can:
diff --git a/docs/sources/datasources/tempo/query-editor/traceql-editor.md b/docs/sources/datasources/tempo/query-editor/traceql-editor.md
index 7752a290229..cc5b38beaaa 100644
--- a/docs/sources/datasources/tempo/query-editor/traceql-editor.md
+++ b/docs/sources/datasources/tempo/query-editor/traceql-editor.md
@@ -12,7 +12,7 @@ labels:
- oss
menuTitle: Write TraceQL queries
title: Write TraceQL queries with the editor
-weight: 300
+weight: 400
refs:
explore:
- pattern: /docs/grafana/
@@ -41,4 +41,4 @@ refs:
[//]: # 'Shared content for the TraceQL query editor'
[//]: # 'This content is located in /docs/sources/shared/datasources/tempo-editor-traceql.md'
-{{< docs/shared source="grafana" lookup="datasources/tempo-editor-traceql.md" version="" >}}
+{{< docs/shared source="grafana" lookup="datasources/tempo-editor-traceql.md" version="" >}}
diff --git a/docs/sources/datasources/tempo/query-editor/traceql-search.md b/docs/sources/datasources/tempo/query-editor/traceql-search.md
index b885f1239d8..84ac0b396c4 100644
--- a/docs/sources/datasources/tempo/query-editor/traceql-search.md
+++ b/docs/sources/datasources/tempo/query-editor/traceql-search.md
@@ -56,4 +56,4 @@ To enable the TraceQL query builder in self-hosted Grafana through version 10.1,
[//]: # 'Shared content for the Search - TraceQL query builder'
-{{< docs/shared source="grafana" lookup="datasources/tempo-search-traceql.md" leveloffset="+1" version="" >}}
+{{< docs/shared source="grafana" lookup="datasources/tempo-search-traceql.md" leveloffset="+1" version="" >}}
diff --git a/docs/sources/datasources/tempo/query-editor/traceql-structure.md b/docs/sources/datasources/tempo/query-editor/traceql-structure.md
new file mode 100644
index 00000000000..b67d57cedf3
--- /dev/null
+++ b/docs/sources/datasources/tempo/query-editor/traceql-structure.md
@@ -0,0 +1,21 @@
+---
+description: Learn how to create TraceQL queries are structured.
+keywords:
+ - queries
+ - TraceQL
+labels:
+ products:
+ - cloud
+ - enterprise
+ - oss
+menuTitle: TraceQL query structure
+title: TraceQL query structure
+weight: 200
+---
+
+# TraceQL query structure
+
+[//]: # 'Shared content for best practices for traces'
+[//]: # 'This content is located in /tempo/docs/sources/shared/trace-structure.md'
+
+{{< docs/shared source="tempo" lookup="traceql-query-structure.md" version="" >}}
diff --git a/docs/sources/datasources/tempo/service-graph.md b/docs/sources/datasources/tempo/service-graph.md
index 0e6c1f8b657..d1644d41b73 100644
--- a/docs/sources/datasources/tempo/service-graph.md
+++ b/docs/sources/datasources/tempo/service-graph.md
@@ -72,8 +72,8 @@ You use the Service Graph to detect performance issues; track increases in error
## Display the Service Graph
-1. [Configure Grafana Alloy](https://grafana.com/docs/tempo/latest/configuration/grafana-alloy/) or [Tempo or GET](https://grafana.com/docs/tempo/latest/metrics-generator/service_graphs/#tempo) to generate Service Graph data.
-1. Link a Prometheus data source in the Tempo data source's [Service Graph]({{< relref "./configure-tempo-data-source#configure-service-graph" >}}) settings.
+1. [Configure Grafana Alloy](https://grafana.com/docs/tempo//configuration/grafana-alloy/) or [Tempo or GET](https://grafana.com/docs/tempo//metrics-generator/service_graphs/#tempo) to generate Service Graph data.
+1. Link a Prometheus data source in the Tempo data source's [Service Graph](./configure-tempo-data-source#configure-service-graph) settings.
1. Navigate to [Explore](ref:explore).
1. Select the Tempo data source.
1. Select the **Service Graph** query type.
@@ -113,10 +113,10 @@ To open the Service Graph view:
1. Run the query.
1. _(Optional)_ Filter your results.
-{{% admonition type="note" %}}
+{{< admonition type="note" >}}
Grafana uses the `traces_spanmetrics_calls_total` metric to display the name, rate, and error rate columns, and `traces_spanmetrics_latency_bucket` to display the duration column.
These metrics must exist in your Prometheus data source.
-{{% /admonition %}}
+{{< /admonition >}}
To open a query in Prometheus with the span name of that row automatically set in the query, click a row in the **rate**, **error rate**, or **duration** columns.
diff --git a/docs/sources/datasources/tempo/traces-in-grafana/_index.md b/docs/sources/datasources/tempo/traces-in-grafana/_index.md
new file mode 100644
index 00000000000..c3f622bb57d
--- /dev/null
+++ b/docs/sources/datasources/tempo/traces-in-grafana/_index.md
@@ -0,0 +1,23 @@
+---
+description: Learn about how you can use tracing data in Grafana Cloud to query data,
+ generate metrics, and link your tracing data with logs, metrics, and profiles.
+keywords:
+ - Grafana
+ - traces
+ - tracing
+title: Use traces in Grafana
+weight: 275
+---
+
+# Use traces in Grafana
+
+Using traces, you can search for traces, generate metrics from spans, and link your tracing data with logs, metrics, and profiles.
+
+This page provides a summary of how you can use tracing data in Grafana.
+
+For general documentation on querying data sources in Grafana, refer to [Query and transform data](https://grafana.com/docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/).
+
+[//]: # 'Shared content for best practices for traces'
+[//]: # 'This content is located in /tempo/docs/sources/shared/tempo-in-grafana.md'
+
+{{< docs/shared source="tempo" lookup="tempo-in-grafana.md" version="" >}}
diff --git a/docs/sources/datasources/tempo/json-trace-file.md b/docs/sources/datasources/tempo/traces-in-grafana/json-trace-file.md
similarity index 82%
rename from docs/sources/datasources/tempo/json-trace-file.md
rename to docs/sources/datasources/tempo/traces-in-grafana/json-trace-file.md
index 0387eab883a..77b45011a2f 100644
--- a/docs/sources/datasources/tempo/json-trace-file.md
+++ b/docs/sources/datasources/tempo/traces-in-grafana/json-trace-file.md
@@ -12,7 +12,9 @@ labels:
- oss
menuTitle: Upload JSON trace file
title: Upload a JSON trace file
-weight: 400
+weight: 900
+aliases:
+ - ../json-trace-file/ # /docs/grafana/latest/datasources/tempo/json-trace-file
---
# Upload a JSON trace file
@@ -20,7 +22,15 @@ weight: 400
You can upload a JSON file that contains a single trace and visualize it.
If the file has multiple traces, Grafana visualizes the first trace.
-**To download a trace or Service Graph through the inspector:**
+To upload a trace file:
+
+1. Select **Explore** in Grafana.
+1. Select **Import trace** in the right corner.
+1. Upload your JSON trace file.
+
+## Download a trace or service graph
+
+To download a trace or Service Graph through the [Inspector panel](https://grafana.com/docs/grafana//explore/explore-inspector/):
1. Open the inspector.
1. Navigate to the **Data** tab.
diff --git a/docs/sources/datasources/tempo/link-trace-id.md b/docs/sources/datasources/tempo/traces-in-grafana/link-trace-id.md
similarity index 87%
rename from docs/sources/datasources/tempo/link-trace-id.md
rename to docs/sources/datasources/tempo/traces-in-grafana/link-trace-id.md
index d53496e3784..07c7f6f2bec 100644
--- a/docs/sources/datasources/tempo/link-trace-id.md
+++ b/docs/sources/datasources/tempo/traces-in-grafana/link-trace-id.md
@@ -12,7 +12,10 @@ labels:
- oss
menuTitle: Link to a trace ID
title: Link to a trace ID
-weight: 700
+weight: 800
+aliases:
+ - ../link-trace-id/ # /docs/grafana/latest/datasources/tempo/link-trace-id/
+
refs:
configure-grafana-feature-toggles:
- pattern: /docs/grafana/
@@ -69,10 +72,10 @@ You can link to Tempo traces from logs or metrics.
You can link to Tempo traces from logs in Loki, Elasticsearch, Splunk, and other logs data sources by configuring an internal link.
-To configure this feature, see the [Derived fields]({{< relref "../loki#configure-derived-fields" >}}) section of the Loki data source docs or the [Data links]({{< relref "../elasticsearch#data-links" >}}) section of the Elasticsearch or Splunk data source docs.
+To configure this feature, refer to the [Derived fields](../../loki#configure-derived-fields) section of the Loki data source docs or the [Data links](../../elasticsearch#data-links) section of the Elasticsearch or Splunk data source docs.
## Link to a trace ID from metrics
You can link to Tempo traces from metrics in Prometheus data sources by configuring an exemplar.
-To configure this feature, see the [introduction to exemplars](ref:exemplars) documentation.
+To configure this feature, refer to the [Exemplars](ref:exemplars) documentation.
diff --git a/docs/sources/developers/http_api/dashboard.md b/docs/sources/developers/http_api/dashboard.md
index d37b0e080c9..54bfaf63156 100644
--- a/docs/sources/developers/http_api/dashboard.md
+++ b/docs/sources/developers/http_api/dashboard.md
@@ -139,7 +139,6 @@ The **412** status code is used for explaining that you cannot create the dashbo
There can be different reasons for this:
- The dashboard has been changed by someone else, `status=version-mismatch`
-- A dashboard with the same name in the folder already exists, `status=name-exists`
- A dashboard with the same uid already exists, `status=name-exists`
- The dashboard belongs to plugin ``, `status=plugin-dashboard`
@@ -156,8 +155,6 @@ Content-Length: 97
}
```
-In case of title already exists the `status` property will be `name-exists`.
-
## Get dashboard by uid
`GET /api/dashboards/uid/:uid`
@@ -358,7 +355,7 @@ Content-Type: application/json
Status Codes:
-- **200** – Deleted
+- **200** – Restored
- **401** – Unauthorized
- **403** – Access denied
- **404** – Not found
diff --git a/docs/sources/developers/http_api/team.md b/docs/sources/developers/http_api/team.md
index f7eec3d6260..e94d80be650 100644
--- a/docs/sources/developers/http_api/team.md
+++ b/docs/sources/developers/http_api/team.md
@@ -26,8 +26,6 @@ Access to these API endpoints is restricted as follows:
- All authenticated users are able to view details of teams they are a member of.
- Organization Admins are able to manage all teams and team members.
-- If you enable `editors_can_admin` configuration flag, then Organization Editors can create teams and manage teams where they are Admin.
- - If you enable `editors_can_admin` configuration flag, Editors can find out whether a team that they are not members of exists by trying to create a team with the same name.
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes" >}}) for more information.
@@ -179,7 +177,7 @@ Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt
HTTP/1.1 200
Content-Type: application/json
-{"message":"Team created","teamId":2}
+{"message":"Team created","teamId":2,"uid":"ceaulqadfoav4e"}
```
Status Codes:
diff --git a/docs/sources/explore/correlations-editor-in-explore.md b/docs/sources/explore/correlations-editor-in-explore.md
index 50053226158..dd0a0ce1936 100644
--- a/docs/sources/explore/correlations-editor-in-explore.md
+++ b/docs/sources/explore/correlations-editor-in-explore.md
@@ -3,6 +3,7 @@ labels:
products:
- enterprise
- oss
+ - cloud
title: Correlations Editor in Explore
weight: 20
---
@@ -13,22 +14,22 @@ weight: 20
The Explore editor is available in 10.1 and later versions. In the editor, transformations is available in Grafana 10.3 and later versions.
{{% /admonition %}}
-Correlations allow users to build a link between any two data sources. For more information about correlations in general, please see the [correlations]({{< relref "../administration/correlations" >}}) topic in the administration page.
+Correlations allow users to build a link between any two data sources. For more information about correlations in general, please see the [correlations](/docs/grafana//administration/correlations/) topic in the administration page.
## Create a correlation
1. In Grafana, navigate to the Explore page.
-1. Select a data source that you would like to be [the source data source]({{< relref "../administration/correlations/correlation-configuration#source-data-source-and-result-field" >}}) for a new correlation.
-1. Run a query producing data in [a supported visualization]({{< relref "../administration/correlations#correlations" >}}).
-1. Click **+ Add** in the top toolbar and select **Add correlation** (you can also select **Correlations Editor** from the [Command Palette]({{< relref "../search#command-palette" >}})).
+1. Select a data source that you would like to be [the source data source](/docs/grafana//administration/correlations/correlation-configuration/#source-data-source-and-result-field) for a new correlation.
+1. Run a query producing data in [a supported visualization](/docs/grafana//administration/correlations/#correlations).
+1. Click **+ Add** in the top toolbar and select **Add correlation** (you can also select **Correlations Editor** from the [Command Palette](/docs/grafana//search/#command-palette)).
1. Explore is now in Correlations Editor mode indicated by a blue border and top bar. You can exit Correlations Editor by clicking **Exit** in the top bar.
1. You can now create the following new correlations for the visualization with links that are attached to the data that you can use to build a new query:
- Logs: links are displayed next to field values inside log details for each log row
- Table: every table cell is a link
1. Click on a link to add a new correlation.
- Links are associated with a field that is used as a [result field of a correlation]({{< relref "../administration/correlations/correlation-configuration" >}}).
-1. In the split view that opens, use the right pane to set up [the target query source of the correlation]({{< relref "../administration/correlations/correlation-configuration#target-query" >}}).
-1. Build a target query using [variables syntax]({{< relref "../dashboards/variables/variable-syntax" >}}) with variables from the list provided at the top of the pane. The list contains sample values from the selected data row.
+ Links are associated with a field that is used as a [result field of a correlation](/docs/grafana//administration/correlations/correlation-configuration/).
+1. In the split view that opens, use the right pane to set up [the target query source of the correlation](/docs/grafana//administration/correlations/correlation-configuration/#target-query).
+1. Build a target query using [variables syntax](/docs/grafana//dashboards/variables/variable-syntax/) with variables from the list provided at the top of the pane. The list contains sample values from the selected data row.
1. Provide a label and description (optional).
A label will be used as the name of the link inside the visualization and can contain variables.
1. Provide transformations (optional; see below for details).
@@ -37,7 +38,7 @@ Correlations allow users to build a link between any two data sources. For more
## Transformations
-Transformations allow you to extract values that exist in a field with other data. For example, using a transformation, you can extract one portion of a log line to use in a correlation. For more details on transformations in correlations, see [Correlations]({{< relref "../administration/correlations/correlation-configuration/#correlation-transformations" >}}).
+Transformations allow you to extract values that exist in a field with other data. For example, using a transformation, you can extract one portion of a log line to use in a correlation. For more details on transformations in correlations, see [Correlations](/docs/grafana//explore/correlations-editor-in-explore/#transformations).
After clicking one of the generated links in the editor mode, you can add transformations by clicking **Add transformation** in the Transformations dropdown menu.
@@ -47,7 +48,7 @@ You can use a transformation in your correlation with the following steps:
Select the portion of the field that you want to use for the transformation. For example, a log line.
Once selected, the value of this field will be used to assist you in building the transformation.
1. Select the type of the transformation.
- See [correlations]({{< relref "../administration/correlations/correlation-configuration/#correlation-transformations" >}}) for the options and relevant settings.
+ See [correlations](/docs/grafana//explore/correlations-editor-in-explore/#transformations) for the options and relevant settings.
1. Based on your selection, you might see one or more variables populate, or you might need to provide more specifications in options that are displayed.
1. Select **Add transformation to correlation** to add the specified variables to the list of available variables.
@@ -57,7 +58,7 @@ For regular expressions in this dialog box, the `mapValue` referred to in other
## Correlations examples
-The following examples show how to create correlations using the Correlations Editor in Explore. If you'd like to follow these examples, make sure to set up a [test data source]({{< relref "../datasources/testdata#testdata-data-source" >}}).
+The following examples show how to create correlations using the Correlations Editor in Explore. If you'd like to follow these examples, make sure to set up a [test data source](/docs/grafana//datasources/testdata/#testdata-data-source).
### Create a text to graph correlation
@@ -65,7 +66,7 @@ This example shows how to create a correlation using Correlations Editor in Expl
Correlations allow you to use results of one query to run a new query in any data source. In this example, you will run a query that renders tabular data. The data will be used to run a different query that yields a graph result.
-To follow this example, make sure you have set up [a test data source]({{< relref "../datasources/testdata#testdata-data-source" >}}).
+To follow this example, make sure you have set up [a test data source](/docs/grafana//datasources/testdata/#testdata-data-source).
1. In Grafana, navigate to **Explore**.
1. Select the **test data source** from the dropdown menu at the top left of the page.
@@ -100,7 +101,7 @@ You can apply the same steps to any data source. Correlations allow you to creat
In this example, you will create a correlation to demonstrate how to use transformations to extract values from the log line and another field.
-To follow this example, make sure you have set up [a test data source]({{< relref "../datasources/testdata#testdata-data-source" >}}).
+To follow this example, make sure you have set up [a test data source](/docs/grafana//datasources/testdata/#testdata-data-source).
1. In Grafana, navigate to **Explore**.
1. Select the **test data source** from the dropdown menu at the top left of the page.
diff --git a/docs/sources/explore/get-started-with-explore.md b/docs/sources/explore/get-started-with-explore.md
index 7e78e4e80c3..b25f855bac9 100644
--- a/docs/sources/explore/get-started-with-explore.md
+++ b/docs/sources/explore/get-started-with-explore.md
@@ -38,14 +38,10 @@ Watch the following video to get started using Explore:
## Before you begin
-In order to access Explore, you must have either the `editor` or `administrator` role, unless the [`viewers_can_edit` option](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/#viewers_can_edit) is enabled. Refer to [Role and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions/) for more information on what each role can access.
+In order to access Explore, you must have either the `editor` or `administrator` basic role or the `data sources explore` role. Refer to [Role and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions/) for more information on what each role can access.
Refer to [Role-based access control (RBAC)](https://grafana.com/docs/grafana//administration/roles-and-permissions/access-control/) in Grafana Enterprise to understand how you can manage Explore with role-based permissions.
-{{< admonition type="note" >}}
-If you are using Grafana Cloud, open a [support ticket in the Cloud Portal](https://grafana.com/auth/sign-in) to enable the `viewers_can_edit` option.
-{{< /admonition >}}
-
## Explore elements
Explore consists of a toolbar, outline, query editor, the ability to add multiple queries, a query history and a query inspector.
diff --git a/docs/sources/explore/simplified-exploration/_index.md b/docs/sources/explore/simplified-exploration/_index.md
index 60612ae02ba..360b37aee5a 100644
--- a/docs/sources/explore/simplified-exploration/_index.md
+++ b/docs/sources/explore/simplified-exploration/_index.md
@@ -8,27 +8,27 @@ title: Simplified exploration
menuTitle: Simplified exploration
weight: 100
hero:
- title: Simplified exploration with the Explore apps
+ title: Simplified exploration with the Drilldown apps
level: 1
width: 100
height: 100
- description: Use Explore Profiles to investigate and identify issues using profiling data.
+ description: Use the Grafana Drilldown apps to investigate and identify issues using telemetry data.
cards:
title_class: pt-0 lh-1
items:
- - title: Explore Metrics
+ - title: Grafana Metrics Drilldown
href: ./metrics/
description: Quickly find related metrics with a few clicks, without needing to write PromQL queries to retrieve metrics.
height: 24
- - title: Explore Logs
+ - title: Grafana Logs Drilldown
href: ./logs/
description: Visualize log volumes to easily detect anomalies or significant changes over time, without needing to compose LogQL queries.
height: 24
- - title: Explore Traces
+ - title: Grafana Traces Drilldown
href: ./traces/
description: Use Rate, Errors, and Duration (RED) metrics derived from traces to investigate and understand errors and latency issues within complex distributed systems.
height: 24
- - title: Explore Profiles
+ - title: Grafana Profiles Drilldown
href: ./profiles/
description: View and analyze high-level service performance, identify problem processes for optimization, and diagnose issues to determine root causes.
height: 24
@@ -36,14 +36,16 @@ cards:
# Simplified exploration
-The Grafana Explore apps are designed for effortless data exploration through intuitive, queryless interactions.
+The Grafana Drilldown apps are designed for effortless data exploration through intuitive, queryless interactions.
Easily explore telemetry signals with these specialized tools, tailored specifically for the Grafana databases to provide quick and accurate insights.
+{{< docs/shared source="grafana" lookup="plugins/rename-note.md" version="" >}}
+
To learn more, read:
- [From multi-line queries to no-code investigations: meeting Grafana users where they are](https://grafana.com/blog/2024/10/22/from-multi-line-queries-to-no-code-investigations-meeting-grafana-users-where-they-are/)
-- [A queryless experience for exploring metrics, logs, traces, and profiles: Introducing the Explore apps suite for Grafana](https://grafana.com/blog/2024/09/24/queryless-metrics-logs-traces-profiles/).
+- [A queryless experience for exploring metrics, logs, traces, and profiles: Introducing the Drilldown apps suite for Grafana](https://grafana.com/blog/2024/09/24/queryless-metrics-logs-traces-profiles/).
{{< youtube id="MSHeWWsHaIA" >}}
diff --git a/docs/sources/explore/simplified-exploration/metrics/index.md b/docs/sources/explore/simplified-exploration/metrics/index.md
index 127dd6cb265..eb6d23509e5 100644
--- a/docs/sources/explore/simplified-exploration/metrics/index.md
+++ b/docs/sources/explore/simplified-exploration/metrics/index.md
@@ -4,19 +4,21 @@ labels:
- cloud
- enterprise
- oss
-title: Explore Metrics
+title: Metrics Drilldown
aliases:
- ../explore-metrics/ # /docs/grafana/latest/explore/explore-metrics/
canonical: https://grafana.com/docs/grafana/latest/explore/simplified-exploration/metrics/
-description: Explore Metrics lets you browse Prometheus-compatible metrics using an intuitive, queryless experience.
+description: Grafana Metrics Drilldown lets you browse Prometheus-compatible metrics using an intuitive, queryless experience.
weight: 200
---
-# Grafana Explore Metrics
+# Grafana Metrics Drilldown
-Grafana Explore Metrics is a query-less experience for browsing **Prometheus-compatible** metrics. Quickly find related metrics with just a few simple clicks, without needing to write PromQL queries to retrieve metrics.
+Grafana Metrics Drilldown is a query-less experience for browsing **Prometheus-compatible** metrics. Quickly find related metrics with just a few simple clicks, without needing to write PromQL queries to retrieve metrics.
-With Explore Metrics, you can:
+{{< docs/shared source="grafana" lookup="plugins/rename-note.md" version="" >}}
+
+With Metrics Drilldown, you can:
- Easily segment metrics based on their labels, so you can immediately spot anomalies and identify issues.
- Automatically display the optimal visualization for each metric type (gauge vs. counter, for example) without manual setup.
@@ -25,26 +27,25 @@ With Explore Metrics, you can:
- View a history of user steps when navigating through metrics and their filters.
- Seamlessly pivot to related telemetry, including log data.
-{{< docs/play title="Explore Metrics" url="https://play.grafana.org/explore/metrics/trail?from=now-1h&to=now&var-ds=grafanacloud-demoinfra-prom&var-filters=&refresh=&metricPrefix=all" >}}
+{{< docs/play title="Metrics Drilldown" url="https://play.grafana.org/explore/metrics/trail?from=now-1h&to=now&var-ds=grafanacloud-demoinfra-prom&var-filters=&refresh=&metricPrefix=all" >}}
-You can access Explore Metrics either as a standalone experience or as part of Grafana dashboards.
+You can access Metrics Drilldown either as a standalone experience or as part of Grafana dashboards.
## Standalone experience
-To access Explore Metrics as a standalone experience:
+To access Metrics Drilldown as a standalone experience:
-1. Click the arrow next to **Explore** in the Grafana left-side menu and click **Metrics**. You are taken to an overview page that shows recent metrics, bookmarks, and the option to select a new metric exploration.
-1. To get started with a new exploration, click **+ New metric exploration**.
+1. Click the arrow next to **Drilldown** in the Grafana left-side menu and click **Metrics**. You are taken to an overview page that shows recent metrics, bookmarks, and the option to select a new metric exploration.
+1. To get started with a new exploration, click **Let's start!**.
1. Select **Prometheus** or any Prometheus-compatible data source available in the drop-down menu under **Data source**.
-1. Click **+ Add label** to select a label-value pair from the drop-down menu. You can add multiple label-value pairs. A label type will appear above the selected label with a drop-down list of options from which to choose. For example, if you select the label `container` a drop-down list of available containers appears.
+1. Click **+ Add label** to select a label-value pair from the drop-down menu. You can add multiple label-value pairs. A label type appears above the selected label with a drop-down list of options from which to choose. For example, if you select the label `container` a drop-down list of available containers appears.
1. You can also search for metrics using keywords under **Search metrics** in the search bar.
1. Use the time picker to select a date and time range from the drop-down menu or use an absolute time range.
1. Click the down arrow next to the **Refresh** icon to set a refresh rate from the drop-down menu. The default is `Off`.
-1. Click the **Settings** icon and toggle **Always keep selected metric graph in-view** to keep your main graph always in view on the Breakdown drill-down tab.
The **History** button in the upper left corner tracks every step navigating through metric exploration.
-
+
### Metrics exploration
@@ -54,19 +55,19 @@ To further explore a metric, click **Select** in the upper right corner of the m
- The **Overview** tab provides a description for each metric, as well as the metric `type` and `unit` associated with the metric. It also provides a list of labels associated with the metric. Click on any label to view drill-down visualizations.
- The **Breakdown** tab depicts time series visualizations for each of the label-value pairs for the selected metric. You can further drill down on each label and click **Add to filter** to add the label/value pair into your filters. You can also change the **View** from grid to rows.
-- The **Related metrics** tab depicts related metrics with relevant key words. You can repeat the drill down process for any related metric. Toggle **SHow previews** to preview visualizations.
+- The **Related metrics** tab depicts related metrics with relevant key words. You can repeat the drill down process for any related metric. Toggle **Show previews** to preview visualizations.
-Once you have gathered your metrics exploration data you can:
+After you have gathered your metrics exploration data you can:
-- Click the **Explore** icon on the right side to open the graph in Explore, where you can modify the query or add the graph to a dashboard or incident.
-- Click the **Share** icon on the right side to copy the metric drill down URL to the clipboard so it can be shared.
+- Click the **Open in Explore** icon on the right side to open the graph in Explore, where you can modify the query or add the graph to a dashboard or incident.
+- Click the **Copy URL** icon on the right side to copy the metric drill down URL to the clipboard so it can be shared.
- Click the **Star** icon on the right side to bookmark and save the metrics exploration.
## Dashboard experience
-To access Explore Metrics via a dashboard:
+To access Metrics Drilldown via a dashboard:
1. Navigate to your dashboard.
1. Select a time series panel.
-1. Click the panel menu in the upper right and select **Explore Metrics**. If there are multiple metrics, click on the one you want to explore.
-1. You will see a slide out drawer with the Metrics Experience, starting with the drill down. You can access the standalone experience by clicking **Open** in the upper right.
+1. Click the panel menu in the upper right and select **Metrics Drilldown**. If there are multiple metrics, click on the one you want to explore.
+1. You see a slide out drawer with the Metrics Experience, starting with the drill down. You can access the standalone experience by clicking **Open** in the upper right.
diff --git a/docs/sources/explore/trace-integration.md b/docs/sources/explore/trace-integration.md
index f9b0779b12d..532bb3ca8f2 100644
--- a/docs/sources/explore/trace-integration.md
+++ b/docs/sources/explore/trace-integration.md
@@ -157,7 +157,8 @@ The node graph requires data to be returned from the data source in a specific f
## Service graph
-A service graph visualizes span metrics, including rates, error rates, and durations (RED), along with service relationships. Once the requirements are configured, this pre-configured view is immediately available.
+A service graph visualizes rates, error rates, and durations (RED), along with service relationships.
+After the requirements are configured, this pre-configured view is immediately available.
For additional information refer to the following documentation:
diff --git a/docs/sources/fundamentals/exemplars/index.md b/docs/sources/fundamentals/exemplars/index.md
index 363bc122b47..4181c50cac5 100644
--- a/docs/sources/fundamentals/exemplars/index.md
+++ b/docs/sources/fundamentals/exemplars/index.md
@@ -20,21 +20,31 @@ weight: 800
# Introduction to exemplars
-An exemplar is a specific trace representative of measurement taken in a given time interval. While metrics excel at giving you an aggregated view of your system, traces give you a fine grained view of a single request; exemplars are a way to link the two.
+An exemplar is a specific trace representative of measurement taken in a given time interval.
+While metrics excel at giving you an aggregated view of your system, traces give you a fine grained view of a single request; exemplars are a way to link the two.
-Suppose your company website is experiencing a surge in traffic volumes. While more than eighty percent of the users are able to access the website in under two seconds, some users are experiencing a higher than normal response time resulting in bad user experience.
+Suppose your company website is experiencing a surge in traffic volumes.
+While more than eighty percent of the users are able to access the website in under two seconds, some users are experiencing a higher than normal response time resulting in bad user experience.
-To identify the factors that are contributing to the latency, you must compare a trace for a fast response against a trace for a slow response. Given the vast amount of data in a typical production environment, it will be extremely laborious and time-consuming effort.
+To identify the factors that are contributing to the latency, you must compare a trace for a fast response against a trace for a slow response.
+Given the vast amount of data in a typical production environment, it's an extremely laborious and time-consuming effort.
-Use exemplars to help isolate problems within your data distribution by pinpointing query traces exhibiting high latency within a time interval. Once you localize the latency problem to a few exemplar traces, you can combine it with additional system based information or location properties to perform a root cause analysis faster, leading to quick resolutions to performance issues.
+Use exemplars to help isolate problems within your data distribution by pinpointing query traces exhibiting high latency within a time interval.
+After you localize the latency problem to a few exemplar traces, you can combine it with additional system based information or location properties to perform a root cause analysis faster, leading to quick resolutions to performance issues.
-Support for exemplars is available for the Prometheus data source only. Once you enable the functionality, exemplar data is available by default. For more information on exemplar configuration and how to enable exemplars, refer to [configuring exemplars in the Prometheus data source]({{< relref "../../datasources/prometheus/configure-prometheus-data-source#exemplars" >}}).
+Support for exemplars is available for the Prometheus data source only.
+After you enable the functionality, exemplar data is available by default.
+For more information on exemplar configuration and how to enable exemplars, refer to [configuring exemplars in the Prometheus data source](../../datasources/prometheus/configure-prometheus-data-source/#exemplars).
-Grafana shows exemplars alongside a metric in the Explore view and in dashboards. Each exemplar displays as a highlighted star. You can hover your cursor over an exemplar to view the unique trace ID, which is a combination of a key value pair. To investigate further, click the blue button next to the `traceID` property.
+Grafana shows exemplars alongside a metric in the Explore view and in dashboards.
+Each exemplar displays as a highlighted star.
+You can hover your cursor over an exemplar to view the unique trace ID, which is a combination of a key value pair.
+To investigate further, click the blue button next to the `traceID` property.
{{< figure src="/media/docs/grafana/exemplars/screenshot-exemplars.png" class="docs-image--no-shadow" max-width= "750px" caption="Screenshot showing the detail window of an exemplar" >}}
-Refer to [View exemplar data]({{< relref "#view-exemplar-data" >}}) for instructions on how to drill down and view exemplar trace details from metrics and logs. To know more about exemplars, refer to the blogpost [Intro to exemplars, which enable Grafana Tempo’s distributed tracing at massive scale](/blog/2021/03/31/intro-to-exemplars-which-enable-grafana-tempos-distributed-tracing-at-massive-scale/).
+Refer to [View exemplar data](#view-exemplar-data) for instructions on how to drill down and view exemplar trace details from metrics and logs.
+To know more about exemplars, refer to the blog post [Intro to exemplars, which enable Grafana Tempo’s distributed tracing at massive scale](/blog/2021/03/31/intro-to-exemplars-which-enable-grafana-tempos-distributed-tracing-at-massive-scale/).
## View exemplar data
@@ -42,15 +52,19 @@ When support for exemplar support is enabled for a Prometheus data source, you c
### In Explore
-Explore visualizes exemplar traces as highlighted stars alongside metrics data. For more information on how Explore visualizes trace data, refer to [Tracing in Explore]({{< relref "../../explore/trace-integration" >}}).
+Explore visualizes exemplar traces as highlighted stars alongside metrics data.
+For more information on how Explore visualizes trace data, refer to [Tracing in Explore](../../explore/trace-integration/).
To examine the details of an exemplar trace:
-1. Place your cursor over an exemplar (highlighted star). Depending on the trace data source you are using, you will see a blue button with the label `Query with `. In the following example, the tracing data source is Tempo.
+1. Place your cursor over an exemplar (highlighted star).
+ Depending on the trace data source you are using, you may see a blue button with the label `Query with `.
+ In the following example, the tracing data source is Tempo.
{{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-details.png" class="docs-image--no-shadow" max-width= "350px" caption="Screenshot showing exemplar details" >}}
-1. Click the **Query with Tempo** option next to the `traceID` property. The trace details, including the spans within the trace are listed in a separate panel on the right.
+1. Click the **Query with Tempo** option next to the `traceID` property.
+ The trace details, including the spans within the trace are listed in a separate panel on the right.
{{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-explore-view.png" class="docs-image--no-shadow" max-width= "900px" caption="Explorer view with panel showing trace details" >}}
@@ -58,13 +72,20 @@ For more information on how to drill down and analyze the trace and span details
### In logs
-You can also view exemplar trace details from the Loki logs in Explore. Use regex within the Derived fields links for Loki to extract the `traceID` information. Now when you expand Loki logs, you can see a `traceID` property under the **Detected fields** section. To learn more about how to extract a part of a log message into an internal or external link, refer to [using derived fields in Loki]({{< relref "../../explore/logs-integration" >}}).
+You can also view exemplar trace details from the Loki logs in Explore.
+Use regular expressions within the Derived fields links for Loki to extract the `traceID` information.
+Now when you expand Loki logs, you can see a `traceID` property under the **Detected fields** section.
+To learn more about how to extract a part of a log message into an internal or external link, refer to [using derived fields in Loki](../../explore/logs-integration/).
To view the details of an exemplar trace:
-1. Expand a log line and scroll down to the `Fields` section. Depending on your backend trace data source, you will see a blue button with the label ``.
+1. Expand a log line and scroll down to the `Fields` section.
+ Depending on your backend trace data source, you may see a blue button with the label ``.
-1. Click the blue button next to the `traceID` property. Typically, it will have the name of the backend data source. In the following example, the tracing data source is Tempo. The trace details, including the spans within the trace are listed in a separate panel on the right.
+1. Click the blue button next to the `traceID` property.
+ Typically, it has the name of the backend data source.
+ In the following example, the tracing data source is Tempo.
+ The trace details, including the spans within the trace are listed in a separate panel on the right.
{{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-loki-logs.png" class="docs-image--no-shadow" max-width= "750px" caption="Explorer view with panel showing trace details" >}}
@@ -78,16 +99,20 @@ This panel shows the details of the trace in different segments.
You can add more traces to the results using the `Add query` button.
-- The next segment shows the entire span for the specific trace as a narrow strip. All levels of the trace from the client all the way down to database query is displayed, which provides a bird's eye view of the time distribution across all layers over which the HTTP request was processed.
+- The next segment shows the entire span for the specific trace as a narrow strip.
+ All levels of the trace from the client all the way down to database query is displayed, which provides a bird's eye view of the time distribution across all layers over which the HTTP request was processed.
1. You can click within this strip view to display a magnified view of a smaller time segment within the span. This magnified view shows up in the bottom segment of the panel.
1. In the magnified view, you can expand or collapse the various levels of the trace to drill down to the specific span of interest.
- For example, if the strip view shows that most of the latency was within the app layer, you can expand the trace down the app layer to investigate the problem further. To expand a particular layer of span, click the left icon. The same button can collapse an expanded span.
+ For example, if the strip view shows that most of the latency was within the app layer, you can expand the trace down the app layer to investigate the problem further.
+ To expand a particular layer of span, click the left icon.
+ The same button can collapse an expanded span.
- To see the details of the span at any level, click the span itself.
- This displays additional metadata associated with the span. The metadata itself is initially shown in a narrow strip but you can see more details by clicking the metadata strip.
+ This displays additional metadata associated with the span.
+ The metadata itself is initially shown in a narrow strip but you can see more details by clicking the metadata strip.
{{< figure src="/media/docs/grafana/exemplars/screenshot-exemplar-span-details.png" class="docs-image--no-shadow" max-width= "600px" caption="Span details" >}}
diff --git a/docs/sources/getting-started/get-started-grafana-ms-sql-server.md b/docs/sources/getting-started/get-started-grafana-ms-sql-server.md
index 2ca4dcac2b4..4518d2adfc3 100644
--- a/docs/sources/getting-started/get-started-grafana-ms-sql-server.md
+++ b/docs/sources/getting-started/get-started-grafana-ms-sql-server.md
@@ -24,7 +24,7 @@ MS SQL Server can be installed on Windows or Linux operating systems and also on
You can install MS SQL Server on the host running Grafana or on a remote server. To install the software from the [downloads page](https://www.microsoft.com/en-us/sql-server/sql-server-downloads), follow their setup prompts.
-If you are on a Windows host but want to use Grafana and MS SQL data source on a Linux environment, refer to the [WSL to set up your Grafana development environment](/blog/2021/03/03/.how-to-set-up-a-grafana-development-environment-on-a-windows-pc-using-wsl). This will allow you to leverage the resources available in [grafana/grafana](https://github.com/grafana/grafana) GitHub repository. Here you will find a collection of supported data sources, including MS SQL Server, along with test data and pre-configured dashboards for use.
+If you are on a Windows host but want to use Grafana and MS SQL data source on a Linux environment, refer to the [WSL to set up your Grafana development environment](/blog/2021/03/03/how-to-set-up-a-grafana-development-environment-on-a-windows-pc-using-wsl) blog post. This will allow you to leverage the resources available in [grafana/grafana](https://github.com/grafana/grafana) GitHub repository. Here you will find a collection of supported data sources, including MS SQL Server, along with test data and pre-configured dashboards for use.
#### Add the MS SQL data source
diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md
index 7de85993dc6..15db695b9bc 100644
--- a/docs/sources/introduction/grafana-enterprise.md
+++ b/docs/sources/introduction/grafana-enterprise.md
@@ -13,7 +13,7 @@ weight: 200
Grafana Enterprise is a commercial edition of Grafana that includes additional features not found in the open source version.
-Building on everything you already know and love about Grafana open source, Grafana Enterprise includes [exclusive datasource plugins]({{< relref "#enterprise-data-sources" >}}) and [additional features]({{< relref "#enterprise-features" >}}). You also get 24x7x365 support and training from the core Grafana team.
+Building on everything you already know and love about Grafana open source, Grafana Enterprise includes [exclusive data source plugins](#enterprise-data-sources) and [additional features](#enterprise-features). You also get 24x7x365 support and training from the core Grafana team.
To learn more about Grafana Enterprise, refer to [our product page](/enterprise).
@@ -21,7 +21,7 @@ To learn more about Grafana Enterprise, refer to [our product page](/enterprise)
Many Grafana Enterprise features are also available in [Grafana Cloud](/docs/grafana-cloud) Free, Pro, and Advanced accounts. For details, refer to [Grafana Cloud pricing](/pricing/#featuresTable).
-To migrate to Grafana Cloud, refer to [Migrate from Grafana Enterprise to Grafana Cloud](/docs/grafana-cloud/account-management/e2c-guide/).
+To migrate to Grafana Cloud, refer to [Migrate from Grafana Enterprise to Grafana Cloud](/docs/grafana//administration/migration-guide/)
## Authentication
@@ -29,48 +29,48 @@ Grafana Enterprise includes integrations with more ways to authenticate your use
### Team sync
-[Team sync]({{< relref "../setup-grafana/configure-security/configure-team-sync" >}}) allows you to set up synchronization between teams in Grafana and teams in your auth provider so that your users automatically end up in the right team.
+[Team sync](/docs/grafana//setup-grafana/configure-security/configure-team-sync/) allows you to set up synchronization between teams in Grafana and teams in your auth provider so that your users automatically end up in the right team.
Supported auth providers:
-- [Auth Proxy]({{< relref "../setup-grafana/configure-security/configure-authentication/auth-proxy#team-sync-enterprise-only" >}})
-- [Azure AD](https://grafana.com/docs/grafana//configure-security/configure-authentication/azuread#group-sync-enterprise-only)
-- [Generic OAuth integration](https://grafana.com/docs/grafana//configure-security/configure-authentication/generic-oauth#configure-group-synchronization" >}})
-- [GitHub OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/github#configure-group-synchronization)
-- [GitLab OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/gitlab#configure-group-synchronization)
-- [Google OAuth](https://grafana.com/docs/grafana//configure-security/configure-authentication/google#configure-group-synchronization)
-- [LDAP](https://grafana.com/docs/grafana//configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization)
-- [Okta](https://grafana.com/docs/grafana//configure-security/configure-authentication/okta#configure-group-synchronization-enterprise-only)
-- [SAML](https://grafana.com/docs/grafana//configure-security/configure-authentication/saml#configure-group-synchronization)
+- [Auth Proxy](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/auth-proxy#team-sync-enterprise-only)
+- [Azure AD](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/azuread#group-sync-enterprise-only)
+- [Generic OAuth integration](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/generic-oauth#configure-group-synchronization)
+- [GitHub OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/github#configure-group-synchronization)
+- [GitLab OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/gitlab#configure-group-synchronization)
+- [Google OAuth](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/google#configure-group-synchronization)
+- [LDAP](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization)
+- [Okta](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/okta#configure-group-synchronization-enterprise-only)
+- [SAML](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/saml#configure-group-synchronization)
### Enhanced LDAP integration
-With [enhanced LDAP integration]({{< relref "../setup-grafana/configure-security/configure-authentication/enhanced-ldap" >}}), you can set up active LDAP synchronization.
+With [enhanced LDAP integration](/docs/grafana//setup-grafana/configure-security/configure-authentication/enhanced-ldap/), you can set up active LDAP synchronization.
### SAML authentication
-[SAML authentication]({{< relref "../setup-grafana/configure-security/configure-authentication/saml" >}}) enables users to authenticate with single sign-on services that use Security Assertion Markup Language (SAML).
+[SAML authentication](/docs/grafana//setup-grafana/configure-security/configure-authentication/saml/) enables users to authenticate with single sign-on services that use Security Assertion Markup Language (SAML).
### Protected roles
-With [protected roles]({{< relref "../setup-grafana/configure-security/configure-authentication#protected-roles" >}}), you can define user roles that are exempt from being converted from one authentication type to another when changing auth providers.
+With [protected roles](/docs/grafana//setup-grafana/configure-security/configure-authentication/#protected-roles), you can define user roles that are exempt from being converted from one authentication type to another when changing auth providers.
## Enterprise features
Grafana Enterprise adds the following features:
-- [Role-based access control]({{< relref "../administration/roles-and-permissions/access-control" >}}) to control access with role-based permissions.
-- [Data source permissions]({{< relref "../administration/data-source-management#data-source-permissions" >}}) to restrict query access to specific teams and users.
-- [Data source query and resource caching]({{< relref "../administration/data-source-management#query-and-resource-caching" >}}) to temporarily store query results in Grafana to reduce data source load and rate limiting.
-- [Reporting]({{< relref "../dashboards/create-reports" >}}) to generate a PDF report from any dashboard and set up a schedule to have it emailed to whomever you choose.
-- [Export dashboard as PDF]({{< relref "../dashboards/share-dashboards-panels#export-a-dashboard-as-pdf" >}})
-- [Custom branding]({{< relref "../setup-grafana/configure-grafana/configure-custom-branding" >}}) to customize Grafana from the brand and logo to the footer links.
-- [Usage insights]({{< relref "../dashboards/assess-dashboard-usage" >}}) to understand how your Grafana instance is used.
-- [Recorded queries]({{< relref "../administration/recorded-queries" >}}) to see trends over time for your data sources.
-- [Vault integration]({{< relref "../setup-grafana/configure-security/configure-database-encryption#encrypting-your-database-with-a-key-from-a-key-management-service-kms" >}}) to manage your configuration or provisioning secrets with Vault.
-- [Auditing]({{< relref "../setup-grafana/configure-security/audit-grafana" >}}) tracks important changes to your Grafana instance to help you manage and mitigate suspicious activity and meet compliance requirements.
-- [Request security]({{< relref "../setup-grafana/configure-security/configure-request-security" >}}) makes it possible to restrict outgoing requests from the Grafana server.
-- [Settings updates at runtime]({{< relref "../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}) allows you to update Grafana settings at runtime without requiring a restart.
+- [Role-based access control](https://grafana.com/docs/grafana//administration/roles-and-permissions/access-control/) to control access with role-based permissions.
+- [Data source permissions](https://grafana.com/docs/grafana/