diff --git a/.betterer.eslint.config.js b/.betterer.eslint.config.js new file mode 100644 index 00000000000..ee4f3825179 --- /dev/null +++ b/.betterer.eslint.config.js @@ -0,0 +1,45 @@ +// @ts-check +/** + * @type {Array} + */ +module.exports = [ + { + files: ['**/*.{js,jsx,ts,tsx}'], + rules: { + '@typescript-eslint/no-explicit-any': 'error', + '@grafana/no-aria-label-selectors': 'error', + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: ['@grafana/ui*', '*/Layout/*'], + importNames: ['Layout', 'HorizontalGroup', 'VerticalGroup'], + message: 'Use Stack component instead.', + }, + ], + }, + ], + }, + }, + { + files: ['**/*.{ts,tsx}'], + ignores: ['**/*.{test,spec}.{ts,tsx}', '**/__mocks__/**', '**/public/test/**'], + rules: { + '@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }], + }, + }, + { + files: ['public/app/**/*.{ts,tsx}'], + rules: { + 'no-barrel-files/no-barrel-files': 'error', + }, + }, + { + files: ['public/**/*.tsx', 'packages/grafana-ui/**/*.tsx'], + ignores: ['public/app/plugins/**', '**/*.story.tsx', '**/*.{test,spec}.{ts,tsx}', '**/__mocks__/', 'public/test'], + rules: { + '@grafana/no-untranslated-strings': 'error', + }, + }, +]; diff --git a/.betterer.results b/.betterer.results index dc17bc5297c..c64fc238700 100644 --- a/.betterer.results +++ b/.betterer.results @@ -41,8 +41,7 @@ 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, "Do not use any type assertions.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"] + [0, 0, 0, "Do not use any type assertions.", "3"] ], "packages/grafana-data/src/dataframe/processDataFrame.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -69,12 +68,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "18"], [0, 0, 0, "Do not use any type assertions.", "19"] ], - "packages/grafana-data/src/datetime/datemath.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "packages/grafana-data/src/datetime/durationutil.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "packages/grafana-data/src/datetime/moment_wrapper.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -121,8 +114,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Do not use any type assertions.", "6"] + [0, 0, 0, "Do not use any type assertions.", "5"] ], "packages/grafana-data/src/panel/registryFactories.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -150,25 +142,15 @@ exports[`better eslint`] = { "packages/grafana-data/src/transformations/standardTransformersRegistry.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "packages/grafana-data/src/transformations/transformDataFrame.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "packages/grafana-data/src/transformations/transformers/nulls/nullInsertThreshold.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "packages/grafana-data/src/transformations/transformers/reduce.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], "packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"] + [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], "packages/grafana-data/src/types/ScopedVars.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -188,14 +170,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], "packages/grafana-data/src/types/app.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Do not use any type assertions.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"] - ], - "packages/grafana-data/src/types/config.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Do not use any type assertions.", "2"] ], "packages/grafana-data/src/types/dashboard.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -247,8 +224,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "20"], [0, 0, 0, "Unexpected any. Specify a different type.", "21"], [0, 0, 0, "Unexpected any. Specify a different type.", "22"], - [0, 0, 0, "Unexpected any. Specify a different type.", "23"], - [0, 0, 0, "Unexpected any. Specify a different type.", "24"] + [0, 0, 0, "Unexpected any. Specify a different type.", "23"] ], "packages/grafana-data/src/types/explore.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -303,8 +279,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"], [0, 0, 0, "Unexpected any. Specify a different type.", "11"], - [0, 0, 0, "Unexpected any. Specify a different type.", "12"], - [0, 0, 0, "Unexpected any. Specify a different type.", "13"] + [0, 0, 0, "Unexpected any. Specify a different type.", "12"] ], "packages/grafana-data/src/types/plugin.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -342,23 +317,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Unexpected any. Specify a different type.", "8"], [0, 0, 0, "Unexpected any. Specify a different type.", "9"], - [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Unexpected any. Specify a different type.", "11"], - [0, 0, 0, "Unexpected any. Specify a different type.", "12"], - [0, 0, 0, "Unexpected any. Specify a different type.", "13"], - [0, 0, 0, "Unexpected any. Specify a different type.", "14"], - [0, 0, 0, "Unexpected any. Specify a different type.", "15"], - [0, 0, 0, "Unexpected any. Specify a different type.", "16"], - [0, 0, 0, "Unexpected any. Specify a different type.", "17"], - [0, 0, 0, "Unexpected any. Specify a different type.", "18"], - [0, 0, 0, "Unexpected any. Specify a different type.", "19"], - [0, 0, 0, "Unexpected any. Specify a different type.", "20"], - [0, 0, 0, "Unexpected any. Specify a different type.", "21"], - [0, 0, 0, "Unexpected any. Specify a different type.", "22"], - [0, 0, 0, "Unexpected any. Specify a different type.", "23"], - [0, 0, 0, "Unexpected any. Specify a different type.", "24"], - [0, 0, 0, "Unexpected any. Specify a different type.", "25"], - [0, 0, 0, "Unexpected any. Specify a different type.", "26"] + [0, 0, 0, "Unexpected any. Specify a different type.", "10"] ], "packages/grafana-data/src/utils/Registry.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -374,9 +333,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], - "packages/grafana-data/src/utils/dataLinks.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "packages/grafana-data/src/utils/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] @@ -422,6 +378,9 @@ exports[`better eslint`] = { "packages/grafana-data/test/__mocks__/pluginMocks.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "packages/grafana-e2e-selectors/src/resolver.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], "packages/grafana-o11y-ds-frontend/src/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -598,9 +557,6 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/InfoBox/InfoBox.story.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], - "packages/grafana-ui/src/components/InfoBox/InfoBox.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "packages/grafana-ui/src/components/JSONFormatter/json_explorer/json_explorer.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -825,15 +781,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "packages/grafana-ui/src/utils/logger.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] - ], - "packages/grafana-ui/src/utils/storybook/withTheme.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/utils/useAsyncDependency.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -847,191 +794,31 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`notifyApp\`)", "2"], [0, 0, 0, "Do not re-export imported variable (\`hideAppNotification\`)", "3"] ], - "public/app/core/components/AccessControl/PermissionListItem.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/core/components/AccessControl/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], - "public/app/core/components/AppChrome/AppChrome.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/AppChrome/TopBar/SignInLink.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/AppChrome/TopBar/TopSearchBarCommandPaletteTrigger.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/core/components/AppNotifications/AppNotificationItem.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/core/components/DynamicImports/SafeDynamicImport.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/core/components/EmptyListCTA/EmptyListCTA.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/FolderFilter/FolderFilter.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/ForgottenPassword/ChangePassword.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/core/components/ForgottenPassword/ForgottenPassword.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"] - ], - "public/app/core/components/FormPrompt/FormPrompt.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"] - ], "public/app/core/components/GraphNG/GraphNG.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "Do not use any type assertions.", "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.", "5"], - [0, 0, 0, "Do not use any type assertions.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"], - [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Do not use any type assertions.", "9"] + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"] ], "public/app/core/components/LocalStorageValueProvider/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./LocalStorageValueProvider\`)", "0"] ], - "public/app/core/components/Login/LoginLayout.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/Login/LoginServiceButtons.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/NestedFolderPicker/NestedFolderList.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/NestedFolderPicker/Trigger.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/core/components/PageNotFound/EntityNotFound.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"] - ], - "public/app/core/components/PanelTypeFilter/PanelTypeFilter.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/core/components/PluginHelp/PluginHelp.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/core/components/QueryOperationRow/OperationRowHelp.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/core/components/QueryOperationRow/QueryOperationRow.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/core/components/QueryOperationRow/QueryOperationRowHeader.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] - ], - "public/app/core/components/RolePicker/BuiltinRoleSelector.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/RolePicker/RolePickerInput.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"] - ], - "public/app/core/components/RolePicker/RolePickerMenu.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/core/components/RolePicker/RolePickerSubMenu.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/RolePicker/ValueContainer.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/core/components/Select/OldFolderPicker.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/core/components/Signup/SignupPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/core/components/Signup/VerifyEmail.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"] - ], "public/app/core/components/TagFilter/TagFilter.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/core/components/TimeSeries/utils.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], - "public/app/core/components/Upgrade/ProBadge.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/core/components/Upgrade/UpgradeBox.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, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"], - [0, 0, 0, "Styles should be written using objects.", "14"], - [0, 0, 0, "Styles should be written using objects.", "15"], - [0, 0, 0, "Styles should be written using objects.", "16"], - [0, 0, 0, "Styles should be written using objects.", "17"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "18"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "19"], - [0, 0, 0, "Styles should be written using objects.", "20"], - [0, 0, 0, "Styles should be written using objects.", "21"], - [0, 0, 0, "Styles should be written using objects.", "22"], - [0, 0, 0, "Styles should be written using objects.", "23"] - ], - "public/app/core/components/help/HelpModal.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/core/config.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`config\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`Settings\`)", "1"] @@ -1046,15 +833,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`TimeSeries\`)", "6"], [0, 0, 0, "Do not re-export imported variable (\`updateLegendValues\`)", "7"] ], - "public/app/core/navigation/GrafanaRouteError.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"] - ], - "public/app/core/navigation/RouterDebugger.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/core/navigation/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -1145,183 +923,18 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], - "public/app/features/actions/ActionEditorModalContent.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/features/actions/ActionsInlineEditor.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], "public/app/features/actions/ParamsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/admin/AdminEditOrgPage.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/admin/AdminFeatureTogglesPage.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"] - ], "public/app/features/admin/AdminFeatureTogglesTable.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"] ], - "public/app/features/admin/AdminListOrgsPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/AdminOrgsTable.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"] - ], - "public/app/features/admin/AdminSettings.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/LicenseChrome.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/features/admin/ServerStats.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"] - ], "public/app/features/admin/UpgradePage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "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"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"], - [0, 0, 0, "Styles should be written using objects.", "14"], - [0, 0, 0, "Styles should be written using objects.", "15"] - ], - "public/app/features/admin/UserCreatePage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/UserLdapSyncInfo.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"] - ], - "public/app/features/admin/UserListAdminPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/UserListPublicDashboardPage/DashboardsListModalButton.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] - ], - "public/app/features/admin/UserListPublicDashboardPage/UserListPublicDashboardPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/UserOrgs.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"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] - ], - "public/app/features/admin/UserPermissions.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, "Styles should be written using objects.", "5"] - ], - "public/app/features/admin/UserProfile.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, "Styles should be written using objects.", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"] - ], - "public/app/features/admin/UserSessions.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"] - ], - "public/app/features/admin/Users/AnonUsersTable.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/Users/OrgUsersTable.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/admin/Users/UsersTable.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], - "public/app/features/admin/ldap/LdapConnectionStatus.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/features/admin/ldap/LdapPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/features/admin/ldap/LdapSyncInfo.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/ldap/LdapUserGroups.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/ldap/LdapUserInfo.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/admin/ldap/LdapUserPermissions.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"] - ], "public/app/features/alerting/routes.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -1614,16 +1227,8 @@ exports[`better eslint`] = { [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"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] - ], - "public/app/features/alerting/unified/components/receivers/TemplateForm.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 ", "11"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "12"] ], "public/app/features/alerting/unified/components/receivers/TemplatePreview.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -1746,21 +1351,8 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "Do not use any type assertions.", "3"] ], - "public/app/features/alerting/unified/components/rule-editor/FolderAndGroup.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"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] + "public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx:5381": [ + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/alerting/unified/components/rule-editor/NeedHelpInfo.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -1777,8 +1369,7 @@ exports[`better eslint`] = { [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"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "12"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] ], "public/app/features/alerting/unified/components/rule-editor/PreviewRule.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -1813,9 +1404,6 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/components/rule-editor/RuleEditorSection.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/alerting/unified/components/rule-editor/RuleFolderPicker.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/alerting/unified/components/rule-editor/RuleInspector.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -1931,25 +1519,6 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/components/rule-editor/rule-types/RuleTypePicker.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/alerting/unified/components/rule-list/AlertRuleListItem.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"] - ], - "public/app/features/alerting/unified/components/rule-list/EvaluationGroup.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/alerting/unified/components/rule-list/RuleList.v1.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/features/alerting/unified/components/rule-list/RuleList.v2.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/alerting/unified/components/rule-viewer/FederatedRuleWarning.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -2059,9 +1628,7 @@ exports[`better eslint`] = { [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"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "8"] ], "public/app/features/alerting/unified/components/rules/RuleListStateSection.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -2206,10 +1773,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Do not use any type assertions.", "8"] ], - "public/app/features/alerting/unified/state/actions.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], "public/app/features/alerting/unified/types/receiver-form.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -2278,9 +1841,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], "public/app/features/api-keys/MigrateToServiceAccountsCard.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -2288,10 +1849,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/auth-config/AuthDrawer.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -2363,10 +1921,8 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/connections/components/ConnectionsRedirectNotice/ConnectionsRedirectNotice.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "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 ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/connections/components/ConnectionsRedirectNotice/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] @@ -2389,10 +1945,6 @@ exports[`better eslint`] = { "public/app/features/connections/tabs/ConnectData/CardGrid/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], - "public/app/features/connections/tabs/ConnectData/CategoryHeader/CategoryHeader.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/features/connections/tabs/ConnectData/CategoryHeader/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], @@ -2400,21 +1952,12 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [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 ", "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"] ], "public/app/features/connections/tabs/ConnectData/NoAccessModal/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] @@ -2425,28 +1968,9 @@ exports[`better eslint`] = { "public/app/features/connections/tabs/ConnectData/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], - "public/app/features/correlations/CorrelationsPage.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] - ], - "public/app/features/correlations/Forms/AddCorrelationForm.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/features/correlations/Forms/ConfigureCorrelationBasicInfoForm.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/features/correlations/Forms/ConfigureCorrelationSourceForm.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/features/correlations/Forms/ConfigureCorrelationTargetForm.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"] + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/correlations/components/EmptyCorrelationsCTA.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -2629,6 +2153,14 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "7"], [0, 0, 0, "Do not use any type assertions.", "8"] ], + "public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"] + ], "public/app/features/dashboard-scene/settings/DeleteDashboardButton.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -2759,9 +2291,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./VersionHistoryButtons\`)", "3"], [0, 0, 0, "Do not re-export imported variable (\`./VersionHistoryComparison\`)", "4"] ], - "public/app/features/dashboard-scene/sharing/ShareExportTab.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/dashboard-scene/sharing/public-dashboards/ConfigPublicDashboard.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], @@ -3020,9 +2549,8 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "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 ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/dashboard/components/TransformationsEditor/TransformationPicker.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], @@ -3233,9 +2761,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"], [0, 0, 0, "Do not use export all (\`export * from ...\`)", "1"] ], - "public/app/features/datasources/components/BasicSettings.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/datasources/components/ButtonRow.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -3264,19 +2789,9 @@ exports[`better eslint`] = { "public/app/features/datasources/components/DataSourcePluginState.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/datasources/components/DataSourceTestingStatus.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"] - ], "public/app/features/datasources/components/DataSourceTypeCard.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], - "public/app/features/datasources/components/DataSourceTypeCardList.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/datasources/components/DataSourcesListCard.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -3288,38 +2803,6 @@ exports[`better eslint`] = { "public/app/features/datasources/components/NewDataSource.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/datasources/components/picker/DataSourceCard.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] - ], - "public/app/features/datasources/components/picker/DataSourceList.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] - ], - "public/app/features/datasources/components/picker/DataSourceLogo.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/features/datasources/components/picker/DataSourceModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"] - ], "public/app/features/datasources/components/picker/DataSourcePicker.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], @@ -3351,57 +2834,24 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], - "public/app/features/dimensions/editors/ColorDimensionEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/features/dimensions/editors/FileUploader.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/dimensions/editors/FolderPickerTab.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/features/dimensions/editors/ResourceCards.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/dimensions/editors/ResourceDimensionEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/dimensions/editors/ResourcePicker.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/dimensions/editors/ResourcePickerPopover.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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] - ], - "public/app/features/dimensions/editors/ScalarDimensionEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/features/dimensions/editors/ScaleDimensionEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/dimensions/editors/TextDimensionEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -3411,20 +2861,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"] - ], - "public/app/features/dimensions/editors/URLPickerTab.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingEditRow.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], @@ -3444,8 +2881,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/dimensions/editors/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"], @@ -3510,15 +2946,10 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/explore/Logs/LiveLogs.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "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 ", "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/explore/Logs/Logs.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -3528,32 +2959,17 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/Logs/LogsMetaRow.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "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 ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/explore/Logs/LogsNavigation.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], - "public/app/features/explore/Logs/LogsNavigationPages.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/Logs/LogsSamplePanel.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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/explore/Logs/LogsTableEmptyFields.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -3566,24 +2982,9 @@ exports[`better eslint`] = { "public/app/features/explore/Logs/LogsTableNavField.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/explore/Logs/LogsVolumePanel.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/features/explore/Logs/LogsVolumePanelList.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, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] - ], - "public/app/features/explore/Logs/utils/LogsCrossFadeTransition.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/explore/MetaInfoText.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -3594,95 +2995,23 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/explore/NodeGraph/NodeGraphContainer.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] - ], - "public/app/features/explore/PrometheusListView/ItemLabels.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/features/explore/PrometheusListView/ItemValues.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/explore/PrometheusListView/RawListContainer.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] - ], - "public/app/features/explore/PrometheusListView/RawListItem.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/PrometheusListView/RawListItemAttributes.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "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 ", "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/explore/QueryLibrary/QueryTemplatesTable/QueryDescriptionCell.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/explore/RichHistory/RichHistoryCard.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"] - ], - "public/app/features/explore/RichHistory/RichHistoryQueriesTab.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"] - ], - "public/app/features/explore/RichHistory/RichHistorySettingsTab.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], "public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/explore/SupplementaryResultError.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -3861,12 +3190,9 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/expressions/components/Condition.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], + [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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/expressions/components/Math.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -3878,19 +3204,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"], - [0, 0, 0, "Styles should be written using objects.", "14"], - [0, 0, 0, "Styles should be written using objects.", "15"], - [0, 0, 0, "Styles should be written using objects.", "16"] - ], - "public/app/features/expressions/components/Threshold.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 ", "9"] ], "public/app/features/expressions/guards.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -3898,13 +3212,10 @@ exports[`better eslint`] = { "public/app/features/geo/editor/GazetteerPathEditor.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, "Styles should be written using objects.", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/geo/editor/locationModeEditor.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] + [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], "public/app/features/geo/gazetteer/gazetteer.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -3924,9 +3235,6 @@ exports[`better eslint`] = { "public/app/features/gops/configuration-tracker/components/ProgressBar.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/inspector/DetailText.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/inspector/InspectDataOptions.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "1"], @@ -3949,18 +3257,16 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/inspector/InspectStatsTab.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], "public/app/features/inspector/QueryInspector.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], + [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, "Use data-testid for E2E selectors instead of aria-label", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "7"] + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "6"] ], "public/app/features/invites/InviteeRow.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -3988,21 +3294,14 @@ exports[`better eslint`] = { ], "public/app/features/library-panels/components/LibraryPanelInfo/LibraryPanelInfo.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, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], - "public/app/features/library-panels/components/OpenLibraryPanelModal/OpenLibraryPanelModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4013,14 +3312,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] ], - "public/app/features/library-panels/styles.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] - ], "public/app/features/live/centrifuge/LiveDataStream.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -4038,123 +3329,35 @@ exports[`better eslint`] = { "public/app/features/logs/components/InfiniteScroll.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/logs/components/LoadingIndicator.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/logs/components/LogDetails.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"] ], - "public/app/features/logs/components/LogDetailsRow.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] - ], "public/app/features/logs/components/LogLabelStats.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "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"] - ], - "public/app/features/logs/components/LogLabelStatsRow.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"] - ], - "public/app/features/logs/components/LogLabels.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/features/logs/components/LogRowMessageDisplayedFields.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] + [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"] ], "public/app/features/logs/components/LogRows.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], - "public/app/features/logs/components/getLogRowStyles.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"], - [0, 0, 0, "Styles should be written using objects.", "14"], - [0, 0, 0, "Styles should be written using objects.", "15"], - [0, 0, 0, "Styles should be written using objects.", "16"], - [0, 0, 0, "Styles should be written using objects.", "17"], - [0, 0, 0, "Styles should be written using objects.", "18"], - [0, 0, 0, "Styles should be written using objects.", "19"], - [0, 0, 0, "Styles should be written using objects.", "20"], - [0, 0, 0, "Styles should be written using objects.", "21"], - [0, 0, 0, "Styles should be written using objects.", "22"], - [0, 0, 0, "Styles should be written using objects.", "23"], - [0, 0, 0, "Styles should be written using objects.", "24"], - [0, 0, 0, "Styles should be written using objects.", "25"], - [0, 0, 0, "Styles should be written using objects.", "26"], - [0, 0, 0, "Styles should be written using objects.", "27"], - [0, 0, 0, "Styles should be written using objects.", "28"], - [0, 0, 0, "Styles should be written using objects.", "29"], - [0, 0, 0, "Styles should be written using objects.", "30"], - [0, 0, 0, "Styles should be written using objects.", "31"], - [0, 0, 0, "Styles should be written using objects.", "32"], - [0, 0, 0, "Styles should be written using objects.", "33"], - [0, 0, 0, "Styles should be written using objects.", "34"] - ], "public/app/features/logs/components/log-context/LogContextButtons.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/logs/components/log-context/LogRowContextModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "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 ", "16"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "17"] + [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"] ], "public/app/features/logs/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/manage-dashboards/DashboardImportPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/manage-dashboards/components/ImportDashboardForm.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4162,9 +3365,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/manage-dashboards/components/ImportDashboardLibraryPanelsList.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/manage-dashboards/components/ImportDashboardOverview.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4172,18 +3373,8 @@ 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/DeletePublicDashboardModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "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"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] + [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/state/actions.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -4258,13 +3449,6 @@ exports[`better eslint`] = { "public/app/features/panel/components/VizTypePicker/PanelTypeCard.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], - "public/app/features/panel/components/VizTypePicker/VisualizationSuggestionCard.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], "public/app/features/panel/components/VizTypePicker/VisualizationSuggestions.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -4301,8 +3485,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/plugins/admin/components/Badges/PluginUpdateAvailableBadge.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/plugins/admin/components/Badges/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./PluginDisabledBadge\`)", "0"], @@ -4312,9 +3495,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./PluginAngularBadge\`)", "4"], [0, 0, 0, "Do not re-export imported variable (\`./PluginDeprecatedBadge\`)", "5"] ], - "public/app/features/plugins/admin/components/Badges/sharedStyles.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithApp.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -4326,9 +3506,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/HorizontalGroup.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "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"], @@ -4348,8 +3525,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./InstallControlsButton\`)", "1"] ], "public/app/features/plugins/admin/components/PluginActions.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/plugins/admin/components/PluginDashboards.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4377,13 +3553,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] ], "public/app/features/plugins/admin/components/PluginDetailsHeaderDependencies.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/features/plugins/admin/components/PluginDetailsHeaderSignature.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/plugins/admin/components/PluginDetailsPage.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -4401,21 +3571,12 @@ exports[`better eslint`] = { ], "public/app/features/plugins/admin/components/PluginSignatureDetailsBadge.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, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] - ], - "public/app/features/plugins/admin/components/PluginSubtitle.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/plugins/admin/components/PluginUsage.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, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/plugins/admin/components/VersionList.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4423,13 +3584,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] - ], - "public/app/features/plugins/admin/hooks/usePluginInfo.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/plugins/admin/pages/Browse.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -4524,15 +3679,7 @@ exports[`better eslint`] = { [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/query/components/QueryErrorAlert.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4543,16 +3690,9 @@ exports[`better eslint`] = { [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "5"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] ], "public/app/features/query/components/QueryGroupOptions.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4646,20 +3786,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/search/page/components/SearchResultsTable.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/search/page/components/columns.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -4676,6 +3803,10 @@ exports[`better eslint`] = { "public/app/features/search/service/sql.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/search/service/unified.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] + ], "public/app/features/search/service/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -4688,9 +3819,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, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/serviceaccounts/ServiceAccountPage.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -4716,12 +3844,10 @@ exports[`better eslint`] = { ], "public/app/features/serviceaccounts/components/ServiceAccountProfile.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, "Styles should be written using objects.", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/serviceaccounts/components/ServiceAccountProfileRow.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], "public/app/features/serviceaccounts/components/ServiceAccountTokensTable.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4730,14 +3856,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] ], "public/app/features/serviceaccounts/components/ServiceAccountsListItem.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], @@ -4747,65 +3866,6 @@ exports[`better eslint`] = { "public/app/features/serviceaccounts/state/reducers.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/storage/AddRootView.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/features/storage/Breadcrumb.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/features/storage/CreateNewFolderModal.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/storage/FileView.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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"] - ], - "public/app/features/storage/FolderView.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, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] - ], - "public/app/features/storage/RootView.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] - ], - "public/app/features/storage/StorageFolderPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], - "public/app/features/storage/StoragePage.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, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"] - ], - "public/app/features/storage/UploadButton.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, "Styles should be written using objects.", "3"] - ], - "public/app/features/storage/storage.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/support-bundles/SupportBundles.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -4880,8 +3940,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/trails/DataTrailCard.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 ", "0"] ], "public/app/features/trails/DataTrailSettings.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -4889,11 +3948,6 @@ exports[`better eslint`] = { "public/app/features/trails/DataTrailsHistory.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/trails/DataTrailsHome.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"] - ], "public/app/features/trails/MetricScene.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] @@ -4948,9 +4002,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], - "public/app/features/transformers/configFromQuery/ConfigFromQueryTransformerEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/features/transformers/editors/CalculateFieldTransformerEditor/CumulativeOptionsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -4990,10 +4041,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/transformers/editors/GroupByTransformerEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/transformers/editors/MergeTransformerEditor.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -5017,9 +4065,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/transformers/extractFields/extractFields.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5034,10 +4080,7 @@ exports[`better eslint`] = { [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, "Do not use any type assertions.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] + [0, 0, 0, "Do not use any type assertions.", "5"] ], "public/app/features/transformers/fieldToConfigMapping/fieldToConfigMapping.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -5075,8 +4118,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"], - [0, 0, 0, "Styles should be written using objects.", "14"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] ], "public/app/features/transformers/prepareTimeSeries/prepareTimeSeries.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -5085,10 +4127,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "public/app/features/transformers/spatial/SpatialTransformerEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/features/transformers/spatial/optionsHelper.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -5097,9 +4135,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "4"], [0, 0, 0, "Do not use any type assertions.", "5"] ], - "public/app/features/transformers/standardTransformers.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/features/transformers/suggestionsInput/SuggestionsInput.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -5111,10 +4146,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], "public/app/features/users/UsersActionBar.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -5153,21 +4185,13 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], "public/app/features/variables/editor/VariableEditorListRow.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"], [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"], [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"] + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"] ], "public/app/features/variables/editor/getVariableQueryEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5201,12 +4225,7 @@ exports[`better eslint`] = { [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, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], "public/app/features/variables/inspect/utils.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5339,8 +4358,7 @@ exports[`better eslint`] = { ], "public/app/features/visualization/data-hover/DataHoverRows.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, "Styles should be written using objects.", "2"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] ], "public/app/features/visualization/data-hover/DataHoverView.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -5419,9 +4437,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloud-monitoring/components/Aggregation.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/cloud-monitoring/components/CloudMonitoringCheatSheet.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/cloud-monitoring/components/GraphPeriod.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -5431,9 +4446,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloud-monitoring/components/VariableQueryEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/cloud-monitoring/components/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./Project\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`./GroupBy\`)", "1"], @@ -5502,15 +4514,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./thirdArgAfterSearchQuery\`)", "4"], [0, 0, 0, "Do not re-export imported variable (\`./withinStringQuery\`)", "5"] ], - "public/app/plugins/datasource/cloudwatch/components/ConfigEditor/XrayLinkConfig.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/cloudwatch/components/QueryEditor/LogsQueryEditor/LogsQueryEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/DynamicLabelsField.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/MetricsQueryEditor.test.tsx:5381": [ [0, 0, 0, "* import is invalid because \'Layout,HorizontalGroup,VerticalGroup\' from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -5520,9 +4523,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloudwatch/components/QueryEditor/QueryEditor.test.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./MetricsQueryEditor/SQLCodeEditor\`)", "0"] ], - "public/app/plugins/datasource/cloudwatch/components/shared/LogGroups/LegacyLogGroupNamesSelection.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/cloudwatch/components/shared/MetricStatEditor/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./MetricStatEditor\`)", "0"] ], @@ -5617,23 +4617,12 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Unexpected any. Specify a different type.", "8"] ], - "public/app/plugins/datasource/elasticsearch/components/AddRemove.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/elasticsearch/components/MetricPicker.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/BucketAggregationEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/DateHistogramSettingsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/FiltersSettingsEditor/index.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/TermsSettingsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -5646,59 +4635,16 @@ exports[`better eslint`] = { "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/MetricEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/SettingsEditor/BucketScriptSettingsEditor/index.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/SettingsEditor/SettingField.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/SettingsEditor/TopMetricsSettingsEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/styles.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/QueryEditorRow.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/SettingsEditorContainer.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/datasource/elasticsearch/components/QueryEditor/styles.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/elasticsearch/configuration/DataLink.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] - ], - "public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/plugins/datasource/elasticsearch/datasource.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -5717,10 +4663,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./dataquery.gen\`)", "1"], [0, 0, 0, "Do not re-export imported variable (\`ElasticsearchQuery\`)", "2"] ], - "public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/LabelsEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -5742,8 +4684,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/datasource/grafana-testdata-datasource/components/SimulationSchemaForm.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/plugins/datasource/grafana-testdata-datasource/components/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./StreamingClientEditor\`)", "0"], @@ -5762,18 +4703,7 @@ exports[`better eslint`] = { "public/app/plugins/datasource/grafana/components/QueryEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] - ], - "public/app/plugins/datasource/grafana/components/TimePickerInput.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] - ], - "public/app/plugins/datasource/grafana/components/TimeRegionEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "Do not use any type assertions.", "2"] ], "public/app/plugins/datasource/grafana/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -5786,38 +4716,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Do not use any type assertions.", "8"] ], - "public/app/plugins/datasource/graphite/components/AddGraphiteFunction.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/graphite/components/FunctionParamEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/graphite/components/GraphiteFunctionEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/graphite/components/GraphiteQueryEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/plugins/datasource/graphite/components/MetricTankMetaInspector.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"] - ], - "public/app/plugins/datasource/graphite/components/TagsSection.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/datasource/graphite/components/helpers.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/plugins/datasource/graphite/datasource.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -5827,9 +4725,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], + [0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Do not use any type assertions.", "5"], + [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"], @@ -5843,25 +4741,12 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "16"], [0, 0, 0, "Unexpected any. Specify a different type.", "17"], [0, 0, 0, "Unexpected any. Specify a different type.", "18"], - [0, 0, 0, "Unexpected any. Specify a different type.", "19"], - [0, 0, 0, "Unexpected any. Specify a different type.", "20"], - [0, 0, 0, "Unexpected any. Specify a different type.", "21"], - [0, 0, 0, "Unexpected any. Specify a different type.", "22"], - [0, 0, 0, "Unexpected any. Specify a different type.", "23"], - [0, 0, 0, "Unexpected any. Specify a different type.", "24"], - [0, 0, 0, "Unexpected any. Specify a different type.", "25"], - [0, 0, 0, "Unexpected any. Specify a different type.", "26"], - [0, 0, 0, "Unexpected any. Specify a different type.", "27"], - [0, 0, 0, "Unexpected any. Specify a different type.", "28"], - [0, 0, 0, "Unexpected any. Specify a different type.", "29"] + [0, 0, 0, "Unexpected any. Specify a different type.", "19"] ], "public/app/plugins/datasource/graphite/gfunc.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"] + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/datasource/graphite/graphite_query.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5874,22 +4759,12 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Unexpected any. Specify a different type.", "8"], [0, 0, 0, "Unexpected any. Specify a different type.", "9"], - [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Unexpected any. Specify a different type.", "11"], - [0, 0, 0, "Do not use any type assertions.", "12"], - [0, 0, 0, "Unexpected any. Specify a different type.", "13"], - [0, 0, 0, "Unexpected any. Specify a different type.", "14"], - [0, 0, 0, "Unexpected any. Specify a different type.", "15"], - [0, 0, 0, "Unexpected any. Specify a different type.", "16"] + [0, 0, 0, "Do not use any type assertions.", "10"] ], "public/app/plugins/datasource/graphite/lexer.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"] + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/datasource/graphite/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -5919,18 +4794,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/influxdb/components/editor/config/ConfigEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/influxdb/components/editor/query/flux/FluxQueryEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/plugins/datasource/influxdb/components/editor/query/fsql/FSQLEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/influxdb/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -5944,12 +4807,10 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"], [0, 0, 0, "Unexpected any. Specify a different type.", "11"], - [0, 0, 0, "Unexpected any. Specify a different type.", "12"], - [0, 0, 0, "Do not use any type assertions.", "13"] + [0, 0, 0, "Do not use any type assertions.", "12"] ], "public/app/plugins/datasource/influxdb/influx_query_model.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/plugins/datasource/influxdb/influx_series.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5960,10 +4821,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "5"], [0, 0, 0, "Unexpected any. Specify a different type.", "6"], [0, 0, 0, "Unexpected any. Specify a different type.", "7"], - [0, 0, 0, "Unexpected any. Specify a different type.", "8"], - [0, 0, 0, "Unexpected any. Specify a different type.", "9"], - [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Unexpected any. Specify a different type.", "11"] + [0, 0, 0, "Unexpected any. Specify a different type.", "8"] ], "public/app/plugins/datasource/influxdb/query_part.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5984,10 +4842,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/influxdb/response_parser.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/datasource/jaeger/CheatSheet.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/jaeger/_importedDependencies/model/transform-trace-data.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -5997,10 +4851,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/jaeger/components/QueryEditor.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/plugins/datasource/jaeger/configuration/TraceIdTimeParams.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/jaeger/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -6008,84 +4858,16 @@ exports[`better eslint`] = { "public/app/plugins/datasource/loki/LanguageProvider.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/datasource/loki/components/LokiContextUi.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"] - ], - "public/app/plugins/datasource/loki/components/LokiLabelBrowser.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"] - ], - "public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/loki/configuration/ConfigEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/datasource/loki/configuration/DerivedField.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] - ], - "public/app/plugins/datasource/loki/configuration/DerivedFields.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], "public/app/plugins/datasource/loki/datasource.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "public/app/plugins/datasource/loki/querybuilder/components/LabelBrowserModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/loki/querybuilder/components/LokiQueryCodeEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/plugins/datasource/loki/querybuilder/components/QueryPattern.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"] - ], - "public/app/plugins/datasource/loki/querybuilder/components/QueryPatternsModal.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/plugins/datasource/loki/types.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`LokiQueryDirection\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`LokiQueryType\`)", "1"], @@ -6095,10 +4877,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`MixedDatasource\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`Datasource\`)", "1"] ], - "public/app/plugins/datasource/opentsdb/components/OpenTsdbQueryEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/opentsdb/datasource.d.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -6122,10 +4900,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "16"], [0, 0, 0, "Unexpected any. Specify a different type.", "17"] ], - "public/app/plugins/datasource/parca/QueryEditor/LabelsEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/datasource/parca/webpack.config.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`config\`)", "0"] ], @@ -6214,38 +4988,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/zipkin/webpack.config.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`config\`)", "0"] ], - "public/app/plugins/panel/alertlist/AlertInstances.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/panel/alertlist/UnifiedAlertList.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Styles should be written using objects.", "10"], - [0, 0, 0, "Styles should be written using objects.", "11"], - [0, 0, 0, "Styles should be written using objects.", "12"], - [0, 0, 0, "Styles should be written using objects.", "13"], - [0, 0, 0, "Styles should be written using objects.", "14"], - [0, 0, 0, "Styles should be written using objects.", "15"], - [0, 0, 0, "Styles should be written using objects.", "16"] - ], - "public/app/plugins/panel/alertlist/unified-alerting/UngroupedView.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] - ], "public/app/plugins/panel/annolist/AnnoListPanel.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -6272,11 +5014,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`CandlestickFieldMap\`)", "6"], [0, 0, 0, "Do not re-export imported variable (\`FieldConfig\`)", "7"] ], - "public/app/plugins/panel/datagrid/utils.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/plugins/panel/debug/CursorView.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -6343,24 +5080,6 @@ exports[`better eslint`] = { "public/app/plugins/panel/geomap/utils/tooltip.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/panel/gettingstarted/components/DocsCard.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], - "public/app/plugins/panel/gettingstarted/components/TutorialCard.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] - ], - "public/app/plugins/panel/gettingstarted/components/sharedStyles.ts:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/panel/heatmap/HeatmapPanel.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -6395,18 +5114,8 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "15"], [0, 0, 0, "Do not use any type assertions.", "16"] ], - "public/app/plugins/panel/live/LiveChannelEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/panel/live/LivePanel.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"] + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/plugins/panel/logs/LogsPanel.test.tsx:5381": [ [0, 0, 0, "* import is invalid because \'Layout,HorizontalGroup,VerticalGroup\' from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] @@ -6417,55 +5126,16 @@ exports[`better eslint`] = { "public/app/plugins/panel/nodeGraph/Edge.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/panel/nodeGraph/EdgeLabel.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/plugins/panel/nodeGraph/Legend.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/panel/nodeGraph/Marker.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], - "public/app/plugins/panel/nodeGraph/Node.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] - ], "public/app/plugins/panel/nodeGraph/NodeGraph.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"], - [0, 0, 0, "Styles should be written using objects.", "9"], - [0, 0, 0, "Do not use any type assertions.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"], - [0, 0, 0, "Do not use any type assertions.", "12"], - [0, 0, 0, "Do not use any type assertions.", "13"] + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"] ], "public/app/plugins/panel/nodeGraph/ViewControls.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] - ], - "public/app/plugins/panel/nodeGraph/editor/ArcOptionsEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/panel/nodeGraph/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./NodeGraph\`)", "0"] @@ -6476,17 +5146,6 @@ exports[`better eslint`] = { "public/app/plugins/panel/nodeGraph/types.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./panelcfg.gen\`)", "0"] ], - "public/app/plugins/panel/nodeGraph/useContextMenu.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/panel/piechart/PieChart.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"] - ], "public/app/plugins/panel/piechart/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -6498,13 +5157,8 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "public/app/plugins/panel/table/TablePanel.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], "public/app/plugins/panel/table/cells/SparklineCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] + [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], "public/app/plugins/panel/table/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -6512,13 +5166,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], - "public/app/plugins/panel/text/TextPanel.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/panel/text/TextPanelEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], "public/app/plugins/panel/text/textPanelMigrationHandler.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -6546,15 +5193,6 @@ exports[`better eslint`] = { "public/app/plugins/panel/timeseries/plugins/AnnotationsPlugin2.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/plugins/panel/timeseries/plugins/ThresholdDragHandle.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"] - ], "public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationEditor2.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -6565,50 +5203,16 @@ exports[`better eslint`] = { [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "public/app/plugins/panel/traces/TracesPanel.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"] - ], - "public/app/plugins/panel/welcome/Welcome.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"] - ], - "public/app/plugins/panel/xychart/AutoEditor.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"] - ], - "public/app/plugins/panel/xychart/ManualEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "public/app/plugins/panel/xychart/XYChartPanel.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "public/app/plugins/panel/xychart/scatter.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Do not use any type assertions.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"], - [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Unexpected any. Specify a different type.", "9"], - [0, 0, 0, "Do not use any type assertions.", "10"] - ], - "public/app/plugins/panel/xychart/v2/SeriesEditor.tsx:5381": [ + "public/app/plugins/panel/xychart/SeriesEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Do not use any type assertions.", "3"] ], - "public/app/plugins/panel/xychart/v2/migrations.ts:5381": [ + "public/app/plugins/panel/xychart/migrations.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/panel/xychart/v2/scatter.ts:5381": [ + "public/app/plugins/panel/xychart/scatter.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], @@ -6713,7 +5317,8 @@ exports[`better eslint`] = { ], "public/swagger/SwaggerPage.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/swagger/index.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -6764,9 +5369,6 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], - "packages/grafana-ui/src/components/Combobox/Combobox.story.tsx:5381": [ - [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] - ], "packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], @@ -7071,22 +5673,6 @@ exports[`no gf-form usage`] = { [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] ], - "public/app/features/admin/partials/edit_org.html:5381": [ - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] - ], - "public/app/features/admin/partials/styleguide.html:5381": [ - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], - [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] - ], "public/app/features/annotations/partials/event_editor.html:5381": [ [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], @@ -7248,6 +5834,7 @@ exports[`no gf-form usage`] = { [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] ], "public/app/plugins/datasource/loki/configuration/DerivedField.tsx:5381": [ + [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"] diff --git a/.betterer.ts b/.betterer.ts index 305b6c35961..2ebb3f89390 100644 --- a/.betterer.ts +++ b/.betterer.ts @@ -1,8 +1,8 @@ import { BettererFileTest } from '@betterer/betterer'; +import { ESLint } from 'eslint'; import { promises as fs } from 'fs'; -import { ESLint, Linter } from 'eslint'; -import path from 'path'; -import { glob } from 'glob'; + +import config from './.betterer.eslint.config'; // Why are we ignoring these? // They're all deprecated/being removed so doesn't make sense to fix types @@ -82,81 +82,20 @@ function countEslintErrors() { } const { baseDirectory } = resolver; - const cli = new ESLint({ cwd: baseDirectory }); - - // Get the base config to set up parsing etc correctly - // this is by far the slowest part of this code. It takes eslint about 2 seconds just to find the config - const baseConfig = await cli.calculateConfigForFile(filePaths[0]); - - const baseRules: Partial = { - '@emotion/syntax-preference': [2, 'object'], - '@typescript-eslint/no-explicit-any': 'error', - '@grafana/no-aria-label-selectors': 'error', - 'no-restricted-imports': [ - 'error', - { - patterns: [ - { - group: ['@grafana/ui*', '*/Layout/*'], - importNames: ['Layout', 'HorizontalGroup', 'VerticalGroup'], - message: 'Use Stack component instead.', - }, - ], - }, - ], - }; - - const config: Linter.Config = { - ...baseConfig, - rules: baseRules, - - // Be careful when specifying overrides for the same rules as in baseRules - it will... override - // the same rule, not merge them with different configurations - overrides: [ - { - files: ['**/*.{ts,tsx}'], - excludedFiles: ['*.{test,spec}.{ts,tsx}', '**/__mocks__/**', '**/public/test/**'], - rules: { - '@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }], - }, - }, - - { - files: ['public/app/**/*.{ts,tsx}'], - rules: { - 'no-barrel-files/no-barrel-files': 'error', - }, - }, - { - files: ['public/**/*.tsx', 'packages/grafana-ui/**/*.tsx'], - excludedFiles: [ - 'public/app/plugins/**', - '*.story.tsx', - '*.{test,spec}.{ts,tsx}', - '**/__mocks__/**', - 'public/test/**', - ], - rules: { - '@grafana/no-untranslated-strings': 'error', - }, - }, - ], - }; const runner = new ESLint({ - baseConfig: config, - useEslintrc: false, + overrideConfig: config, cwd: baseDirectory, + warnIgnored: false, }); const lintResults = await runner.lintFiles(Array.from(filePaths)); - lintResults - .filter((lintResult) => lintResult.source) - .forEach(({ messages, filePath }) => { - const file = fileTestResult.addFile(filePath, ''); - messages.forEach((message, index) => { - file.addIssue(0, 0, message.message, `${index}`); - }); + + lintResults.forEach(({ messages, filePath }) => { + const file = fileTestResult.addFile(filePath, ''); + messages.forEach((message, index) => { + file.addIssue(0, 0, message.message, `${index}`); }); + }); }); } diff --git a/.drone.star b/.drone.star index 30819f32036..f633ee08e85 100644 --- a/.drone.star +++ b/.drone.star @@ -18,18 +18,10 @@ load( "publish_packages_pipeline", ) load("scripts/drone/events/rrc-patch.star", "rrc_patch_pipelines") -load( - "scripts/drone/pipelines/ci_images.star", - "publish_ci_windows_test_image_pipeline", -) load( "scripts/drone/pipelines/publish_images.star", "publish_image_pipelines_public", ) -load( - "scripts/drone/pipelines/windows.star", - "windows_test_backend", -) load( "scripts/drone/rgm.star", "rgm", @@ -46,12 +38,7 @@ def main(_ctx): publish_npm_pipelines() + publish_packages_pipeline() + rgm() + - [windows_test_backend({ - "event": ["promote"], - "target": ["test-windows"], - }, "oss", "testing")] + integration_test_pipelines() + - publish_ci_windows_test_image_pipeline() + cronjobs() + secrets() ) diff --git a/.drone.yml b/.drone.yml index 14b2beaaf57..055db278c6c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -184,12 +184,27 @@ steps: - yarn-install image: node:20.9.0-alpine name: betterer-frontend +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - is_fork=$(curl --retry 5 "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" + - GITHUB_TOKEN=$(cat /github-app/token) + - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" | jq .head.repo.fork) - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -199,12 +214,14 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - yarn run ci:test-frontend depends_on: @@ -230,6 +247,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -248,12 +267,27 @@ platform: os: linux services: [] steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - is_fork=$(curl --retry 5 "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" + - GITHUB_TOKEN=$(cat /github-app/token) + - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" | jq .head.repo.fork) - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -263,12 +297,14 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - echo $DRONE_RUNNER_NAME image: alpine:3.20.3 @@ -318,6 +354,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -336,12 +374,27 @@ platform: os: linux services: [] steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - is_fork=$(curl --retry 5 "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" + - GITHUB_TOKEN=$(cat /github-app/token) + - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" | jq .head.repo.fork) - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -351,12 +404,14 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - echo $DRONE_RUNNER_NAME image: alpine:3.20.3 @@ -426,6 +481,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -455,12 +512,27 @@ steps: CGO_ENABLED: 0 image: golang:1.23.1-alpine name: compile-build-cmd +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - is_fork=$(curl --retry 5 "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" + - GITHUB_TOKEN=$(cat /github-app/token) + - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" | jq .head.repo.fork) - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -470,12 +542,14 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - apk add --update make - make gen-go @@ -515,6 +589,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -533,13 +609,27 @@ platform: os: linux services: [] steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - echo $DRONE_RUNNER_NAME image: alpine:3.20.3 name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -569,13 +659,6 @@ steps: depends_on: [] image: golang:1.23.1-alpine name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.23.1-alpine - name: wire-install - commands: - yarn install --immutable || yarn install --immutable depends_on: [] @@ -606,9 +689,16 @@ steps: token: from_secret: drone_token - commands: + - docker run --privileged --rm tonistiigi/binfmt --install all - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 - -a targz:grafana:linux/arm/v7 --go-version=1.23.1 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --grafana-dir=$$PWD > packages.txt + -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.1 --yarn-cache=$$YARN_CACHE_FOLDER + --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 + --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ + .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD + > packages.txt + - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i depends_on: - yarn-install environment: @@ -620,6 +710,27 @@ steps: volumes: - name: docker path: /var/run/docker.sock +- commands: + - ./bin/grabpl artifacts docker publish --dockerhub-repo grafana/grafana + depends_on: + - rgm-package + environment: + DOCKER_PASSWORD: + from_secret: docker_password + DOCKER_USER: + from_secret: docker_username + GITHUB_APP_ID: + from_secret: delivery-bot-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: delivery-bot-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: delivery-bot-app-private-key + failure: ignore + image: google/cloud-sdk:431.0.0 + name: publish-images-grafana + volumes: + - name: docker + path: /var/run/docker.sock - commands: - yarn e2e:plugin:build depends_on: @@ -715,10 +826,12 @@ steps: image: cypress/included:13.10.0 name: end-to-end-tests-old-arch/various-suite - commands: + - GITHUB_TOKEN=$(cat /github-app/token) - cd / - ./cpp-e2e/scripts/ci-run.sh azure ${DRONE_SOURCE_BRANCH} depends_on: - grafana-server + - github-app-generate-token environment: AZURE_SP_APP_ID: from_secret: azure_sp_app_id @@ -727,11 +840,12 @@ steps: AZURE_TENANT: from_secret: azure_tenant CYPRESS_CI: "true" - GITHUB_TOKEN: - from_secret: github_token HOST: grafana-server image: us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0 name: end-to-end-tests-cloud-plugins-suite-azure + volumes: + - name: github-app + path: /github-app when: paths: include: @@ -774,6 +888,7 @@ steps: - success - failure - commands: + - GITHUB_TOKEN=$(cat /github-app/token) - if [ ! -d ./playwright-report/trace ]; then echo 'all tests passed'; exit 0; fi - export E2E_PLAYWRIGHT_REPORT_URL=https://storage.googleapis.com/releng-pipeline-artifacts-dev/${DRONE_BUILD_NUMBER}/playwright-report/index.html - 'curl -L -X POST https://api.github.com/repos/grafana/grafana/issues/${DRONE_PULL_REQUEST}/comments @@ -785,17 +900,19 @@ steps: \"}"' depends_on: - playwright-e2e-report-upload - environment: - GITHUB_TOKEN: - from_secret: github_token + - github-app-generate-token failure: ignore image: byrnedo/alpine-curl:0.1.8 name: playwright-e2e-report-post-link + volumes: + - name: github-app + path: /github-app when: status: - success - failure - commands: + - export GITHUB_TOKEN=$(cat /github-app/token) - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'missing videos'; false; fi - apt-get update @@ -815,15 +932,17 @@ steps: - end-to-end-tests-panels-suite - end-to-end-tests-smoke-tests-suite - end-to-end-tests-various-suite + - github-app-generate-token environment: E2E_TEST_ARTIFACTS_BUCKET: releng-pipeline-artifacts-dev GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY: from_secret: gcp_upload_artifacts_key - GITHUB_TOKEN: - from_secret: github_token failure: ignore image: google/cloud-sdk:431.0.0 name: e2e-tests-artifacts-upload + volumes: + - name: github-app + path: /github-app when: status: - success @@ -855,47 +974,6 @@ steps: failure: always image: grafana/docker-puppeteer:1.1.0 name: test-a11y-frontend -- commands: - - docker run --privileged --rm tonistiigi/binfmt --install all - - /src/grafana-build artifacts -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 --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER - --go-version=1.23.1 --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 --tag-format='{{ - .version_base }}-{{ .buildID }}-{{ .arch }}' --grafana-dir=$$PWD --ubuntu-tag-format='{{ - .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' > docker.txt - - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i - depends_on: - - yarn-install - environment: - _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: - from_secret: dagger_token - image: grafana/grafana-build:main - name: rgm-build-docker - pull: always - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/grabpl artifacts docker publish --dockerhub-repo grafana/grafana - depends_on: - - rgm-build-docker - environment: - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USER: - from_secret: docker_username - GITHUB_APP_ID: - from_secret: delivery-bot-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: delivery-bot-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: delivery-bot-app-private-key - failure: ignore - image: google/cloud-sdk:431.0.0 - name: publish-images-grafana - volumes: - - name: docker - path: /var/run/docker.sock trigger: event: - pull_request @@ -909,6 +987,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -962,8 +1042,9 @@ services: path: /var/lib/mysql - commands: - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled + -alertmanager.utf8-strict-mode-enabled environment: {} - image: grafana/mimir-alpine:r304-3872ccb + image: grafana/mimir-alpine:r316-55f47f8 name: mimir_backend - environment: {} image: redis:6.2.11-alpine @@ -972,12 +1053,27 @@ services: image: memcached:1.6.9-alpine name: memcached steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - is_fork=$(curl --retry 5 "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" + - GITHUB_TOKEN=$(cat /github-app/token) + - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" | jq .head.repo.fork) - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -987,15 +1083,17 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1177,6 +1275,8 @@ volumes: - name: mysql80 temp: medium: memory +- name: github-app + temp: {} --- clone: retries: 3 @@ -1306,12 +1406,27 @@ platform: os: linux services: [] steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - is_fork=$(curl --retry 5 "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" + - GITHUB_TOKEN=$(cat /github-app/token) + - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" | jq .head.repo.fork) - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -1321,12 +1436,14 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - apk add --update git make - make swagger-clean && make openapi3-gen @@ -1337,9 +1454,6 @@ steps: && return 1; fi depends_on: - clone-enterprise - environment: - GITHUB_TOKEN: - from_secret: github_token image: golang:1.23.1-alpine name: swagger-gen trigger: @@ -1350,6 +1464,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -1403,8 +1519,9 @@ services: path: /var/lib/mysql - commands: - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled + -alertmanager.utf8-strict-mode-enabled environment: {} - image: grafana/mimir-alpine:r304-3872ccb + image: grafana/mimir-alpine:r316-55f47f8 name: mimir_backend - environment: {} image: redis:6.2.11-alpine @@ -1413,9 +1530,24 @@ services: image: memcached:1.6.9-alpine name: memcached steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - apk add --update curl jq bash - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" + - GITHUB_TOKEN=$(cat /github-app/token) + - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" ../grafana-enterprise - cd ../grafana-enterprise - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; @@ -1425,12 +1557,14 @@ steps: - ln -s src grafana - cd ./grafana-enterprise - ./build.sh - environment: - GITHUB_TOKEN: - from_secret: github_token + depends_on: + - github-app-generate-token failure: ignore image: alpine/git:2.40.1 name: clone-enterprise + volumes: + - name: github-app + path: /github-app - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd depends_on: [] @@ -1527,6 +1661,8 @@ volumes: - name: mysql80 temp: medium: memory +- name: github-app + temp: {} --- clone: retries: 3 @@ -1951,13 +2087,27 @@ platform: os: linux services: [] steps: +- commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app - commands: - echo $DRONE_RUNNER_NAME image: alpine:3.20.3 name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -1987,13 +2137,6 @@ steps: depends_on: [] image: golang:1.23.1-alpine name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.23.1-alpine - name: wire-install - commands: - yarn install --immutable || yarn install --immutable depends_on: [] @@ -2023,9 +2166,16 @@ steps: image: node:20.9.0-alpine name: build-frontend-packages - commands: + - docker run --privileged --rm tonistiigi/binfmt --install all - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 - -a targz:grafana:linux/arm/v7 --go-version=1.23.1 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --grafana-dir=$$PWD > packages.txt + -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.1 --yarn-cache=$$YARN_CACHE_FOLDER + --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 + --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ + .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD + > packages.txt + - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i depends_on: - update-package-json-version environment: @@ -2037,6 +2187,31 @@ steps: volumes: - name: docker path: /var/run/docker.sock +- commands: + - ./bin/grabpl artifacts docker publish --dockerhub-repo grafana/grafana + depends_on: + - rgm-package + environment: + DOCKER_PASSWORD: + from_secret: docker_password + DOCKER_USER: + from_secret: docker_username + GCP_KEY: + from_secret: gcp_grafanauploads + GITHUB_APP_ID: + from_secret: delivery-bot-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: delivery-bot-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: delivery-bot-app-private-key + image: google/cloud-sdk:431.0.0 + name: publish-images-grafana + volumes: + - name: docker + path: /var/run/docker.sock + when: + repo: + - grafana/grafana - commands: - yarn e2e:plugin:build depends_on: @@ -2132,10 +2307,12 @@ steps: image: cypress/included:13.10.0 name: end-to-end-tests-old-arch/various-suite - commands: + - GITHUB_TOKEN=$(cat /github-app/token) - cd / - ./cpp-e2e/scripts/ci-run.sh azure ${DRONE_SOURCE_BRANCH} depends_on: - grafana-server + - github-app-generate-token environment: AZURE_SP_APP_ID: from_secret: azure_sp_app_id @@ -2144,11 +2321,12 @@ steps: AZURE_TENANT: from_secret: azure_tenant CYPRESS_CI: "true" - GITHUB_TOKEN: - from_secret: github_token HOST: grafana-server image: us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0 name: end-to-end-tests-cloud-plugins-suite-azure + volumes: + - name: github-app + path: /github-app when: paths: include: @@ -2191,6 +2369,7 @@ steps: - success - failure - commands: + - GITHUB_TOKEN=$(cat /github-app/token) - if [ ! -d ./playwright-report/trace ]; then echo 'all tests passed'; exit 0; fi - export E2E_PLAYWRIGHT_REPORT_URL=https://storage.googleapis.com/releng-pipeline-artifacts-dev/${DRONE_BUILD_NUMBER}/playwright-report/index.html - 'curl -L -X POST https://api.github.com/repos/grafana/grafana/issues/${DRONE_PULL_REQUEST}/comments @@ -2202,17 +2381,19 @@ steps: \"}"' depends_on: - playwright-e2e-report-upload - environment: - GITHUB_TOKEN: - from_secret: github_token + - github-app-generate-token failure: ignore image: byrnedo/alpine-curl:0.1.8 name: playwright-e2e-report-post-link + volumes: + - name: github-app + path: /github-app when: status: - success - failure - commands: + - export GITHUB_TOKEN=$(cat /github-app/token) - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'missing videos'; false; fi - apt-get update @@ -2232,15 +2413,17 @@ steps: - end-to-end-tests-panels-suite - end-to-end-tests-smoke-tests-suite - end-to-end-tests-various-suite + - github-app-generate-token environment: E2E_TEST_ARTIFACTS_BUCKET: releng-pipeline-artifacts-dev GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY: from_secret: gcp_upload_artifacts_key - GITHUB_TOKEN: - from_secret: github_token failure: ignore image: google/cloud-sdk:431.0.0 name: e2e-tests-artifacts-upload + volumes: + - name: github-app + path: /github-app when: status: - success @@ -2308,55 +2491,10 @@ steps: when: repo: - grafana/grafana -- commands: - - docker run --privileged --rm tonistiigi/binfmt --install all - - /src/grafana-build artifacts -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 --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER - --go-version=1.23.1 --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.3 --tag-format='{{ - .version_base }}-{{ .buildID }}-{{ .arch }}' --grafana-dir=$$PWD --ubuntu-tag-format='{{ - .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' > docker.txt - - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i - depends_on: - - update-package-json-version - environment: - _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: - from_secret: dagger_token - image: grafana/grafana-build:main - name: rgm-build-docker - pull: always - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/grabpl artifacts docker publish --dockerhub-repo grafana/grafana - depends_on: - - rgm-build-docker - environment: - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USER: - from_secret: docker_username - GCP_KEY: - from_secret: gcp_grafanauploads - GITHUB_APP_ID: - from_secret: delivery-bot-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: delivery-bot-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: delivery-bot-app-private-key - image: google/cloud-sdk:431.0.0 - name: publish-images-grafana - volumes: - - name: docker - path: /var/run/docker.sock - when: - repo: - - grafana/grafana - commands: - ./bin/grabpl artifacts docker publish --dockerhub-repo grafana/grafana-oss depends_on: - - rgm-build-docker + - rgm-package environment: DOCKER_PASSWORD: from_secret: docker_password @@ -2379,7 +2517,7 @@ steps: repo: - grafana/grafana - commands: - - apk add --update bash + - apk add --update bash git - ./scripts/publish-npm-packages.sh --dist-tag 'canary' --registry 'https://registry.npmjs.org' depends_on: - end-to-end-tests-dashboards-suite @@ -2401,10 +2539,7 @@ steps: - commands: - ./bin/build upload-packages --edition oss depends_on: - - end-to-end-tests-dashboards-suite - - end-to-end-tests-panels-suite - - end-to-end-tests-smoke-tests-suite - - end-to-end-tests-various-suite + - rgm-package environment: GCP_KEY: from_secret: gcp_grafanauploads_base64 @@ -2418,7 +2553,7 @@ steps: - commands: - ./bin/build upload-cdn --edition oss depends_on: - - grafana-server + - rgm-package environment: GCP_KEY: from_secret: gcp_grafanauploads @@ -2445,6 +2580,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -2498,8 +2635,9 @@ services: path: /var/lib/mysql - commands: - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled + -alertmanager.utf8-strict-mode-enabled environment: {} - image: grafana/mimir-alpine:r304-3872ccb + image: grafana/mimir-alpine:r316-55f47f8 name: mimir_backend - environment: {} image: redis:6.2.11-alpine @@ -2510,7 +2648,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -2692,53 +2830,6 @@ volumes: clone: retries: 3 depends_on: -- main-test-frontend -- main-test-backend -- main-build-e2e-publish -- main-integration-tests -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: main-windows -platform: - arch: amd64 - os: windows - version: "1809" -services: [] -steps: -- commands: - - echo $env:DRONE_RUNNER_NAME - image: mcr.microsoft.com/windows:1809 - name: identify-runner -- commands: - - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/windows/grabpl.exe - -OutFile grabpl.exe - image: grafana/ci-wix:0.1.1 - name: windows-init -trigger: - branch: main - event: - - push - paths: - exclude: - - '*.md' - - docs/** - - latest.json - repo: - - grafana/grafana -type: docker -volumes: -- host: - path: //./pipe/docker_engine/ - name: docker ---- -clone: - retries: 3 -depends_on: - main-build-e2e-publish - main-integration-tests environment: @@ -2790,7 +2881,6 @@ depends_on: - main-test-backend - main-build-e2e-publish - main-integration-tests -- main-windows kind: pipeline name: main-notify platform: @@ -3130,8 +3220,9 @@ services: path: /var/lib/mysql - commands: - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled + -alertmanager.utf8-strict-mode-enabled environment: {} - image: grafana/mimir-alpine:r304-3872ccb + image: grafana/mimir-alpine:r316-55f47f8 name: mimir_backend - environment: {} image: redis:6.2.11-alpine @@ -3142,7 +3233,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3387,7 +3478,7 @@ steps: name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3519,7 +3610,7 @@ steps: name: identify-runner - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -3621,6 +3712,7 @@ platform: services: [] steps: - commands: + - export GITHUB_TOKEN=$(cat /github-app/token) - apk add perl - v_target=`echo $${TAG} | perl -pe 's/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/v\1.\2.x/'` - curl -L $${GH_CLI_URL} | tar -xz --strip-components=1 -C /usr @@ -3629,10 +3721,11 @@ steps: depends_on: [] environment: GH_CLI_URL: https://github.com/cli/cli/releases/download/v2.50.0/gh_2.50.0_linux_amd64.tar.gz - GITHUB_TOKEN: - from_secret: github_token image: byrnedo/alpine-curl:0.1.8 name: create-release-pr + volumes: + - name: github-app + path: /github-app trigger: event: - promote @@ -3642,6 +3735,8 @@ volumes: - host: path: /var/run/docker.sock name: docker +- name: github-app + temp: {} --- clone: retries: 3 @@ -3691,6 +3786,21 @@ steps: image: grafana/grafana-ci-deploy:1.3.3 name: publish-storybook - commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + path: /github-app +- commands: + - export GITHUB_TOKEN=$(cat /github-app/token) - apk add perl - v_target=`echo $${TAG} | perl -pe 's/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/v\1.\2.x/'` - curl -L $${GH_CLI_URL} | tar -xz --strip-components=1 -C /usr @@ -3698,12 +3808,14 @@ steps: -f latest=$${LATEST} --repo=grafana/grafana release-pr.yml depends_on: - publish-artifacts + - github-app-generate-token environment: GH_CLI_URL: https://github.com/cli/cli/releases/download/v2.50.0/gh_2.50.0_linux_amd64.tar.gz - GITHUB_TOKEN: - from_secret: github_token image: byrnedo/alpine-curl:0.1.8 name: create-release-pr + volumes: + - name: github-app + path: /github-app trigger: event: - promote @@ -4205,6 +4317,7 @@ services: [] steps: - commands: - export GRAFANA_DIR=$$(pwd) + - export GITHUB_TOKEN=$(cat /github-app/token) - cd /src && ./scripts/drone_build_main.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: @@ -4224,8 +4337,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase @@ -4244,6 +4355,8 @@ steps: volumes: - name: docker path: /var/run/docker.sock + - name: github-app + path: /github-app trigger: branch: main event: @@ -4265,6 +4378,78 @@ volumes: clone: retries: 3 depends_on: [] +image_pull_secrets: +- gcr +- gar +kind: pipeline +name: rgm-tag-prerelease +node: + type: no-parallel +platform: + arch: amd64 + os: linux +services: [] +steps: +- commands: + - export GRAFANA_DIR=$$(pwd) + - export GITHUB_TOKEN=$(cat /github-app/token) + - cd /src && ./scripts/drone_build_tag_grafana.sh + environment: + _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: + from_secret: dagger_token + ALPINE_BASE: alpine:3.20.3 + CDN_DESTINATION: + from_secret: rgm_cdn_destination + DESTINATION: + from_secret: destination + DOCKER_PASSWORD: + from_secret: docker_password + DOCKER_USERNAME: + from_secret: docker_username + DOWNLOADS_DESTINATION: + from_secret: rgm_downloads_destination + GCOM_API_KEY: + from_secret: grafana_api_key + GCP_KEY_BASE64: + from_secret: gcp_key_base64 + GO_VERSION: 1.23.1 + GPG_PASSPHRASE: + from_secret: packages_gpg_passphrase + GPG_PRIVATE_KEY: + from_secret: packages_gpg_private_key + GPG_PUBLIC_KEY: + from_secret: packages_gpg_public_key + NPM_TOKEN: + from_secret: npm_token + STORYBOOK_DESTINATION: + from_secret: rgm_storybook_destination + UBUNTU_BASE: ubuntu:22.04 + image: grafana/grafana-build:main + name: rgm-build + pull: always + volumes: + - name: docker + path: /var/run/docker.sock + - name: github-app + path: /github-app +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: [] environment: EDITION: oss image_pull_secrets: @@ -4307,77 +4492,6 @@ volumes: path: /var/run/docker.sock name: docker --- -clone: - retries: 3 -depends_on: [] -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: rgm-tag-prerelease -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - export GRAFANA_DIR=$$(pwd) - - cd /src && ./scripts/drone_build_tag_grafana.sh - environment: - _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: - from_secret: dagger_token - ALPINE_BASE: alpine:3.20.3 - CDN_DESTINATION: - from_secret: rgm_cdn_destination - DESTINATION: - from_secret: destination - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USERNAME: - from_secret: docker_username - DOWNLOADS_DESTINATION: - from_secret: rgm_downloads_destination - GCOM_API_KEY: - from_secret: grafana_api_key - GCP_KEY_BASE64: - from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token - GO_VERSION: 1.23.1 - GPG_PASSPHRASE: - from_secret: packages_gpg_passphrase - GPG_PRIVATE_KEY: - from_secret: packages_gpg_private_key - GPG_PUBLIC_KEY: - from_secret: packages_gpg_public_key - NPM_TOKEN: - from_secret: npm_token - STORYBOOK_DESTINATION: - from_secret: rgm_storybook_destination - UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main - name: rgm-build - pull: always - volumes: - - name: docker - path: /var/run/docker.sock -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: @@ -4386,71 +4500,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: rgm-tag-prerelease-windows -platform: - arch: amd64 - os: windows - version: "1809" -services: [] -steps: -- commands: - - echo $env:DRONE_RUNNER_NAME - image: mcr.microsoft.com/windows:1809 - name: identify-runner -- commands: - - $$ProgressPreference = "SilentlyContinue" - - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/windows/grabpl.exe - -OutFile grabpl.exe - image: grafana/ci-wix:0.1.1 - name: windows-init -- commands: - - $$gcpKey = $$env:GCP_KEY - - '[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($$gcpKey)) - > gcpkey.json' - - dos2unix gcpkey.json - - gcloud auth activate-service-account --key-file=gcpkey.json - - rm gcpkey.json - - cp C:\App\nssm-2.24.zip . - - .\grabpl.exe windows-installer --target gs://grafana-prerelease/artifacts/downloads/${DRONE_TAG}/oss/release/grafana-${DRONE_TAG:1}.windows-amd64.zip - --edition oss ${DRONE_TAG} - - $$fname = ((Get-Childitem grafana*.msi -name) -split "`n")[0] - - gsutil cp $$fname gs://grafana-prerelease/artifacts/downloads/${DRONE_TAG}/oss/release/ - - gsutil cp "$$fname.sha256" gs://grafana-prerelease/artifacts/downloads/${DRONE_TAG}/oss/release/ - depends_on: - - windows-init - environment: - GCP_KEY: - from_secret: gcp_grafanauploads_base64 - GITHUB_TOKEN: - from_secret: github_token - PRERELEASE_BUCKET: - from_secret: prerelease_bucket - image: grafana/ci-wix:0.1.1 - name: build-windows-installer -trigger: - event: - exclude: - - promote - ref: - exclude: - - refs/tags/*-cloud* - include: - - refs/tags/v* -type: docker -volumes: -- host: - path: //./pipe/docker_engine/ - name: docker ---- -clone: - retries: 3 -depends_on: -- rgm-tag-prerelease -- rgm-tag-prerelease-windows -image_pull_secrets: -- gcr -- gar -kind: pipeline name: rgm-tag-verify-prerelease-assets node: type: no-parallel @@ -4505,6 +4554,7 @@ services: [] steps: - commands: - export GRAFANA_DIR=$$(pwd) + - export GITHUB_TOKEN=$(cat /github-app/token) - cd /src && ./scripts/drone_build_tag_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: @@ -4524,8 +4574,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase @@ -4544,6 +4592,8 @@ steps: volumes: - name: docker path: /var/run/docker.sock + - name: github-app + path: /github-app trigger: ref: - refs/heads/v[0-9]* @@ -4741,6 +4791,7 @@ services: [] steps: - commands: - export GRAFANA_DIR=$$(pwd) + - export GITHUB_TOKEN=$(cat /github-app/token) - cd /src && ./scripts/drone_build_nightly_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: @@ -4760,8 +4811,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase @@ -4780,6 +4829,8 @@ steps: volumes: - name: docker path: /var/run/docker.sock + - name: github-app + path: /github-app - commands: - mkdir -p $${DESTINATION}/$${DRONE_BUILD_EVENT} - printenv GCP_KEY_BASE64 | base64 -d > /tmp/key.json @@ -4804,8 +4855,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4870,8 +4919,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4886,6 +4933,7 @@ steps: name: rgm-copy - commands: - export GRAFANA_DIR=$$(pwd) + - export GITHUB_TOKEN=$(cat /github-app/token) - cd /src && ./scripts/drone_publish_nightly_grafana.sh depends_on: - rgm-copy @@ -4907,8 +4955,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase @@ -4927,6 +4973,8 @@ steps: volumes: - name: docker path: /var/run/docker.sock + - name: github-app + path: /github-app - depends_on: - rgm-publish image: us.gcr.io/kubernetes-dev/package-publish:latest @@ -4996,9 +5044,26 @@ platform: services: [] steps: - commands: + - echo $(/usr/bin/github-app-external-token) > /github-app/token + environment: + GITHUB_APP_ID: + from_secret: github-app-app-id + GITHUB_APP_INSTALLATION_ID: + from_secret: github-app-installation-id + GITHUB_APP_PRIVATE_KEY: + from_secret: github-app-private-key + image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 + name: github-app-generate-token + volumes: + - name: github-app + 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.1 + depends_on: + - github-app-generate-token environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -5017,8 +5082,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase @@ -5037,6 +5100,8 @@ steps: volumes: - name: docker path: /var/run/docker.sock + - name: github-app + path: /github-app - commands: - printenv GCP_KEY_BASE64 | base64 -d > /tmp/key.json - gcloud auth activate-service-account --key-file=/tmp/key.json @@ -5058,8 +5123,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GITHUB_TOKEN: - from_secret: github_token GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -5082,59 +5145,6 @@ volumes: path: /var/run/docker.sock name: docker --- -clone: - disable: true -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: testing-test-backend-windows -platform: - arch: amd64 - os: windows - version: "1809" -services: [] -steps: -- commands: - - git clone "https://$$env:GITHUB_TOKEN@github.com/$$env:DRONE_REPO.git" . - - git checkout -f $$env:DRONE_COMMIT - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/ci-wix:0.1.1 - name: clone -- commands: [] - depends_on: - - clone - image: golang:1.23.1-windowsservercore-1809 - name: windows-init -- commands: - - go install github.com/google/wire/cmd/wire@v0.5.0 - - wire gen -tags oss ./pkg/server - depends_on: - - windows-init - image: golang:1.23.1-windowsservercore-1809 - name: wire-install -- commands: - - go test -short -covermode=atomic -timeout=5m ./pkg/... - depends_on: - - wire-install - image: golang:1.23.1-windowsservercore-1809 - name: test-backend -trigger: - event: - - promote - target: - - test-windows -type: docker -volumes: -- host: - path: //./pipe/docker_engine/ - name: docker ---- clone: retries: 3 depends_on: [] @@ -5187,8 +5197,9 @@ services: path: /var/lib/mysql - commands: - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled + -alertmanager.utf8-strict-mode-enabled environment: {} - image: grafana/mimir-alpine:r304-3872ccb + image: grafana/mimir-alpine:r316-55f47f8 name: mimir_backend - environment: {} image: redis:6.2.11-alpine @@ -5199,7 +5210,7 @@ services: steps: - commands: - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.56/grabpl + - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.1/grabpl - chmod +x bin/grabpl image: byrnedo/alpine-curl:0.1.8 name: grabpl @@ -5364,55 +5375,6 @@ volumes: temp: medium: memory --- -clone: - disable: true -depends_on: [] -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: publish-ci-windows-test-image -platform: - arch: amd64 - os: windows - version: "1809" -services: [] -steps: -- commands: - - git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-ci-sandbox.git" - . - - git checkout -f $$env:DRONE_COMMIT - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/ci-wix:0.1.1 - name: clone -- commands: - - cd scripts\build\ci-windows-test - - docker login -u $$env:DOCKER_USERNAME -p $$env:DOCKER_PASSWORD - - docker build -t grafana/grafana-ci-windows-test:$$env:TAG . - - docker push grafana/grafana-ci-windows-test:$$env:TAG - environment: - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USERNAME: - from_secret: docker_username - image: docker:windowsservercore-1809 - name: build-and-publish - volumes: - - name: docker - path: //./pipe/docker_engine/ -trigger: - event: - - promote - target: - - ci-windows-test-image -type: docker -volumes: -- host: - path: //./pipe/docker_engine/ - name: docker ---- clone: retries: 3 kind: pipeline @@ -5709,7 +5671,7 @@ steps: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM python:3.8 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM postgres:12.3-alpine - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/mimir-alpine:r304-3872ccb + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/mimir-alpine:r316-55f47f8 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:5.7.39 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:8.0.32 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM redis:6.2.11-alpine @@ -5723,6 +5685,8 @@ steps: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM jwilder/dockerize:0.6.1 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM koalaman/shellcheck:stable - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM rockylinux:9 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM scottyhardy/docker-wine:stable-9.0 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 depends_on: - authenticate-gcr image: aquasec/trivy:0.21.0 @@ -5746,7 +5710,7 @@ steps: - trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack - trivy --exit-code 1 --severity HIGH,CRITICAL python:3.8 - trivy --exit-code 1 --severity HIGH,CRITICAL postgres:12.3-alpine - - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/mimir-alpine:r304-3872ccb + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/mimir-alpine:r316-55f47f8 - trivy --exit-code 1 --severity HIGH,CRITICAL mysql:5.7.39 - trivy --exit-code 1 --severity HIGH,CRITICAL mysql:8.0.32 - trivy --exit-code 1 --severity HIGH,CRITICAL redis:6.2.11-alpine @@ -5760,6 +5724,8 @@ steps: - trivy --exit-code 1 --severity HIGH,CRITICAL jwilder/dockerize:0.6.1 - trivy --exit-code 1 --severity HIGH,CRITICAL koalaman/shellcheck:stable - trivy --exit-code 1 --severity HIGH,CRITICAL rockylinux:9 + - trivy --exit-code 1 --severity HIGH,CRITICAL scottyhardy/docker-wine:stable-9.0 + - trivy --exit-code 1 --severity HIGH,CRITICAL us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 depends_on: - authenticate-gcr environment: @@ -5792,6 +5758,24 @@ volumes: - name: config temp: {} --- +get: + name: app-id + path: ci/data/repo/grafana/grafana/github-app +kind: secret +name: github-app-app-id +--- +get: + name: app-installation-id + path: ci/data/repo/grafana/grafana/github-app +kind: secret +name: github-app-installation-id +--- +get: + name: private-key + path: ci/data/repo/grafana/grafana/github-app +kind: secret +name: github-app-private-key +--- get: name: credentials.json path: infra/data/ci/grafana-release-eng/grafanauploads @@ -5822,12 +5806,6 @@ get: kind: secret name: gar --- -get: - name: pat - path: ci/data/repo/grafana/grafana/grafanabot -kind: secret -name: github_token ---- get: name: machine-user-token path: infra/data/ci/drone @@ -5991,6 +5969,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: 41df5b1fdbd1b3c9aa915919ae5be16d2a188cbaf6b243c14fd66f94db0db8d8 +hmac: b164dd562488c482f2e670688fca02ae20dcd2e7841ec80c95838472842b4b5e ... diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 1b83fc33e02..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,27 +0,0 @@ -.git -.github -.yarn -build -compiled -/data -deployment_tools_config.json -/devenv -dist -/e2e/tmp -node_modules -/pkg -/public/lib/monaco -/scripts/grafana-server/tmp -vendor -e2e/test-plugins -playwright-report - -# TS generate from cue by cuetsy -**/*.gen.ts - -# Auto-generated internationalization files -/public/locales/_build/ -/public/locales/**/*.js - -# Auto-generated icon file -/packages/grafana-ui/src/components/Icon/iconBundle.ts diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index b34a9c242e0..00000000000 --- a/.eslintrc +++ /dev/null @@ -1,162 +0,0 @@ -{ - "extends": ["@grafana/eslint-config", "plugin:react/jsx-runtime"], - "root": true, - "plugins": [ - "@emotion", - "lodash", - "jest", - "import", - "jsx-a11y", - "@grafana", - "no-barrel-files", - // Included so betterer doesn't fail when processing all files, - // as other parts of the code use testing-library plugin - "testing-library", - ], - "settings": { - "import/internal-regex": "^(app/)|(@grafana)", - "import/external-module-folders": ["node_modules", ".yarn"], - }, - "rules": { - "@grafana/no-border-radius-literal": "error", - "@grafana/no-unreduced-motion": "error", - "react/prop-types": "off", - // need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options - "react/no-unknown-property": ["error", { "ignore": ["css"] }], - "@emotion/jsx-import": "error", - "lodash/import-scope": [2, "member"], - "jest/no-focused-tests": "error", - "import/order": [ - "error", - { - "groups": [["builtin", "external"], "internal", "parent", "sibling", "index"], - "newlines-between": "always", - "alphabetize": { "order": "asc" }, - }, - ], - "no-restricted-imports": [ - "error", - { - "paths": [ - { - "name": "react-redux", - "importNames": ["useDispatch", "useSelector"], - "message": "Please import from app/types instead.", - }, - { - "name": "react-i18next", - "importNames": ["Trans", "t"], - "message": "Please import from app/core/internationalization instead", - }, - ], - }, - ], - - // Use typescript's no-redeclare for compatibility with overrides - "no-redeclare": "off", - "@typescript-eslint/no-redeclare": ["error"], - }, - "overrides": [ - { - "files": ["packages/grafana-ui/src/components/uPlot/**/*.{ts,tsx}"], - "rules": { - "react-hooks/rules-of-hooks": "off", - "react-hooks/exhaustive-deps": "off", - }, - }, - { - "files": ["packages/grafana-ui/src/components/ThemeDemos/**/*.{ts,tsx}"], - "rules": { - "@emotion/jsx-import": "off", - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - }, - }, - { - "files": ["public/dashboards/scripted*.js"], - "rules": { - "no-redeclare": "error", - "@typescript-eslint/no-redeclare": "off", - }, - }, - { - "extends": ["plugin:jsx-a11y/recommended"], - "files": ["**/*.tsx"], - "excludedFiles": ["**/*.{spec,test}.tsx"], - "rules": { - // rules marked "off" are those left in the recommended preset we need to fix - // we should remove the corresponding line and fix them one by one - // any marked "error" contain specific overrides we'll need to keep - "jsx-a11y/no-autofocus": [ - "error", - { - "ignoreNonDOM": true, - }, - ], - "jsx-a11y/label-has-associated-control": [ - "error", - { - "controlComponents": ["NumberInput"], - "depth": 2, - }, - ], - }, - }, - { - "files": [ - "public/app/plugins/datasource/azuremonitor/*.{ts,tsx}", - "public/app/plugins/datasource/azuremonitor/**/*.{ts,tsx}", - "public/app/plugins/datasource/cloud-monitoring/*.{ts,tsx}", - "public/app/plugins/datasource/cloud-monitoring/**/*.{ts,tsx}", - "public/app/plugins/datasource/elasticsearch/*.{ts,tsx}", - "public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}", - "public/app/plugins/datasource/grafana-postgresql-datasource/*.{ts,tsx}", - "public/app/plugins/datasource/grafana-postgresql-datasource/**/*.{ts,tsx}", - "public/app/plugins/datasource/grafana-pyroscope-datasource/*.{ts,tsx}", - "public/app/plugins/datasource/grafana-pyroscope-datasource/**/*.{ts,tsx}", - "public/app/plugins/datasource/grafana-testdata-datasource/*.{ts,tsx}", - "public/app/plugins/datasource/grafana-testdata-datasource/**/*.{ts,tsx}", - "public/app/plugins/datasource/jaeger/*.{ts,tsx}", - "public/app/plugins/datasource/jaeger/**/*.{ts,tsx}", - "public/app/plugins/datasource/loki/*.{ts,tsx}", - "public/app/plugins/datasource/loki/**/*.{ts,tsx}", - "public/app/plugins/datasource/mysql/*.{ts,tsx}", - "public/app/plugins/datasource/mysql/**/*.{ts,tsx}", - "public/app/plugins/datasource/parca/*.{ts,tsx}", - "public/app/plugins/datasource/parca/**/*.{ts,tsx}", - "public/app/plugins/datasource/tempo/*.{ts,tsx}", - "public/app/plugins/datasource/tempo/**/*.{ts,tsx}", - "public/app/plugins/datasource/loki/*.{ts,tsx}", - "public/app/plugins/datasource/loki/**/*.{ts,tsx}", - "public/app/plugins/datasource/elasticsearch/*.{ts,tsx}", - "public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}", - "public/app/plugins/datasource/cloudwatch/*.{ts,tsx}", - "public/app/plugins/datasource/cloudwatch/**/*.{ts,tsx}", - "public/app/plugins/datasource/zipkin/*.{ts,tsx}", - "public/app/plugins/datasource/zipkin/**/*.{ts,tsx}", - ], - "settings": { - "import/resolver": { - "node": { - "extensions": [".ts", ".tsx"], - }, - }, - }, - "rules": { - "import/no-restricted-paths": [ - "error", - { - "zones": [ - { - "target": "./public/app/plugins", - "from": "./public", - "except": ["./app/plugins"], - "message": "Core plugins are not allowed to depend on Grafana core packages", - }, - ], - }, - ], - }, - }, - ], -} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bfecf44889b..e4542d59375 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,8 +12,8 @@ # This should make it easy to add new rules without breaking existing ones. # Documentation -/.changelog-archive @grafana/grafana-release-guild -/CHANGELOG.md @grafana/grafana-release-guild +/.changelog-archive @grafana/grafana-developer-enablement-squad +/CHANGELOG.md @grafana/grafana-developer-enablement-squad /CODE_OF_CONDUCT.md @grafana/grafana-community-support /CONTRIBUTING.md @grafana/grafana-community-support /GOVERNANCE.md @RichiH @@ -38,17 +38,11 @@ /docs/.codespellignore @grafana/docs-tooling /docs/sources/ @irenerl24 -/docs/sources/administration/ @jdbaldry /docs/sources/alerting/ @brendamuir /docs/sources/dashboards/ @imatwawana -/docs/sources/datasources/ @jdbaldry -/docs/sources/explore/ @grafana/explore-squad @lwandz13 -/docs/sources/fundamentals @irenerl24 -/docs/sources/getting-started/ @irenerl24 -/docs/sources/introduction/ @irenerl24 +/docs/sources/datasources/ @lwandz13 /docs/sources/panels-visualizations/ @imatwawana -/docs/sources/release-notes/ @Eve832 @GrafanaWriter -/docs/sources/setup-grafana/ @irenerl24 +/docs/sources/release-notes/ @irenerl24 @GrafanaWriter /docs/sources/upgrade-guide/ @imatwawana /docs/sources/whatsnew/ @imatwawana @@ -78,6 +72,7 @@ /pkg/apis/ @grafana/grafana-app-platform-squad /pkg/apis/alerting_notifications @grafana/grafana-app-platform-squad @grafana/alerting-backend @grafana/alerting-frontend /pkg/apis/query @grafana/grafana-datasources-core-services +/pkg/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend /pkg/bus/ @grafana/grafana-search-and-storage /pkg/cmd/ @grafana/grafana-backend-group /pkg/cmd/grafana-cli/commands/install_command.go @grafana/plugins-platform-backend @@ -127,7 +122,7 @@ /pkg/services/apikey/ @grafana/identity-squad /pkg/services/cleanup/ @grafana/grafana-backend-group /pkg/services/contexthandler/ @grafana/grafana-backend-group @grafana/grafana-app-platform-squad -/pkg/services/correlations/ @grafana/explore-squad +/pkg/services/correlations/ @grafana/dataviz-squad /pkg/services/dashboardimport/ @grafana/grafana-backend-group /pkg/services/dashboards/ @grafana/grafana-app-platform-squad /pkg/services/dashboardversion/ @grafana/grafana-backend-group @@ -145,7 +140,7 @@ /pkg/services/provisioning/ @grafana/grafana-search-and-storage /pkg/services/provisioning/alerting/ @grafana/alerting-backend /pkg/services/query/ @grafana/grafana-app-platform-squad -/pkg/services/queryhistory/ @grafana/explore-squad +/pkg/services/queryhistory/ @grafana/observability-traces-and-profiling /pkg/services/quota/ @grafana/grafana-search-and-storage /pkg/services/screenshot/ @grafana/grafana-backend-group /pkg/services/search/ @grafana/grafana-search-and-storage @@ -165,8 +160,9 @@ /pkg/setting/ @grafana/grafana-backend-services-squad /pkg/tests/ @grafana/grafana-backend-services-squad /pkg/tests/apis/ @grafana/grafana-app-platform-squad +/pkg/tests/apis/query @grafana/grafana-datasources-core-services /pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend -/pkg/tests/api/correlations/ @grafana/explore-squad +/pkg/tests/api/correlations/ @grafana/dataviz-squad /pkg/tsdb/grafanads/ @grafana/grafana-backend-group /pkg/tsdb/opentsdb/ @grafana/partner-datasources /pkg/util/ @grafana/grafana-backend-group @@ -194,6 +190,7 @@ /devenv/dev-dashboards-without-uid/ @grafana/dashboards-squad /devenv/dev-dashboards/ @grafana/dashboards-squad /devenv/docker/blocks/alert_webhook_listener/ @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 /devenv/docker/blocks/etcd @grafana/grafana-app-platform-squad @@ -240,6 +237,7 @@ /devenv/docker/loadtest/ @grafana/grafana-backend-services-squad /devenv/docker/rpmtest/ @grafana/grafana-backend-services-squad /devenv/jsonnet/ @grafana/dataviz-squad +/devenv/local_cdn/ @grafana/frontend-ops /devenv/local-npm/ @grafana/frontend-ops /devenv/setup.sh @grafana/grafana-backend-services-squad /devenv/plugins.yaml @grafana/plugins-platform-frontend @@ -252,15 +250,15 @@ # Continuous Integration -.drone.yml @grafana/grafana-release-guild -.drone.star @grafana/grafana-release-guild -/scripts/drone/ @grafana/grafana-release-guild -/pkg/build/ @grafana/grafana-release-guild -/.dockerignore @grafana/grafana-release-guild -/Dockerfile @grafana/grafana-release-guild -/Makefile @grafana/grafana-release-guild -/scripts/build/ @grafana/grafana-release-guild -/scripts/list-release-artifacts.sh @grafana/grafana-release-guild +.drone.yml @grafana/grafana-developer-enablement-squad +.drone.star @grafana/grafana-developer-enablement-squad +/scripts/drone/ @grafana/grafana-developer-enablement-squad +/pkg/build/ @grafana/grafana-developer-enablement-squad +/.dockerignore @grafana/grafana-developer-enablement-squad +/Dockerfile @grafana/grafana-developer-enablement-squad +/Makefile @grafana/grafana-developer-enablement-squad +/scripts/build/ @grafana/grafana-developer-enablement-squad +/scripts/list-release-artifacts.sh @grafana/grafana-developer-enablement-squad /.trivyignore @grafana/grafana-backend-services-squad # OSS Plugin Partnerships backend code @@ -279,6 +277,8 @@ # OSS Big Tent backend code /pkg/tsdb/mysql/ @grafana/oss-big-tent /pkg/tsdb/grafana-postgresql-datasource/ @grafana/oss-big-tent +/pkg/tsdb/zipkin/ @grafana/oss-big-tent +/pkg/tsdb/jaeger/ @grafana/oss-big-tent # Partner Datasources backend code /pkg/tsdb/mssql/ @grafana/partner-datasources @@ -385,7 +385,8 @@ /.nxignore @grafana/frontend-ops /tsconfig.json @grafana/frontend-ops /.editorconfig @grafana/frontend-ops -/.eslintignore @grafana/frontend-ops +/eslint.config.js @grafana/frontend-ops +/.betterer.eslint.config.js @grafana/frontend-ops /.gitattributes @grafana/frontend-ops /.gitignore @grafana/frontend-ops /.nvmrc @grafana/frontend-ops @@ -395,7 +396,6 @@ /yarn.lock @grafana/frontend-ops /lerna.json @grafana/frontend-ops /.prettierrc.js @grafana/frontend-ops -/.eslintrc @grafana/frontend-ops /.vim @zoltanbedi /jest.config.js @grafana/frontend-ops /latest.json @grafana/frontend-ops @@ -419,7 +419,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad -/public/app/core/history/ @grafana/explore-squad +/public/app/core/history/ @grafana/observability-traces-and-profiling /public/app/features/admin/ @grafana/identity-access-team # Temp owners until Enterprise team takes over @@ -434,7 +434,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/visualization/data-hover/ @grafana/dataviz-squad /public/app/features/commandPalette/ @grafana/grafana-frontend-platform /public/app/features/connections/ @grafana/plugins-platform-frontend -/public/app/features/correlations/ @grafana/explore-squad +/public/app/features/correlations/ @grafana/dataviz-squad /public/app/features/dashboard/ @grafana/dashboards-squad /public/app/features/dashboard/components/TransformationsEditor/ @grafana/dataviz-squad /public/app/features/dashboard-scene/ @grafana/dashboards-squad @@ -442,7 +442,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/datasources/ @grafana/plugins-platform-frontend /public/app/features/dimensions/ @grafana/dataviz-squad /public/app/features/dataframe-import/ @grafana/dataviz-squad -/public/app/features/explore/ @grafana/explore-squad +/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/inspector/ @grafana/dashboards-squad @@ -458,14 +458,13 @@ 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/explore-squad +/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 /public/app/features/browse-dashboards/ @grafana/grafana-frontend-platform /public/app/features/search/ @grafana/grafana-frontend-platform /public/app/features/serviceaccounts/ @grafana/identity-squad -/public/app/features/storage/ @grafana/grafana-app-platform-squad /public/app/features/teams/ @grafana/access-squad /public/app/features/templating/ @grafana/dashboards-squad /public/app/features/trails/ @grafana/observability-metrics @@ -474,6 +473,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/users/ @grafana/access-squad /public/app/features/variables/ @grafana/dashboards-squad /public/app/features/preferences/ @grafana/grafana-frontend-platform +/public/app/features/bookmarks/ @grafana/grafana-frontend-platform /public/app/plugins/panel/alertlist/ @grafana/alerting-frontend /public/app/plugins/panel/annolist/ @grafana/grafana-frontend-platform /public/app/plugins/panel/barchart/ @grafana/dataviz-squad @@ -552,27 +552,27 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/benchmark-access-control.sh @grafana/access-squad /scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend -/scripts/ci-* @grafana/grafana-release-guild -/scripts/circle-* @grafana/grafana-release-guild -/scripts/publish-npm-packages.sh @grafana/grafana-release-guild @grafana/plugins-platform-frontend -/scripts/validate-npm-packages.sh @grafana/grafana-release-guild @grafana/plugins-platform-frontend +/scripts/ci-* @grafana/grafana-developer-enablement-squad +/scripts/circle-* @grafana/grafana-developer-enablement-squad +/scripts/publish-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend +/scripts/validate-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend /scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad /scripts/cli/ @grafana/grafana-frontend-platform /scripts/clean-git-or-error.sh @grafana/grafana-as-code /scripts/grafana-server/ @grafana/grafana-frontend-platform -/scripts/helpers/ @grafana/grafana-release-guild +/scripts/helpers/ @grafana/grafana-developer-enablement-squad /scripts/import_many_dashboards.sh @torkelo /scripts/mixin-check.sh @bergquist /scripts/openapi3/ @grafana/grafana-operator-experience-squad /scripts/prepare-packagejson.js @grafana/frontend-ops /scripts/protobuf-check.sh @grafana/plugins-platform-backend /scripts/stripnulls.sh @grafana/grafana-as-code -/scripts/tag_release.sh @grafana/grafana-release-guild -/scripts/trigger_docker_build.sh @grafana/grafana-release-guild -/scripts/trigger_grafana_packer.sh @grafana/grafana-release-guild -/scripts/trigger_windows_build.sh @grafana/grafana-release-guild +/scripts/tag_release.sh @grafana/grafana-developer-enablement-squad +/scripts/trigger_docker_build.sh @grafana/grafana-developer-enablement-squad +/scripts/trigger_grafana_packer.sh @grafana/grafana-developer-enablement-squad +/scripts/trigger_windows_build.sh @grafana/grafana-developer-enablement-squad /scripts/cleanup-husky.sh @grafana/frontend-ops -/scripts/verify-repo-update/ @grafana/grafana-release-guild +/scripts/verify-repo-update/ @grafana/grafana-developer-enablement-squad /scripts/generate-icon-bundle.js @grafana/plugins-platform-frontend @grafana/grafana-frontend-platform /scripts/generate-rtk-apis.ts @grafana/grafana-frontend-platform /scripts/generate-alerting-rtk-apis.ts @grafana/alerting-frontend @@ -631,7 +631,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /pkg/services/rendering/ @grafana/sharing-squad # SSE - Server Side Expressions -/pkg/expr/ @grafana/observability-metrics +/pkg/expr/ @grafana/grafana-datasources-core-services # Cloud middleware /grafana-mixin/ @grafana/grafana-backend-services-squad @@ -663,6 +663,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /pkg/services/caching/ @grafana/grafana-operator-experience-squad /pkg/services/cloudmigration/ @grafana/grafana-operator-experience-squad /pkg/services/gcom/ @grafana/grafana-operator-experience-squad +/pkg/services/authapi/ @grafana/grafana-operator-experience-squad # Feature toggles /pkg/services/featuremgmt/ @grafana/grafana-backend-services-squad @@ -671,6 +672,7 @@ playwright.config.ts @grafana/plugins-platform-frontend # Kind definitions /kinds/dashboard @grafana/dashboards-squad /kinds/ @grafana/grafana-as-code +kindsv2/ @grafana/dashboards-squad # Kind system and code generation embed.go @grafana/grafana-as-code @@ -679,6 +681,7 @@ embed.go @grafana/grafana-as-code /pkg/registry/apis/ @grafana/grafana-app-platform-squad /pkg/registry/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend /pkg/registry/apis/query @grafana/grafana-datasources-core-services +/pkg/registry/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend /pkg/codegen/ @grafana/grafana-as-code /pkg/codegen/generators @grafana/grafana-as-code /pkg/kinds/*/*_gen.go @grafana/grafana-as-code @@ -695,43 +698,43 @@ embed.go @grafana/grafana-as-code /.github/dependabot.yml @grafana/frontend-ops /.github/issue-opened.json @grafana/grafana-community-support /.github/metrics-collector.json @torkelo -/.github/pr-checks.json @marefr -/.github/pr-commands.json @marefr +/.github/pr-checks.json @tolzhabayev +/.github/pr-commands.json @tolzhabayev /.github/renovate.json5 @grafana/frontend-ops /.github/teams.yml @armandgrillet /.github/workflows/alerting-swagger-gen.yml @grafana/alerting-backend -/.github/workflows/auto-milestone.yml @grafana/grafana-release-guild -/.github/workflows/backport.yml @grafana/grafana-release-guild -/.github/workflows/bump-version.yml @grafana/grafana-release-guild -/.github/workflows/close-milestone.yml @grafana/grafana-release-guild -/.github/workflows/release-pr.yml @grafana/grafana-release-guild -/.github/workflows/release-comms.yml @grafana/grafana-release-guild +/.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/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/codeowners-validator.yml @tolzhabayev /.github/workflows/codeql-analysis.yml @DanCech /.github/workflows/commands.yml @torkelo -/.github/workflows/community-release.yml @grafana/grafana-release-guild +/.github/workflows/community-release.yml @grafana/grafana-developer-enablement-squad /.github/workflows/detect-breaking-changes-* @grafana/plugins-platform-frontend /.github/workflows/doc-validator.yml @grafana/docs-tooling /.github/workflows/epic-add-to-platform-ux-parent-project.yml @meanmina -/.github/workflows/github-release.yml @grafana/grafana-release-guild +/.github/workflows/github-release.yml @grafana/grafana-developer-enablement-squad /.github/workflows/issue-labeled.yml @armandgrillet /.github/workflows/issue-opened.yml @grafana/grafana-community-support /.github/workflows/metrics-collector.yml @torkelo -/.github/workflows/milestone.yml @marefr -/.github/workflows/pr-checks.yml @marefr +/.github/workflows/milestone.yml @tolzhabayev +/.github/workflows/pr-checks.yml @tolzhabayev /.github/workflows/pr-codeql-analysis-go.yml @DanCech /.github/workflows/pr-codeql-analysis-javascript.yml @DanCech /.github/workflows/pr-codeql-analysis-python.yml @DanCech -/.github/workflows/pr-commands.yml @marefr -/.github/workflows/pr-patch-check.yml @grafana/grafana-release-guild -/.github/workflows/sync-mirror.yml @grafana/grafana-release-guild +/.github/workflows/pr-commands.yml @tolzhabayev +/.github/workflows/pr-patch-check.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/sync-mirror.yml @grafana/grafana-developer-enablement-squad /.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-release-guild +/.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-release-guild -/.github/workflows/update-changelog.yml @grafana/grafana-release-guild +/.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/update-changelog.yml @grafana/grafana-developer-enablement-squad /.github/workflows/update-make-docs.yml @grafana/docs-tooling /.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-cat /.github/workflows/publish-kinds-next.yml @grafana/platform-cat @@ -739,11 +742,12 @@ embed.go @grafana/grafana-as-code /.github/workflows/verify-kinds.yml @grafana/platform-cat /.github/workflows/dashboards-issue-add-label.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-release-guild +/.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 /.github/workflows/i18n-crowdin-upload.yml @grafana/grafana-frontend-platform /.github/workflows/i18n-crowdin-download.yml @grafana/grafana-frontend-platform /.github/workflows/pr-go-workspace-check.yml @grafana/grafana-app-platform-squad +/.github/workflows/pr-dependabot-update-go-workspace.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-k8s-codegen-check.yml @grafana/grafana-app-platform-squad /.github/workflows/go_lint.yml @grafana/grafana-backend-services-squad /.github/workflows/trivy-scan.yml @grafana/grafana-backend-services-squad diff --git a/.github/commands.json b/.github/commands.json index cf42850a7ae..5f730c64a4a 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -603,14 +603,6 @@ "url": "https://github.com/orgs/grafana/projects/660" } }, - { - "type": "label", - "name": "area/expressions", - "action": "addToProject", - "addToProject": { - "url": "https://github.com/orgs/grafana/projects/112" - } - }, { "type": "label", "name": "area/backend/api", @@ -1202,5 +1194,13 @@ "addToProject": { "url": "https://github.com/orgs/grafana/projects/736" } + }, + { + "type": "label", + "name": "area/expressions", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/699" + } } ] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5db8a102626..7be5eee68fa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,21 @@ updates: schedule: interval: "daily" - package-ecosystem: "gomod" - directory: "/" + directories: + - "/" + - "/apps/playlist" + - "/pkg/aggregator" + - "/pkg/apimachinery" + - "/pkg/apiserver" + - "/pkg/build" + - "/pkg/build/wire" + - "/pkg/promlib" + - "/pkg/semconv" + - "/pkg/storage/unified/apistore" + - "/pkg/storage/unified/resource" + - "/pkg/util/xorm" schedule: - interval: "weekly" + interval: "daily" + time: "02:00" + timezone: Etc/UTC + open-pull-requests-limit: 10 diff --git a/.github/renovate.json5 b/.github/renovate.json5 index ede3743b904..b26cb9ff77d 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,9 +1,7 @@ { - "extends": [ - "config:base" - ], - "enabledManagers": ["npm"], - "ignoreDeps": [ + extends: ["config:recommended"], + enabledManagers: ["npm"], + ignoreDeps: [ "@types/history", // this can be removed entirely when we upgrade history since v5 exposes types directly "history", // we should bump this together with react-router-dom (see https://github.com/grafana/grafana/issues/76744) "react-router-dom", // we should bump this together with history (see https://github.com/grafana/grafana/issues/76744) @@ -14,91 +12,71 @@ "slate", // we don't want to continue using this on the long run, use Monaco editor instead of Slate "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 + "@types/slate", // we don't want to continue using this on the long run, use Monaco editor instead of Slate + + // Temporarily pause updating lerna and nx until we resolve build issues + "lerna", + "nx" ], - "includePaths": ["package.json", "packages/**", "public/app/plugins/**"], - "ignorePaths": ["emails/**", "plugins-bundled/**", "**/mocks/**"], - "labels": ["area/frontend", "dependencies", "no-changelog"], - "postUpdateOptions": ["yarnDedupeHighest"], - "packageRules": [ + includePaths: ["package.json", "packages/**", "public/app/plugins/**"], + ignorePaths: ["emails/**", "plugins-bundled/**", "**/mocks/**"], + labels: ["area/frontend", "dependencies", "no-changelog"], + postUpdateOptions: ["yarnDedupeHighest"], + packageRules: [ { - "automerge": true, - "matchCurrentVersion": "!/^0/", - "matchUpdateTypes": ["patch"], - "excludePackagePatterns": ["^@?storybook", "^@locker"] + automerge: true, + matchCurrentVersion: "!/^0/", + matchUpdateTypes: ["patch"], + matchPackageNames: ["!/^@?storybook/", "!/^@locker/"], }, { - "matchPackagePatterns": ["^@?storybook"], - "extends": ["schedule:monthly"], - "groupName": "Storybook updates" + extends: ["schedule:monthly"], + groupName: "Storybook updates", + matchPackageNames: ["/^@?storybook/"], }, { - "groupName": "React Aria", - "matchPackagePrefixes": [ - "@react-aria/", - "@react-stately/" - ] + groupName: "React Aria", + matchPackageNames: ["@react-aria/{/,}**", "@react-stately/{/,}**"], }, { - "groupName": "Moveable", - "matchPackageNames": [ - "moveable", - "react-moveable" - ] + groupName: "Moveable", + matchPackageNames: ["moveable", "react-moveable"], }, { - "groupName": "Slate", - "matchPackageNames": [ - "@types/slate", - "@types/slate-react", - "slate", - "slate-react" - ] + groupName: "Slate", + matchPackageNames: ["@types/slate", "@types/slate-react", "slate", "slate-react"], }, { - "groupName": "d3", - "matchPackagePrefixes": [ - "d3", - "@types/d3" - ] + groupName: "d3", + matchPackageNames: ["d3{/,}**", "@types/d3{/,}**"], }, { - "groupName": "scenes", - "matchPackageNames": [ - "@grafana/scenes", - "@grafana/scenes-react", - ] + groupName: "scenes", + matchPackageNames: ["@grafana/scenes", "@grafana/scenes-react"], }, { - "groupName": "visx", - "matchPackagePrefixes": [ - "@visx/" - ] + groupName: "visx", + matchPackageNames: ["@visx/{/,}**"], }, { - "groupName": "uLibraries", - "matchPackageNames": [ - "@leeoniya/ufuzzy", - "uplot" - ], - "reviewers": ["leeoniya"], + groupName: "uLibraries", + matchPackageNames: ["@leeoniya/ufuzzy", "uplot"], + reviewers: ["leeoniya"], }, { - "groupName": "locker", - "matchPackagePrefixes": [ - "@locker/" - ], - "reviewers": ["team:grafana/plugins-platform-frontend"], + groupName: "locker", + reviewers: ["team:grafana/plugins-platform-frontend"], + matchPackageNames: ["@locker/{/,}**"], }, ], - "pin": { - "enabled": false + pin: { + enabled: false, + }, + prConcurrentLimit: 10, + rebaseWhen: "conflicted", + reviewers: ["team:grafana/frontend-ops"], + separateMajorMinor: false, + vulnerabilityAlerts: { + addLabels: ["area/security"], }, - "prConcurrentLimit": 10, - "rebaseWhen": "conflicted", - "reviewers": ["team:grafana/frontend-ops"], - "separateMajorMinor": false, - "vulnerabilityAlerts": { - "addLabels": ["area/security"] - } } diff --git a/.github/workflows/dashboards-issue-add-label.yml b/.github/workflows/dashboards-issue-add-label.yml index 766c89c7242..95abce4355b 100644 --- a/.github/workflows/dashboards-issue-add-label.yml +++ b/.github/workflows/dashboards-issue-add-label.yml @@ -3,8 +3,11 @@ on: issues: types: [opened, closed, edited, reopened, assigned, unassigned, labeled, unlabeled] +permissions: + contents: read + id-token: write + env: - GITHUB_TOKEN: ${{ secrets.ISSUE_COMMANDS_TOKEN }} ORGANIZATION: ${{ github.repository_owner }} REPO: ${{ github.event.repository.name }} TARGET_PROJECT: 202 @@ -13,27 +16,28 @@ env: concurrency: group: issue-label-when-in-project-${{ github.event.number }} jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.ISSUE_COMMANDS_TOKEN != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - main: - needs: config - if: needs.config.outputs.has-secrets + if: github.repository == 'grafana/grafana' runs-on: ubuntu-latest steps: - - name: log in - run: gh api user -q .login + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault + repo_secrets: | + GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id + GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ env.GH_APP_ID }} + private_key: ${{ env.GH_APP_PEM }} - name: Check if issue is in target project + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} run: | gh api graphql -f query=' query($org: String!, $repo: String!) { @@ -62,6 +66,8 @@ jobs: done - name: Add label to issue if: env.IN_TARGET_PROJ + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} run: | gh api graphql -f query=' mutation ($labelableId: ID!, $labelIds: [ID!]!) { diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index a46a12134bf..8cbe3030ffe 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -8,7 +8,7 @@ on: type: string latest: required: false - default: false + default: "0" description: Mark this release as latest (`1`) or not (`0`, default) type: string dry_run: @@ -23,6 +23,7 @@ on: type: string latest: required: false + default: "0" description: Mark this release as latest (`1`) or not (`0`, default) type: string dry_run: diff --git a/.github/workflows/pr-dependabot-update-go-workspace.yml b/.github/workflows/pr-dependabot-update-go-workspace.yml new file mode 100644 index 00000000000..026e145244f --- /dev/null +++ b/.github/workflows/pr-dependabot-update-go-workspace.yml @@ -0,0 +1,49 @@ +name: "Update Go Workspace for Dependabot PRs" +on: + pull_request: + branches: [main] + paths: + - .github/workflows/pr-dependabot-update-go-workspace.yml + - go.mod + - go.sum + - go.work + - go.work.sum + - '**/go.mod' + - '**/go.sum' + - '**.go' +permissions: + contents: write +jobs: + update: + runs-on: "ubuntu-latest" + if: ${{ github.actor == 'dependabot[bot]' }} + continue-on-error: true + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - name: Set go version + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + + - name: Update workspace + run: make update-workspace + + - name: Commit and push workspace changes + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + run: | + if ! git diff --exit-code --quiet; then + git config --local user.name "Grot" + git config --local user.email "grot@grafana.com" + git config --local --add --bool push.autoSetupRemote true + echo "Committing and pushing workspace changes" + git remote + git branch -l + git commit -a -m "update workspace" + git push origin $BRANCH_NAME + fi diff --git a/.github/workflows/pr-go-workspace-check.yml b/.github/workflows/pr-go-workspace-check.yml index bc5385ef0e0..889e7653ec0 100644 --- a/.github/workflows/pr-go-workspace-check.yml +++ b/.github/workflows/pr-go-workspace-check.yml @@ -4,6 +4,15 @@ on: workflow_dispatch: pull_request: branches: [main] + paths: + - .github/workflows/pr-go-workspace-check.yml + - go.mod + - go.sum + - go.work + - go.work.sum + - '**/go.mod' + - '**/go.sum' + - '**.go' jobs: check: diff --git a/.github/workflows/release-comms.yml b/.github/workflows/release-comms.yml index dc7355ef0b8..02d53057184 100644 --- a/.github/workflows/release-comms.yml +++ b/.github/workflows/release-comms.yml @@ -8,10 +8,11 @@ on: dry_run: required: false default: true + type: boolean version: required: true latest: - type: bool + type: boolean default: false pull_request: types: @@ -30,17 +31,18 @@ jobs: latest: ${{ steps.output.outputs.latest }} runs-on: ubuntu-latest steps: + # The github-release action expects a `LATEST` value of a string of either '1' or '0' - if: ${{ github.event_name == 'workflow_dispatch' }} run: | echo setting up GITHUB_ENV for ${{ github.event_name }} echo "VERSION=${{ inputs.version }}" >> $GITHUB_ENV echo "DRY_RUN=${{ inputs.dry_run }}" >> $GITHUB_ENV - echo "LATEST=${{ inputs.latest }}" >> $GITHUB_ENV + echo "LATEST=${{ inputs.latest && '1' || '0' }}" >> $GITHUB_ENV - if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') }} run: | echo "VERSION=$(echo ${{ github.head_ref }} | sed -e 's/release\/.*\///g')" >> $GITHUB_ENV echo "DRY_RUN=${{ contains(github.event.pull_request.labels.*.name, 'release/dry-run') }}" >> $GITHUB_ENV - echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') }}" >> $GITHUB_ENV + echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') && '1' || '0' }}" >> $GITHUB_ENV - id: output run: | echo "dry_run: $DRY_RUN" diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 1cd7bec885a..b1346236770 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -4,48 +4,59 @@ on: # only run on PRs where go.mod/go.sum/etc have been updated paths: - go.* + - .github/workflows/trivy-scan.yml push: branches: - main paths: - go.* + - .github/workflows/trivy-scan.yml jobs: trivy-scan: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 + - name: Install Trivy + uses: aquasecurity/setup-trivy@v0.2.2 + with: + version: v0.56.2 + cache: true + - name: Download Trivy DB + run: | + trivy fs --no-progress --download-db-only --db-repository public.ecr.aws/aquasecurity/trivy-db - name: Run Trivy vulnerability scanner (table output) - uses: aquasecurity/trivy-action@0.28.0 - with: - # 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 - scan-type: 'fs' - scanners: 'vuln' - format: 'table' - exit-code: 1 - ignore-unfixed: true - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' - trivyignores: .trivyignore - # for the PR check, ignore JS-related issues - skip-files: 'yarn.lock,package.json' + # 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 + run: | + trivy fs \ + --scanners vuln \ + --format table \ + --exit-code 1 \ + --ignore-unfixed \ + --pkg-types os,library \ + --severity CRITICAL,HIGH \ + --ignorefile .trivyignore \ + --skip-files yarn.lock,package.json \ + --skip-db-update \ + . - name: Run Trivy vulnerability scanner (SARIF) - uses: aquasecurity/trivy-action@0.28.0 - with: - scan-type: 'fs' - scanners: 'vuln' - # Note: The SARIF format ignores severity and uploads all vulns for - # later triage. The table-format step above is used to fail the build - # if there are any critical or high vulnerabilities. - # See https://github.com/aquasecurity/trivy-action/issues/95 - format: 'sarif' - output: 'trivy-results.sarif' - ignore-unfixed: true - vuln-type: 'os,library' - trivyignores: .trivyignore + # Use the trivy binary rather than the aquasecurity/trivy-action action + # to avoid a few bugs + run: | + trivy fs \ + --scanners vuln \ + --format sarif \ + --output trivy-results.sarif \ + --ignore-unfixed \ + --pkg-types os,library \ + --ignorefile .trivyignore \ + --skip-db-update \ + . if: always() && github.repository == 'grafana/grafana' - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v3 diff --git a/.gitignore b/.gitignore index 576e0c5258b..3b2d8f1d5d7 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ profile.cov /pkg/extensions/* /pkg/build/cmd/artifactspage.go /pkg/build/cmd/artifactspage.tmpl.html +/pkg/build/cmd/exportversion.go /pkg/server/wireexts_enterprise.go /pkg/cmd/grafana-cli/runner/wireexts_enterprise.go !/pkg/extensions/main.go @@ -223,3 +224,5 @@ public/app/plugins/**/dist/ # Mock service worker used for fake API responses in frontend development public/mockServiceWorker.js + +/e2e/test-plugins/*/dist \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 28499f1fb2e..85338ddd439 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,6 +16,15 @@ "cfg:app_mode=development", ] }, + { + "name": "Attach to Test Process", + "type": "go", + "request": "attach", + "mode": "remote", + "host": "127.0.0.1", + "port": 50480, + "apiVersion": 2, + }, { "name": "Run API Server (testdata)", "type": "go", diff --git a/.yarn/patches/history-npm-4.10.1-ee217563ae.patch b/.yarn/patches/history-npm-4.10.1-ee217563ae.patch deleted file mode 100644 index 6cd9f7f4b42..00000000000 --- a/.yarn/patches/history-npm-4.10.1-ee217563ae.patch +++ /dev/null @@ -1,63 +0,0 @@ -diff --git a/cjs/history.js b/cjs/history.js -index fcd8ebab613c6d87b9ac824feb30ab1080cf0ef2..4df20d5cb2f9ba5fc8777899aada53f49399560b 100644 ---- a/cjs/history.js -+++ b/cjs/history.js -@@ -103,16 +103,6 @@ function createLocation(path, state, key, currentLocation) { - if (state !== undefined && location.state === undefined) location.state = state; - } - -- try { -- location.pathname = decodeURI(location.pathname); -- } catch (e) { -- if (e instanceof URIError) { -- throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' + 'This is likely caused by an invalid percent-encoding.'); -- } else { -- throw e; -- } -- } -- - if (key) location.key = key; - - if (currentLocation) { -diff --git a/esm/history.js b/esm/history.js -index df67820fe3eed558c44fca07a82b0cd409d46720..e0e0d4f69a407e8de782b3fdf8297d42708e110a 100644 ---- a/esm/history.js -+++ b/esm/history.js -@@ -80,16 +80,6 @@ function createLocation(path, state, key, currentLocation) { - if (state !== undefined && location.state === undefined) location.state = state; - } - -- try { -- location.pathname = decodeURI(location.pathname); -- } catch (e) { -- if (e instanceof URIError) { -- throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' + 'This is likely caused by an invalid percent-encoding.'); -- } else { -- throw e; -- } -- } -- - if (key) location.key = key; - - if (currentLocation) { -diff --git a/umd/history.js b/umd/history.js -index 80e4ff66c44a2a71d4f842cc05a252e48dd18e9a..f8f4901be95e48c66f5626fbf051747a2ffbe41d 100644 ---- a/umd/history.js -+++ b/umd/history.js -@@ -207,16 +207,6 @@ - if (state !== undefined && location.state === undefined) location.state = state; - } - -- try { -- location.pathname = decodeURI(location.pathname); -- } catch (e) { -- if (e instanceof URIError) { -- throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' + 'This is likely caused by an invalid percent-encoding.'); -- } else { -- throw e; -- } -- } -- - if (key) location.key = key; - - if (currentLocation) { diff --git a/CHANGELOG.md b/CHANGELOG.md index eb3fb214b62..4469fb9a34d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,193 @@ - + -# 11.1.7+security-01 (2024-10-17) +# 11.3.1 (2024-11-19) + +### Features and enhancements + +- **Alerting:** Make context deadline on AlertNG service startup configurable [#96135](https://github.com/grafana/grafana/pull/96135), [@fayzal-g](https://github.com/fayzal-g) +- **MigrationAssistant:** Restrict dashboards, folders and datasources by the org id of the signed in user [#96345](https://github.com/grafana/grafana/pull/96345), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **User:** Check SignedInUser OrgID in RevokeInvite [#95490](https://github.com/grafana/grafana/pull/95490), [@mgyongyosi](https://github.com/mgyongyosi) ### Bug fixes -- **SQL Expressions**: Fixes CVE-2024-9264 +- **Alerting:** Fix escaping of silence matchers in utf8 mode [#95347](https://github.com/grafana/grafana/pull/95347), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Fix overflow for long receiver names [#95133](https://github.com/grafana/grafana/pull/95133), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix saving advanced mode toggle state in the alert rule editor [#95981](https://github.com/grafana/grafana/pull/95981), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Fix setting datasource uid, when datasource is string in old version [#96273](https://github.com/grafana/grafana/pull/96273), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Force refetch prom rules when refreshing panel [#96125](https://github.com/grafana/grafana/pull/96125), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Anonymous User:** Adds validator service for anonymous users [#94994](https://github.com/grafana/grafana/pull/94994), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **Anonymous User:** Adds validator service for anonymous users (Enterprise) +- **Azure Monitor:** Support metric namespaces fallback [#95155](https://github.com/grafana/grafana/pull/95155), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Fix duplicated traces in multi-resource trace query [#95247](https://github.com/grafana/grafana/pull/95247), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Handle namespace request rejection [#95909](https://github.com/grafana/grafana/pull/95909), [@aangelisc](https://github.com/aangelisc) +- **CloudWatch:** Interpolate region in log context query [#94990](https://github.com/grafana/grafana/pull/94990), [@iwysiu](https://github.com/iwysiu) +- **Dashboard datasource:** Return annotations as series when query topic is "annotations" [#95971](https://github.com/grafana/grafana/pull/95971), [@kaydelaney](https://github.com/kaydelaney) +- **Dashboard:** Append orgId to URL [#95963](https://github.com/grafana/grafana/pull/95963), [@bfmatei](https://github.com/bfmatei) +- **Dashboards:** Fixes performance issue expanding a row [#95321](https://github.com/grafana/grafana/pull/95321), [@torkelo](https://github.com/torkelo) +- **Flame Graph:** Fix crash when it receives empty data [#96211](https://github.com/grafana/grafana/pull/96211), [@yincongcyincong](https://github.com/yincongcyincong) +- **Folders:** Add admin permissions upon creation of a folder w. SA [#95365](https://github.com/grafana/grafana/pull/95365), [@eleijonmarck](https://github.com/eleijonmarck) +- **Folders:** Don't show error pop-up if the user can't fetch the root folder [#95600](https://github.com/grafana/grafana/pull/95600), [@IevaVasiljeva](https://github.com/IevaVasiljeva) +- **Migration:** Remove table aliasing in delete statement to make it work for mariadb [#95232](https://github.com/grafana/grafana/pull/95232), [@kalleep](https://github.com/kalleep) +- **ServerLock:** Fix pg concurrency/locking issue [#95935](https://github.com/grafana/grafana/pull/95935), [@mgyongyosi](https://github.com/mgyongyosi) +- **Service Accounts:** Run service account creation in transaction [#94803](https://github.com/grafana/grafana/pull/94803), [@IevaVasiljeva](https://github.com/IevaVasiljeva) +- **Table:** Fix text wrapping applying to wrong field [#95425](https://github.com/grafana/grafana/pull/95425), [@codeincarnate](https://github.com/codeincarnate) +- **Unified Storage:** Use ssl_mode instead of sslmode [#95662](https://github.com/grafana/grafana/pull/95662), [@chaudyg](https://github.com/chaudyg) - + + + +# 11.2.4 (2024-11-19) + +### Features and enhancements + +- **Alerting:** Make context deadline on AlertNG service startup configurable [#96133](https://github.com/grafana/grafana/pull/96133), [@fayzal-g](https://github.com/fayzal-g) +- **MigrationAssistant:** Restrict dashboards, folders and datasources by the org id of the signed in user [#96344](https://github.com/grafana/grafana/pull/96344), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **Transformations:** Add 'transpose' transform [#95076](https://github.com/grafana/grafana/pull/95076), [@jmdane](https://github.com/jmdane) +- **User:** Check SignedInUser OrgID in RevokeInvite [#95489](https://github.com/grafana/grafana/pull/95489), [@mgyongyosi](https://github.com/mgyongyosi) + +### Bug fixes + +- **Alerting:** Force refetch prom rules when refreshing panel [#96124](https://github.com/grafana/grafana/pull/96124), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Anonymous User:** Adds validator service for anonymous users [#94993](https://github.com/grafana/grafana/pull/94993), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **Anonymous User:** Adds validator service for anonymous users (Enterprise) +- **Azure Monitor:** Support metric namespaces fallback [#95154](https://github.com/grafana/grafana/pull/95154), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Fix duplicated traces in multi-resource trace query [#95246](https://github.com/grafana/grafana/pull/95246), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Handle namespace request rejection [#95908](https://github.com/grafana/grafana/pull/95908), [@aangelisc](https://github.com/aangelisc) +- **Folders:** Add admin permissions upon creation of a folder w. SA [#95416](https://github.com/grafana/grafana/pull/95416), [@eleijonmarck](https://github.com/eleijonmarck) +- **Migration:** Remove table aliasing in delete statement to make it work for mariadb [#95231](https://github.com/grafana/grafana/pull/95231), [@kalleep](https://github.com/kalleep) +- **ServerLock:** Fix pg concurrency/locking issue [#95934](https://github.com/grafana/grafana/pull/95934), [@mgyongyosi](https://github.com/mgyongyosi) +- **ServerSideExpressions:** Disable SQL Expressions to prevent RCE and LFI vulnerability [#94959](https://github.com/grafana/grafana/pull/94959), [@samjewell](https://github.com/samjewell) + + + + +# 11.1.9 (2024-11-19) + +### Features and enhancements + +- **Alerting:** Make context deadline on AlertNG service startup configurable [#96132](https://github.com/grafana/grafana/pull/96132), [@fayzal-g](https://github.com/fayzal-g) +- **User:** Check SignedInUser OrgID in RevokeInvite [#95488](https://github.com/grafana/grafana/pull/95488), [@mgyongyosi](https://github.com/mgyongyosi) + +### Bug fixes + +- **Alerting:** Force refetch prom rules when refreshing panel [#96123](https://github.com/grafana/grafana/pull/96123), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Anonymous User:** Adds validator service for anonymous users [#94992](https://github.com/grafana/grafana/pull/94992), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **Anonymous User:** Adds validator service for anonymous users (Enterprise) +- **Azure Monitor:** Support metric namespaces fallback [#95153](https://github.com/grafana/grafana/pull/95153), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Fix duplicated traces in multi-resource trace query [#95245](https://github.com/grafana/grafana/pull/95245), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Handle namespace request rejection [#95907](https://github.com/grafana/grafana/pull/95907), [@aangelisc](https://github.com/aangelisc) +- **Migration:** Remove table aliasing in delete statement to make it work for mariadb [#95230](https://github.com/grafana/grafana/pull/95230), [@kalleep](https://github.com/kalleep) +- **Prometheus:** Fix interpolating adhoc filters with template variables [#95977](https://github.com/grafana/grafana/pull/95977), [@cazeaux](https://github.com/cazeaux) +- **ServerLock:** Fix pg concurrency/locking issue [#95933](https://github.com/grafana/grafana/pull/95933), [@mgyongyosi](https://github.com/mgyongyosi) +- **ServerSideExpressions:** Disable SQL Expressions to prevent RCE and LFI vulnerability [#94969](https://github.com/grafana/grafana/pull/94969), [@scottlepp](https://github.com/scottlepp) + + + + +# 11.0.8 (2024-11-19) + +### Features and enhancements + +- **Alerting:** Make context deadline on AlertNG service startup configurable [#96131](https://github.com/grafana/grafana/pull/96131), [@fayzal-g](https://github.com/fayzal-g) +- **User:** Check SignedInUser OrgID in RevokeInvite [#95487](https://github.com/grafana/grafana/pull/95487), [@mgyongyosi](https://github.com/mgyongyosi) + +### Bug fixes + +- **Anonymous User:** Adds validator service for anonymous users [#95151](https://github.com/grafana/grafana/pull/95151), [@leandro-deveikis](https://github.com/leandro-deveikis) +- **Anonymous User:** Adds validator service for anonymous users (Enterprise) +- **Azure Monitor:** Support metric namespaces fallback [#95152](https://github.com/grafana/grafana/pull/95152), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Fix duplicated traces in multi-resource trace query [#95244](https://github.com/grafana/grafana/pull/95244), [@aangelisc](https://github.com/aangelisc) +- **Azure:** Handle namespace request rejection [#95906](https://github.com/grafana/grafana/pull/95906), [@aangelisc](https://github.com/aangelisc) +- **Migration:** Remove table aliasing in delete statement to make it work for mariadb [#95229](https://github.com/grafana/grafana/pull/95229), [@kalleep](https://github.com/kalleep) +- **Prometheus:** Fix interpolating adhoc filters with template variables [#95986](https://github.com/grafana/grafana/pull/95986), [@cazeaux](https://github.com/cazeaux) +- **ServerLock:** Fix pg concurrency/locking issue [#95932](https://github.com/grafana/grafana/pull/95932), [@mgyongyosi](https://github.com/mgyongyosi) +- **ServerSideExpressions:** Disable SQL Expressions to prevent RCE and LFI vulnerability [#94971](https://github.com/grafana/grafana/pull/94971), [@samjewell](https://github.com/samjewell) + + + + +# 10.4.13 (2024-11-19) + + + + +# 11.3.0+security-01 (2024-11-12) + +### Bug fixes + +- **MigrationAssistant:** Fix Migration Assistant issue [CVE-2024-9476] + + + + +# 11.2.3+security-01 (2024-11-12) + +- **MigrationAssistant:** Fix Migration Assistant issue [CVE-2024-9476] + + + + +# 10.4.12 (2024-11-08) + +### Bug fixes + +- **Alerting:** Make context deadline on AlertNG service startup configurable [#96058](https://github.com/grafana/grafana/pull/96058), [@fayzal-g](https://github.com/fayzal-g) + + + + +# 11.3.0 (2024-10-22) + +### Features and enhancements + +- **Alerting:** Add manage permissions UI logic for Contact Points [#92885](https://github.com/grafana/grafana/pull/92885), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Allow linking to silence form with `__alert_rule_uid__` value preset [#93526](https://github.com/grafana/grafana/pull/93526), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Hide query name when using simplified mode in the alert rule [#93779](https://github.com/grafana/grafana/pull/93779), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Limit and clean up old alert rules versions [#89754](https://github.com/grafana/grafana/pull/89754), [@igloo12](https://github.com/igloo12) +- **Alerting:** Style nits for the simple query mode [#93930](https://github.com/grafana/grafana/pull/93930), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Update texts in annotations step [#93977](https://github.com/grafana/grafana/pull/93977), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Use useProduceNewAlertmanagerConfiguration for contact points [#88456](https://github.com/grafana/grafana/pull/88456), [@gillesdemey](https://github.com/gillesdemey) +- **Auth:** Attach external session info to Grafana session [#93849](https://github.com/grafana/grafana/pull/93849), [@mgyongyosi](https://github.com/mgyongyosi) +- **Auth:** Replace jmespath/go-jmespath with jmespath-community/go-jmespath [#94203](https://github.com/grafana/grafana/pull/94203), [@mgyongyosi](https://github.com/mgyongyosi) +- **CloudMigrations:** Add support for migration of Library Elements (Panels) resources [#93898](https://github.com/grafana/grafana/pull/93898), [@macabu](https://github.com/macabu) +- **Cloudwatch:** Update grafana-aws-sdk [#94155](https://github.com/grafana/grafana/pull/94155), [@iwysiu](https://github.com/iwysiu) +- **Explore Logs:** Preinstall for onprem Grafana instances [#94221](https://github.com/grafana/grafana/pull/94221), [@svennergr](https://github.com/svennergr) +- **ExploreMetrics:** Ensure compatibility with Incremental Querying [#94355](https://github.com/grafana/grafana/pull/94355), [@NWRichmond](https://github.com/NWRichmond) +- **FieldConfig:** Add support for Actions [#92874](https://github.com/grafana/grafana/pull/92874), [@adela-almasan](https://github.com/adela-almasan) +- **Plugin Extensions:** Require meta-data to be defined in `plugin.json` during development mode [#93429](https://github.com/grafana/grafana/pull/93429), [@leventebalogh](https://github.com/leventebalogh) +- **Reporting:** Display template variables in the PDF (Enterprise) +- **Tempo:** Add deprecation notice for Aggregate By [#94050](https://github.com/grafana/grafana/pull/94050), [@joey-grafana](https://github.com/joey-grafana) + +### Bug fixes + +- **Alerting/Chore:** Fix TimeRangeInput not working across multiple months [#93622](https://github.com/grafana/grafana/pull/93622), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Fix default value for input in simple condition [#94248](https://github.com/grafana/grafana/pull/94248), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix eval interval not being saved when creating a new group [#93821](https://github.com/grafana/grafana/pull/93821), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Fix incorrect permission on POST external rule groups endpoint [CVE-2024-8118] [#93940](https://github.com/grafana/grafana/pull/93940), [@alexweav](https://github.com/alexweav) +- **Alerting:** Fix panics when attempting to create an Alertmanager after failing [#94023](https://github.com/grafana/grafana/pull/94023), [@santihernandezc](https://github.com/santihernandezc) +- **DashboardScene:** Fixes url issue with subpath when exiting edit mode [#93962](https://github.com/grafana/grafana/pull/93962), [@torkelo](https://github.com/torkelo) +- **Dashboards:** Enable scenes by default [#93818](https://github.com/grafana/grafana/pull/93818), [@ivanortegaalba](https://github.com/ivanortegaalba) +- **Dashboards:** Fixes view & edit keyboard shortcuts when grafana is behind a subpath [#93955](https://github.com/grafana/grafana/pull/93955), [@torkelo](https://github.com/torkelo) +- **ElasticSearch:** Fix errorsource in newInstanceSettings [#93859](https://github.com/grafana/grafana/pull/93859), [@iwysiu](https://github.com/iwysiu) +- **Reporting:** Fix reports on multi-org instance (Enterprise) +- **SubMenu:** Fix expanding sub menu items on touch devices [#93208](https://github.com/grafana/grafana/pull/93208), [@yincongcyincong](https://github.com/yincongcyincong) + + + + +# 11.2.3 (2024-10-22) + +### Bug fixes + +- **Alerting:** Fix incorrect permission on POST external rule groups endpoint [CVE-2024-8118] [#93947](https://github.com/grafana/grafana/pull/93947), [@alexweav](https://github.com/alexweav) +- **AzureMonitor:** Fix App Insights portal URL for multi-resource trace queries [#94475](https://github.com/grafana/grafana/pull/94475), [@aangelisc](https://github.com/aangelisc) +- **Canvas:** Allow API calls to grafana origin [#94129](https://github.com/grafana/grafana/pull/94129), [@adela-almasan](https://github.com/adela-almasan) +- **Folders:** Correctly show new folder button under root folder [#94712](https://github.com/grafana/grafana/pull/94712), [@IevaVasiljeva](https://github.com/IevaVasiljeva) +- **OrgSync:** Do not set default Organization for a user to a non-existent Organization [#94549](https://github.com/grafana/grafana/pull/94549), [@mgyongyosi](https://github.com/mgyongyosi) +- **Plugins:** Skip install errors if dependency plugin already exists [#94717](https://github.com/grafana/grafana/pull/94717), [@wbrowne](https://github.com/wbrowne) +- **ServerSideExpressions:** Disable SQL Expressions to prevent RCE and LFI vulnerability [#94959](https://github.com/grafana/grafana/pull/94959), [@samjewell](https://github.com/samjewell) + + # 11.2.2+security-01 (2024-10-17) @@ -27,6 +208,27 @@ - **SQL Expressions**: Fixes CVE-2024-9264 + + +# 11.1.8 (2024-10-22) + +### Bug fixes + +- **Alerting:** Fix incorrect permission on POST external rule groups endpoint [CVE-2024-8118] [#93948](https://github.com/grafana/grafana/pull/93948), [@alexweav](https://github.com/alexweav) +- **AzureMonitor:** Fix App Insights portal URL for multi-resource trace queries [#94474](https://github.com/grafana/grafana/pull/94474), [@aangelisc](https://github.com/aangelisc) +- **OrgSync:** Do not set default Organization for a user to a non-existent Organization [#94551](https://github.com/grafana/grafana/pull/94551), [@mgyongyosi](https://github.com/mgyongyosi) +- **ServerSideExpressions:** Disable SQL Expressions to prevent RCE and LFI vulnerability [#94969](https://github.com/grafana/grafana/pull/94969), [@scottlepp](https://github.com/scottlepp) + + + + +# 11.1.7+security-01 (2024-10-17) + +### Bug fixes + +- **SQL Expressions**: Fixes CVE-2024-9264 + + # 11.1.6+security-01 (2024-10-17) @@ -97,6 +299,19 @@ - **Plugins:** Avoid returning 404 for `AutoEnabled` apps [#93487](https://github.com/grafana/grafana/pull/93487), [@wbrowne](https://github.com/wbrowne) + + +# 11.0.7 (2024-10-22) + +### Bug fixes + +- **Alerting:** Fix incorrect permission on POST external rule groups endpoint [CVE-2024-8118] [#93949](https://github.com/grafana/grafana/pull/93949), [@alexweav](https://github.com/alexweav) +- **AzureMonitor:** Fix App Insights portal URL for multi-resource trace queries [#94489](https://github.com/grafana/grafana/pull/94489), [@aangelisc](https://github.com/aangelisc) +- **Dashboard:** Make dashboard search faster [#94702](https://github.com/grafana/grafana/pull/94702), [@knuzhdin](https://github.com/knuzhdin) +- **OrgSync:** Do not set default Organization for a user to a non-existent Organization [#94552](https://github.com/grafana/grafana/pull/94552), [@mgyongyosi](https://github.com/mgyongyosi) +- **ServerSideExpressions:** Disable SQL Expressions to prevent RCE and LFI vulnerability [#94971](https://github.com/grafana/grafana/pull/94971), [@samjewell](https://github.com/samjewell) + + # 11.0.6 (2024-10-01) @@ -114,6 +329,17 @@ - **Plugins:** Avoid returning 404 for `AutoEnabled` apps [#93486](https://github.com/grafana/grafana/pull/93486), [@wbrowne](https://github.com/wbrowne) + + +# 10.4.11 (2024-10-22) + +### Bug fixes + +- **Alerting:** Fix broken panelId links [#94686](https://github.com/grafana/grafana/pull/94686), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix incorrect permission on POST external rule groups endpoint [CVE-2024-8118] [#93946](https://github.com/grafana/grafana/pull/93946), [@alexweav](https://github.com/alexweav) +- **Dashboard:** Make dashboard search faster [#94703](https://github.com/grafana/grafana/pull/94703), [@knuzhdin](https://github.com/knuzhdin) + + # 10.4.10 (2024-10-01) @@ -129,6 +355,16 @@ - **Correlations:** Limit access to correlations page to users who can access Explore [#93673](https://github.com/grafana/grafana/pull/93673), [@ifrost](https://github.com/ifrost) + + +# 10.3.12 (2024-10-22) + +### Bug fixes + +- **Alerting:** Fix incorrect permission on POST external rule groups endpoint [CVE-2024-8118] [#93945](https://github.com/grafana/grafana/pull/93945), [@alexweav](https://github.com/alexweav) +- **Dashboard:** Make dashboard search faster [#94704](https://github.com/grafana/grafana/pull/94704), [@knuzhdin](https://github.com/knuzhdin) + + # 10.3.11 (2024-10-01) diff --git a/Dockerfile b/Dockerfile index f66b04bd747..563a75e954a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -ARG BASE_IMAGE=alpine:3.19.1 +ARG BASE_IMAGE=alpine:3.20 ARG JS_IMAGE=node:20-alpine ARG JS_PLATFORM=linux/amd64 ARG GO_IMAGE=golang:1.23.1-alpine @@ -30,7 +30,7 @@ COPY tsconfig.json .eslintrc .editorconfig .browserslistrc .prettierrc.js ./ COPY scripts scripts COPY emails emails -ENV NODE_ENV production +ENV NODE_ENV=production RUN yarn build FROM ${GO_IMAGE} as go-builder @@ -77,6 +77,7 @@ RUN if [[ "$BINGO" = "true" ]]; then \ COPY embed.go Makefile build.go package.json ./ COPY cue.mod cue.mod COPY kinds kinds +COPY kindsv2 kindsv2 COPY local local COPY packages/grafana-schema packages/grafana-schema COPY public/app/plugins public/app/plugins diff --git a/HALL_OF_FAME.md b/HALL_OF_FAME.md index f8950454930..a110543906b 100644 --- a/HALL_OF_FAME.md +++ b/HALL_OF_FAME.md @@ -3,3 +3,4 @@ List of previous team members that have had a big impact on the company or the product and contributed during a long period of time. - Hugo Häggmark ([Björn Lundén](https://www.bjornlunden.se/)) +- Marcus Efraimsson diff --git a/Makefile b/Makefile index ccabdbd733a..0298bb55d83 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,11 @@ gen-cue: ## Do all CUE/Thema code generation go generate ./kinds/gen.go go generate ./public/app/plugins/gen.go +.PHONY: gen-cuev2 +gen-cuev2: ## Do all CUE code generation + @echo "generate code from .cue files (v2)" + go generate ./kindsv2/gen.go + .PHONY: gen-feature-toggles gen-feature-toggles: ## First go test run fails because it will re-generate the feature toggles. @@ -416,7 +421,7 @@ protobuf: ## Compile protobuf definitions buf generate pkg/plugins/backendplugin/pluginextensionv2 --template pkg/plugins/backendplugin/pluginextensionv2/buf.gen.yaml 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/zanzana/proto/v1 --template pkg/services/authz/zanzana/proto/v1/buf.gen.yaml + buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml .PHONY: clean clean: ## Clean up intermediate build artifacts. diff --git a/apps/alerting/notifications/Makefile b/apps/alerting/notifications/Makefile new file mode 100644 index 00000000000..4fcd5fddd83 --- /dev/null +++ b/apps/alerting/notifications/Makefile @@ -0,0 +1,4 @@ +.PHONY: generate +generate: + ## --crdencoding none is needed to avoid infinite loop while generating recursive models' + grafana-app-sdk generate -c . -g ./apis --crdencoding none diff --git a/apps/alerting/notifications/routingtree.cue b/apps/alerting/notifications/routingtree.cue new file mode 100644 index 00000000000..7fac2ab2165 --- /dev/null +++ b/apps/alerting/notifications/routingtree.cue @@ -0,0 +1,49 @@ +package core + +route: { + kind: "RoutingTree" + group: "notifications" + apiResource: { + groupOverride: "notifications.alerting.grafana.app" + } + codegen: { + frontend: false + backend: true + } + pluralName: "RoutingTrees" + current: "v0alpha1" + versions: { + "v0alpha1": { + schema: { + #RouteDefaults: { + receiver: string + group_by?: [...string] + group_wait?: string + group_interval?: string + repeat_interval?: string + } + #Matcher: { + type: "=" |"!="|"=~"|"!~" @cuetsy(kind="enum") + label: string + value: string + } + #Route: { + receiver?: string + matchers?: [...#Matcher] + continue: bool + + group_by?: [...string] + mute_time_intervals?: [...string] + routes?: [...#Route] + group_wait?: string + group_interval?: string + repeat_interval?: string + } + spec: { + defaults: #RouteDefaults + routes: [...#Route] + } + } + } + } +} \ No newline at end of file diff --git a/apps/playlist/Makefile b/apps/playlist/Makefile index c0b234151e2..f704166e2ef 100644 --- a/apps/playlist/Makefile +++ b/apps/playlist/Makefile @@ -1,3 +1,3 @@ .PHONY: generate generate: - @grafana-app-sdk generate -g ./apis --kindgrouping=group --postprocess \ No newline at end of file + @grafana-app-sdk generate -g ./pkg/apis --kindgrouping=group --postprocess --crdencoding none \ No newline at end of file diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 65aabaad99f..50f9a69af47 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -3,37 +3,81 @@ module github.com/grafana/grafana/apps/playlist go 1.23.1 require ( - github.com/grafana/grafana-app-sdk v0.19.0 + github.com/grafana/grafana-app-sdk v0.23.1 k8s.io/apimachinery v0.31.1 + k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 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/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 github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/imdario/mergo v0.3.16 // 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.9 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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.60.1 // 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/otel v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/text v0.19.0 // indirect - google.golang.org/protobuf v1.35.1 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/time v0.6.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/api v0.31.1 // indirect + k8s.io/apiextensions-apiserver v0.31.1 // indirect + k8s.io/client-go v0.31.1 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 2baa98e9260..af1e0aa37fe 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -1,19 +1,36 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +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/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= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -26,18 +43,35 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/grafana/grafana-app-sdk v0.19.0 h1:RY9HvCFR+4WUy81n53wejZnof9qE6++pd6r24d6+JYs= -github.com/grafana/grafana-app-sdk v0.19.0/go.mod h1:y0BgzYxc+a7CwOqkwUhN9zXd5cgZJjd2zAbgHEd/xzo= +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.23.1 h1:BRpUG0bA0oVxjthkmO2thuJBo3nbjaRSSmZJHw+mA8I= +github.com/grafana/grafana-app-sdk v0.23.1/go.mod h1:KzgPnTJfMeckGmMctv6CJb8Jr/o/5rwARDyjXoeR0Fc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +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/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +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= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -45,9 +79,27 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +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/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +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.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +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= +github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -56,10 +108,30 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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= @@ -71,16 +143,24 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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/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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -89,8 +169,16 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/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= @@ -101,8 +189,14 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= 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-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= diff --git a/apps/playlist/kinds/playlist.cue b/apps/playlist/kinds/playlist.cue index 72a0813b987..3cbfee9bef2 100644 --- a/apps/playlist/kinds/playlist.cue +++ b/apps/playlist/kinds/playlist.cue @@ -5,6 +5,8 @@ externalName: { group: "playlist" apiResource: { groupOverride: "playlist.grafana.app" + mutation: operations: ["create","update"] + validation: operations: ["create","update"] } codegen: { frontend: false diff --git a/apps/playlist/pkg/apis/manifest.go b/apps/playlist/pkg/apis/manifest.go new file mode 100644 index 00000000000..369f30a62fd --- /dev/null +++ b/apps/playlist/pkg/apis/manifest.go @@ -0,0 +1,64 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package apis + +import ( + "encoding/json" + + "github.com/grafana/grafana-app-sdk/app" +) + +var ( + rawSchemaPlaylistv0alpha1 = []byte(`{"spec":{"properties":{"interval":{"type":"string"},"items":{"items":{"properties":{"type":{"description":"type of the item.","enum":["dashboard_by_tag","dashboard_by_uid","dashboard_by_id"],"type":"string"},"value":{"description":"Value depends on type and describes the playlist item.\n - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This\n is not portable as the numerical identifier is non-deterministic between different instances.\n Will be replaced by dashboard_by_uid in the future. (deprecated)\n - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All\n dashboards behind the tag will be added to the playlist.\n - dashboard_by_uid: The value is the dashboard UID","type":"string"}},"required":["type","value"],"type":"object"},"type":"array"},"title":{"type":"string"}},"required":["title","interval","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}}`) + versionSchemaPlaylistv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaPlaylistv0alpha1, &versionSchemaPlaylistv0alpha1) +) + +var appManifestData = app.ManifestData{ + AppName: "playlist", + Group: "playlist.grafana.app", + Kinds: []app.ManifestKind{ + { + Kind: "Playlist", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + Admission: &app.AdmissionCapabilities{ + Validation: &app.ValidationCapability{ + Operations: []app.AdmissionOperation{ + app.AdmissionOperationCreate, + app.AdmissionOperationUpdate, + }, + }, + Mutation: &app.MutationCapability{ + Operations: []app.AdmissionOperation{ + app.AdmissionOperationCreate, + app.AdmissionOperationUpdate, + }, + }, + }, + Schema: &versionSchemaPlaylistv0alpha1, + }, + }, + }, + }, +} + +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("playlist") +} diff --git a/apps/playlist/apis/playlist/v0alpha1/playlist_codec_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_codec_gen.go similarity index 100% rename from apps/playlist/apis/playlist/v0alpha1/playlist_codec_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/playlist_codec_gen.go diff --git a/apps/playlist/apis/playlist/v0alpha1/playlist_metadata_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_metadata_gen.go similarity index 100% rename from apps/playlist/apis/playlist/v0alpha1/playlist_metadata_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/playlist_metadata_gen.go diff --git a/apps/playlist/apis/playlist/v0alpha1/playlist_object_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go similarity index 98% rename from apps/playlist/apis/playlist/v0alpha1/playlist_object_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go index 3d501ef0ca6..8d25ebe8590 100644 --- a/apps/playlist/apis/playlist/v0alpha1/playlist_object_gen.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go @@ -65,12 +65,13 @@ func (o *Playlist) SetSubresource(name string, value any) error { } func (o *Playlist) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() return resource.StaticMetadata{ Name: o.ObjectMeta.Name, Namespace: o.ObjectMeta.Namespace, - Group: o.GroupVersionKind().Group, - Version: o.GroupVersionKind().Version, - Kind: o.GroupVersionKind().Kind, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, } } diff --git a/apps/playlist/apis/playlist/v0alpha1/playlist_schema_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_schema_gen.go similarity index 100% rename from apps/playlist/apis/playlist/v0alpha1/playlist_schema_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/playlist_schema_gen.go diff --git a/apps/playlist/apis/playlist/v0alpha1/playlist_spec_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_spec_gen.go similarity index 100% rename from apps/playlist/apis/playlist/v0alpha1/playlist_spec_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/playlist_spec_gen.go diff --git a/apps/playlist/apis/playlist/v0alpha1/playlist_status_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_status_gen.go similarity index 100% rename from apps/playlist/apis/playlist/v0alpha1/playlist_status_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/playlist_status_gen.go diff --git a/apps/playlist/apis/playlist/v0alpha1/zz_openapi_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go similarity index 78% rename from apps/playlist/apis/playlist/v0alpha1/zz_openapi_gen.go rename to apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go index 7aa4de07f4b..9c5e6bb2119 100644 --- a/apps/playlist/apis/playlist/v0alpha1/zz_openapi_gen.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go @@ -12,17 +12,17 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.Playlist": schema_playlist_apis_playlist_v0alpha1_Playlist(ref), - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistItem": schema_playlist_apis_playlist_v0alpha1_PlaylistItem(ref), - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistList": schema_playlist_apis_playlist_v0alpha1_PlaylistList(ref), - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistOperatorState": schema_playlist_apis_playlist_v0alpha1_PlaylistOperatorState(ref), - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistSpec": schema_playlist_apis_playlist_v0alpha1_PlaylistSpec(ref), - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistStatus": schema_playlist_apis_playlist_v0alpha1_PlaylistStatus(ref), - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlayliststatusOperatorState": schema_playlist_apis_playlist_v0alpha1_PlayliststatusOperatorState(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.Playlist": schema_pkg_apis_playlist_v0alpha1_Playlist(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistItem": schema_pkg_apis_playlist_v0alpha1_PlaylistItem(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistList": schema_pkg_apis_playlist_v0alpha1_PlaylistList(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistOperatorState": schema_pkg_apis_playlist_v0alpha1_PlaylistOperatorState(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistSpec": schema_pkg_apis_playlist_v0alpha1_PlaylistSpec(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistStatus": schema_pkg_apis_playlist_v0alpha1_PlaylistStatus(ref), + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlayliststatusOperatorState": schema_pkg_apis_playlist_v0alpha1_PlayliststatusOperatorState(ref), } } -func schema_playlist_apis_playlist_v0alpha1_Playlist(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_Playlist(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -51,13 +51,13 @@ func schema_playlist_apis_playlist_v0alpha1_Playlist(ref common.ReferenceCallbac "spec": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistSpec"), + Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistStatus"), + Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistStatus"), }, }, }, @@ -65,11 +65,11 @@ func schema_playlist_apis_playlist_v0alpha1_Playlist(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistSpec", "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistSpec", "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } -func schema_playlist_apis_playlist_v0alpha1_PlaylistItem(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_PlaylistItem(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -99,7 +99,7 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistItem(ref common.ReferenceCal } } -func schema_playlist_apis_playlist_v0alpha1_PlaylistList(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_PlaylistList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -132,7 +132,7 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistList(ref common.ReferenceCal Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.Playlist"), + Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.Playlist"), }, }, }, @@ -143,11 +143,11 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistList(ref common.ReferenceCal }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.Playlist", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.Playlist", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } -func schema_playlist_apis_playlist_v0alpha1_PlaylistOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_PlaylistOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -199,7 +199,7 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistOperatorState(ref common.Ref } } -func schema_playlist_apis_playlist_v0alpha1_PlaylistSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_PlaylistSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -220,7 +220,7 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistSpec(ref common.ReferenceCal Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistItem"), + Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistItem"), }, }, }, @@ -238,11 +238,11 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistSpec(ref common.ReferenceCal }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlaylistItem"}, + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistItem"}, } } -func schema_playlist_apis_playlist_v0alpha1_PlaylistStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_PlaylistStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -273,7 +273,7 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistStatus(ref common.ReferenceC Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlayliststatusOperatorState"), + Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlayliststatusOperatorState"), }, }, }, @@ -283,11 +283,11 @@ func schema_playlist_apis_playlist_v0alpha1_PlaylistStatus(ref common.ReferenceC }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/playlist/apis/playlist/v0alpha1.PlayliststatusOperatorState"}, + "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlayliststatusOperatorState"}, } } -func schema_playlist_apis_playlist_v0alpha1_PlayliststatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_playlist_v0alpha1_PlayliststatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ diff --git a/apps/playlist/pkg/app/app.go b/apps/playlist/pkg/app/app.go new file mode 100644 index 00000000000..36d6b16e580 --- /dev/null +++ b/apps/playlist/pkg/app/app.go @@ -0,0 +1,87 @@ +package app + +import ( + "context" + "fmt" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/operator" + "github.com/grafana/grafana-app-sdk/resource" + "github.com/grafana/grafana-app-sdk/simple" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/klog/v2" + + playlistv0alpha1 "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1" + "github.com/grafana/grafana/apps/playlist/pkg/watchers" +) + +type PlaylistConfig struct { + EnableWatchers bool +} + +func New(cfg app.Config) (app.App, error) { + var ( + playlistWatcher operator.ResourceWatcher + err error + ) + + playlistConfig, ok := cfg.SpecificConfig.(*PlaylistConfig) + if ok && playlistConfig.EnableWatchers { + playlistWatcher, err = watchers.NewPlaylistWatcher() + if err != nil { + return nil, fmt.Errorf("unable to create PlaylistWatcher: %w", err) + } + } + + simpleConfig := simple.AppConfig{ + Name: "playlist", + KubeConfig: cfg.KubeConfig, + InformerConfig: simple.AppInformerConfig{ + ErrorHandler: func(ctx context.Context, err error) { + klog.ErrorS(err, "Informer processing error") + }, + }, + ManagedKinds: []simple.AppManagedKind{ + { + Kind: playlistv0alpha1.PlaylistKind(), + Watcher: playlistWatcher, + Mutator: &simple.Mutator{ + MutateFunc: func(ctx context.Context, req *app.AdmissionRequest) (*app.MutatingResponse, error) { + // modify req.Object if needed + return &app.MutatingResponse{ + UpdatedObject: req.Object, + }, nil + }, + }, + Validator: &simple.Validator{ + ValidateFunc: func(ctx context.Context, req *app.AdmissionRequest) error { + // do something here if needed + return nil + }, + }, + }, + }, + } + + a, err := simple.NewApp(simpleConfig) + if err != nil { + return nil, err + } + + err = a.ValidateManifest(cfg.ManifestData) + if err != nil { + return nil, err + } + + return a, nil +} + +func GetKinds() map[schema.GroupVersion]resource.Kind { + gv := schema.GroupVersion{ + Group: playlistv0alpha1.PlaylistKind().Group(), + Version: playlistv0alpha1.PlaylistKind().Version(), + } + return map[schema.GroupVersion]resource.Kind{ + gv: playlistv0alpha1.PlaylistKind(), + } +} diff --git a/apps/playlist/pkg/watchers/watcher_playlist.go b/apps/playlist/pkg/watchers/watcher_playlist.go new file mode 100644 index 00000000000..44be08c25ae --- /dev/null +++ b/apps/playlist/pkg/watchers/watcher_playlist.go @@ -0,0 +1,75 @@ +package watchers + +import ( + "context" + "fmt" + + "github.com/grafana/grafana-app-sdk/operator" + "github.com/grafana/grafana-app-sdk/resource" + "k8s.io/klog/v2" + + playlist "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1" +) + +var _ operator.ResourceWatcher = &PlaylistWatcher{} + +type PlaylistWatcher struct{} + +func NewPlaylistWatcher() (*PlaylistWatcher, error) { + return &PlaylistWatcher{}, nil +} + +// Add handles add events for playlist.Playlist resources. +func (s *PlaylistWatcher) Add(ctx context.Context, rObj resource.Object) error { + object, ok := rObj.(*playlist.Playlist) + if !ok { + return fmt.Errorf("provided object is not of type *playlist.Playlist (name=%s, namespace=%s, kind=%s)", + rObj.GetStaticMetadata().Name, rObj.GetStaticMetadata().Namespace, rObj.GetStaticMetadata().Kind) + } + + klog.InfoS("Added resource", "name", object.GetStaticMetadata().Identifier().Name) + return nil +} + +// Update handles update events for playlist.Playlist resources. +func (s *PlaylistWatcher) Update(ctx context.Context, rOld resource.Object, rNew resource.Object) error { + oldObject, ok := rOld.(*playlist.Playlist) + if !ok { + return fmt.Errorf("provided object is not of type *playlist.Playlist (name=%s, namespace=%s, kind=%s)", + rOld.GetStaticMetadata().Name, rOld.GetStaticMetadata().Namespace, rOld.GetStaticMetadata().Kind) + } + + _, ok = rNew.(*playlist.Playlist) + if !ok { + return fmt.Errorf("provided object is not of type *playlist.Playlist (name=%s, namespace=%s, kind=%s)", + rNew.GetStaticMetadata().Name, rNew.GetStaticMetadata().Namespace, rNew.GetStaticMetadata().Kind) + } + + klog.InfoS("Updated resource", "name", oldObject.GetStaticMetadata().Identifier().Name) + return nil +} + +// Delete handles delete events for playlist.Playlist resources. +func (s *PlaylistWatcher) Delete(ctx context.Context, rObj resource.Object) error { + object, ok := rObj.(*playlist.Playlist) + if !ok { + return fmt.Errorf("provided object is not of type *playlist.Playlist (name=%s, namespace=%s, kind=%s)", + rObj.GetStaticMetadata().Name, rObj.GetStaticMetadata().Namespace, rObj.GetStaticMetadata().Kind) + } + + klog.InfoS("Deleted resource", "name", object.GetStaticMetadata().Identifier().Name) + return nil +} + +// Sync is not a standard resource.Watcher function, but is used when wrapping this watcher in an operator.OpinionatedWatcher. +// It handles resources which MAY have been updated during an outage period where the watcher was not able to consume events. +func (s *PlaylistWatcher) Sync(ctx context.Context, rObj resource.Object) error { + object, ok := rObj.(*playlist.Playlist) + if !ok { + return fmt.Errorf("provided object is not of type *playlist.Playlist (name=%s, namespace=%s, kind=%s)", + rObj.GetStaticMetadata().Name, rObj.GetStaticMetadata().Namespace, rObj.GetStaticMetadata().Kind) + } + + klog.InfoS("Possible resource update", "name", object.GetStaticMetadata().Identifier().Name) + return nil +} diff --git a/conf/defaults.ini b/conf/defaults.ini index 0c0eecdcf23..c9ef90d5f3d 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -316,6 +316,9 @@ feedback_links_enabled = true # Static context that is being added to analytics events reporting_static_context = +# Logs interaction events to the browser javascript console, intended for development only +browser_console_reporter = false + #################################### Security ############################ [security] # disable creation of admin user on first start of grafana @@ -623,6 +626,11 @@ id_response_header_namespaces = user api-key service-account # This feature currently **only supports single-organization deployments** managed_service_accounts_enabled = false +#################################### Passwordless Auth ########################### +[auth.passwordless] +enabled = false +code_expiration = 20m + #################################### SSO Settings ########################### [sso_settings] # interval for reloading the SSO Settings from the database @@ -940,7 +948,7 @@ session_duration = "15m" # Set the plugins that will receive AWS settings for each request (via plugin context) # By default this will include all Grafana Labs owned AWS plugins, or those that make use of AWS settings (ElasticSearch, Prometheus). -forward_settings_to_plugins = cloudwatch, grafana-athena-datasource, grafana-redshift-datasource, grafana-x-ray-datasource, grafana-timestream-datasource, grafana-iot-sitewise-datasource, grafana-iot-twinmaker-app, grafana-opensearch-datasource, aws-datasource-provisioner, elasticsearch, prometheus +forward_settings_to_plugins = cloudwatch, grafana-athena-datasource, grafana-redshift-datasource, grafana-x-ray-datasource, grafana-timestream-datasource, grafana-iot-sitewise-datasource, grafana-iot-twinmaker-app, grafana-opensearch-datasource, aws-datasource-provisioner, elasticsearch, prometheus, grafana-amazonprometheus-datasource, grafana-aurora-datasource #################################### Azure ############################### [azure] @@ -1213,6 +1221,9 @@ enabled = # Comma-separated list of organization IDs for which to disable unified alerting. Only supported if unified alerting is enabled. disabled_orgs = +# Specify how long to wait for the alerting service to initialize +initialization_timeout = 30s + # Specify the frequency of polling for admin config changes. # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. admin_config_poll_interval = 60s @@ -1750,7 +1761,8 @@ hide_angular_deprecation = # Comma separated list of plugin ids for which environment variables should be forwarded. Used only when feature flag pluginsSkipHostEnvVars is enabled. forward_host_env_vars = # Comma separated list of plugin ids to install as part of the startup process. -preinstall = grafana-lokiexplore-app +# By default, the following plugins will be preinstalled: "grafana-lokiexplore-app" +preinstall = # Controls whether preinstall plugins asynchronously (in the background) or synchronously (blocking). Useful when preinstalled plugins are used with provisioning. preinstall_async = true # Disables preinstall feature. It has the same effect as setting preinstall to an empty list. @@ -2022,3 +2034,6 @@ fail_tests_on_console = true # Whether or not to enable the MSW mock API, which intercepts requests and returns mock data # Should only be used for local development or demo purposes mock_api = false +# Whether to enable betterer eslint rules for local development +# Useful if you want to always see betterer rules that we're trying to fix so they're more prevalent +betterer_eslint_rules = false diff --git a/conf/sample.ini b/conf/sample.ini index 644449780b2..740f13e394b 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -315,6 +315,9 @@ # Static context that is being added to analytics events ;reporting_static_context = grafanaInstance=12, os=linux +# Logs interaction events to the browser javascript console, intended for development only +;browser_console_reporter = false + #################################### Security #################################### [security] # disable creation of admin user on first start of grafana @@ -1201,6 +1204,9 @@ # Comma-separated list of organization IDs for which to disable unified alerting. Only supported if unified alerting is enabled. ;disabled_orgs = +# Specify how long to wait for the alerting service to initialize +;initialization_timeout = 30s + # Specify the frequency of polling for admin config changes. # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. ;admin_config_poll_interval = 60s diff --git a/devenv/bulk-folders/bulk-folders.sh b/devenv/bulk-folders/bulk-folders.sh index 6da744d6f45..5d374d2fad6 100755 --- a/devenv/bulk-folders/bulk-folders.sh +++ b/devenv/bulk-folders/bulk-folders.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash echo "Deleting previous bulk folders" find ./bulk-folders -type d -name "Bulk Folder*" -exec rm -rf "{}" \; diff --git a/devenv/create_docker_compose.sh b/devenv/create_docker_compose.sh index 9fef9636017..d937f95cec4 100755 --- a/devenv/create_docker_compose.sh +++ b/devenv/create_docker_compose.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash blocks_dir=docker/blocks docker_dir=docker diff --git a/devenv/datasources.yaml b/devenv/datasources.yaml index 0cfa7810670..5a05761f65e 100644 --- a/devenv/datasources.yaml +++ b/devenv/datasources.yaml @@ -321,3 +321,11 @@ datasources: access: proxy url: http://localhost:4040 editable: false + + + - name: gdev-e2etestdatasource + type: grafana-e2etest-datasource + uid: gdev-e2etest-datasource + access: proxy + url: http://localhost:4040 + editable: false diff --git a/devenv/dev-dashboards/transforms/config-from-query.json b/devenv/dev-dashboards/transforms/config-from-query.json index c9dd139a501..cb65f966667 100644 --- a/devenv/dev-dashboards/transforms/config-from-query.json +++ b/devenv/dev-dashboards/transforms/config-from-query.json @@ -1,552 +1,810 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "description": "", - "editable": true, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "thresholdsStyle": { - "mode": "line" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "hide": false, - "max": 100, - "min": 1, - "refId": "A", - "scenarioId": "random_walk", - "startValue": 50 - }, - { - "alias": "", - "csvContent": "min,max,threshold1\n1000,1000,8000\n0,100,80\n\n", - "refId": "config", - "scenarioId": "csv_content" - } - ], - "title": "Min, max, threshold from separate query", - "transformations": [ - { - "id": "configFromData", - "options": { - "configRefId": "config", - "mappings": [] - } - } - ], - "type": "timeseries" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "left", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "SensorA" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-text" - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 5, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Name, Value, SensorA, MyUnit, MyColor\nGoogle, 10, 50, km/h, blue\nGoogle, 100, 100,km/h, orange\n", - "hide": false, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Custom mappings and apply to self", - "transformations": [ - { - "id": "configFromData", - "options": { - "applyTo": { - "id": "byName", - "options": "SensorA" - }, - "applyToConfig": true, - "configRefId": "A", - "mappings": [ - { - "configProperty": "unit", - "fieldName": "MyUnit", - "handlerKey": "unit" - }, - { - "fieldName": "MyColor", - "handlerKey": "color" - } - ] - } - } - ], - "type": "table" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Value" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - } - ] - } - ] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 9 - }, - "id": 7, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "ID, DisplayText\n21412312312, Homer\n12421412413, Simpsons \n12321312313, Bart", - "hide": false, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Mapping data", - "transformations": [ - { - "id": "configFromData", - "options": { - "applyToConfig": true, - "configRefId": "A", - "mappings": [ - { - "fieldName": "Color", - "handlerKey": "mappings.color" - }, - { - "fieldName": "Value", - "handlerKey": "mappings.value" - } - ] - } - } - ], - "type": "table" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Value" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 9 - }, - "id": 6, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Value, Color\nOK, blue\nPretty bad, red\nYay it's green, green\nSomething is off, orange\nNo idea, #88AA00\nAm I purple?, purple", - "hide": false, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Value mappings from query result applied to itself", - "transformations": [ - { - "id": "configFromData", - "options": { - "applyTo": { - "id": "byName", - "options": "Value" - }, - "applyToConfig": true, - "configRefId": "A", - "mappings": [ - { - "fieldName": "Color", - "handlerKey": "mappings.color" - }, - { - "fieldName": "Value", - "handlerKey": "mappings.value" - } - ] - } - } - ], - "type": "table" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 14 - }, - "id": 8, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "ID, Value\n21412312312, 100\n12421412413, 20\n12321312313, 10", - "hide": false, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Display data", - "transformations": [ - { - "id": "configFromData", - "options": { - "applyToConfig": true, - "configRefId": "A", - "mappings": [ - { - "fieldName": "Color", - "handlerKey": "mappings.color" - }, - { - "fieldName": "Value", - "handlerKey": "mappings.value" - } - ] - } - } - ], - "type": "table" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 19 - }, - "id": 9, - "options": { - "barWidth": 0.97, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "orientation": "horizontal", - "showValue": "auto", - "text": {}, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "ID, Value\nA21412312312, 100\nA12421412413, 20\nA12321312313, 10\n", - "hide": false, - "refId": "data", - "scenarioId": "csv_content" - }, - { - "csvContent": "ID, DisplayText\nA21412312312, Homer\nA12421412413, Marge \nA12321312313, Bart", - "hide": false, - "refId": "mappings", - "scenarioId": "csv_content" - } - ], - "title": "Value mapping ID -> DisplayText from separate query", - "transformations": [ - { - "id": "configFromData", - "options": { - "applyTo": { - "id": "byName", - "options": "ID" - }, - "applyToConfig": false, - "configRefId": "mappings", - "mappings": [ - { - "fieldName": "ID", - "handlerKey": "mappings.value" - }, - { - "fieldName": "DisplayText", - "handlerKey": "mappings.text" - } - ] - } - } - ], - "type": "barchart" + "type": "dashboard" } - ], - "refresh": "", - "schemaVersion": 33, - "tags": [ - "gdev", - "transform" - ], - "templating": { - "list": [] + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 100, + "min": 1, + "refId": "A", + "scenarioId": "random_walk", + "startValue": 50 + }, + { + "alias": "", + "csvContent": "min,max,threshold1\n1000,1000,8000\n0,100,80\n\n", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "config", + "scenarioId": "csv_content" + } + ], + "title": "Min, max, threshold from separate query", + "transformations": [ + { + "id": "configFromData", + "options": { + "configRefId": "config", + "mappings": [] + } + } + ], + "type": "timeseries" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "SensorA" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 5, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.4.0-pre", + "targets": [ + { + "csvContent": "Name, Value, SensorA, MyUnit, MyColor\nGoogle, 10, 50, km/h, blue\nGoogle, 100, 100,km/h, orange\n", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Custom mappings and apply to self", + "transformations": [ + { + "id": "configFromData", + "options": { + "applyTo": { + "id": "byName", + "options": "SensorA" + }, + "applyToConfig": true, + "configRefId": "A", + "mappings": [ + { + "configProperty": "unit", + "fieldName": "MyUnit", + "handlerKey": "unit" + }, + { + "fieldName": "MyColor", + "handlerKey": "color" + } + ] + } + } + ], + "type": "table" }, - "timepicker": {}, - "timezone": "", - "title": "Transforms - Config from query", - "uid": "Juj4_7ink", - "version": 1 - } + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 7, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.4.0-pre", + "targets": [ + { + "csvContent": "ID, DisplayText\n21412312312, Homer\n12421412413, Simpsons \n12321312313, Bart", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Mapping data", + "transformations": [ + { + "id": "configFromData", + "options": { + "applyToConfig": true, + "configRefId": "A", + "mappings": [ + { + "fieldName": "Color", + "handlerKey": "mappings.color" + }, + { + "fieldName": "Value", + "handlerKey": "mappings.value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.4.0-pre", + "targets": [ + { + "csvContent": "Value, Color\nOK, blue\nPretty bad, red\nYay it's green, green\nSomething is off, orange\nNo idea, #88AA00\nAm I purple?, purple", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Value mappings from query result applied to itself", + "transformations": [ + { + "id": "configFromData", + "options": { + "applyTo": { + "id": "byName", + "options": "Value" + }, + "applyToConfig": true, + "configRefId": "A", + "mappings": [ + { + "fieldName": "Color", + "handlerKey": "mappings.color" + }, + { + "fieldName": "Value", + "handlerKey": "mappings.value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 8, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.4.0-pre", + "targets": [ + { + "csvContent": "ID, Value\n21412312312, 100\n12421412413, 20\n12321312313, 10", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Display data", + "transformations": [ + { + "id": "configFromData", + "options": { + "applyToConfig": true, + "configRefId": "A", + "mappings": [ + { + "fieldName": "Color", + "handlerKey": "mappings.color" + }, + { + "fieldName": "Value", + "handlerKey": "mappings.value" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 9, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "csvContent": "ID, Value\nA21412312312, 100\nA12421412413, 20\nA12321312313, 10\n", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "refId": "data", + "scenarioId": "csv_content" + }, + { + "csvContent": "ID, DisplayText\nA21412312312, Homer\nA12421412413, Marge \nA12321312313, Bart", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "refId": "mappings", + "scenarioId": "csv_content" + } + ], + "title": "Value mapping ID -> DisplayText from separate query", + "transformations": [ + { + "id": "configFromData", + "options": { + "applyTo": { + "id": "byName", + "options": "ID" + }, + "applyToConfig": false, + "configRefId": "mappings", + "mappings": [ + { + "fieldName": "ID", + "handlerKey": "mappings.value" + }, + { + "fieldName": "DisplayText", + "handlerKey": "mappings.text" + } + ] + } + } + ], + "type": "barchart" + }, + { + "datasource": { + "default": true, + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed+area" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 19 + }, + "id": 10, + "maxDataPoints": 100, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 300000\n }\n },\n {\n \"name\": \"A-series\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {},\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1732120345536,\n 1732120645536,\n 1732120945536,\n 1732121245536,\n 1732121545536,\n 1732121845536,\n 1732122145536,\n 1732122445536,\n 1732122745536,\n 1732123045536,\n 1732123345536,\n 1732123645536,\n 1732123945536,\n 1732124245536,\n 1732124545536,\n 1732124845536,\n 1732125145536,\n 1732125445536,\n 1732125745536,\n 1732126045536,\n 1732126345536,\n 1732126645536,\n 1732126945536,\n 1732127245536,\n 1732127545536,\n 1732127845536,\n 1732128145536,\n 1732128445536,\n 1732128745536,\n 1732129045536,\n 1732129345536,\n 1732129645536,\n 1732129945536,\n 1732130245536,\n 1732130545536,\n 1732130845536,\n 1732131145536,\n 1732131445536,\n 1732131745536,\n 1732132045536,\n 1732132345536,\n 1732132645536,\n 1732132945536,\n 1732133245536,\n 1732133545536,\n 1732133845536,\n 1732134145536,\n 1732134445536,\n 1732134745536,\n 1732135045536,\n 1732135345536,\n 1732135645536,\n 1732135945536,\n 1732136245536,\n 1732136545536,\n 1732136845536,\n 1732137145536,\n 1732137445536,\n 1732137745536,\n 1732138045536,\n 1732138345536,\n 1732138645536,\n 1732138945536,\n 1732139245536,\n 1732139545536,\n 1732139845536,\n 1732140145536,\n 1732140445536,\n 1732140745536,\n 1732141045536,\n 1732141345536,\n 1732141645536\n ],\n [\n 36.67835770082578,\n 35.674537924065,\n 8.339763723800829,\n 16.313291374141446,\n 66.05915891584247,\n 55.975417240601566,\n 33.75563648171818,\n 10.561077849025175,\n 20.31936089572975,\n 26.11219409670694,\n 57.542750561307564,\n 67.10954340535248,\n 82.95323961635275,\n 100.9691805551439,\n 59.829706792214644,\n 94.58723331927925,\n 89.3082374466047,\n 58.69065135820439,\n 97.144192150251,\n 139.99199318295675,\n 157.9473973408396,\n 177.94452058033198,\n 188.84065573954362,\n 154.3930906887033,\n 130.14406878049226,\n 116.65818233680316,\n 100.96041794526472,\n 144.65142921584447,\n 175.75178611497054,\n 203.55271609883386,\n 238.4931714915047,\n 253.38754249911452,\n 271.1735238723396,\n 258.54418620287515,\n 260.8463123020904,\n 216.10614084307323,\n 253.30389406688175,\n 249.37108721413884,\n 243.7226799137106,\n 216.74579233434042,\n 262.50043010512826,\n 238.71564300219498,\n 218.3552317737898,\n 195.6154411937393,\n 154.1987522722987,\n 124.00066408416897,\n 146.6474694384778,\n 101.68405646311294,\n 104.5791139459948,\n 85.39428966503652,\n 78.45166775446714,\n 56.285707917841535,\n 36.22861441808941,\n 35.098428846082555,\n 68.67835646605371,\n 101.67142528391042,\n 151.04038339587296,\n 114.77414457402928,\n 72.65341528313934,\n 113.42643748928826,\n 151.09282092262364,\n 163.24422498859587,\n 183.86606816236363,\n 230.24678524811478,\n 205.94887669562561,\n 211.24387656976373,\n 217.26738326873522,\n 214.66898480692646,\n 206.95531499977153,\n 194.19724584765092,\n 146.16071387746757,\n 188.30193538777615\n ]\n ]\n }\n }\n]", + "refId": "A", + "scenarioId": "raw_frame" + } + ], + "title": "Config from query / threshold does not overwrite Base threshold", + "transformations": [ + { + "id": "configFromData", + "options": { + "configRefId": "A", + "mappings": [ + { + "fieldName": "A-series", + "handlerArguments": { + "threshold": { + "color": "yellow" + } + }, + "handlerKey": "threshold1" + } + ] + } + } + ], + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 40, + "tags": [ + "gdev", + "transform" + ], + "templating": { + "list": [] + }, + "time": { + "from": "2024-11-20T16:31:51.747Z", + "to": "2024-11-20T22:28:15.688Z" + }, + "timepicker": {}, + "timezone": "", + "title": "Transforms - Config from query", + "uid": "Juj4_7ink", + "version": 1, + "weekStart": "" +} diff --git a/devenv/docker/blocks/caddy_tls/README.md b/devenv/docker/blocks/caddy_tls/README.md new file mode 100644 index 00000000000..464f076ffcc --- /dev/null +++ b/devenv/docker/blocks/caddy_tls/README.md @@ -0,0 +1,33 @@ +# TLS Caddy Server + +Starts a [Caddy server](https://caddyserver.com/) with TLS configured. + +## Setup + +- Caddy is setup to run on port 2081, so when configuring the webhook receiver in Grafana Alerting you should use the +following the following URL: `https://localhost:2081` +- Also, Caddy is configured to use a self-signed certificate and to check the client certificate (`require_and_verify` mode) +- Caddy is setup to log requests and has debug mode enabled to make it easier to investigate possible issues + +## TLS Certificates + +If you want to configure a webhook contact point in Grafana Alerting with TLS, you need to provide a certificate and key. + +You can find them in `/etc/caddy` directory in the container: + +``` shell +docker exec devenv-caddy_tls-1 ls /etc/caddy/ +``` + +### CA Certificate + +``` shell +docker exec devenv-caddy_tls-1 cat /etc/caddy/ca.pem +``` + +### Client certificates + +``` shell +docker exec devenv-caddy_tls-1 cat /etc/caddy/client.pem +docker exec devenv-caddy_tls-1 cat /etc/caddy/client.key +``` diff --git a/devenv/docker/blocks/caddy_tls/build/Caddyfile b/devenv/docker/blocks/caddy_tls/build/Caddyfile new file mode 100644 index 00000000000..3674950f821 --- /dev/null +++ b/devenv/docker/blocks/caddy_tls/build/Caddyfile @@ -0,0 +1,14 @@ +{ + debug +} + +localhost:2081 { + log + tls /etc/caddy/server.pem /etc/caddy/server.key { + ca_root /etc/caddy/ca.pem + client_auth { + mode require_and_verify + trust_pool file /etc/caddy/client.pem /etc/caddy/ca.pem + } + } +} diff --git a/devenv/docker/blocks/caddy_tls/build/Dockerfile b/devenv/docker/blocks/caddy_tls/build/Dockerfile new file mode 100644 index 00000000000..ebe6d1b4f4a --- /dev/null +++ b/devenv/docker/blocks/caddy_tls/build/Dockerfile @@ -0,0 +1,12 @@ +FROM caddy:2.8.4-alpine + +WORKDIR /etc/caddy +EXPOSE 2081 + +COPY Caddyfile ./Caddyfile +COPY san.cnf ./san.cnf +COPY gen_certs.sh ./gen_certs.sh + +RUN apk update && apk upgrade --no-cache && apk add openssl + +RUN ./gen_certs.sh diff --git a/devenv/docker/blocks/caddy_tls/build/gen_certs.sh b/devenv/docker/blocks/caddy_tls/build/gen_certs.sh new file mode 100755 index 00000000000..b82d2b58abb --- /dev/null +++ b/devenv/docker/blocks/caddy_tls/build/gen_certs.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +DAYS_VALID=3650 + +# Create CA certificate +openssl genpkey -algorithm RSA -out ca.key +openssl req -new -x509 -days $DAYS_VALID -key ca.key -out ca.pem -subj "/CN=My CA" + +# Create server certificate +openssl genpkey -algorithm RSA -out server.key +openssl req -new -key server.key -out server.csr -subj "/CN=localhost" +openssl x509 -req -days $DAYS_VALID -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -extfile san.cnf -extensions v3_req + +# Create client key and certificate +openssl genpkey -algorithm RSA -out client.key +openssl req -new -key client.key -out client.csr -subj "/CN=Client" +openssl x509 -req -days $DAYS_VALID -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem -extfile san.cnf -extensions v3_req diff --git a/devenv/docker/blocks/caddy_tls/build/san.cnf b/devenv/docker/blocks/caddy_tls/build/san.cnf new file mode 100644 index 00000000000..d7148ac8d4f --- /dev/null +++ b/devenv/docker/blocks/caddy_tls/build/san.cnf @@ -0,0 +1,7 @@ +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +IP.1 = 127.0.0.1 +IP.2 = ::1 diff --git a/devenv/docker/blocks/caddy_tls/docker-compose.yaml b/devenv/docker/blocks/caddy_tls/docker-compose.yaml new file mode 100644 index 00000000000..1c83b88ca1e --- /dev/null +++ b/devenv/docker/blocks/caddy_tls/docker-compose.yaml @@ -0,0 +1,5 @@ + caddy_tls: + build: + context: docker/blocks/caddy_tls/build + ports: + - "2081:2081" diff --git a/devenv/docker/blocks/collectd/Dockerfile b/devenv/docker/blocks/collectd/Dockerfile index ea046659bd1..00762cd0149 100644 --- a/devenv/docker/blocks/collectd/Dockerfile +++ b/devenv/docker/blocks/collectd/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:xenial -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive RUN apt-get -y update RUN apt-get -y install collectd curl python-pip diff --git a/devenv/docker/blocks/elastic/data/data.js b/devenv/docker/blocks/elastic/data/data.js index eb3b8c3d624..936c81e3f5b 100644 --- a/devenv/docker/blocks/elastic/data/data.js +++ b/devenv/docker/blocks/elastic/data/data.js @@ -150,7 +150,8 @@ function getRandomLogItem(counter, timestamp) { value: counter, metric: chooseRandomElement(['cpu', 'memory', 'latency']), description: "this is description", - slash: "Access to the path '\\\\tkasnpo\\KASNPO\\Files\\contacts.xml' is denied." + slash: "Access to the path '\\\\tkasnpo\\KASNPO\\Files\\contacts.xml' is denied.", + url: "/foo/blah" }; } diff --git a/devenv/docker/blocks/mimir_backend/docker-compose.yaml b/devenv/docker/blocks/mimir_backend/docker-compose.yaml index a09635e80e3..544b836e1dd 100644 --- a/devenv/docker/blocks/mimir_backend/docker-compose.yaml +++ b/devenv/docker/blocks/mimir_backend/docker-compose.yaml @@ -1,9 +1,10 @@ mimir_backend: - image: grafana/mimir-alpine:r304-3872ccb + image: grafana/mimir-alpine:r316-55f47f8 container_name: mimir_backend command: - -target=backend - -alertmanager.grafana-alertmanager-compatibility-enabled + - -alertmanager.utf8-strict-mode-enabled nginx: environment: - NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx diff --git a/devenv/docker/buildcontainer/Dockerfile b/devenv/docker/buildcontainer/Dockerfile index ae85c8fbad2..a6a636439c3 100644 --- a/devenv/docker/buildcontainer/Dockerfile +++ b/devenv/docker/buildcontainer/Dockerfile @@ -2,18 +2,18 @@ FROM centos:6.6 RUN yum install -y initscripts curl tar gcc libc6-dev git -ENV GOLANG_VERSION 1.4.2 +ENV GOLANG_VERSION=1.4.2 RUN curl -sSL https://golang.org/dl/go$GOLANG_VERSION.src.tar.gz \ | tar -v -C /usr/src -xz RUN cd /usr/src/go/src && ./make.bash --no-clean 2>&1 -ENV PATH /usr/src/go/bin:$PATH +ENV PATH=/usr/src/go/bin:$PATH RUN mkdir -p /go/src /go/bin && chmod -R 777 /go -ENV GOPATH /go -ENV PATH /go/bin:$PATH +ENV GOPATH=/go +ENV PATH=/go/bin:$PATH WORKDIR /go/src/github.com/grafana/grafana diff --git a/devenv/docker/ha-test-unified-alerting/Dockerfile b/devenv/docker/ha-test-unified-alerting/Dockerfile index cc9b14e2e4e..38965c12636 100644 --- a/devenv/docker/ha-test-unified-alerting/Dockerfile +++ b/devenv/docker/ha-test-unified-alerting/Dockerfile @@ -7,6 +7,6 @@ WORKDIR /go/src/webhook RUN mkdir /tmp/logs RUN go build -o /bin webhook-listener.go -ENV PORT 8080 +ENV PORT=8080 ENTRYPOINT [ "/bin/webhook-listener" ] diff --git a/devenv/local_cdn/default.conf b/devenv/local_cdn/default.conf new file mode 100644 index 00000000000..e35c16ee146 --- /dev/null +++ b/devenv/local_cdn/default.conf @@ -0,0 +1,24 @@ +server { + root /data; + autoindex on; + + location / { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '$http_origin' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Headers' '*' always; + add_header 'Access-Control-Allow-Methods' '*'; + # add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain; charset=utf-8'; + add_header 'Content-Length' 0; + return 204; + } + + add_header 'Access-Control-Allow-Origin' '$http_origin' always; + add_header 'Access-Control-Allow-Methods' '*' always; + add_header 'Access-Control-Allow-Headers' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + + rewrite ^/grafana-oss/11.4.0-pre/public(.*)$ $1 last; + } +} diff --git a/devenv/local_cdn/docker-compose.yaml b/devenv/local_cdn/docker-compose.yaml new file mode 100644 index 00000000000..93860f897f9 --- /dev/null +++ b/devenv/local_cdn/docker-compose.yaml @@ -0,0 +1,11 @@ +version: '2' + +services: + grafana_local_cdn: + image: nginx:alpine + container_name: grafana_local_cdn + ports: + - "8080:80" + volumes: + - ../../public:/data + - ./default.conf:/etc/nginx/conf.d/default.conf \ No newline at end of file diff --git a/devenv/setup.sh b/devenv/setup.sh index ec1b8ea65a6..00eb50b5257 100755 --- a/devenv/setup.sh +++ b/devenv/setup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash bulkDashboard() { diff --git a/docs/sources/administration/enterprise-licensing/_index.md b/docs/sources/administration/enterprise-licensing/_index.md index 5d793097684..131ec8d0f79 100644 --- a/docs/sources/administration/enterprise-licensing/_index.md +++ b/docs/sources/administration/enterprise-licensing/_index.md @@ -234,7 +234,7 @@ To determine the number of active users: A tiered license defines dashboard viewers, and dashboard editors and administrators, as two distinct user types that each have their own user limit. -As of Grafana Enterprise version 9.0, Grafana only counts and enforces the _total_ number of active users in your Grafana instance. For example, if you purchase 150 active users, you can have 20 admins, 70 editors, and 60 viewers, or you can have 150 admins. Grafana will enforce the total number of active users even if you use a license that grants a specific number of admins or editors and a certain number of viewers. This is a more permissive policy than before, which gives you the flexibility to change users' roles. +Grafana only counts and enforces the _total_ number of active users in your Grafana instance. For example, if you purchase 150 active users, you can have 20 admins, 70 editors, and 60 viewers, or you can have 150 admins. Grafana will enforce the total number of active users even if you use a license that grants a specific number of admins or editors and a certain number of viewers. This is a more permissive policy than before, which gives you the flexibility to change users' roles. If you are running a pre-9.0 version of Grafana Enterprise, please refer to the documentation for that version to learn more about license enforcement in your current version. diff --git a/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-ecs/index.md b/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-ecs/index.md index fa06cd3aa02..5d3858c25cc 100644 --- a/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-ecs/index.md +++ b/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-ecs/index.md @@ -35,9 +35,9 @@ To activate your license, complete the following tasks. For more information about deploying an application on Amazon ECS, refer to [Creating an Amazon ECS service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/create-service.html). -1. As you create the Amazon ECS service, use the Grafana Enterprise version 8.3.0 or later container image. +1. As you create the Amazon ECS service, use the intended Grafana Enterprise container image version. - For example, enter `grafana/grafana-enterprise:8.3.3`. + For example, enter `grafana/grafana-enterprise:11.3.0`. > Only Grafana Enterprise versions 8.3.0 and later support licenses granted through AWS Marketplace. diff --git a/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-eks/index.md b/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-eks/index.md index 49060c0989d..a5698448447 100644 --- a/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-eks/index.md +++ b/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/activate-license-on-eks/index.md @@ -20,7 +20,7 @@ weight: 200 # Activate a Grafana Enterprise license from AWS Marketplace on EKS -If you have purchased a Grafana Enterprise subscription through AWS Marketplace, you must activate it in order to use Grafana Enterprise data source plugins and features in Grafana. +If you have purchased a Grafana Enterprise subscription through AWS Marketplace, you must activate it to use Grafana Enterprise data source plugins and features in Grafana. ## Before you begin: @@ -37,9 +37,9 @@ To activate your license, complete the following tasks: For more information about installing Grafana on Kubernetes using the Helm Chart, refer to the [Grafana Helm Chart](https://github.com/grafana/helm-charts/tree/main/charts/grafana#readme). -1. Use `kubectl set image deployment/my-release grafana=grafana/grafana-enterprise:` to update the container image to Grafana Enterprise version 8.3.0 or later. +1. Use `kubectl set image deployment/my-release grafana=grafana/grafana-enterprise:` to update the container image. - For example, enter `grafana/grafana-enterprise:8.3.3`. + For example, enter `grafana/grafana-enterprise:11.3.0`. > Only Grafana Enterprise versions 8.3.0 and later support licenses granted through AWS Marketplace. diff --git a/docs/sources/administration/plugin-management/index.md b/docs/sources/administration/plugin-management/index.md index 510c488b9ff..9e051c06b70 100644 --- a/docs/sources/administration/plugin-management/index.md +++ b/docs/sources/administration/plugin-management/index.md @@ -103,53 +103,45 @@ To browse for available plugins: ### Install a plugin -To install a plugin: +The most common way to install a plugin is through the Grafana UI, but alternative methods are also available. 1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins. 1. Browse and find a plugin. 1. Click the plugin's logo. 1. Click **Install**. -When the update is complete, you'll see a confirmation message that the installation was successful. +There are also additional ways to install plugins depending on your setup. -### Update a plugin +#### Install a plugin using Grafana CLI -To update a plugin: +Grafana CLI allows you to install, upgrade, and manage your Grafana plugins using a command line. For more information about Grafana CLI plugin commands, refer to [Plugin commands]({{< relref "../../cli/#plugins-commands" >}}). -1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins. -1. Click the **Installed** filter to show only installed plugins. -1. Click the plugin's logo. -1. Click **Update**. +#### Install a plugin from a ZIP file -When the update is complete, you'll see a confirmation message that the update was successful. +This method is typically used for plugins not available in the Plugin Catalog or in environments without internet access. -### Uninstall a plugin +Download the archive containing the plugin assets, and install it by extracting the archive into the plugin directory. For example: -To uninstall a plugin: +```bash +unzip my-plugin-0.2.0.zip -d YOUR_PLUGIN_DIR/my-plugin +``` -1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins. -1. Click the plugin's logo. -1. Click the **Installed** filter to show only installed plugins. -1. Click **Uninstall**. +The path to the plugin directory is defined in the configuration file. For more information, refer to [Configuration]({{< relref "../../setup-grafana/configure-grafana/#plugins" >}}). -When the update is complete, you'll see a confirmation message that the uninstall was successful. +#### Install a plugin in air-gapped environment -## Install Grafana plugins +Plugin installation usually requires an internet connection. You can check which endpoints are used during the installation on your instance and add them to your instance’s allowlist. -Grafana supports data source, panel, and app plugins. +If this is not possible you can go via installing a plugin using [Grafana CLI](#install-a-plugin-using-grafana-cli) or as a [ZIP file](#install-a-plugin-from-a-zip-file). -1. In a web browser, navigate to the [Grafana plugin catalog](https://grafana.com/plugins) and find a plugin that you want to install. -1. Click the plugin, and then click the **Installation** tab. +You can fetch any plugin from Grafana.com API following the download link referenced in the API. +Here is an example based on `grafana-lokiexplore-app` plugins. -### Install plugin on Grafana Cloud +1. Open `https://grafana.com/api/plugins/grafana-lokiexplore-app` and look for `links` section +1. Find a `download` url which looks something like `https://grafana.com/api/plugins/grafana-lokiexplore-app/versions/1.0.2/download` +1. Use this URL to download the plugin ZIP file, which you can then install as described above. -On the **Installation tab**, in the **For** field, click the name of the Grafana instance on which you want to install the plugin. - -Grafana Cloud handles the plugin installation automatically. - -If you're logged in to Grafana Cloud when you add a plugin, log out and then log back in again to use the new plugin. - -### Install plugins using the Grafana Helm chart +#### Install plugins using the Grafana Helm chart With the Grafana Helm chart, add the plugins you want to install as a list using the `plugins` field in the your values file. For more information about the configuration, refer to [the Helm chart configuration reference](https://github.com/grafana/helm-charts/tree/main/charts/grafana#configuration). @@ -162,21 +154,29 @@ plugins: - redis-datasource ``` -### Install plugin on local Grafana +When the update is complete, a confirmation message will indicate the installation was successful. -Follow the instructions on the **Install** tab. You can either install the plugin with a Grafana CLI command or by downloading and uncompressing a zip file into the Grafana plugins directory. We recommend using Grafana CLI in most instances. The zip option is available if your Grafana server doesn't have access to the internet. +### Update a plugin -For more information about Grafana CLI plugin commands, refer to [Plugin commands]({{< relref "../../cli/#plugins-commands" >}}). +To update a plugin: -#### Install a packaged plugin +1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins. +1. Click the **Installed** filter to show only installed plugins. +1. Click the plugin's logo. +1. Click **Update**. -After the user has downloaded the archive containing the plugin assets, they can install it by extracting the archive into their plugin directory. For example: +When the update is complete, a confirmation message will indicate the installation was successful. -```bash -unzip my-plugin-0.2.0.zip -d YOUR_PLUGIN_DIR/my-plugin -``` +### Uninstall a plugin -The path to the plugin directory is defined in the configuration file. For more information, refer to [Configuration]({{< relref "../../setup-grafana/configure-grafana/#plugins" >}}). +To uninstall a plugin: + +1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins. +1. Click the plugin's logo. +1. Click the **Installed** filter to show only installed plugins. +1. Click **Uninstall**. + +When the update is complete, a confirmation message will indicate the installation was successful. ## Plugin signatures @@ -228,6 +228,58 @@ 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 %}} +## Plugin Frontend Sandbox + +{{% admonition type="caution" %}} +Plugin Frontend Sandbox is currently in [public preview](/docs/release-life-cycle/). Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available. +{{% /admonition %}} + +The Plugin Frontend Sandbox is a security feature that isolates plugin frontend code from the main Grafana application. +When enabled, plugins run in a separate JavaScript context, which provides several security benefits: + +- Prevents plugins from modifying parts of the Grafana interface outside their designated areas +- Stops plugins from interfering with other plugins functionality +- Protects core Grafana features from being altered by plugins +- Prevents plugins from modifying global browser objects and behaviors + +Plugins running inside the Frontend Sandbox should continue to work normally without any noticeable changes in their intended functionality. + +### Enable Frontend Sandbox + +The Frontend Sandbox feature is currently behind the `pluginsFrontendSandbox` feature flag. To enable it, you'll need to: + +1. Enable the feature flag in your Grafana configuration. For more information about enabling feature flags, refer to [Configure feature toggles](/setup-grafana/configure-grafana/feature-toggles/). + +2. For self-hosted Grafana installations, add the plugin IDs you want to sandbox in the `security` section using the `enable_frontend_sandbox_for_plugins` configuration option. + +For Grafana Cloud users, you can simply use the toggle switch in the plugin catalog page to enable or disable the sandbox for each plugin. By default, the sandbox is disabled for all plugins. + +{{% admonition type="note" %}} +Enabling the Frontend Sandbox might impact the performance of certain plugins. Only disable the sandbox if you fully trust the plugin and understand the security implications. +{{% /admonition %}} + +### Compatibility + +The Frontend Sandbox is available in public preview in Grafana >=11.4. It is compatible with all types of plugins including app plugins, panel plugins, and data source plugins. Angular-based plugins are not supported. Plugins developed and signed by Grafana Labs are excluded and cannot be sandboxed. + +### When to Use Frontend Sandbox + +We strongly recommend enabling the Frontend Sandbox for plugins that allow users to write custom JavaScript code for data visualization or manipulation. These plugins, while powerful, can potentially execute arbitrary JavaScript code in your Grafana instance. The sandbox provides an additional layer of security by restricting what this code can access and modify. + +Examples of plugins where the sandbox is particularly important include: + +- Panel plugins that allow users to write custom JavaScript code +- Plugins from untrusted sources + +### Troubleshooting + +If a plugin isn't functioning correctly with the Frontend Sandbox enabled: + +1. Temporarily disable the sandbox for that specific plugin +1. Test if the plugin works correctly without the sandbox +1. If the plugin only works with the sandbox disabled, ensure you trust the plugin source before continuing to use it without sandbox protection +1. Report any sandbox-related issues to the plugin developer + ## Learn more - [Browse plugins](/grafana/plugins) diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md index a3681a643c9..34f32dbbc4f 100644 --- a/docs/sources/administration/provisioning/index.md +++ b/docs/sources/administration/provisioning/index.md @@ -81,7 +81,7 @@ The configuration file can also list data sources to automatically delete, calle Grafana deletes the data sources listed in `deleteDatasources` _before_ adding or updating those in the `datasources` list. You can configure Grafana to automatically delete provisioned data sources when they're removed from the provisioning file. -To do so, add `prune: true` to the root of your provisioning file. +To do so, add `prune: true` to the root of your data source provisioning file. With this configuration, Grafana also removes the provisioned data sources if you remove the provisioning file entirely. ### Running multiple Grafana instances @@ -227,9 +227,9 @@ Data sources tagged with _HTTP\*_ communicate using the HTTP protocol, which inc | encrypt | string | MSSQL | Determines SSL encryption handling. Options include: `disable` - data sent between client and server is not encrypted; `false` - data sent between client and server is not encrypted beyond the login packet; `true` - data sent between client and server is encrypted. Default is `false`. | | postgresVersion | number | PostgreSQL | Postgres version as a number (903/904/905/906/1000) meaning v9.3, v9.4, ..., v10 | | timescaledb | boolean | PostgreSQL | Enable usage of TimescaleDB extension | -| maxOpenConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of open connections to the database (Grafana v5.4+) | -| maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool (Grafana v5.4+) | -| connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused (Grafana v5.4+) | +| maxOpenConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of open connections to the database | +| maxIdleConns | number | MySQL, PostgreSQL and MSSQL | Maximum number of connections in the idle connection pool | +| connMaxLifetime | number | MySQL, PostgreSQL and MSSQL | Maximum amount of time in seconds a connection may be reused | | keepCookies | array | _HTTP\*_ | Cookies that needs to be passed along while communicating with data sources | | prometheusVersion | string | Prometheus | The version of the Prometheus data source, such as `2.37.0`, `2.24.0` | | prometheusType | string | Prometheus | Prometheus database type. Options are `Prometheus`, `Cortex`, `Mimir` or`Thanos`. | @@ -253,17 +253,17 @@ All of these settings are optional. The _HTTP\*_ tag denotes data sources that communicate using the HTTP protocol, including all core data source plugins except MySQL, PostgreSQL, and MS SQL. {{< /admonition >}} -| Name | Type | Data source | Description | -| ----------------- | ------ | ---------------------------------- | -------------------------------------------------------- | -| tlsCACert | string | _HTTP\*_, MySQL, PostgreSQL | CA cert for out going requests | -| tlsClientCert | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client cert for outgoing requests | -| tlsClientKey | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client key for outgoing requests | -| password | string | _HTTP\*_, MySQL, PostgreSQL, MSSQL | password | -| basicAuthPassword | string | _HTTP\*_ | password for basic authentication | -| accessKey | string | Cloudwatch | Access key for connecting to Cloudwatch | -| secretKey | string | Cloudwatch | Secret key for connecting to Cloudwatch | -| sigV4AccessKey | string | Elasticsearch and Prometheus | SigV4 access key. Required when using keys auth provider | -| sigV4SecretKey | string | Elasticsearch and Prometheus | SigV4 secret key. Required when using keys auth provider | +| Name | Type | Data source | Description | +| ----------------- | ------ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| tlsCACert | string | _HTTP\*_, MySQL, PostgreSQL | CA cert for out going requests. You can point directly to your stored cert by using an environment variable following the `$__file{path/to/ca}` format. | +| tlsClientCert | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client cert for outgoing requests. You can point directly to your stored cert by using an environment variable following the `$__file{path/to/cert}` format. | +| tlsClientKey | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client key for outgoing requests. You can point directly to your stored key by using an environment variable following the `$__file{path/to/key}` format. | +| password | string | _HTTP\*_, MySQL, PostgreSQL, MSSQL | password | +| basicAuthPassword | string | _HTTP\*_ | password for basic authentication | +| accessKey | string | Cloudwatch | Access key for connecting to Cloudwatch | +| secretKey | string | Cloudwatch | Secret key for connecting to Cloudwatch | +| sigV4AccessKey | string | Elasticsearch and Prometheus | SigV4 access key. Required when using keys auth provider | +| sigV4SecretKey | string | Elasticsearch and Prometheus | SigV4 secret key. Required when using keys auth provider | #### Custom HTTP headers for data sources @@ -614,12 +614,22 @@ The following sections detail the supported settings and secure settings for eac #### Alert notification `webhook` -| Name | Secure setting | -| ---------- | -------------- | -| url | | -| httpMethod | | -| username | | -| password | yes | +| Name | Secure setting | +| ----------- | -------------- | +| url | | +| http_method | | +| username | | +| password | yes | +| tls_config | | + +##### TLS config + +| Name | Secure setting | +| ------------------ | -------------- | +| insecureSkipVerify | | +| clientCertificate | yes | +| clientKey | yes | +| caCertificate | yes | #### Alert notification `googlechat` diff --git a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md index 1276f049da2..9aab2bfca61 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md @@ -100,6 +100,8 @@ The following list contains role-based access control actions. | `folders:delete` |
  • `folders:*`
  • `folders:uid:*`
| Delete one or more folders and their subfolders. | | `folders:read` |
  • `folders:*`
  • `folders:uid:*`
| Read one or more folders and their subfolders. | | `folders:write` |
  • `folders:*`
  • `folders:uid:*`
| Update one or more folders and their subfolders. | +| `groupsync.mappings:read` | None | List group attribute sync mappings. To use this permission, enable the `groupAttributeSync` feature toggle. | +| `groupsync.mappings:write` | None | List, create, update, and delete group attribute sync mappings. To use this permission, enable the `groupAttributeSync` feature toggle. | | `ldap.config:reload` | None | Reload the LDAP configuration. | | `ldap.status:read` | None | Verify the availability of the LDAP server or servers. | | `ldap.user:read` | None | Read users via LDAP. | @@ -188,36 +190,38 @@ The following list contains role-based access control actions used by Grafana Ad | Action | Applicable scopes | Description | | ---------------------------------------------------- | ----------------- | ----------------------------------------------------- | -| `grafana‑adaptive‑metrics‑app.plugin:access` | None | Access the Adaptive Metrics plugin in Grafana Cloud. | -| `grafana‑adaptive‑metrics‑app.config:read` | None | Read the Adaptive Metrics app configuration. | -| `grafana‑adaptive‑metrics‑app.config:write` | None | Update the Adaptive Metrics app configuration. | -| `grafana‑adaptive‑metrics‑app.recommendations:read` | None | Read aggregation recommendations. | -| `grafana‑adaptive‑metrics‑app.recommendations:apply` | None | Apply aggregation recommendations. | -| `grafana‑adaptive‑metrics‑app.rules:read` | None | Read aggregation rules. | -| `grafana‑adaptive‑metrics‑app.rules:write` | None | Create aggregation rules. | -| `grafana‑adaptive‑metrics‑app.rules:delete` | None | Delete aggregation rules. | -| `grafana‑adaptive‑metrics‑app.exemptions:read` | None | Read recommendation exemptions. | -| `grafana‑adaptive‑metrics‑app.exemptions:write` | None | Create, update, and delete recommendation exemptions. | +| `grafana-adaptive-metrics-app.plugin:access` | None | Access the Adaptive Metrics plugin in Grafana Cloud. | +| `grafana-adaptive-metrics-app.config:read` | None | Read the Adaptive Metrics app configuration. | +| `grafana-adaptive-metrics-app.config:write` | None | Update the Adaptive Metrics app configuration. | +| `grafana-adaptive-metrics-app.recommendations:read` | None | Read aggregation recommendations. | +| `grafana-adaptive-metrics-app.recommendations:apply` | None | Apply aggregation recommendations. | +| `grafana-adaptive-metrics-app.rules:read` | None | Read aggregation rules. | +| `grafana-adaptive-metrics-app.rules:write` | None | Create aggregation rules. | +| `grafana-adaptive-metrics-app.rules:delete` | None | Delete aggregation rules. | +| `grafana-adaptive-metrics-app.exemptions:read` | None | Read recommendation exemptions. | +| `grafana-adaptive-metrics-app.exemptions:write` | None | Create, update, and delete recommendation exemptions. | ### Grafana Alerting Notification action definitions -To enable these permissions, enable the `alertingApiServer` feature toggle. +To use these permissions, enable the `alertingApiServer` feature toggle. -| Action | Applicable scopes | Description | -| -------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | -| `alert.notifications.receivers:read` | `receivers:*`
`receivers:uid:*` | Read contact points. None | -| `alert.notifications.receivers.secrets:read` | `receivers:*`
`receivers:uid:*` | Export contact points with decrypted secrets.None | -| `alert.notifications.receivers:create` | None | Create a new contact points. The creator is automatically granted full access to the created contact point.None | -| `alert.notifications.receivers:write` | `receivers:*`
`receivers:uid:*` | Update existing contact points.None | -| `alert.notifications.receivers:delete` | `receivers:*`
`receivers:uid:*` | Update and delete existing contact points.None | -| `receivers.permissions:read` | `receivers:*`
`receivers:uid:*` | Read permissions for contact points.None | -| `receivers.permissions:write` | `receivers:*`
`receivers:uid:*` | Manage permissions for contact points.None | -| `alert.notifications.time-intervals:read` | None | Read mute time intervals.None | -| `alert.notifications.time-intervals:write` | None | Create new or update existing mute time intervals.None | -| `alert.notifications.time-intervals:delete` | None | Delete existing time intervals.None | -| `alert.notifications.templates:read` | None | Read templates. | -| `alert.notifications.templates:write` | None | Create new or update existing templates.None | -| `alert.notifications.templates:delete` | None | Delete existing templates.None | +| Action | Applicable scopes | Description | +| -------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `alert.notifications.receivers:read` | `receivers:*`
`receivers:uid:*` | Read contact points. | +| `alert.notifications.receivers.secrets:read` | `receivers:*`
`receivers:uid:*` | Export contact points with decrypted secrets. | +| `alert.notifications.receivers:create` | None | Create a new contact points. The creator is automatically granted full access to the created contact point. | +| `alert.notifications.receivers:write` | `receivers:*`
`receivers:uid:*` | Update existing contact points. | +| `alert.notifications.receivers:delete` | `receivers:*`
`receivers:uid:*` | Update and delete existing contact points. | +| `receivers.permissions:read` | `receivers:*`
`receivers:uid:*` | Read permissions for contact points. | +| `receivers.permissions:write` | `receivers:*`
`receivers:uid:*` | Manage permissions for contact points. | +| `alert.notifications.time-intervals:read` | None | Read mute time intervals. | +| `alert.notifications.time-intervals:write` | None | Create new or update existing mute time intervals. | +| `alert.notifications.time-intervals:delete` | None | Delete existing time intervals. | +| `alert.notifications.templates:read` | None | Read templates. | +| `alert.notifications.templates:write` | None | Create new or update existing templates. | +| `alert.notifications.templates:delete` | None | Delete existing templates. | +| `alert.notifications.routes:read` | None | Read notification policies. | +| `alert.notifications.routes:write` | None | Create new, update or delete notification policies | ## Scope definitions 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 ccc283db650..ed4b64817e7 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 @@ -54,13 +54,13 @@ The following tables list permissions associated with basic and fixed roles. ## Basic role assignments -| Basic role | UID | Associated fixed roles | Description | -| ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 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` | 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` | 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. | -| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) | +| Basic role | UID | Associated fixed roles | Description | +| ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 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` | 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. | +| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) | ## Fixed role definitions @@ -115,6 +115,8 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage | `fixed:folders.permissions:reader` | `fixed_E06l4cx0JFm47EeLBE4nmv3pnSo` | `folders.permissions:read` | Read all folder permissions. | | `fixed:folders.permissions:writer` | `fixed_3GAgpQ_hWG8o7-lwNb86_VB37eI` | All permissions from `fixed:folders.permissions:reader` and
`folders.permissions:write` | Read and update all folder permissions. | | `fixed:ldap:reader` | `fixed_lMcOPwSkxKY-qCK8NMJc5k6izLE` | `ldap.user:read`
`ldap.status:read` | Read the LDAP configuration and LDAP status information. | +| `fixed:groupsync:reader` | `fixed_tLIbDrE6kw93sKqooF8GVS9BF4E` | `groupsync.mappings:read` | List all group attribute sync mappings. To use this role, enable the `groupAttributeSync` feature toggle. | +| `fixed:groupsync:writer` | `fixed_q7XUYx_efzxxsVmWhQgpiYClwBs` | `groupsync.mappings:read`
`groupsync.mappings:write` | Create, read, update, and delete all group attribute sync mappings. To use this role, enable the `groupAttributeSync` feature toggle. | | `fixed:ldap:writer` | `fixed_p6AvnU4GCQyIh7-hbwI-bk3GYnU` | All permissions from `fixed:ldap:reader` and
`ldap.user:sync`
`ldap.config:reload` | Read and update the LDAP configuration, and read LDAP status information. | | `fixed:library.panels:creator` | `fixed_6eX6ItfegCIY5zLmPqTDW8ZV7KY` | `library.panels:create`
`folders:read` | Create library panel at the root level. | | `fixed:library.panels:general.reader` | `fixed_ct0DghiBWR_2BiQm3EvNPDVmpio` | `library.panels:read` | Read all library panels at the root level. | diff --git a/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md b/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md index 81120f03b63..d567045b41c 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md @@ -27,7 +27,7 @@ filters = accesscontrol:debug accesscontrol.evaluator:debug dashboard.permission ## Enable audit logging {{% admonition type="note" %}} -Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) version 7.3 and later, and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} You can enable auditing in the Grafana configuration file. diff --git a/docs/sources/alerting/_index.md b/docs/sources/alerting/_index.md index 0d341c0afe5..ef33ff193ce 100644 --- a/docs/sources/alerting/_index.md +++ b/docs/sources/alerting/_index.md @@ -27,10 +27,6 @@ cards: href: ./fundamentals/ description: Learn more about the fundamentals and available features that help you create, manage, and respond to alerts; and improve your team’s ability to resolve issues quickly. height: 24 - - title: Set up - href: ./set-up/ - description: Set up your implementation of Grafana Alerting. - height: 24 - title: Configure alert rules href: ./alerting-rules/ description: Create, manage, view, and adjust alert rules to alert on your metrics data or log entries from multiple data sources — no matter where your data is stored. @@ -39,13 +35,13 @@ cards: href: ./configure-notifications/ description: Choose how, when, and where to send your alert notifications. height: 24 - - title: Detect and respond + - title: Monitor status href: ./manage-notifications/ description: Monitor, respond to, and triage issues within your services. height: 24 - - title: Monitor - href: ./monitor/ - description: Monitor your alerting metrics to ensure you identify potential issues before they become critical. + - title: Additional configuration + href: ./set-up/ + description: Use advanced configuration options to further tailor your alerting setup. These options can enhance security, scalability, and automation in complex environments. height: 24 --- diff --git a/docs/sources/alerting/alerting-rules/create-alerts-panels.md b/docs/sources/alerting/alerting-rules/create-alerts-panels.md deleted file mode 100644 index 290936fd7ea..00000000000 --- a/docs/sources/alerting/alerting-rules/create-alerts-panels.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-alerts-panels/ -description: Create alert rules from panels. Reuse the queries in the panel and create alert rules based on them. -keywords: - - grafana - - alerting - - panels - - create - - grafana-managed - - data source-managed -labels: - products: - - cloud - - enterprise - - oss -title: Create alert rules from panels -weight: 400 ---- - -## Create alert rules from panels - -Create alert rules from time series panels. By doing so, you can reuse the queries in the panel and create alert rules based on them. - -1. Navigate to a dashboard in the **Dashboards** section. -2. Hover over the top-right corner of a time series panel and click the panel menu icon. -3. From the dropdown menu, select **More...** > **New alert rule**. - -The New alert rule form opens where you can configure and create your alert rule based on the query used in the panel. - -{{% admonition type="note" %}} -Changes to the panel aren't reflected on the linked alert rules. If you change a query, you have to update it in both the panel and the alert rule. - -Alert rules are only supported in [time series](ref:time-series) visualizations. -{{% /admonition %}} - -{{< docs/play title="visualizations with linked alerts in Grafana" url="https://play.grafana.org/d/000000074/" >}} - -## View alert rules from panels - -To view alert rules associated with a time series panel, complete the following steps. - -1. Open the panel editor by hovering over the top-right corner of any panel -1. Click the panel menu icon that appears. -1. Click **Edit**. -1. Click the **Alert** tab to view existing alert rules or create a new one. 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 33d5b94b1df..7c3a5986f61 100644 --- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md @@ -64,16 +64,21 @@ refs: destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/ - alert-list: + link-alert-rules-to-panels: - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/alert-list/ + destination: /docs/grafana//alerting/alerting-rules/link-alert-rules-to-panels/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/alert-list/ - time-series: + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/ + data-sources: - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/time-series/ + destination: /docs/grafana//datasources/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series/ + - destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/ + compatible-data-sources: + - 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 --- # Configure Grafana-managed alert rules @@ -84,6 +89,9 @@ Multiple alert instances can be created as a result of one alert rule (also know {{% admonition type="note" %}} For Grafana Cloud Free Forever, you can create up to 100 free Grafana-managed alert rules with each alert rule having a maximum of 1000 alert instances. + +For all paid tiers (Cloud Pro and Advanced), there is a soft limit of 2000 alert rules and unlimited alert instances. To increase the limit, open a support ticket from the [Cloud portal](https://grafana.com/docs/grafana-cloud/account-management/support/). + {{% /admonition %}} Grafana-managed alert rules can only be edited or deleted by users with Edit permissions for the folder storing the rules. @@ -93,6 +101,13 @@ To make a backup of your configuration and to be able to restore deleted alertin ## Before you begin +If you are using Grafana OSS: + +1. Configure your [data sources](ref:data-sources). +2. Check which [data sources](ref:compatible-data-sources) are compatible with and supported by Grafana Alerting. + +If you are using Grafana OSS, Enterprise, or Cloud: + You can use default or advanced options for Grafana-managed alert rule creation. The default options streamline rule creation with a cleaner header and a single query and condition. For more complex rules, use advanced options to add multiple queries and expressions. Default and advanced options are enabled by default for Grafana Cloud users and this feature is being rolled out progressively. @@ -257,13 +272,9 @@ Annotations add metadata to provide more information on the alert in your alert Webpage where you keep your runbook for the alert 1. Optional: Add a custom annotation -1. Optional: Add a **dashboard and panel link**. +1. Optional: **Link dashboard and panel**. - Links alert rules to panels in a dashboard. - - {{% admonition type="note" %}} - At the moment, alert rules are only supported in [time series](ref:time-series) and [alert list](ref:alert-list) visualizations. - {{% /admonition %}} + [Link the alert rule to a panel](ref:link-alert-rules-to-panels) to facilitate alert investigation. 1. Click **Save rule**. diff --git a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md deleted file mode 100644 index 3d187ba99ad..00000000000 --- a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -aliases: - - ../unified-alerting/alerting-rules/create-cortex-loki-managed-recording-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-cortex-loki-managed-recording-rule/ - - ../unified-alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ -canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ -description: Create recording rules for an external Grafana Mimir or Loki instance -keywords: - - grafana - - alerting - - guide - - rules - - recording rules - - configure -labels: - products: - - cloud - - enterprise - - oss -title: Configure recording rules -weight: 300 -refs: - configure-grafana: - - pattern: /docs/ - destination: /docs/grafana//setup-grafana/configure-grafana/ - annotation-label: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/ ---- - -# Configure recording rules - -Recording rules calculate frequently needed expressions or computationally expensive expressions in advance and save the result as a new set of time series. Querying this new time series is faster, especially for dashboards since they query the same expression every time the dashboards refresh. - -Grafana-managed recording rules offer the same Prometheus-like semantics, but allow you to query any Grafana data source supported by alerting. In addition to the above, you can use this to import and map data from other data sources into Prometheus. - -For more information on recording rules in Prometheus, refer to [Defining recording rules in Prometheus](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/). - -## Configure data source-managed recording rules - -Configure data source-managed recording rules. - -### Before you begin - -- Verify that you have write permission to the Prometheus or Loki data source. Otherwise, you will not be able to create or update Grafana Mimir managed alerting rules. - -- For Grafana Mimir and Loki data sources, enable the ruler API by configuring their respective services. - - - **Loki** - The `local` rule storage type, default for the Loki data source, supports only viewing of rules. To edit rules, configure one of the other rule storage types. - - - **Grafana Mimir** - use the `/prometheus` prefix. The Prometheus data source supports both Grafana Mimir and Prometheus, and Grafana expects that both the [Query API](/docs/mimir/latest/operators-guide/reference-http-api/#querier--query-frontend) and [Ruler API](/docs/mimir/latest/operators-guide/reference-http-api/#ruler) are under the same URL. You cannot provide a separate URL for the Ruler API. - -To configure data-source managed recording rules, complete the following steps. - -1. Click **Alerts & IRM** -> **Alerting** -> - **Alert rules**. -1. Scroll to the **Data source-managed section** and click **+New recording rule**. - -#### Enter recording rule name - -The recording rule name must be a Prometheus metric name and contain no whitespace. - -#### Define recording rule - -Select your data source and enter a query. The queries used in data source-managed recording rules always run as instant queries. - -#### Add namespace and group - -1. From the **Namespace** dropdown, select an existing rule namespace or add a new one. - - Namespaces can contain one or more rule groups and only have an organizational purpose. - -1. From the **Group** dropdown, select an existing group within the selected namespace or add a new one. - - Newly created rules are appended to the end of the group. Rules within a group are run sequentially at a regular interval, with the same evaluation time. - -#### Add labels - -1. Add custom labels selecting existing key-value pairs from the drop down, or add new labels by entering the new key or value. - -1. Click **Save rule** to save the rule or **Save rule and exit** to save the rule and go back to the Alerting page. - -## Configure Grafana-managed recording rules - -Configure Grafana-managed recording rules. - -{{< admonition type="note" >}} -In Grafana OSS and Enterprise, you can create Grafana-managed recording rules if you enable the `grafanaManagedRecordingRules` feature flag. -For Grafana Cloud, it is enabled by default. - -For more information on enabling feature toggles, refer to [Configure feature toggles](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/feature-toggles). -{{< /admonition >}} - -### Before you begin - -- Enable the `grafanaManagedRecordingRules` [feature flag](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/feature-toggles/). - -Grafana does not contain an embedded time-series database. You must bring your own Prometheus-compatible database to store series generated by Grafana-managed recording rules. - -First, enable the feature by setting `enabled = true` in the `[recording_rules]` section of the Grafana config .ini. Then, provide the URL of your Prometheus-compatible remote-write endpoint in the `url` field, along with optional credentials or headers. - -``` -[recording_rules] -enabled = true -url = http://my-example-prometheus.local:9090/api/prom/push -basic_auth_username = my-user -basic_auth_password = my-pass - -[recording_rules.custom_headers] -X-My-Header = MyValue -``` - -You must provide a URL if `enabled` is set to `true`. - -To configure Grafana-managed recording rules, complete the following steps. - -1. Click **Alerts & IRM** -> **Alerting** -> - **Alert rules**. -1. Scroll to the **Grafana-managed section** and click **+New recording rule**. - -#### Enter a recording rule and metric name - -Enter a names to identify your recording rule and metric. The metric name must be a Prometheus metric name and contain no whitespace. - -For more information, refer to [Metrics and labels](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). - -#### Define recording rule - -Define a query to get the data you want to measure and set the recording rule output. - -1. Select a data source. -1. From the **Options** dropdown, specify a time range. - -{{< admonition type="note" >}} -Grafana Alerting only supports fixed relative time ranges, for example, `now-24hr: now`. - -It does not support absolute time ranges: `2021-12-02 00:00:00 to 2021-12-05 23:59:592` or semi-relative time ranges: `now/d to: now`. -{{< /admonition >}} - -1. Add a query. - - To add multiple queries, click **Add query**. - -2. Add one or more [expressions]. - - a. For each expression, select either **Classic condition** to create a single recording rule, or choose from the **Math**, **Reduce**, and **Resample** options. - - {{% admonition type="note" %}} - When using Prometheus, you can use an instant vector and built-in functions, so you don't need to add additional expressions. - {{% /admonition %}} - - b. Click **Preview** to verify that the expression is successful. - -3. Click **Set as recording rule output** on the query or expression you want to set as your rule output. - -#### Set evaluation behavior - -Use recording rule evaluation to determine how frequently a recording rule should be evaluated. - -To do this, you need to make sure that your recording rule is in the right evaluation group with an interval that works best for your use case. - -1. Select a folder or click **+ New folder**. -1. Select an evaluation group or click **+ New evaluation group**. - - If you are creating a new evaluation group, specify the interval for the group. - - All rules within the same group are evaluated concurrently over the same time interval. Every recording rule in a group uses the same evaluation time, meaning that all queries from the same group are always aligned with each other. - -1. Turn on pause recording rule evaluation, if required. - - {{< admonition type="note" >}} - You can pause recording rule evaluation. - {{< /admonition >}} - -#### Add labels - -1. Add custom labels selecting existing key-value pairs from the drop down, or add new labels by entering the new key or value. - -1. Click **Save rule** to save the rule or **Save rule and exit** to save the rule and go back to the Alerting page. - -#### Advanced Configuration - -[min_interval](ref:configure-grafana) sets the minimum interval to enforce between rule evaluations. The default value is 10s which equals the scheduler interval. Rules are adjusted if they are less than this value or if they are not multiple of the scheduler interval (10s). Higher values can help with resource management as fewer evaluations are scheduled over time. - -This setting has precedence over each individual rule frequency. If a rule frequency is lower than this value, then this value is enforced. - -This setting applies to both Grafana-managed alert and recording rules. diff --git a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-rule.md b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-rule.md index 4e691b8a960..7877903a521 100644 --- a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-rule.md @@ -1,8 +1,8 @@ --- aliases: - - ../unified-alerting/alerting-rules/create-cortex-loki-managed-recording-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-cortex-loki-managed-recording-rule/ - - ../unified-alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ - ../unified-alerting/alerting-rules/create-mimir-loki-managed-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-mimir-loki-managed-rule/ + - ../unified-alerting/alerting-rules/edit-cortex-loki-namespace-group/ # /docs/grafana//alerting/unified-alerting/alerting-rules/edit-cortex-loki-namespace-group/ + - ../unified-alerting/alerting-rules/edit-mimir-loki-namespace-group/ # /docs/grafana//alerting/unified-alerting/alerting-rules/edit-mimir-loki-namespace-group/ canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-mimir-loki-managed-rule/ description: Configure data source-managed alert rules alert for an external Grafana Mimir or Loki instance keywords: @@ -29,16 +29,11 @@ refs: destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/ - alert-list: + link-alert-rules-to-panels: - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/alert-list/ + destination: /docs/grafana//alerting/alerting-rules/link-alert-rules-to-panels/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/alert-list/ - time-series: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/time-series/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/ --- # Configure data source-managed alert rules @@ -142,12 +137,8 @@ Annotations add metadata to provide more information on the alert in your alert Webpage where you keep your runbook for the alert 1. Optional: Add a custom annotation -1. Optional: Add a **dashboard and panel link**. +1. Optional: **Link dashboard and panel**. - Links alerts to panels in a dashboard. - - {{% admonition type="note" %}} - At the moment, alert rules are only supported in [time series](ref:time-series) and [alert list](ref:alert-list) visualizations. - {{% /admonition %}} + [Link the alert rule to a panel](ref:link-alert-rules-to-panels) to facilitate alert investigation. 1. Click **Save rule**. diff --git a/docs/sources/alerting/alerting-rules/create-recording-rules/_index.md b/docs/sources/alerting/alerting-rules/create-recording-rules/_index.md new file mode 100644 index 00000000000..e6e0e8c68ed --- /dev/null +++ b/docs/sources/alerting/alerting-rules/create-recording-rules/_index.md @@ -0,0 +1,54 @@ +--- +aliases: + - ../fundamentals/alert-rules/recording-rules/ # /docs/grafana//alerting/fundamentals/alert-rules/recording-rules/ + - ../unified-alerting/alerting-rules/create-cortex-loki-managed-recording-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-cortex-loki-managed-recording-rule/ + - ../unified-alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ # /docs/grafana//alerting/unified-alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ + - ../alerting-rules/create-mimir-loki-managed-recording-rule/ # /docs/grafana//alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/ +description: Recording rules allow you to pre-compute frequently needed or computationally expensive expressions and save the results as a new set of time series. Querying precomputed results is faster and can reduce system load. +keywords: + - grafana + - alerting + - guide + - rules + - recording rules + - configure +labels: + products: + - cloud + - enterprise + - oss +title: Create recording rules +weight: 400 +refs: + grafana-managed-recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ + data-source-managed-recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/create-data-source-managed-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/create-data-source-managed-recording-rules/ +--- + +# Configure recording rules + +Recording rules allows you to periodically pre-compute frequently used or computationally expensive queries, saving the results as a new time series metric. + +For instance, you can create a recording rule generating a new metric, `error_9001_count`, which counts occurrences of a specific log error within one minute. Then, query the `error_9001_count` metric in dashboards and alert rules. + +Recording rules can be helpful in various scenarios, such as: + +- **Faster queries** are needed: Performing heavy aggregations or querying large data sets is quicker with precomputed results than real-time queries. +- **Reducing system load:** Precomputing specific queries in advance can reduce system overload caused by multiple simultaneous queries. +- **Simplifying complex aggregations:** Create a new metric from complex aggregations to facilitate alert and dashboard setup. +- **Reusing queries across alerts:** Improve efficiency by reusing the same query across similar alert rules and dashboards. + +The evaluation group of the recording rule determines how often the metric is pre-computed. + +Similar to alert rules, Grafana supports two types of recording rules: + +1. [Grafana-managed recording rules](ref:grafana-managed-recording-rules), which can query any Grafana data source supported by alerting. +2. [Data source-managed recording rules](ref:data-source-managed-recording-rules), which can query Prometheus-based data sources like Mimir or Loki. diff --git a/docs/sources/alerting/alerting-rules/create-recording-rules/create-data-source-managed-recording-rules.md b/docs/sources/alerting/alerting-rules/create-recording-rules/create-data-source-managed-recording-rules.md new file mode 100644 index 00000000000..8e87ca4c2ce --- /dev/null +++ b/docs/sources/alerting/alerting-rules/create-recording-rules/create-data-source-managed-recording-rules.md @@ -0,0 +1,79 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/create-data-source-managed-recording-rules/ +description: Recording rules allow you to pre-compute expensive queries in advance and save the results as a new set of time series. Data source-managed recording rules can create a recording rule for Prometheus-based data sources like Mimir or Loki. +keywords: + - grafana + - alerting + - guide + - rules + - recording rules + - configure +labels: + products: + - cloud + - enterprise + - oss +title: Create data source-managed recording rules +weight: 402 +refs: + create-recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/ +--- + +# Create data source-managed recording rules + +[Recording rules](ref:create-recording-rules) allow you to periodically pre-compute frequently used or computationally expensive queries, saving the results as a new time series metric. + +Alert rules and dashboards can then query the new metric resulting from the recording rule. This is faster than querying real-time data and can help to reduce system load. + +Data source-managed recording rules can query Prometheus-based data sources like Mimir or Loki. For more information on recording rules in Prometheus, refer to [Defining recording rules in Prometheus](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/). + +Note that in data source-managed groups, the alert rules and recording rules within the same evaluation group are evaluated sequentially. This is useful to ensure that a recording rule is evaluated before any other alert rule queries the pre-computed metric. + +## Before you begin + +- Verify that you have write permission to the Prometheus or Loki data source. Otherwise, you will not be able to create or update Grafana Mimir managed alerting rules. + +- For Grafana Mimir and Loki data sources, enable the ruler API by configuring their respective services. + + - **Loki** - The `local` rule storage type, default for the Loki data source, supports only viewing of rules. To edit rules, configure one of the other rule storage types. + + - **Grafana Mimir** - use the `/prometheus` prefix. The Prometheus data source supports both Grafana Mimir and Prometheus, and Grafana expects that both the [Query API](/docs/mimir/latest/operators-guide/reference-http-api/#querier--query-frontend) and [Ruler API](/docs/mimir/latest/operators-guide/reference-http-api/#ruler) are under the same URL. You cannot provide a separate URL for the Ruler API. + +## Add new recording rule + +To create a new data source-managed recording rule: + +1. Click **Alerts & IRM** -> **Alerting** -> **Alert rules**. +1. Scroll to the **Data source-managed section** and click **+New recording rule**. + +## Enter recording rule name + +The recording rule name must be a Prometheus metric name and contain no whitespace. + +## Define recording rule + +Select your data source and enter a query. The queries used in data source-managed recording rules always run as instant queries. + +## Add namespace and group + +1. From the **Namespace** dropdown, select an existing rule namespace or add a new one. + + Namespaces can contain one or more rule groups and only have an organizational purpose. + +1. From the **Group** dropdown, select an existing group within the selected namespace or add a new one. + + Newly created rules are appended to the end of the group. Rules within a group are run sequentially at a regular interval, with the same evaluation time. + +## Add labels + +Optionally, you can add custom labels to the resulting metric by selecting existing key-value pairs from the drop down or entering the new key or value. + +## Query the new metric in dashboards or alert rules + +Click **Save rule** or **Save rule and exit** to save the rule. + +Once saved, the new recording metric is available for use in dashboards and alert rules. diff --git a/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md b/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md new file mode 100644 index 00000000000..d3b0db3632f --- /dev/null +++ b/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md @@ -0,0 +1,151 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ +description: Recording rules allow you to pre-compute expensive queries in advance and save the results as a new set of time series. Grafana-managed recording rules can create a recording rule for any data source supported by alerting. +keywords: + - grafana + - alerting + - guide + - rules + - recording rules + - configure +labels: + products: + - cloud + - enterprise + - oss +title: Create Grafana-managed recording rules +weight: 401 +refs: + expressions: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#expression-queries + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#expression-queries + create-recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/ + alerting-data-sources: + - 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 + configure-grafana-min-interval: + - pattern: /docs/ + destination: /docs/grafana//setup-grafana/configure-grafana/#min_interval + configure-feature-toggles: + - pattern: /docs/ + destination: /docs/grafana//setup-grafana/configure-grafana/feature-toggles/ +--- + +# Create Grafana-managed recording rules + +[Recording rules](ref:create-recording-rules) allow you to periodically pre-compute frequently used or computationally expensive queries, saving the results as a new time series metric. + +Alert rules and dashboards can then query the new metric resulting from the recording rule. This is faster than querying real-time data and can help to reduce system load. + +Grafana does not contain an embedded time-series database to store recording rule results. You must bring your own Prometheus-compatible database to store the series generated by recording rules. + +Grafana-managed recording rules offer the same Prometheus-like semantics but allow you to query [data sources supported by alerting](ref:alerting-data-sources). Additionally, you can use recording rules to import and map data from other data sources into Prometheus. + +{{< admonition type="note" >}} + +Grafana-managed recording rules are enabled by default in Grafana Cloud. + +In Grafana OSS and Enterprise, you must enable them by following the [Before you begin](#before-you-begin) instructions. + +{{< /admonition >}} + +To configure Grafana-managed recording rules, complete the following steps. + +## Before you begin + +This section only applies to Grafana OSS and Grafana Enterprise. + +First, enable the `grafanaManagedRecordingRules` [feature flag](ref:configure-feature-toggles). + +Then, enable the feature by setting `enabled = true` in the `[recording_rules]` section of the Grafana config .ini. Provide the URL of your Prometheus-compatible remote-write endpoint in the `url` field, along with optional credentials or headers. + +``` +[recording_rules] +enabled = true +url = http://my-example-prometheus.local:9090/api/prom/push +basic_auth_username = my-user +basic_auth_password = my-pass + +[recording_rules.custom_headers] +X-My-Header = MyValue +``` + +## Add new recording rule + +To create a new Grafana-managed recording rule: + +1. Click **Alerts & IRM** -> **Alerting** -> + **Alert rules**. +1. Scroll to the **Grafana-managed section** and click **+New recording rule**. + +1. Enter the names to identify your recording rule and metric. + + The metric name must be a Prometheus metric name and contain no whitespace. For details, refer to [Prometheus metric names](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). + +## Define recording rule + +Define a query to get the data you want to measure and set the recording rule output. + +1. Select a data source. +1. From the **Options** dropdown, specify a time range. + + {{< admonition type="note" >}} + Grafana Alerting only supports fixed relative time ranges, for example, `now-24hr: now`. + + It does not support absolute time ranges: `2021-12-02 00:00:00 to 2021-12-05 23:59:592` or semi-relative time ranges: `now/d to: now`. + {{< /admonition >}} + +1. Add a query. + + To add multiple queries, click **Add query**. + +1. Add one or more [expressions](ref:expressions). + + a. For each expression, select either **Classic condition** to create a single recording rule, or choose from the **Math**, **Reduce**, and **Resample** options. + + When using Prometheus, you can use an instant vector and built-in functions, so you don't need to add additional expressions. + + b. Click **Preview** to verify that the expression is successful. + +1. Click **Set as recording rule output** on the query or expression you want to set as your rule output. + +## Set evaluation behavior + +Use recording rule evaluation to determine how frequently a recording rule should be evaluated. + +To do this, you need to make sure that your recording rule is in the right evaluation group with an interval that works best for your use case. + +1. Select a folder or click **+ New folder**. +1. Select an evaluation group or click **+ New evaluation group**. + + If you are creating a new evaluation group, specify the interval for the group. + + All rules within the same group are evaluated concurrently over the same time interval. Every recording rule in a group uses the same evaluation time, meaning that all queries from the same group are always aligned with each other. + +1. Before or after creating a recording rule, you have the option to **Pause evaluation** if necessary. + +### Advanced configuration + +[min_interval](ref:configure-grafana-min-interval) sets the minimum interval to enforce between rule evaluations. The default value is 10s which equals the scheduler interval. Rules are adjusted if they are less than this value or if they are not multiple of the scheduler interval (10s). Higher values can help with resource management as fewer evaluations are scheduled over time. + +This setting has precedence over each individual rule frequency. If a rule frequency is lower than this value, then this value is enforced. + +This setting applies to both Grafana-managed alert and recording rules. + +## Add labels + +Optionally, you can add custom labels to the resulting metric by selecting existing key-value pairs from the drop down or entering the new key or value. + +## Query the new metric in dashboards or alert rules + +Click **Save rule** or **Save rule and exit** to save the rule. + +Once saved, the new recording metric is available for use in dashboards and alert rules. 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 new file mode 100644 index 00000000000..dbeb38ba8b4 --- /dev/null +++ b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md @@ -0,0 +1,84 @@ +--- +aliases: + - ../../alerting/alerting-rules/create-alerts-panels/ # /docs/grafana//alerting/alerting-rules/create-alerts-panels/ +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/link-alert-rules-to-panels/ +description: Grafana allows you to link alert rules with panels and dashboards. This helps connect alerts with an existing dashboard and informs alert responders on where to investigate. +keywords: + - grafana + - alerting + - panels + - create + - grafana-managed + - data source-managed +labels: + products: + - cloud + - enterprise + - oss +title: Create and link alert rules to panels +weight: 300 +refs: + time-series-visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/time-series/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series/ + annotations: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#annotations + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#annotations + view-alert-state-on-panels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/manage-notifications/view-alert-state-on-panels/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/manage-notifications/view-alert-state-on-panels/ +--- + +# Create and link alert rules to panels + +Grafana allows you to link an alert rule to a dashboard panel. This can help you: + +- Inform alert responders about where to investigate and which data to examine. +- Visualize the alert state directly from dashboards. + +An alert rule is linked to a panel by setting the [`dashboardUId` and `panelId` annotations](ref:annotations). Both annotations must be set together. + +## 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). + +1. Configure the alert rule. +1. In the **Configure notification message** section, click **Link dashboard and panel**. +1. Select an existing dashboard, then choose a panel from the selected dashboard. +1. Complete the alert rule configuration and click **Save rule** to initiate the alert rule. + +You can then [view the alert state on the panel](ref:view-alert-state-on-panels). + +{{< figure src="/media/docs/alerting/panel-displays-alert-state.png" max-width="1200px" caption="A panel displaying the alert status and state changes." >}} + +## Create alert rules from panels + +To streamline alert creation, you can create an alert rule directly from a panel. + +1. Navigate to a dashboard in the **Dashboards** section. +1. Hover over the top-right corner of a panel and click the panel menu icon. +1. From the dropdown menu, select **More...** > **New alert rule**. +1. This opens the **Edit rule** form and pre-fills some values: + - Sets the `dashboardUId` and `panelId` annotations to the corresponding dashboard and panel. + - Sets the alert rule query using the panel query. +1. Complete the alert rule configuration and click **Save rule** to initiate the alert rule. + +You can then [view the alert state on the panel](ref:view-alert-state-on-panels). + +{{% admonition type="note" %}} +Changes to panel and alert rule queries aren't synchronized. If you change a query, you have to update it in both the panel and the alert rule. +{{% /admonition %}} + +## Access linked alert rules from panels + +This option is available only in [time series panels](ref:time-series-visualizations). To access alert rules associated to a time series panel, complete the following steps. + +1. Hover over the top-right corner of the panel and click the panel menu icon. +1. Click **Edit**. +1. Click the **Alert** tab to view existing alert rules or create a new one. diff --git a/docs/sources/alerting/alerting-rules/templates/_index.md b/docs/sources/alerting/alerting-rules/templates/_index.md new file mode 100644 index 00000000000..77ef6073989 --- /dev/null +++ b/docs/sources/alerting/alerting-rules/templates/_index.md @@ -0,0 +1,215 @@ +--- +aliases: + - ../fundamentals/annotation-label/variables-label-annotation/ # /docs/grafana//alerting/fundamentals/annotation-label/variables-label-annotation/ + - ../alerting-rules/templating-labels-annotations/ # /docs/grafana//alerting-rules/templating-labels-annotations/ +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/ +description: Learn how to template annotations and labels to include data from queries and expressions in alert messages +keywords: + - grafana + - alerting + - templating + - labels + - annotations +labels: + products: + - cloud + - enterprise + - oss +title: Template annotations and labels +weight: 500 +refs: + labels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#labels + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#labels + values: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#values + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#values + annotations: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#annotations + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#annotations + explore: + - pattern: /docs/ + destination: /docs/grafana//explore/ + intro-to-templates: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/templates/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/templates/ + alert-rule-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/ + alert-rule-template-examples: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/examples/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/examples/ + notification-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ + notification-data-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/#notification-data + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/#notification-data + view-alert-state: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/manage-notifications/view-state-health/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/manage-notifications/view-state-health/ + preview-notifications: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/manage-notification-templates/#preview-notification-templates + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/manage-notification-templates/#preview-notification-templates +--- + +# Template annotations and labels + +You can use templates to customize alert and notification messages, including dynamic data from alert rule queries. + +In Grafana Alerting, you can template alert messages in two ways. + +1. **Template annotations and labels**: In the alert rule definition, you can template annotations and labels to include extra information from query data to the alert, adding meaningful details based on the query results. +1. **Template notifications**: You can template notifications to control the content and appearance of your notifications. + +## How templating works + +In this diagram, you can see the differences between both types of templates. + +{{< figure src="/media/docs/alerting/how-notification-templates-works.png" max-width="1200px" alt="How templating works" >}} + +Refer to [Templates Introduction](ref:intro-to-templates) for a more detailed explanation of this diagram. + +Both types of templates are written in the Go templating system. However, it's important to understand that variables and functions used in notification templates are different from those used in annotation and label templates. + +1. **Template annotations and labels**: These templates add extra information to individual alert instances. Template variables like [`$labels`](ref:labels) and [`$values`](ref:values) represent alert query data of the individual alert instance. +1. **Template notifications**: Notification templates format the notification content for a group of alerts. Variables like [`.Alerts`](ref:notification-data-reference) include all firing and resolved alerts in the notification. + +## Template annotations + +[Annotations](ref:annotations) add additional information to alert instances and are often used to help identify the alert and guide responders on how to address the issue. + +Annotations are key-value pairs defined in the alert rule. They can contain plain text or template code that is evaluated when the alert fires. + +Grafana includes several optional annotations, such as `description`, `summary`, `runbook_url`, `dashboardUId` and `panelId`, which can be edited in the alert rule. You can also create your custom annotations. For example, you might create a new annotation named `location` to report the location of the system that triggered the alert. + +Here’s an example of a `summary` annotation explaining why the alert was triggered, using plain text. + +``` +CPU usage has exceeded 80% for the last 5 minutes. +``` + +However, if you want to display dynamic query values in annotations, you need to use template code. Common use cases include: + +- Displaying the query value or threshold that triggered the alert. +- Highlighting label information that identifies the alert, such as environment, region, or priority. +- Providing specific instructions based on query values. +- Customizing runbook links depending on query or label values. +- Including contact information based on alert labels. + +For instance, you can template the previous example to display the specific instance and CPU value that triggered the alert. + +``` +CPU usage for {{ $labels.instance }} has exceeded 80% ({{ $values.A.Value }}) for the last 5 minutes. +``` + +Alternatively, you can use the `index` function to print query values. + +``` +CPU usage for {{ index $labels "instance" }} has exceeded 80% ({{ index $values "A" }}) for the last 5 minutes. +``` + +The result of the annotation would be as follows. + +``` +CPU usage for Instance 1 has exceeded 80% (81.2345) for the last 5 minutes. +``` + +### How to template an annotation + +Complete the following steps to template an annotation. + +1. Navigate to **Alerts & IRM** -> **Alert rules** -> create or edit an alert rule. +1. Scroll down to the **Configure notification message** section. +1. Copy in your template in the corresponding annotation field (`summary`, `description`, `runbook_url`, `custom`). + +### Preview annotation templates + +You can template annotations when creating or editing an alert rule. + +{{< figure src="/media/docs/alerting/alert-rule-using-annotation-template.png" max-width="1200px" alt="An alert rule templating the annotation summary" >}} + +Two common methods are used to test and preview annotation templates: + +1. Trigger the alert and [view the alert instance state in the Grafana UI](ref:view-alert-state), where all annotations of the alert instance are displayed. +1. Use a notification template that displays all annotations, then [preview the notification template](ref:preview-notifications) using the alert instance. + +## Template labels + +The set of [labels](ref:labels) for an alert instance is used to uniquely identify that alert among all other alert instances. + +Labels determine how alerts are routed and managed for notifications, making their design key to the effectiveness of your alerting system. + +Labels can be returned from an alert rule query, such as the `pod` label in a Kubernetes Prometheus query. You can also define additional labels in the alert rule to provide extra information for processing alerts. + +Like annotations, labels are key-value pairs that can contain plain text or template code evaluated when the alert fires. + +Template labels when the labels returned by your queries are insufficient. For instance: + +- A new label based on a query value can group a subset of alerts differently, changing how notifications are sent. +- A new label based on a query value can be used in a notification policy to alter the notification contact point. + +Here’s an example of templating a `severity` label based on the query value. + +``` +{{ if (gt $values.A.Value 90.0) -}} +critical +{{ else if (gt $values.A.Value 80.0) -}} +high +{{ else if (gt $values.A.Value 60.0) -}} +medium +{{ else -}} +low +{{- end }} +``` + +In this example, the value of the `severity` label is determined by the query value, and the possible options are `critical`, `high`, `medium`, or `low`. You can then use the `severity` label to change their notifications—for instance, sending `critical` alerts immediately or routing `low` alerts to a specific team for further review. + +{{% admonition type="note" %}} +You should avoid displaying query values in labels, as this may create numerous unique alert instances—one for each distinct label value. Instead, use annotations for query values. +{{% /admonition %}} + +### How to template a label + +Complete the following steps to template a label. + +1. Navigate to **Alerts & IRM** -> **Alert rules** -> create or edit an alert rule. +1. Scroll down to the **Configure labels and notifications** section. +1. Click **+ Add labels**. +1. Enter a **key** that identifies the label. +1. Copy in your template in the **value** field. + +### Preview label templates + +You can template label values when creating or editing an alert rule. + +To preview label values, select `Use notification policy`, and then click on `Preview routing`. + +{{< figure src="/media/docs/alerting/alert-instance-routing-preview.png" max-width="1200px" alt="Routing preview displays label values" >}} + +## More information + +For further details on how to template alert rules, refer to: + +- [Annotation and label template reference](ref:alert-rule-template-reference) +- [Annotation and label examples](ref:alert-rule-template-examples) diff --git a/docs/sources/alerting/alerting-rules/templates/examples.md b/docs/sources/alerting/alerting-rules/templates/examples.md new file mode 100644 index 00000000000..47e562544a2 --- /dev/null +++ b/docs/sources/alerting/alerting-rules/templates/examples.md @@ -0,0 +1,308 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/examples/ +description: Examples of templating labels and annotations in Grafana alert rules +keywords: + - grafana + - alerting + - templating + - labels + - annotations +labels: + products: + - cloud + - enterprise + - oss +title: Labels and annotations template examples +menuTitle: Examples +weight: 102 +refs: + labels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#labels + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#labels + annotations: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#annotations + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#annotations + alert-rule-templates: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/ + - pattern: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/ + reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/ + reference-labels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#labels + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#labels + reference-values: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#values + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#values + reference-humanize: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#humanize + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#humanize + reference-humanizepercentage: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#humanizepercentage + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#humanizepercentage + reference-match: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#match + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#match + reference-functions: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#functions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#functions + language-functions: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/language/#functions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language/#functions + language-index: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/language/#functions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language/#functions + language: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/language + - pattern: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language +--- + +# Labels and annotations template examples + +Templating allows you to add dynamic data from queries to alert labels and annotations. Dynamic data enhances alert context, making it easier for responders to quickly assess and address the issue. + +This page provides common examples for templating labels and annotations. For more information on templating, refer to: + +- [Template annotations and labels](ref:alert-rule-templates) +- [Annotation and label template reference](ref:reference) +- [Alerting template language](ref:language) + +## Annotation example + +[Annotations](ref:annotations) add extra details to alert instances and are often used to provide helpful information for identifying the issue and guiding the response. + +A common use case for annotations is to display the specific query value or threshold that triggered the alert. + +For example, you can display the query value from the [`$values`](ref:reference-values) variable to inform about the CPU value that triggered the alert. + +``` +CPU usage has exceeded 80% ({{ $values.A.value }}) for the last 5 minutes. +``` + +Alternatively, you can use the [`index()`](ref:language-index) function to retrieve the query value as follows. + +``` +CPU usage has exceeded 80% ({{ index $values "A" }}) for the last 5 minutes. +``` + +```template_output +CPU usage has exceeded 80% (81.2345) for the last 5 minutes. +``` + +### Include labels for extra details + +To provide additional context, you can include labels from the query. For instance, access the [`$labels`](ref:reference-labels) variable to display a label that informs about the affected instance: + +``` +CPU usage for {{ $labels.instance }} has exceeded 80% ({{ $values.A.Value }}) for the last 5 minutes. +``` + +```template_output +CPU usage for Instance 1 has exceeded 80% (81.2345) for the last 5 minutes. +``` + +Annotations can also be used to provide a summary of key alert labels, such as the environment and alert severity. For instance, you can display a summary of the alert with important labels like: + +``` +Alert triggered in {{ $labels.environment }} with severity {{ $labels.severity }} +``` + +```template_output +Alert triggered in production with severity critical. +``` + +### Print a range query + +To print the value of an instant query you can print its Ref ID using the `index` function or the `$values` variable: + +``` +{{ $values.A.Value }} +``` + +For range queries, reduce them from a time series to an instant vector using a reduce expression. You can then print the result by referencing its Ref ID. For example, if the reduce expression averages `A` with the Ref ID `B`, you would then print `$values.B`: + +``` +{{ $values.B.Value }} +``` + +### Humanize the value of a query + +To print the humanized value of an instant query, use the [`humanize`](ref:reference-humanize) function: + +``` +{{ humanize $values.A.Value }} +``` + +Alternatively: + +``` +{{ humanize (index $values "A").Value }} +``` + +```template_output +554.9 +``` + +To print the value of an instant query as a percentage, use the [`humanizePercentage`](ref:reference-humanizepercentage) function: + +``` +{{ humanizePercentage $values.A.Value }} +``` + +```template_output +10% +``` + +For additional functions to display or format data, refer to: + +- [Annotation and label template functions](ref:reference-functions) +- [Template language functions](ref:language-functions) + +## Label example + +[Labels](ref:labels) determine how alerts are routed and managed, ensuring that notifications reach the right teams at the right time. If the labels returned by your queries don’t fully capture the necessary context, you can create a new label and sets its value based on query data. + +### Based on query value + +Here’s an example of creating a `severity` label based on a query value: + +```go +{{ if (gt $values.A.Value 90.0) -}} +critical +{{ else if (gt $values.A.Value 80.0) -}} +high +{{ else if (gt $values.A.Value 60.0) -}} +medium +{{ else -}} +low +{{- end }} +``` + +In this example, the `severity` label is determined by the query value: + +- `critical` for values above 90, +- `high` for values above 80, +- `medium` for values above 60, +- and `low` for anything below. + +You can then use the `severity` label to control how alerts are handled. For instance, you could send `critical` alerts immediately, while routing `low` severity alerts to a team for further investigation. + +{{% admonition type="note" %}} +You should avoid displaying query values in labels, as this may create many alert instances—one for each distinct label value. Instead, use annotations to convey query values. +{{% /admonition %}} + +### Based on query label + +You can use labels to differentiate alerts coming from various environments (e.g., production, staging, dev). For example, you may want to add a label that sets the environment based on the instance’s label. Here’s how you can template it: + +```go +{{ if eq $labels.instance "prod-server-1" }}production +{{ else if eq $labels.instance "staging-server-1" }}staging +{{ else }}development +{{ end }} +``` + +This would print: + +- For instance `prod-server-1`, the label would be `production`. +- For `staging-server-1`, the label would be `staging`. +- All other instances would be labeled `development`. + +To make this template more flexible, you can use a regular expression that matches the instance name with the instance name prefix using the [`match()`](ref:reference-match) function: + +```go +{{ if match "^prod-server-.*" $labels.instance }}production +{{ else if match "^staging-server-.*" $labels.instance}}staging +{{ else }}development +{{ end }} +``` + +{{< collapse title="Legacy Alerting templates" >}} + +## Legacy Alerting templates + +For users working with Grafana's legacy alerting system, templates can still be utilized to extract useful information from alert conditions. However, it's important to note that you cannot use `$labels` to print labels from the query if you are using classic conditions, and must use `$values` instead. The reason for this is classic conditions discard these labels to enforce uni-dimensional behavior (at most one alert per alert rule). If classic conditions didn't discard these labels, then queries that returned many time series would cause alerts to flap between firing and resolved constantly as the labels would change every time the alert rule was evaluated. + +Instead, the `$values` variable contains the reduced values of all time series for all conditions that are firing. For example, if you have an alert rule with a query A that returns two time series, and a classic condition B with two conditions, then `$values` would contain `B0`, `B1`, `B2` and `B3`. If the classic condition B had just one condition, then `$values` would contain just `B0` and `B1`. + +#### Print all labels from a classic condition + +To print all labels of all firing time series use the following template (make sure to replace `B` in the regular expression with the Ref ID of the classic condition if it's different): + +```go +{{ range $k, $v := $values -}} +{{ if (match "B[0-9]+" $k) -}} +{{ $k }}: {{ $v.Labels }}{{ end }} +{{ end }} +``` + +For example, a classic condition for two time series exceeding a single condition would print: + +``` +B0: instance=server1 +B1: instance=server2 +``` + +If the classic condition has two or more conditions, and a time series exceeds multiple conditions at the same time, then its labels will be duplicated for each condition that is exceeded: + +``` +B0: instance=server1 +B1: instance=server2 +B2: instance=server1 +B3: instance=server2 +``` + +If you need to print unique labels you should consider changing your alert rules from uni-dimensional to multi-dimensional instead. You can do this by replacing your classic condition with reduce and math expressions. + +#### Print all values from a classic condition + +To print all values from a classic condition take the previous example and replace `$v.Labels` with `$v.Value`: + +```go +{{ range $k, $v := $values -}} +{{ if (match "B[0-9]+" $k) -}} +{{ $k }}: {{ $v.Value }}{{ end }} +{{ end }} +``` + +For example, a classic condition for two time series exceeding a single condition would print: + +``` +B0: 81.2345 +B1: 84.5678 +``` + +If the classic condition has two or more conditions, and a time series exceeds multiple conditions at the same time, then `$values` will contain the values of all conditions: + +``` +B0: 81.2345 +B1: 92.3456 +B2: 84.5678 +B3: 95.6789 +``` + +{{< /collapse >}} diff --git a/docs/sources/alerting/alerting-rules/templates/language.md b/docs/sources/alerting/alerting-rules/templates/language.md new file mode 100644 index 00000000000..80591c8464c --- /dev/null +++ b/docs/sources/alerting/alerting-rules/templates/language.md @@ -0,0 +1,81 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/language/ +description: Use Go template language to create your notification and alert rule templates +keywords: + - grafana + - alerting + - templates + - write templates +labels: + products: + - cloud + - enterprise + - oss +title: Alerting template language +menuTitle: Template language +refs: + alert-rule-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/ + alert-rule-template-reference-variables: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#variables + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#variables + notification-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ + reference-notificationdata: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/#notification-data + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/#notification-data +--- + +# Alerting template language + +Notification templates and alert rule templates, such as annotations and labels, both use the Go template language, [text/template](https://pkg.go.dev/text/template). + +Both types of templates can use the same keywords, functions, and comparison operators of the Go template language, such as `range`, `if`, `and`, `index`, `eq`, and more. + +However, it's important to note that because notifications and alert rules operate in distinct contexts, some additional variables and functions are only available for either notification or alert rule templates. Refer to: + +- [Annotation and label template reference](ref:alert-rule-template-reference) +- [Notification template reference](ref:notification-template-reference) + +This documentation provides an overview of the functions and operators of the Go template language that are available for both notification and alert rule templates. + +## Print + +To print the value of something, use `{{` and `}}`. You can print the value of a [variable](#variables), a field of a variable, the result of a function, or the value of dot. + +```go +{{ $values }} +{{ $values.A.Value }} +{{ humanize 1000.0 }} +{{ .Alerts }} +``` + +## Dot + +In `text/template`, there is a special cursor called dot, written as `.`. You can think of this cursor as a variable whose value changes depending on where in the template it is used. + +At the start of notification templates, dot (`.`) refers to [Notification Data](ref:reference-notificationdata). + +```go +{{ .Alerts }} +``` + +In annotation and label templates, dot (`.`) is initialized with all alert data. It’s recommended to use the [`$labels` and `$values` variables](ref:alert-rule-template-reference-variables) instead to directly access the alert labels and query values. + +{{% admonition type="note" %}} +Dot (`.`) might refer to something else when used in a [range](#range), a [with](#with), or when writing [templates](#templates) used in other templates. +{{% /admonition %}} + +[//]: <> (The above section is not included in the shared file because `refs` links are not supported in shared files.) + +{{< docs/shared lookup="alerts/template-language.md" source="grafana" version="" >}} diff --git a/docs/sources/alerting/alerting-rules/templates/reference.md b/docs/sources/alerting/alerting-rules/templates/reference.md new file mode 100644 index 00000000000..f33a8632cb3 --- /dev/null +++ b/docs/sources/alerting/alerting-rules/templates/reference.md @@ -0,0 +1,445 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/reference/ +description: Reference for variables and functions in Grafana alert rule templating. +keywords: + - grafana + - alerting + - templating + - labels + - annotations +labels: + products: + - cloud + - enterprise + - oss +title: Annotation and label template reference +menuTitle: Template reference +weight: 101 +refs: + notification-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ + language: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/language/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language/ + language-functions: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/language/#functions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language/#functions + language-index: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/language/#functions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language/#functions + print-all-labels-from-a-classic-condition: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/examples/#print-all-labels-from-a-classic-condition + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/examples/#print-all-labels-from-a-classic-condition + 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/ +--- + +# Annotation and label template reference + +Annotations and labels in alert rules can be defined using plain text. However, you can also define templates to customize their values with dynamic data from alert rule queries. + +For example, you can template the `summary` annotation to include information from query values, providing relevant alert context for responders. Refer to [Template annotations and labels](ref:template-annotations-and-labels) for various use cases. + +In templates, variables represent dynamic values from queries, while functions perform actions to transform or format this data. + +## Variables + +Variables represent dynamic values from alert rule queries that can be displayed or accessed in your templates. + +The `$` and `.` symbols are used to reference variables and their properties. You can reference variables directly in your alert rule definitions using the `$` symbol followed by the variable name. Similarly, you can access properties of variables using the dot (`.`) notation in alert rule templates. + +``` +{{ $values.A.Value }} +``` + +Templates are based on the **Go templating system**. Refer to [Template language](ref:language) for additional information. + +The following variables are available when templating annotations and labels: + +| Variables | Description | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| [$labels](#labels) | Contains all labels from the query. | +| [$values](#values) | Contains the labels and floating point values of all instant queries and expressions, indexed by their Ref IDs. | +| [$value](#value) | A string containing the labels and values of all instant queries; threshold, reduce and math expressions, and classic conditions in the alert rule. | + +### $labels + +The `$labels` variable contains all labels from the query. + +{{< figure src="/media/docs/alerting/query-labels-and_value.png" max-width="1200px" caption="An alert rule displaying labels and value from a query" >}} + +For example, suppose you have a query that returns CPU usage for all of your servers, and you have an alert rule that fires when any of your servers have exceeded 80% CPU usage for the last 5 minutes. You want to add a summary annotation to the alert that tells you which server is experiencing high CPU usage. With the `$labels` variable you can write a template that prints a human-readable sentence such as: + +``` +CPU usage for {{ $labels.instance }} has exceeded 80% for the last 5 minutes +``` + +The outcome of this template would be: + +``` +CPU usage for server1 has exceeded 80% for the last 5 minutes +``` + +> If you are using a classic condition then `$labels` will not contain any labels from the query. Classic conditions discard these labels in order to enforce uni-dimensional behavior (at most one alert per alert rule). If you want to use labels from the query in your template then use the example [here](ref:print-all-labels-from-a-classic-condition). + +### $values + +The `$values` variable is a table containing the labels and floating point values of all instant queries and expressions, indexed by their Ref IDs (e.g, `A`, `B`, `C`, etc.). It does not contain the results of range queries, as they can return hundreds or thousands of rows. + +Each Ref IDs, such as `$values.A`, has the following properties + +| Property | Type | Description | +| -------- | --------------- | ------------------------------------------------------------ | +| `Value` | Float | The value returned by the instant query or expression. | +| `Labels` | Key/value pairs | The labels associated with the instance query or expression. | + +Here's the previous example printing now the value of the instant query with Ref ID `A`: + +``` +{{ $values.A.Value }} CPU usage for {{ $labels.instance }} over the last 5 minutes. +``` + +If the alert has the label `instance=server1` and the query returns `81.2345`, the template would print: + +``` +81.2345 CPU usage for instance1 over the last 5 minutes. +``` + +If the query in Ref ID `A` is a range query rather than an instant query then add a reduce expression with Ref ID `B` and replace `$values.A.Value` with `$values.B.Value`: + +``` +{{ $values.B.Value }} CPU usage for {{ $labels.instance }} over the last 5 minutes. +``` + +Alternatively, you can use the `index()` function to retrieve the query value: + +``` +{{ index $values "B" }} CPU usage for {{ index $labels "instance" }} over the last 5 minutes. +``` + +#### $value + +The `$value` variable is a string containing the labels and values of all instant queries; threshold, reduce and math expressions, and classic conditions in the alert rule. + +This example prints the `$value` variable: + +``` +{{ $value }}: CPU usage has exceeded 80% for the last 5 minutes. +``` + +It would display something like this: + +``` +[ var='A' labels={instance=instance1} value=81.234 ]: CPU usage has exceeded 80% for the last 5 minutes. +``` + +Instead, we recommend using [$values](#values), which contains the same information as `$value` but is structured in an easier-to-use table format. + +## Functions + +Functions can perform actions in templates such as transforming or formatting data. + +Note that the [functions provided by Go's template language](ref:language-functions), such as `index`, `and`, `printf`, and `len`, are available, along with many others. + +In addition, the following functions are also available for templating annotations and labels: + +**Numbers** + +| Name | Arguments | Returns | Description | +| ----------------------------------------- | ---------------- | ------- | ---------------------------------------------------------------- | +| [humanize](#humanize) | number or string | string | Humanizes decimal numbers. | +| [humanize1024](#humanize1024) | number or string | string | Like `humanize`, but but uses 1024 as the base rather than 1000. | +| [humanizeDuration](#humanizeduration) | number or string | string | Humanizes a duration in seconds. | +| [humanizePercentage](#humanizepercentage) | number or string | string | Humanizes a ratio value to a percentage. | +| [humanizeTimestamp](#humanizetimestamp) | number or string | string | Humanizes a Unix timestamp. | +| [toTime](#totime) | number or string | time | Converts a Unix timestamp in seconds to time. | + +**Strings** + +| Name | Arguments | Returns | Description | +| ------------------------------- | -------------------------- | ------- | --------------------------------------------------------------------------------------------- | +| [title](#title) | string | string | Capitalizes the first character of each word. | +| [toUpper](#toupper) | string | string | Returns all text in uppercase. | +| [toLower](#tolower) | string | string | Returns all text in lowercase. | +| [stripPort](#stripport) | string | string | Returns only host. | +| [match](#match) | pattern, text | boolean | Matches the text against a regular expression pattern. | +| [reReplaceAll](#rereplaceall) | pattern, replacement, text | string | Replaces text matching the regular expression. | +| [graphLink](#graphlink) | expr | string | Returns the path to the graphical view in `Explore` for the given expression and data source. | +| [tableLink](#tablelink) | expr | string | Returns the path to the tabular view in `Explore` for the given expression and data source. | +| [parseDuration](#parseduration) | string | float | Parses a duration string such as "1h" into the number of seconds it represents. | +| [stripDomain](#stripdomain) | string | string | Returns the result of removing the domain part of a FQDN. | + +**Others** + +| Name | Arguments | Returns | Description | +| --------------------------- | ------------- | ---------------------- | -------------------------------------------------------------------------------- | +| [args](#args) | []interface{} | map[string]interface{} | Translates a list of objects to a map with keys arg0, arg1 etc. | +| [safeHtml](#safehtml) | string | string | Marks string as HTML not requiring auto-escaping. | +| [externalURL](#externalurl) | none | string | Returns the external URL of the Grafana server as configured in the ini file(s). | +| [pathPrefix](#pathprefix) | none | string | Returns the path of the Grafana server as configured in the ini file(s). | + +For further context on these functions, note that templating in Grafana is based on the [Prometheus template implementation](https://prometheus.io/docs/prometheus/latest/configuration/template_reference/), enabling the use of these functions and Prometheus-like templates for formatting alert messages within Grafana. + +#### humanize + +The `humanize` function humanizes decimal numbers: + +``` +{{ humanize 1000.0 }} +``` + +``` +1k +``` + +#### humanize1024 + +The `humanize1024` works similar to `humanize` but but uses 1024 as the base rather than 1000: + +``` +{{ humanize1024 1024.0 }} +``` + +``` +1ki +``` + +#### humanizeDuration + +The `humanizeDuration` function humanizes a duration in seconds: + +``` +{{ humanizeDuration 60.0 }} +``` + +``` +1m 0s +``` + +#### humanizePercentage + +The `humanizePercentage` function humanizes a ratio value between 0 and 1 to a percentage: + +``` +{{ humanizePercentage 0.2 }} +``` + +``` +20% +``` + +#### humanizeTimestamp + +The `humanizeTimestamp` function humanizes a Unix timestamp: + +``` +{{ humanizeTimestamp 1577836800.0 }} +``` + +``` +2020-01-01 00:00:00 +0000 UTC +``` + +#### toTime + +The `toTime` function converts a Unix timestamp in seconds to time.: + +``` +{{ toTime 1727802106 }} +``` + +``` +2024-10-01 17:01:46 +0000 UTC +``` + +#### title + +The `title` function capitalizes the first character of each word: + +``` +{{ title "hello, world!" }} +``` + +``` +Hello, World! +``` + +#### toUpper + +The `toUpper` function returns all text in uppercase: + +``` +{{ toUpper "Hello, world!" }} +``` + +``` +HELLO, WORLD! +``` + +#### toLower + +The `toLower` function returns all text in lowercase: + +``` +{{ toLower "Hello, world!" }} +``` + +``` +hello, world! +``` + +#### stripPort + +The `stripPort` splits string into host and port, then returns only host: + +``` +{{ stripPort "example.com:8080" }} +``` + +``` +example.com +``` + +#### match + +The `match` function matches the text against a regular expression pattern: + +``` +{{ match "a.*" "abc" }} +``` + +``` +true +``` + +#### reReplaceAll + +The `reReplaceAll` function replaces text matching the regular expression: + +``` +{{ reReplaceAll "localhost:(.*)" "example.com:$1" "localhost:8080" }} +``` + +``` +example.com:8080 +``` + +#### graphLink + +The `graphLink` function returns the path to the graphical view in [Explore](ref:explore) for the given expression and data source: + +``` +{{ graphLink "{\"expr\": \"up\", \"datasource\": \"gdev-prometheus\"}" }} +``` + +``` +/explore?left=["now-1h","now","gdev-prometheus",{"datasource":"gdev-prometheus","expr":"up","instant":false,"range":true}] +``` + +#### parseDuration + +The `parseDuration` function parses a duration string such as "1h" into the number of seconds it represents. + +``` +{{ parseDuration "1h" }} +``` + +``` +3600 +``` + +#### stripDomain + +The `stripDomain` removes the domain part of a FQDN, leaving port untouched: + +``` +{{ stripDomain "example.com:8080" }} +``` + +``` +example:8080 +``` + +#### tableLink + +The `tableLink` function returns the path to the tabular view in [Explore](ref:explore) for the given expression and data source: + +``` +{{ tableLink "{\"expr\": \"up\", \"datasource\": \"gdev-prometheus\"}" }} +``` + +``` +/explore?left=["now-1h","now","gdev-prometheus",{"datasource":"gdev-prometheus","expr":"up","instant":true,"range":false}] +``` + +#### args + +The `args` function translates a list of objects to a map with keys arg0, arg1 etc. This is intended to allow multiple arguments to be passed to templates: + +``` +{{define "x"}}{{.arg0}} {{.arg1}}{{end}}{{template "x" (args 1 "2")}} +``` + +``` +1 2 +``` + +#### safeHtml + +The `safeHtml` function marks string as HTML not requiring auto-escaping: + +``` +{{ safeHtml "Text"}} +``` + +``` +Text +``` + +#### externalURL + +The `externalURL` function returns the external URL of the Grafana server as configured in the ini file(s): + +``` +{{ externalURL }} +``` + +``` +https://example.com/grafana +``` + +#### pathPrefix + +The `pathPrefix` function returns the path of the Grafana server as configured in the ini file(s): + +``` +{{ pathPrefix }} +``` + +``` +/grafana +``` + +## Differences with notification templates + +Both notification templates and alert rule templates use the Go templating system. However, the [functions and variables available in notification templates](ref:notification-template-reference) differ from those used in annotations and labels templates, which are described in this documentation. + +Annotation and label templates operate in the context of an individual alert instance, while notification templates apply to a notification that includes a group of alert(s). + +For example, notification templates provide the `.Alerts` variable, which includes the list of all firing and resolved alerts in the notification. This variable is not available in alert rule templates, which operate within the context of a single alert instance. + +Additionally, you cannot reuse templates for labels and annotations as you can with notification templates. Instead, you need to write each template inline within the label or annotation fields and manually copy them wherever you want to reuse the templates. diff --git a/docs/sources/alerting/alerting-rules/templating-labels-annotations.md b/docs/sources/alerting/alerting-rules/templating-labels-annotations.md deleted file mode 100644 index d25fa40daec..00000000000 --- a/docs/sources/alerting/alerting-rules/templating-labels-annotations.md +++ /dev/null @@ -1,459 +0,0 @@ ---- -aliases: - - ../fundamentals/annotation-label/variables-label-annotation/ # /docs/grafana//alerting/fundamentals/annotation-label/variables-label-annotation/ -canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templating-labels-annotations/ -description: Learn about how to template labels and annotations -keywords: - - grafana - - alerting - - templating - - labels - - annotations -labels: - products: - - cloud - - enterprise - - oss -title: Template labels and annotations -weight: 500 -refs: - explore: - - pattern: /docs/ - destination: /docs/grafana//explore/ ---- - -# Template labels and annotations - -You can use templates to include data from queries and expressions in labels and annotations. For example, you might want to set the severity label for an alert based on the value of the query, or use the instance label from the query in a summary annotation so you know which server is experiencing high CPU usage. - -When using custom labels with templates it is important to make sure that the label value does not change between consecutive evaluations of the alert rule as this will end up creating large numbers of distinct alerts. However, it is OK for the template to produce different label values for different alerts. For example, do not put the value of the query in a custom label as this will end up creating a new set of alerts each time the value changes. Instead use annotations. - -All templates should be written in [text/template](https://pkg.go.dev/text/template). Regardless of whether you are templating a label or an annotation, you should write each template inline inside the label or annotation that you are templating. This means you cannot share templates between labels and annotations, and instead you will need to copy templates wherever you want to use them. - -Each template is evaluated whenever the alert rule is evaluated, and is evaluated for every alert separately. For example, if your alert rule has a templated summary annotation, and the alert rule has 10 firing alerts, then the template will be executed 10 times, once for each alert. You should try to avoid doing expensive computations in your templates as much as possible. - -{{% admonition type="caution" %}} -Extra whitespace in label templates can break matches with notification policies. -{{% /admonition %}} - -## Variables - -In Grafana templating, the `$` and `.` symbols are used to reference variables and their properties. You can reference variables directly in your alert rule definitions using the `$` symbol followed by the variable name. Similarly, you can access properties of variables using the dot (`.`) notation within alert rule definitions. - -The following variables are available to you when templating labels and annotations: - -### The labels variable - -The `$labels` variable contains all labels from the query. For example, suppose you have a query that returns CPU usage for all of your servers, and you have an alert rule that fires when any of your servers have exceeded 80% CPU usage for the last 5 minutes. You want to add a summary annotation to the alert that tells you which server is experiencing high CPU usage. With the `$labels` variable you can write a template that prints a human-readable sentence such as: - -``` -CPU usage for {{ index $labels "instance" }} has exceeded 80% for the last 5 minutes -``` - -> If you are using a classic condition then `$labels` will not contain any labels from the query. Classic conditions discard these labels in order to enforce uni-dimensional behavior (at most one alert per alert rule). If you want to use labels from the query in your template then use the example [here](#print-all-labels-from-a-classic-condition). - -### The value variable - -The `$value` variable is a string containing the labels and values of all instant queries; threshold, reduce and math expressions, and classic conditions in the alert rule. It does not contain the results of range queries, as these can return anywhere from 10s to 10,000s of rows or metrics. If it did, for especially large queries a single alert could use 10s of MBs of memory and Grafana would run out of memory very quickly. - -To print the `$value` variable in the summary you would write something like this: - -``` -CPU usage for {{ index $labels "instance" }} has exceeded 80% for the last 5 minutes: {{ $value }} -``` - -And would look something like this: - -``` -CPU usage for instance1 has exceeded 80% for the last 5 minutes: [ var='A' labels={instance=instance1} value=81.234 ] -``` - -Here `var='A'` refers to the instant query with Ref ID A, `labels={instance=instance1}` refers to the labels, and `value=81.234` refers to the average CPU usage over the last 5 minutes. - -If you want to print just some of the string instead of the full string then use the `$values` variable. It contains the same information as `$value`, but in a structured table, and is much easier to use then writing a regular expression to match just the text you want. - -### The values variable - -The `$values` variable is a table containing the labels and floating point values of all instant queries and expressions, indexed by their Ref IDs. - -To print the value of the instant query with Ref ID A: - -``` -CPU usage for {{ index $labels "instance" }} has exceeded 80% for the last 5 minutes: {{ index $values "A" }} -``` - -For example, given an alert with the labels `instance=server1` and an instant query with the value `81.2345`, this would print: - -``` -CPU usage for instance1 has exceeded 80% for the last 5 minutes: 81.2345 -``` - -If the query in Ref ID A is a range query rather than an instant query then add a reduce expression with Ref ID B and replace `(index $values "A")` with `(index $values "B")`: - -``` -CPU usage for {{ index $labels "instance" }} has exceeded 80% for the last 5 minutes: {{ index $values "B" }} -``` - -## Examples - -The following examples attempt to show the most common use-cases we have seen for templates. You can use these examples verbatim, or adapt them as necessary for your use case. For more information on how to write text/template refer see [the beginner's guide to alert notification templates in Grafana](https://grafana.com/blog/2023/04/05/grafana-alerting-a-beginners-guide-to-templating-alert-notifications/). - -### Print all labels, comma separated - -To print all labels, comma separated, print the `$labels` variable: - -``` -{{ $labels }} -``` - -For example, given an alert with the labels `alertname=High CPU usage`, `grafana_folder=CPU alerts` and `instance=server1`, this would print: - -``` -alertname=High CPU usage, grafana_folder=CPU alerts, instance=server1 -``` - -> If you are using classic conditions then `$labels` will not contain any labels from the query. Refer to [the $labels variable](#the-labels-variable) for more information. - -### Print all labels, one per line - -To print all labels, one per line, use a `range` to iterate over each key/value pair and print them individually. Here `$k` refers to the name and `$v` refers to the value of the current label: - -``` -{{ range $k, $v := $labels -}} -{{ $k }}={{ $v }} -{{ end }} -``` - -For example, given an alert with the labels `alertname=High CPU usage`, `grafana_folder=CPU alerts` and `instance=server1`, this would print: - -``` -alertname=High CPU usage -grafana_folder=CPU alerts -instance=server1 -``` - -> If you are using classic conditions then `$labels` will not contain any labels from the query. Refer to [the $labels variable](#the-labels-variable) for more information. - -### Print an individual label - -To print an individual label use the `index` function with the `$labels` variable: - -``` -The host {{ index $labels "instance" }} has exceeded 80% CPU usage for the last 5 minutes -``` - -For example, given an alert with the labels `instance=server1`, this would print: - -``` -The host server1 has exceeded 80% CPU usage for the last 5 minutes -``` - -> If you are using classic conditions then `$labels` will not contain any labels from the query. Refer to [the $labels variable](#the-labels-variable) for more information. - -### Print the value of a query - -To print the value of an instant query you can print its Ref ID using the `index` function and the `$values` variable: - -``` -{{ index $values "A" }} -``` - -For example, given an instant query that returns the value 81.2345, this will print: - -``` -81.2345 -``` - -To print the value of a range query you must first reduce it from a time series to an instant vector with a reduce expression. You can then print the result of the reduce expression by using its Ref ID instead. For example, if the reduce expression takes the average of A and has the Ref ID B you would write: - -``` -{{ index $values "B" }} -``` - -### Print the humanized value of a query - -To print the humanized value of an instant query use the `humanize` function: - -``` -{{ humanize (index $values "A").Value }} -``` - -For example, given an instant query that returns the value 81.2345, this will print: - -``` -81.234 -``` - -To print the humanized value of a range query you must first reduce it from a time series to an instant vector with a reduce expression. You can then print the result of the reduce expression by using its Ref ID instead. For example, if the reduce expression takes the average of A and has the Ref ID B you would write: - -``` -{{ humanize (index $values "B").Value }} -``` - -### Print the value of a query as a percentage - -To print the value of an instant query as a percentage use the `humanizePercentage` function: - -``` -{{ humanizePercentage (index $values "A").Value }} -``` - -This function expects the value to be a decimal number between 0 and 1. If the value is instead a decimal number between 0 and 100 you can either divide it by 100 in your query or using a math expression. If the query is a range query you must first reduce it from a time series to an instant vector with a reduce expression. - -### Set a severity from the value of a query - -To set a severity label from the value of a query use an if statement and the greater than comparison function. Make sure to use decimals (`80.0`, `50.0`, `0.0`, etc) when doing comparisons against `$values` as text/template does not support type coercion. You can find a list of all the supported comparison functions [here](https://pkg.go.dev/text/template#hdr-Functions). - -``` -{{ if (gt $values.A.Value 80.0) -}} -high -{{ else if (gt $values.A.Value 50.0) -}} -medium -{{ else -}} -low -{{- end }} -``` - -### Print all labels from a classic condition - -You cannot use `$labels` to print labels from the query if you are using classic conditions, and must use `$values` instead. The reason for this is classic conditions discard these labels to enforce uni-dimensional behavior (at most one alert per alert rule). If classic conditions didn't discard these labels, then queries that returned many time series would cause alerts to flap between firing and resolved constantly as the labels would change every time the alert rule was evaluated. - -Instead, the `$values` variable contains the reduced values of all time series for all conditions that are firing. For example, if you have an alert rule with a query A that returns two time series, and a classic condition B with two conditions, then `$values` would contain `B0`, `B1`, `B2` and `B3`. If the classic condition B had just one condition, then `$values` would contain just `B0` and `B1`. - -To print all labels of all firing time series use the following template (make sure to replace `B` in the regular expression with the Ref ID of the classic condition if it's different): - -``` -{{ range $k, $v := $values -}} -{{ if (match "B[0-9]+" $k) -}} -{{ $k }}: {{ $v.Labels }}{{ end }} -{{ end }} -``` - -For example, a classic condition for two time series exceeding a single condition would print: - -``` -B0: instance=server1 -B1: instance=server2 -``` - -If the classic condition has two or more conditions, and a time series exceeds multiple conditions at the same time, then its labels will be duplicated for each condition that is exceeded: - -``` -B0: instance=server1 -B1: instance=server2 -B2: instance=server1 -B3: instance=server2 -``` - -If you need to print unique labels you should consider changing your alert rules from uni-dimensional to multi-dimensional instead. You can do this by replacing your classic condition with reduce and math expressions. - -### Print all values from a classic condition - -To print all values from a classic condition take the previous example and replace `$v.Labels` with `$v.Value`: - -``` -{{ range $k, $v := $values -}} -{{ if (match "B[0-9]+" $k) -}} -{{ $k }}: {{ $v.Value }}{{ end }} -{{ end }} -``` - -For example, a classic condition for two time series exceeding a single condition would print: - -``` -B0: 81.2345 -B1: 84.5678 -``` - -If the classic condition has two or more conditions, and a time series exceeds multiple conditions at the same time, then `$values` will contain the values of all conditions: - -``` -B0: 81.2345 -B1: 92.3456 -B2: 84.5678 -B3: 95.6789 -``` - -## Functions - -The following functions are available to you when templating labels and annotations: - -### args - -The `args` function translates a list of objects to a map with keys arg0, arg1 etc. This is intended to allow multiple arguments to be passed to templates: - -``` -{{define "x"}}{{.arg0}} {{.arg1}}{{end}}{{template "x" (args 1 "2")}} -``` - -``` -1 2 -``` - -### externalURL - -The `externalURL` function returns the external URL of the Grafana server as configured in the ini file(s): - -``` -{{ externalURL }} -``` - -``` -https://example.com/grafana -``` - -### graphLink - -The `graphLink` function returns the path to the graphical view in [Explore](ref:explore) for the given expression and data source: - -``` -{{ graphLink "{\"expr\": \"up\", \"datasource\": \"gdev-prometheus\"}" }} -``` - -``` -/explore?left=["now-1h","now","gdev-prometheus",{"datasource":"gdev-prometheus","expr":"up","instant":false,"range":true}] -``` - -### humanize - -The `humanize` function humanizes decimal numbers: - -``` -{{ humanize 1000.0 }} -``` - -``` -1k -``` - -### humanize1024 - -The `humanize1024` works similar to `humanize` but but uses 1024 as the base rather than 1000: - -``` -{{ humanize1024 1024.0 }} -``` - -``` -1ki -``` - -### humanizeDuration - -The `humanizeDuration` function humanizes a duration in seconds: - -``` -{{ humanizeDuration 60.0 }} -``` - -``` -1m 0s -``` - -### humanizePercentage - -The `humanizePercentage` function humanizes a ratio value to a percentage: - -``` -{{ humanizePercentage 0.2 }} -``` - -``` -20% -``` - -### humanizeTimestamp - -The `humanizeTimestamp` function humanizes a Unix timestamp: - -``` -{{ humanizeTimestamp 1577836800.0 }} -``` - -``` -2020-01-01 00:00:00 +0000 UTC -``` - -### match - -The `match` function matches the text against a regular expression pattern: - -``` -{{ match "a.*" "abc" }} -``` - -``` -true -``` - -### pathPrefix - -The `pathPrefix` function returns the path of the Grafana server as configured in the ini file(s): - -``` -{{ pathPrefix }} -``` - -``` -/grafana -``` - -### tableLink - -The `tableLink` function returns the path to the tabular view in [Explore](ref:explore) for the given expression and data source: - -``` -{{ tableLink "{\"expr\": \"up\", \"datasource\": \"gdev-prometheus\"}" }} -``` - -``` -/explore?left=["now-1h","now","gdev-prometheus",{"datasource":"gdev-prometheus","expr":"up","instant":true,"range":false}] -``` - -### title - -The `title` function capitalizes the first character of each word: - -``` -{{ title "hello, world!" }} -``` - -``` -Hello, World! -``` - -### toLower - -The `toLower` function returns all text in lowercase: - -``` -{{ toLower "Hello, world!" }} -``` - -``` -hello, world! -``` - -### toUpper - -The `toUpper` function returns all text in uppercase: - -``` -{{ toUpper "Hello, world!" }} -``` - -``` -HELLO, WORLD! -``` - -### reReplaceAll - -The `reReplaceAll` function replaces text matching the regular expression: - -``` -{{ reReplaceAll "localhost:(.*)" "example.com:$1" "localhost:8080" }} -``` - -``` -example.com:8080 -``` diff --git a/docs/sources/alerting/configure-notifications/_index.md b/docs/sources/alerting/configure-notifications/_index.md index 9e334326cb5..71acdff8fc4 100644 --- a/docs/sources/alerting/configure-notifications/_index.md +++ b/docs/sources/alerting/configure-notifications/_index.md @@ -26,9 +26,9 @@ refs: destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/notification-policies/ templates-page: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/notifications/templates/ + destination: /docs/grafana//alerting/fundamentals/templates/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/templates/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/templates/ --- # Configure notifications 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 7ab9ffe5385..3daab96de2e 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/_index.md @@ -26,6 +26,16 @@ labels: title: Configure contact points weight: 410 refs: + sns: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/integrations/configure-amazon-sns/ + gchat: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/integrations/configure-google-chat/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/integrations/configure-google-chat/ email: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/integrations/configure-email/ @@ -81,27 +91,28 @@ 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/ + manage-notification-templates: + - pattern: /docs/grafana/ + 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 contact points -Use contact points to select your preferred communication channel for receiving notifications when your alert rules are firing. You can add, edit, delete, export, and test a contact point. +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. -Testing a contact point is only available for Grafana Alertmanager. +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. On the **Contact Points** tab, you can: +- Add, edit, and view contact points and integrations. - Search for name and type of contact points and integrations. -- View all existing contact points and integrations. - View how many notification policies each contact point is being used for and navigate directly to the linked notification policies. - View the status of notification deliveries. - Export individual contact points or all contact points in JSON, YAML, or Terraform format. - 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. -On the **Notification templates** tab, you can: - -- View, edit, copy or delete existing notification templates. - ## Add a contact point Complete the following steps to add a contact point. @@ -114,41 +125,23 @@ Complete the following steps to add a 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. In Notification settings, optionally select **Disable resolved message** if you do not want to be notified when an alert resolves. -1. To add another contact point integration, click **Add contact point integration** and repeat steps 6 through 8. 1. Save your changes. -## Use notification templates +## Add another contact point integration -Use templates in contact points to customize your notifications. +A contact point can have multiple integrations, or destinations for sending notifications. -Complete the following steps to add templates to your contact point. +To add another integration to a contact point, complete the following steps. -1. Click an existing contact point or create a new one -1. In **Optional settings**, click any field that contains templates. - - For example, if you are creating an email contact point integration, click **Message** or **Subject**. - -1. Click **Edit**. - A dialog box opens where you can select templates. -1. [Optional] Click **Select existing template** to select a template and preview it using the default payload. - - Click **Save** to use just a single template in the field. - - You can also copy the selected template and use it in the custom tab. - -1. [Optional] Click **Enter custom message** to customize and edit the field directly. Note that the title changes depending on the field you are editing. - - Click **Save** to use just a single template in the field. - -1. You can switch between the two tabs to access the list of available templates and copy them across to the customized version. - -1. Click **Save contact point**. +1. Add or edit an existing contact point. +1. Click **Add contact point integration** and repeat the same steps as [Add a contact point](#add-a-contact-point). + - From **Integration**, select a type and fill out mandatory fields. + - In **Optional settings**, specify additional settings for the selected contact point integration. +1. Save your changes. ## Test a contact point -** For Grafana Alertmanager only.** - -Complete the following steps to test a contact point. +Testing a contact point is only available for Grafana Alertmanager. Complete the following steps to test a contact point. 1. In the left-side menu, click **Alerts & IRM** and then **Alerting**. 1. Click **Contact points** to view a list of existing contact points. @@ -166,17 +159,17 @@ The following table lists the contact point integrations supported by Grafana. | Name | Type | | ---------------------------- | ------------------------- | | Alertmanager | `prometheus-alertmanager` | -| Amazon SNS | `sns` | +| [Amazon SNS](ref:sns) | `sns` | | Cisco Webex Teams | `webex` | | DingDing | `dingding` | | [Discord](ref:discord) | `discord` | | [Email](ref:email) | `email` | -| Google Chat | `googlechat` | +| [Google Chat](ref:gchat) | `googlechat` | | [Grafana Oncall](ref:oncall) | `oncall` | | Kafka REST Proxy | `kafka` | -| [MQTT](ref:mqtt) | `mqtt` | | Line | `line` | | [Microsoft Teams](ref:teams) | `teams` | +| [MQTT](ref:mqtt) | `mqtt` | | [Opsgenie](ref:opsgenie) | `opsgenie` | | [Pagerduty](ref:pagerduty) | `pagerduty` | | Pushover | `pushover` | @@ -185,7 +178,17 @@ The following table lists the contact point integrations supported by Grafana. | [Telegram](ref:telegram) | `telegram` | | Threema Gateway | `threema` | | VictorOps | `victorops` | -| WeCom | `wecom` | | [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). + +## 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. 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 e0209ac8f50..84274e64043 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 @@ -13,7 +13,7 @@ labels: - oss menuTitle: Amazon SNS title: Configure Amazon SNS for Alerting -weight: 0 +weight: 100 --- # Configure Amazon SNS for Alerting diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-discord.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-discord.md index 399c3b31579..354fed75faf 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-discord.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-discord.md @@ -13,7 +13,7 @@ labels: - oss menuTitle: Discord title: Configure Discord for Alerting -weight: 0 +weight: 105 --- # Configure Discord for Alerting diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-email.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-email.md index 3b2b1898dc5..02db8cf0172 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-email.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-email.md @@ -13,13 +13,21 @@ labels: - oss menuTitle: Email title: Configure email for Alerting -weight: 0 +weight: 110 +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 email for Alerting Use the Grafana Alerting - email integration to send email notifications when your alerts are firing. An email is sent when an alert fires and when an alert gets resolved. +Note that you can customize the `subject` and `message` of the email using [notification templates](ref:notification-templates). However, you cannot add HTML and CSS to email notifications for visual changes. + ## Before you begin {{}} diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-google-chat.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-google-chat.md index b0440d06ff2..712bd1b0cbc 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-google-chat.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-google-chat.md @@ -13,7 +13,7 @@ labels: - oss menuTitle: Google Chat title: Configure Google Chat for Alerting -weight: 0 +weight: 115 --- # Configure Google Chat for Alerting diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt.md index 666ea653e18..1c3e06c1243 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-mqtt.md @@ -12,9 +12,9 @@ labels: - cloud - enterprise - oss -menuTitle: MQTT notifier +menuTitle: MQTT title: Configure the MQTT notifier for Alerting -weight: 0 +weight: 140 --- # Configure the MQTT notifier for Alerting diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-oncall.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-oncall.md index 3cfed955a00..c4dce18c44f 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-oncall.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-oncall.md @@ -2,6 +2,7 @@ aliases: - ../../../alerting-rules/manage-contact-points/configure-oncall/ # /docs/grafana//alerting/alerting-rules/manage-contact-points/configure-oncall/ - ../../../alerting-rules/manage-contact-points/integrations/configure-oncall/ # /docs/grafana//alerting/alerting-rules/manage-contact-points/integrations/configure-oncall/ + - ../configure-oncall/ # /docs/grafana//alerting/alerting-rules/manage-contact-points/configure-oncall/ canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/integrations/configure-oncall/ description: Configure the Alerting - Grafana OnCall integration to connect alerts generated by Grafana Alerting with Grafana OnCall keywords: @@ -9,8 +10,6 @@ keywords: - alerting - oncall - integration -aliases: - - ../configure-oncall/ # /docs/grafana//alerting/alerting-rules/manage-contact-points/configure-oncall/ labels: products: - cloud @@ -18,7 +17,7 @@ labels: - oss menuTitle: Grafana OnCall title: Configure Grafana OnCall for Alerting -weight: 0 +weight: 120 --- # Configure Grafana OnCall for Alerting @@ -26,7 +25,7 @@ weight: 0 Use the Grafana Alerting - Grafana OnCall integration to effortlessly connect alerts generated by Grafana Alerting with Grafana OnCall, where you can then route them according to defined escalation chains and schedules. {{< admonition type="note" >}} -You can also configure the integration from Grafana OnCall. For more information, refer to [Grafana OnCall documentation](http://grafana.com/docs/oncall/latest/integrations/grafana-alerting/). +You can also configure the integration from Grafana OnCall. For more information, refer to [Grafana OnCall documentation](http://grafana.com/docs/oncall/latest/configure/integrations/references/grafana-alerting/). {{< /admonition >}} ## Before you begin diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md index e1f455af2dc..feb4d406caa 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md @@ -13,7 +13,7 @@ labels: - oss menuTitle: Opsgenie title: Configure Opsgenie for Alerting -weight: 0 +weight: 145 --- # Configure Opsgenie for Alerting diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-slack.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-slack.md index 359cebc1bd3..8f56174ed60 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-slack.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-slack.md @@ -13,8 +13,13 @@ labels: - oss menuTitle: Slack title: Configure Slack for Alerting -weight: 0 +weight: 155 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/ nested-policy: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/configure-notifications/create-notification-policy/#add-new-nested-policy @@ -36,9 +41,9 @@ There are two ways of integrating Slack into Grafana Alerting. Webhooks is the simpler way to post messages into Slack. Slack automatically creates a bot user with all the necessary permissions to post messages to one particular channel of your choice. -{{< admonition type="note" >}} -Grafana Alerting only allows one Slack channel per contact point. -{{< /admonition >}} +Note that you can only setup one Slack channel per contact point. + +You can customize the `title` and `body` of the Slack message using [notification templates](ref:notification-templates); however, you cannot modify its visual appearance with custom blocks. ## Before you begin @@ -53,17 +58,12 @@ If you are using a Slack API Token, complete the following steps. 1. Right click the channel you want to receive notifications in. 1. Click View channel details. 1. Scroll down and copy the Channel ID. - {{< admonition type="note" >}} - While going through these steps, Slack may prompt you to Reinstall your app in order for the changes to take effect. - {{< /admonition >}} + + Note that while going through these steps, Slack may prompt you to Reinstall your app in order for the changes to take effect. ### Webhook URL -If you are using a Webhook URL, follow steps 1 and 5 in the [Slack API Quickstart](https://api.slack.com/start/quickstart). - -{{< admonition type="note" >}} -Make sure you copy the Slack app Webhook URL. You need this when setting up your contact point integration in Grafana Alerting. -{{< /admonition >}} +If you are using a Webhook URL, follow steps 1 and 5 in the [Slack API Quickstart](https://api.slack.com/start/quickstart), and copy the Slack app Webhook URL. You need this when setting up your contact point integration in Grafana Alerting. ## Procedure diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-teams.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-teams.md index 4f720bb616d..0dae7c4d038 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-teams.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-teams.md @@ -13,13 +13,21 @@ labels: - oss menuTitle: Microsoft Teams title: Configure Microsoft Teams for Alerting -weight: 0 +weight: 135 +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 Microsoft Teams for Alerting Use the Grafana Alerting - Microsoft Teams integration to receive notifications in your team’s channel when your alerts are firing. +Note that you can customize the `title` and `message` of the notification using [notification templates](ref:notification-templates); however, you cannot modify its visual appearance with adaptive cards. + ## Before you begin To set up Microsoft Teams for integration with Grafana Alerting, create a new workflow that accepts Webhook requests. This allows Grafana to send alert notifications to Microsoft Teams channels. diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-telegram.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-telegram.md index 1c86f94d0cc..260e05717cc 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-telegram.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-telegram.md @@ -13,7 +13,7 @@ labels: - oss menuTitle: Telegram title: Configure Telegram for Alerting -weight: 0 +weight: 160 --- # Configure Telegram for Alerting diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/pager-duty.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/pager-duty.md index ebfdec1f364..ac5f381464c 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/pager-duty.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/pager-duty.md @@ -14,7 +14,7 @@ labels: - oss menuTitle: PagerDuty title: Configure PagerDuty for Alerting -weight: 0 +weight: 150 --- # Configure PagerDuty for Alerting 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 a1329970152..6562b948095 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 @@ -19,9 +19,15 @@ labels: - cloud - enterprise - oss -menuTitle: Webhook notifier +menuTitle: Webhook title: Configure the webhook notifier for Alerting -weight: 0 +weight: 165 +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 the webhook notifier for Alerting @@ -139,7 +145,11 @@ The webhook notification is a simple way to send information about a state chang | imageURL | string | URL of a screenshot of a panel assigned to the rule that created this notification | {{< admonition type="note" >}} -Alert rules are not coupled to dashboards anymore therefore the fields related to dashboards `dashboardId` and `panelId` have been removed. + +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 diff --git a/docs/sources/alerting/configure-notifications/template-notifications/_index.md b/docs/sources/alerting/configure-notifications/template-notifications/_index.md index a3947af0eaa..88b4b1ffd37 100644 --- a/docs/sources/alerting/configure-notifications/template-notifications/_index.md +++ b/docs/sources/alerting/configure-notifications/template-notifications/_index.md @@ -13,55 +13,98 @@ labels: - cloud - enterprise - oss -title: Configure notification messages +title: Template notifications weight: 430 refs: - use-notification-templates: + template-annotations-and-labels: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/use-notification-templates/ + destination: /docs/grafana//alerting/alerting-rules/templates/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/use-notification-templates/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/ + manage-notification-templates: + - pattern: /docs/grafana/ + 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/ reference: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ - using-go-templating-language: + examples: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/using-go-templating-language/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/examples/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/using-go-templating-language/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/examples/ --- -# Configure notification messages +# Template notifications -Customize the content of your notifications with notifications templates. +You can use notification templates to change the title, message, and format of notifications. -You can use notification templates to change the title, message, and format of the message in your notifications. +Grafana provides a **default template** for notification titles (`default.title`) and one default template for notification messages (`default.message`). Both templates display common alert details. -Notification templates are not tied to specific contact point integrations, such as email or Slack. However, you can choose to create separate notification templates for different contact point integrations. +You can also create a notification template to customize the content and format of your notification messages. For example: -You can use notification templates to: - -- Customize content: Personalize the subject of an email or the title of a message. Modify text within notifications, like selecting or omitting certain labels, annotations, and links. Format text with bold and italic styles, and add or remove line breaks. +- Personalize the subject of an email or the title of a message. +- Modify text within notifications, like selecting or omitting certain labels, annotations, and links. +- Format text with bold and italic styles, and add or remove line breaks. However, there are limitations. You cannot: -- Modify Visual Appearance: Add HTML and CSS to email notifications for visual changes. Alter the design of notifications in messaging services like Slack and Microsoft Teams, such as adding custom blocks or adaptive cards. -- Manage Media and Data: Adjust the number and size of images or their placement in notifications. Customize webhook data structure or format, including JSON fields or sending data in XML. Modify HTTP headers in webhooks beyond those in the contact point configuration. +- Modify Visual Appearance: Add HTML or CSS to email notifications for visual changes. Alter the design of notifications in messaging services like Slack or Microsoft Teams, such as adding custom blocks or adaptive cards. +- Manage Media and Data: Customize the data structure or format passed to the templates, like adding new JSON fields or sending XML data for webhooks. Modify HTTP headers in webhooks beyond those defined in the configuration, or adjust the number, size, or placement of images. -## Learn more +Here's an [example](ref:examples) that displays the summary and description annotations for each alert in the notification: -[Using Go's templating language](ref:using-go-templating-language) +```go +{{ define "custom.alerts" -}} +{{ len .Alerts }} alert(s) +{{ range .Alerts -}} + {{ template "alert.summary_and_description" . -}} +{{ end -}} +{{ end -}} +{{ define "alert.summary_and_description" }} + Summary: {{.Annotations.summary}} + Status: {{ .Status }} + Description: {{.Annotations.description}} +{{ end -}} +``` -Learn how to write the content of your notification templates in Go’s templating language. +The notification message would look like this: -Create reusable notification templates for your contact points. +``` +2 alert(s) -[Use notification templates](ref:use-notification-templates) + Summary: The database server db1 has exceeded 75% of available disk space. + Status: firing + Description: This alert fires when a database server is at risk of running out of disk space. You should take measures to increase the maximum available disk space as soon as possible to avoid possible corruption. -Use notification templates to send notifications to your contact points. + Summary: The web server web1 has been responding to 5% of HTTP requests with 5xx errors for the last 5 minutes. + Status: resolved + Description: This alert fires when a web server responds with more 5xx errors than is expected. This could be an issue with the web server or a backend service. +``` -[Reference](ref:reference) +{{% admonition type="note" %}} +Avoid adding extra information about alert instances in notification templates, as this information will only be visible in the notification message. -Data that is available when writing templates. +Instead, you should [use annotations or labels](ref:template-annotations-and-labels) to add information directly to the alert, ensuring it's also visible in the alert state and alert history within Grafana. You can then print the new alert annotation or label in notification templates. +{{% /admonition %}} + +#### Select a notification template for a contact point + +Notification templates are not tied to specific contact point integrations, such as email or Slack, and the same template can be shared across multiple contact points. + +The notification template is assigned to the contact point to determine the notification message sent to contact point integrations. + +{{< figure src="/media/docs/alerting/how-notification-templates-works.png" max-width="1200px" caption="A flow of the alert notification process, from querying the alert rule to sending the alert notification message." >}} + +By default, Grafana provides default templates, such as `{{define "default.title"}}` and `{{define "default.message"}}`, to format notification messages. + +## More information + +For further details on how to write notification templates, refer to: + +- [Select, create, and preview a notification template](ref:manage-notification-templates) +- [Notification template reference](ref:reference) +- [Notification template examples](ref:examples) diff --git a/docs/sources/alerting/configure-notifications/template-notifications/create-notification-templates.md b/docs/sources/alerting/configure-notifications/template-notifications/create-notification-templates.md deleted file mode 100644 index 0e6d8a78eb3..00000000000 --- a/docs/sources/alerting/configure-notifications/template-notifications/create-notification-templates.md +++ /dev/null @@ -1,376 +0,0 @@ ---- -aliases: - - ../../manage-notifications/template-notifications/create-notification-templates/ # /docs/grafana//alerting/manage-notifications/template-notifications/create-notification-templates/ -canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/create-notification-templates/ -description: Create notification templates to sent to your contact points -keywords: - - grafana - - alerting - - notifications - - templates - - create templates - - edit templates - - delete templates -labels: - products: - - cloud - - enterprise - - oss -title: Create notification templates -weight: 200 ---- - -# Create notification templates - -Create reusable notification templates to send to your contact points. - -You can add one or more templates to your notification template. - -Your notification template name must be unique. You cannot have two templates with the same name in the same notification template or in different notification templates. Avoid defining templates with the same name as default templates, such as: `__subject`, `__text_values_list`, `__text_alert_list`, `default.title` and `default.message`. - -To create a notification template, complete the following steps. - -1. Click **Alerts & IRM** -> **Contact points**. -1. Click the **Notification Templates** tab and then **+ Add notification template**. - -1. Enter a name for the notification template. - -1. Write the content of the template in the content field. - -1. Save your changes. - -`{{ define "email.subject" }}` and `{{ end }}` is automatically added to the start and end of the content: - -To create a notification template that contains more than one template: - -1. Click **+ Add notification template**. - -2. Enter a name for the notification template. - -3. Write each template in the Content field, including `{{ define "name-of-template" }}` and `{{ end }}` at the start and end of each template. - -4. Save your changes. - -## Preview notification templates - -Preview how your notification templates should look before using them in your contact points, helping you understand the result of the template you are creating as well as enabling you to fix any errors before saving it. - -**Note:** This feature is only for Grafana Alertmanager. - -To preview your notification templates: - -1. Navigate to **Alerts&IRM** -> **Alerting** -> **Contact points** -> **Notification Templates**. -1. Click **+ Add notification template** or edit an existing template. -1. Add or update your template content. - - Default data is provided and you can add or edit alert data to it as well as alert instances. You can add alert data directly in the Payload data window itself or click **Select alert instances** or **Add custom alerts**. - -1. Optional: To add alert data from existing alert instances: - - a. Click **Select alert instances**. - - b. Hover over the alert instances to view more information on each alert instance. - - c. Click **Confirm** to add the alert instance(s) to the payload. - -1. Optional: To add alert data using the Alert data editor, click **Add custom data:** - - a. Add annotations, custom labels and/or set a dashboard or a panel. - - b. Toggle Firing/resolved depending on whether you want to add firing or resolved alerts to your notification. - - c. Click **Add alert data**. - - d. Click **Refresh preview** to see what your template content should look like and the corresponding payload data. - - If there are any errors in your template, they are displayed in the Preview and you can correct them before saving. - -1. Save your changes. - -## Template the subject of an email - -Template the subject of an email to contain the number of firing and resolved alerts: - -``` -1 firing alert(s), 0 resolved alerts(s) -``` - -1. Create a template called `email.subject` with the following content: - - ``` - {{ define "email.subject" }} - {{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s) - {{ end }} - ``` - -2. Execute the template from the subject field in your contact point integration: - - ``` - {{ template "email.subject" . }} - ``` - -## Template the message of an email - -Template the message of an email to contain a summary of all firing and resolved alerts: - -``` -There are 2 firing alert(s), and 1 resolved alert(s) - -Firing alerts: - -- alertname=Test 1 grafana_folder=GrafanaCloud has value(s) B=1 -- alertname=Test 2 grafana_folder=GrafanaCloud has value(s) B=2 - -Resolved alerts: - -- alertname=Test 3 grafana_folder=GrafanaCloud has value(s) B=0 -``` - -1. Create a notification template called `email` with two templates in the content: `email.message_alert` and `email.message`. - - The `email.message_alert` template is used to print the labels and values for each firing and resolved alert while the `email.message` template contains the structure of the email. - - ``` - {{- define "email.message_alert" -}} - {{- range .Labels.SortedPairs }}{{ .Name }}={{ .Value }} {{ end }} has value(s) - {{- range $k, $v := .Values }} {{ $k }}={{ $v }}{{ end }} - {{- end -}} - - {{ define "email.message" }} - There are {{ len .Alerts.Firing }} firing alert(s), and {{ len .Alerts.Resolved }} resolved alert(s) - - {{ if .Alerts.Firing -}} - Firing alerts: - {{- range .Alerts.Firing }} - - {{ template "email.message_alert" . }} - {{- end }} - {{- end }} - - {{ if .Alerts.Resolved -}} - Resolved alerts: - {{- range .Alerts.Resolved }} - - {{ template "email.message_alert" . }} - {{- end }} - {{- end }} - - {{ end }} - ``` - -2. Execute the template from the message field in your contact point integration: - - ``` - {{ template "email.message" . }} - ``` - -## Group multiple alert instances into one email notification - -To make alerts more concise, you can group multiple instances of a firing alert into a single email notification in a table format. This way, you avoid long, repetitive emails and make alerts easier to digest. - -Follow these steps to create a custom notification template that consolidates alert instances into a table. - -1. Modify the alert rule to include an annotation that is referenced in the notification template later on. -1. Enter a name for the **custom annotation**: In this example, _ServerInfo_. -1. Enter the following code as the value for the annotation. It retrieves the server's instance name and a corresponding metric value, formatted as a table row: - - ``` - {{ index $labels "instance" }}{{- "\t" -}}{{ index $values "A"}}{{- "\n" -}} - ``` - - This line of code returns the labels and their values in the form of a table. Assuming $labels has `{"instance": "node1"}` and $values has `{"A": "123"}`, the output would be: - - ``` - node1 123 - ``` - -1. Create a notification template that references the _ServerInfo_ annotation. - - ```go - {{ define "Table" }} - {{- "\nHost\t\tValue\n" -}} - {{ range .Alerts -}} - {{ range .Annotations.SortedPairs -}} - {{ if (eq .Name "ServerInfo") -}} - {{ .Value -}} - {{- end }} - {{- end }} - {{- end }} - {{ end }} - ``` - - The notification template outputs a list of server information from the "ServerInfo" annotation for each alert instance. - -1. Navigate to your contact point in Grafana -1. In the **Message** field, reference the template by name (see **Optional Email settings** section): - - ``` - {{ template "Table" . }} - ``` - - This generates a neatly formatted table in the email, grouping information for all affected servers into a single notification. - -## Conditional notification template - -Template alert notifications based on a label. In this example the label represents a namespace. - -1. Use the following code in your notification template to display different messages based on the namespace: - - ```go - {{ define "my_conditional_notification" }} - {{ if eq .CommonLabels.namespace "namespace-a" }} - Alert: CPU limits have reached 80% in namespace-a. - {{ else if eq .CommonLabels.namespace "namespace-b" }} - Alert: CPU limits have reached 80% in namespace-b. - {{ else if eq .CommonLabels.namespace "namespace-c" }} - Alert: CPU limits have reached 80% in namespace-c. - {{ else }} - Alert: CPU limits have reached 80% for {{ .CommonLabels.namespace }} namespace. - {{ end }} - {{ end }} - ``` - - `.CommonLabels` is a map containing the labels that are common to all the alerts firing. - - Make sure to replace the `.namespace` label with a label that exists in your alert rule. - -1. Replace `namespace-a`, `namespace-b`, and `namespace-c` with your specific namespace values. -1. Navigate to your contact point in Grafana -1. In the **Message** field, reference the template by name (see **Optional settings** section): - - ``` - {{ template "my_conditional_notification" . }} - ``` - - This template alters the content of alert notifications depending on the namespace value. - -## Template the title of a Slack message - -Template the title of a Slack message to contain the number of firing and resolved alerts: - -``` -1 firing alert(s), 0 resolved alerts(s) -``` - -1. Create a template called `slack.title` with the following content: - - ``` - {{ define "slack.title" }} - {{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s) - {{ end }} - ``` - -2. Execute the template from the title field in your contact point integration: - - ``` - {{ template "slack.title" . }} - ``` - -## Template the content of a Slack message - -Template the content of a Slack message to contain a description of all firing and resolved alerts, including their labels, annotations, Silence URL and Dashboard URL. - -**Note:** - -This template is for Grafana-managed alerts only. -To use the template for Grafana Mimir/Loki-managed alerts, delete the references to DashboardURL and SilenceURL. -For more information, see the [Prometheus documentation on notifications](https://prometheus.io/docs/alerting/latest/notifications/). - -``` -1 firing alert(s): - -[firing] Test1 -Labels: -- alertname: Test1 -- grafana_folder: GrafanaCloud -Annotations: -- description: This is a test alert -Silence: https://example.com/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTest1&matcher=grafana_folder%3DGrafanaCloud -Go to dashboard: https://example.com/d/dlhdLqF4z?orgId=1 - -1 resolved alert(s): - -[firing] Test2 -Labels: -- alertname: Test2 -- grafana_folder: GrafanaCloud -Annotations: -- description: This is another test alert -Silence: https://example.com/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTest2&matcher=grafana_folder%3DGrafanaCloud -Go to dashboard: https://example.com/d/dlhdLqF4z?orgId=1 -``` - -1. Create a template called `slack` with two templates in the content: `slack.print_alert` and `slack.message`. - - The `slack.print_alert` template is used to print the labels, annotations, SilenceURL and DashboardURL while the `slack.message` template contains the structure of the notification. - - ``` - {{ define "slack.print_alert" -}} - [{{.Status}}] {{ .Labels.alertname }} - Labels: - {{ range .Labels.SortedPairs -}} - - {{ .Name }}: {{ .Value }} - {{ end -}} - {{ if .Annotations -}} - Annotations: - {{ range .Annotations.SortedPairs -}} - - {{ .Name }}: {{ .Value }} - {{ end -}} - {{ end -}} - {{ if .SilenceURL -}} - Silence: {{ .SilenceURL }} - {{ end -}} - {{ if .DashboardURL -}} - Go to dashboard: {{ .DashboardURL }} - {{- end }} - {{- end }} - - {{ define "slack.message" -}} - {{ if .Alerts.Firing -}} - {{ len .Alerts.Firing }} firing alert(s): - {{ range .Alerts.Firing }} - {{ template "slack.print_alert" . }} - {{ end -}} - {{ end }} - {{ if .Alerts.Resolved -}} - {{ len .Alerts.Resolved }} resolved alert(s): - {{ range .Alerts.Resolved }} - {{ template "slack.print_alert" .}} - {{ end -}} - {{ end }} - {{- end }} - ``` - -2. Execute the template from the text body field in your contact point integration: - - ``` - {{ template "slack.message" . }} - ``` - -## Template both email and Slack with shared templates - -Instead of creating separate notification templates for email and Slack, you can share the same template. - -For example, if you want to send an email with this subject and Slack message with this title: - -``` -1 firing alert(s), 0 resolved alerts(s) -``` - -1. Create a template called `common.subject_title` with the following content: - - ``` - {{ define "common.subject_title" }} - {{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s) - {{ end }} - ``` - -2. For email, execute the template from the subject field in your email contact point integration: - - ``` - {{ template "common.subject_title" . }} - ``` - -3. For Slack, execute the template from the title field in your Slack contact point integration: - - ``` - {{ template "common.subject_title" . }} - ``` diff --git a/docs/sources/alerting/configure-notifications/template-notifications/examples.md b/docs/sources/alerting/configure-notifications/template-notifications/examples.md new file mode 100644 index 00000000000..91a1608f101 --- /dev/null +++ b/docs/sources/alerting/configure-notifications/template-notifications/examples.md @@ -0,0 +1,362 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/examples/ +description: Examples of notification templates +keywords: + - grafana + - alerting + - templating + - notification templates +labels: + products: + - cloud + - enterprise + - oss +title: Notification template examples +menuTitle: Examples +weight: 103 +refs: + 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/ + template-notifications: + - 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/ + manage-notification-templates: + - pattern: /docs/grafana/ + 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/ + reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ + reference-notification-data: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/#notification-data + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/#notification-data + reference-alert: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/#alert + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/#alert + language: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/language/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/ + group-alert-notifications: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/group-alert-notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/ +--- + +# Notification template examples + +Notification templates allows you to change the default notification messages. + +You can modify the content and format of notification messages. For example, you can customize the content to show only specific information or adjust the format to suit a particular contact point, such as Slack or Email. + +{{% admonition type="note" %}} +Avoid adding extra information about alert instances in notification templates, as this information is only visible in the notification message. + +Instead, you should [use annotations or labels](ref:template-annotations-and-labels) to add information directly to the alert, ensuring it's also visible in the alert state and alert history within Grafana. You can then print the new alert annotation or label in notification templates. +{{% /admonition %}} + +This page provides various examples illustrating how to template common notification messages. For more details about notification templates, refer to: + +- [Template notifications](ref:template-notifications) +- [Select, create, and preview a notification template](ref:manage-notification-templates) +- [Notification template reference](ref:reference) + +## Basic examples + +Notification templates can access the [notification data](ref:reference-notification-data) using the dot (`.`). The following examples demonstrate some basic uses of the [template language](ref:language). + +For instance, to check if there are common labels (`.CommonLabels`) for all alerts in the notification, use `if`: + +```go +{{ define "custom_message" -}} +{{ if .CommonLabels }} +Alerts have common labels +{{ else }} +There are no common labels +{{ end }} +{{ end }} +``` + +To iterate on the alerts in the notification and print a specific label, use `range` and `index`: + +```go +{{ define "custom_message" -}} +{{ range .Alerts }} +The name of the alert is {{ index .Labels "alertname" }} +{{ end }} +{{ end }} +``` + +Alternatively, you can use the `.` notation to print the value of the key. + +```go +{{ define "custom_message" -}} +{{ range .Alerts }} +The name of the alert is {{ .Labels.alertname }} +{{ end }} +{{ end }} +``` + +```template_output +The name of the alert is InstanceDown + +The name of the alert is CpuOverload +``` + +## Print alerts with summary and description + +Here's an example that displays the summary and description annotations for each alert in the notification. + +```go +{{ define "custom.alerts" -}} +{{ len .Alerts }} alert(s) +{{ range .Alerts -}} + {{ template "alert.summary_and_description" . -}} +{{ end -}} +{{ end -}} +{{ define "alert.summary_and_description" }} + Summary: {{.Annotations.summary}} + Status: {{ .Status }} + Description: {{.Annotations.description}} +{{ end -}} +``` + +In this example: + +- A template (`alert.summary_and_description`) is defined to print the `summary`, `status`, and `description` of one [alert](ref:reference-alert). +- The main template `custom.alerts` iterates the list of alerts (`.Alerts`) in [notification data](ref:reference-notification-data), executing the `alert.summary_and_description` template to print the details of each alert. + +The notification message would look like this: + +```template_output +2 alert(s) + + Summary: The database server db1 has exceeded 75% of available disk space. + Status: firing + Description: This alert fires when a database server is at risk of running out of disk space. You should take measures to increase the maximum available disk space as soon as possible to avoid possible corruption. + + Summary: The web server web1 has been responding to 5% of HTTP requests with 5xx errors for the last 5 minutes. + Status: resolved + Description: This alert fires when a web server responds with more 5xx errors than is expected. This could be an issue with the web server or a backend service. +``` + +## Print firing and resolved alerts + +The following example is similar to the previous one, but it separates firing and resolved alerts. + +```go +{{ define "custom.firing_and_resolved_alerts" -}} +{{ len .Alerts.Resolved }} resolved alert(s) +{{ range .Alerts.Resolved -}} + {{ template "alert.summary_and_description" . -}} +{{ end }} +{{ len .Alerts.Firing }} firing alert(s) +{{ range .Alerts.Firing -}} + {{ template "alert.summary_and_description" . -}} +{{ end -}} +{{ end -}} +{{ define "alert.summary_and_description" }} + Summary: {{.Annotations.summary}} + Status: {{ .Status }} + Description: {{.Annotations.description}} +{{ end -}} +``` + +Instead of `.Alerts`, the template accesses `.Alerts.Firing` and `.Alerts.Resolved` separately to print details for each alert. + +The output might now look like this: + +```template_output +1 resolved alert(s) + + Summary: The database server db1 has exceeded 75% of available disk space. + Status: resolved + Description: This alert fires when a database server is at risk of running out of disk space. You should take measures to increase the maximum available disk space as soon as possible to avoid possible corruption. + +1 firing alert(s) + + Summary: The web server web1 has been responding to 5% of HTTP requests with 5xx errors for the last 5 minutes. + Status: firing + Description: This alert fires when a web server responds with more 5xx errors than is expected. This could be an issue with the web server or a backend service. +``` + +## Print common labels and annotations + +This example displays only the labels and annotations that are common to all alerts in the notification. + +```go +{{ define "custom.common_labels_and_annotations" -}} +{{ len .Alerts.Resolved }} resolved alert(s) +{{ len .Alerts.Firing }} firing alert(s) +Common labels: {{ len .CommonLabels.SortedPairs }} +{{ range .CommonLabels.SortedPairs -}} +- {{ .Name }} = {{ .Value }} +{{ end }} +Common annotations: {{ len .CommonAnnotations.SortedPairs }} +{{ range .CommonAnnotations.SortedPairs }} +- {{ .Name }} = {{ .Value }} +{{ end }} +{{ end -}} +``` + +Note that `.CommonAnnotations` and `.CommonLabels` are part of [notification data](ref:reference-notification-data). + +```template_output +1 resolved alert(s) +1 firing alert(s) +Common labels: 2 +- grafana_folder = server_alerts +- team = server_admin + +Common annotations: 0 +``` + +## Print individual labels and annotations + +This example displays all labels and annotations for each [alert](ref:reference-alert) in the notification. + +```go +{{ define "custom.alert_labels_and_annotations" -}} +{{ len .Alerts.Resolved }} resolved alert(s) +{{ range .Alerts.Resolved -}} + {{ template "alert.labels_and_annotations" . -}} +{{ end }} +{{ len .Alerts.Firing }} firing alert(s) +{{ range .Alerts.Firing -}} + {{ template "alert.labels_and_annotations" . -}} +{{ end -}} +{{ end -}} +{{ define "alert.labels_and_annotations" }} +Alert labels: {{ len .Labels.SortedPairs }} +{{ range .Labels.SortedPairs -}} +- {{ .Name }} = {{ .Value }} +{{ end -}} +Alert annotations: {{ len .Annotations.SortedPairs }} +{{ range .Annotations.SortedPairs -}} +- {{ .Name }} = {{ .Value }} +{{ end -}} +{{ end -}} +``` + +In this example: + +- The `custom.alert_labels_and_annotations` template iterates over the list of resolved and firing alerts, similar to previous examples. It then executes `alert.labels_and_annotations` for each alert. +- The `alert.labels_and_annotations` template prints all the alert labels and annotations by accessing `.Labels.SortedPairs` and `.Annotations.SortedPairs`. + +```template_output +1 resolved alert(s) + +Alert labels: 4 +- alertname = db_server_disk_space +- grafana_folder = server_alerts +- server = db1 +- team = server_admin + +Alert annotations: 2 +- summary = The database server db1 has exceeded 75% of available disk space. +- description = This alert fires when a database server is at risk of running out of disk space. You should take measures to increase the maximum available disk space as soon as possible to avoid possible corruption. + +1 firing alert(s) + +Alert labels: 4 +- alertname = web_server_http_errors +- grafana_folder = server_alerts +- server = web1 +- team = server_admin + +Alert annotations: 2 +- summary = The web server web1 has been responding to 5% of HTTP requests with 5xx errors for the last 5 minutes. +- description = This alert fires when a web server responds with more 5xx errors than is expected. This could be an issue with the web server or a backend service. +``` + +## Print URLs for runbook and alert data in Grafana + +Note that the following example works only for Grafana-managed alerts. It displays some [alert data](ref:reference-alert) such as `DashboardURL`, `PanelURL`, and `SilenceURL`, which are exclusive to Grafana-managed alerts. + +```go +{{ define "custom.alert_additional_details" -}} +{{ len .Alerts.Resolved }} resolved alert(s) +{{ range .Alerts.Resolved -}} + {{ template "alert.additional_details" . -}} +{{ end }} +{{ len .Alerts.Firing }} firing alert(s) +{{ range .Alerts.Firing -}} + {{ template "alert.additional_details" . -}} +{{ end -}} +{{ end -}} +{{ define "alert.additional_details" }} +- Dashboard: {{ .DashboardURL }} +- Panel: {{ .PanelURL }} +- AlertGenerator: {{ .GeneratorURL }} +- Silence: {{ .SilenceURL }} +- RunbookURL: {{ .Annotations.runbook_url}} +{{ end -}} +``` + +The output of this template looks like this: + +```template_output +1 resolved alert(s) + +- Dashboard: https://example.com/d/ +- Panel: https://example.com/d/ +- AlertGenerator: ?orgId=1 +- Silence: https://example.com/alerting/silence/new +- RunbookURL: https://example.com/on-call/db_server_disk_space + +1 firing alert(s) + +- Dashboard: https://example.com/d/ +- Panel: https://example.com/d/ +- AlertGenerator: ?orgId=1 +- Silence: https://example.com/alerting/silence/new +- RunbookURL: https://example.com/on-call/web_server_http_errors +``` + +## Print a notification title or subject + +A title or subject provides a one-line summary of the notification content. + +Here’s a basic example that displays the number of firing and resolved alerts in the notification. + +```go +{{ define "custom_title" -}} +{{ if gt (.Alerts.Firing | len) 0 }}🚨 {{ .Alerts.Firing | len }} firing alerts. {{ end }}{{ if gt (.Alerts.Resolved | len) 0 }}✅ {{ .Alerts.Resolved | len }} resolved alerts.{{ end }} +{{ end -}} +``` + +```template_output +🚨 1 firing alerts. ✅ 1 resolved alerts. +``` + +The next example is a copy of the default title/subject template used in Grafana. + +```go +{{ define "copy_of_default_title" -}} +[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }} +{{ end }} +``` + +This is a more advanced example: + +- Prints the number of firing and resolved alerts in the notification. +- Outputs `.GroupLabels`, the labels used to [group multiple alerts in one notification](ref:group-alert-notifications). +- Prints `CommonLabels`, excluding labels in `.GroupLabels`. + +```template_output +[FIRING:1, RESOLVED:1] api warning (sql_db) +``` diff --git a/docs/sources/alerting/configure-notifications/template-notifications/images-in-notifications.md b/docs/sources/alerting/configure-notifications/template-notifications/images-in-notifications.md index 5b9324f6fc6..c5a84cf7afc 100644 --- a/docs/sources/alerting/configure-notifications/template-notifications/images-in-notifications.md +++ b/docs/sources/alerting/configure-notifications/template-notifications/images-in-notifications.md @@ -14,7 +14,8 @@ labels: - enterprise - oss title: Use images in notifications -weight: 500 +menuTitle: Use images +weight: 105 --- # Use images in notifications @@ -55,6 +56,8 @@ Refer to the table at the end of this page for a list of contact points and thei 8. Grafana does not delete screenshots uploaded to its internal web server. To delete screenshots from `static_root_path/images/attachments` after a certain amount of time, we recommend setting up a CRON job. +9. Note you cannot adjust the number and size of images or their placement in notifications. + ## Configuration {{% admonition type="note" %}} @@ -115,6 +118,7 @@ Grafana supports a wide range of contact points with varied support for images i ## Limitations - This feature is not supported in Mimir or Loki, or when Grafana is configured to send alerts to other Alertmanagers such as the Prometheus Alertmanager. +- This feature is not supported when using custom templates in email notifications. - A number of contact points support at most one image per notification. In this case, just the first image is either uploaded to the receiving service or referenced from cloud storage per notification. - When multiple alerts are sent in a single notification a screenshot might be included for each alert. The order the images are shown is random. - If uploading screenshots to a cloud storage service such as Amazon S3, Azure Blob Storage or Google Cloud Storage; and accessing screenshots in the bucket requires authentication, logging into a VPN or corporate network; image previews might not work in all instant messaging and communication platforms as some services rewrite URLs to use their CDN. @@ -130,7 +134,7 @@ If Grafana has been set up to send images in notifications, however notification 5. If images cannot be taken because of mis-configuration or an issue with image rendering there are logs for `Failed to take an image` including the Dashboard UID, Panel ID, and the error message. 6. Check that the contact point supports images in notifications and whether it supports uploading images to the receiving service or referencing images that have been uploaded to a cloud storage service. -## Metrics +## Monitor Grafana provides the following metrics to observe the performance and failure rate of images in notifications. For example, if a screenshot could not be taken within the expected time (10 seconds) then the counter `grafana_screenshot_failures_total` is updated. diff --git a/docs/sources/alerting/configure-notifications/template-notifications/language.md b/docs/sources/alerting/configure-notifications/template-notifications/language.md new file mode 100644 index 00000000000..3b3426aeb6c --- /dev/null +++ b/docs/sources/alerting/configure-notifications/template-notifications/language.md @@ -0,0 +1,85 @@ +--- +aliases: + - ../../manage-notifications/template-notifications/using-go-templating-language/ # /docs/grafana//alerting/manage-notifications/template-notifications/using-go-templating-language/ + - ../../configure-notifications/template-notifications/using-go-templating-language/ # /docs/grafana//alerting/configure-notifications/template-notifications/using-go-templating-language/ +weight: 104 +canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/language/ +description: Use Go template language to create your notification and alert rule templates +keywords: + - grafana + - alerting + - templates + - write templates +labels: + products: + - cloud + - enterprise + - oss +title: Alerting template language +menuTitle: Template language +refs: + alert-rule-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/ + alert-rule-template-reference-variables: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/#variables + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#variables + notification-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ + reference-notificationdata: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/#notification-data + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/#notification-data +--- + +# Alerting template language + +Notification templates and alert rule templates, such as annotations and labels, both use the Go template language, [text/template](https://pkg.go.dev/text/template). + +Both types of templates can use the same keywords, functions, and comparison operators of the Go template language, such as `range`, `if`, `and`, `index`, `eq`, and more. + +However, it's important to note that because notifications and alert rules operate in distinct context, some additional variables and functions are only available for either notification or alert rule templates. Refer to: + +- [Annotation and label template reference](ref:alert-rule-template-reference) +- [Notification template reference](ref:notification-template-reference) + +This documentation provides an overview of the functions and operators of the Go template language that are available for both notification and alert rule templates. + +## Print + +To print the value of something, use `{{` and `}}`. You can print the value of a [variable](#variables), a field of a variable, the result of a function, or the value of dot. + +``` +{{ $values }} +{{ $values.A.Value }} +{{ humanize 1000.0 }} +{{ .Alerts }} +``` + +## Dot + +In `text/template`, there is a special cursor called dot, written as `.`. You can think of this cursor as a variable whose value changes depending on where in the template it is used. + +At the start of notification templates, dot (`.`) refers to [Notification Data](ref:reference-notificationdata). + +``` +{{ .Alerts }} +``` + +In annotation and label templates, dot (`.`) is initialized with all alert data. It’s recommended to use the [`$labels` and `$values` variables](ref:alert-rule-template-reference-variables) instead to directly access the alert labels and query values. + +{{% admonition type="note" %}} +Dot (`.`) might refer to something else when used in a [range](#range), a [with](#with), or when writing [templates](#templates) used in other templates. +{{% /admonition %}} + +[//]: <> (The above section is not included in the shared file because `refs` links are not supported in shared files.) + +{{< docs/shared lookup="alerts/template-language.md" source="grafana" version="" >}} diff --git a/docs/sources/alerting/configure-notifications/template-notifications/manage-notification-templates.md b/docs/sources/alerting/configure-notifications/template-notifications/manage-notification-templates.md new file mode 100644 index 00000000000..fd958f9a2ac --- /dev/null +++ b/docs/sources/alerting/configure-notifications/template-notifications/manage-notification-templates.md @@ -0,0 +1,130 @@ +--- +aliases: + - ../../manage-notifications/template-notifications/create-notification-templates/ # /docs/grafana//alerting/manage-notifications/template-notifications/create-notification-templates/ + - ../../manage-notifications/template-notifications/use-notification-templates/ # /docs/grafana//alerting/manage-notifications/template-notifications/use-notification-templates/ + - ../../configure-notifications/template-notifications/use-notification-templates/ # /docs/grafana//alerting/manage-notifications/template-notifications/use-notification-templates/ + - ../../configure-notifications/template-notifications/create-notification-templates/ # /docs/grafana//alerting/manage-notifications/template-notifications/create-notification-templates/ +canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/manage-notification-templates/ +description: Create notification templates to sent to your contact points +keywords: + - grafana + - alerting + - notifications + - templates + - create templates + - edit templates + - delete templates +labels: + products: + - cloud + - enterprise + - oss +title: Manage notification templates +menuTitle: Manage templates +weight: 101 +refs: + notification-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ +--- + +# Manage notification templates + +In contact points, you can select notification templates to customize the notification messages sent. + +By default, Grafana provides a template for the notification title (`{{define "default.title"}}`) and a template for the notification message (`{{define "default.message"}}`). Both default templates display common alert details. + +You can also create custom templates to customize the content and format of notification messages, which can then be applied to one or more contact points. In Grafana, a custom notification template is created within a notification template group. + +This documentation provides step-by-step instructions for selecting templates in contact points, previewing templates, and creating notification template groups using the Grafana UI. + +## Select a notification template for a contact point + +To add an existing notification template to your contact point, complete the following steps. + +1. Click an existing contact point or create a new one. +1. In **Optional settings**, click any field that contains templates. + + For example, if you are creating an email contact point integration, click **Message** or **Subject**. + +1. Click **Edit**. + A dialog box opens where you can select notification templates. +1. Click **Select notification template** or **Enter custom message** to customize a template or message + + - You can select an existing notification template and [preview](#preview-a-notification-template) it using the default payload. + - You can also copy the notification template and use it in the **Enter custom message** tab. + +1. Click **Save contact point**. + +## Create a notification template and notification template group + +Create notification templates to customize notification messages and reuse them in contact points. + +In Grafana, custom notification templates (`{{define ""}}`) are created within a notification template group, allowing you to test and implement multiple templates together. + +Your notification template name (`{{define ""}}`) must be unique. You cannot have two templates with the same name in the same notification template group or in different notification template groups. Therefore, avoid using names already defined as default templates, such as: `__subject`, `__text_values_list`, `__text_alert_list`, `default.title` and `default.message`. + +To create a notification template in Grafana, complete the following steps. + +1. Click **Alerts & IRM** -> **Contact points**. +1. Click the **Notification Templates** tab and then **+ Add notification template group**. + +1. Enter a name for the notification template group. + +1. Write the content of the template in the content field. + +1. Save your changes. + + If `{{ define }}` is not included in the content, `{{ define "" }}` and `{{ end }}` is automatically added to the start and end. + +To create a notification template group that contains more than one notification template, complete the following steps. + +1. Click **+ Add notification template group**. + +1. Enter a name for the notification template group. + +1. Write each template in the Content field, including `{{ define "" }}` and `{{ end }}` at the start and end of each template. + +1. Save your changes. + +For more details on how to write notification templates, refer to the [template reference](ref:notification-template-reference) and [examples](ref:notification-template-examples). + +## Preview a notification template + +Preview how your notification templates should look before using them in your contact points, helping you understand the result of the template you are creating as well as enabling you to fix any errors before saving it. + +{{% admonition type="note" %}} +Notification template preview is only for Grafana Alertmanager. +{{% /admonition %}} + +To preview your notification templates: + +1. Navigate to **Alerts&IRM** -> **Alerting** -> **Contact points** -> **Notification Templates**. +1. Click **+ Add notification template group** or edit an existing template group. +1. Add or update your template content. + + Default data is provided and you can add or edit alert data to it as well as alert instances. You can add alert data directly in the Payload data window itself or click **Select alert instances** or **Add custom alerts**. + +1. Optional: To add alert data from existing alert instances: + + a. Click **Select alert instances**. + + b. Hover over the alert instances to view more information on each alert instance. + + c. Click **Confirm** to add the alert instance(s) to the payload. + +1. Optional: To add alert data using the Alert data editor, click **Add custom data:** + + a. Add annotations, custom labels and/or set a dashboard or a panel. + + b. Toggle Firing/resolved depending on whether you want to add firing or resolved alerts to your notification. + + c. Click **Add alert data**. + + d. Click **Refresh preview** to see what your template content should look like and the corresponding payload data. + + If there are any errors in your template, they are displayed in the Preview and you can correct them before saving. + +1. Save your changes. diff --git a/docs/sources/alerting/configure-notifications/template-notifications/reference.md b/docs/sources/alerting/configure-notifications/template-notifications/reference.md index f0b84f67d16..e8921ab6bea 100644 --- a/docs/sources/alerting/configure-notifications/template-notifications/reference.md +++ b/docs/sources/alerting/configure-notifications/template-notifications/reference.md @@ -13,80 +13,244 @@ labels: - cloud - enterprise - oss -title: Reference -weight: 400 +title: Notification template reference +menuTitle: Template reference +weight: 102 +refs: + alert-rule-template-reference: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/reference/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/ + alert-grouping: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/group-alert-notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/ + template-language: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/language/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/ + template-language-functions: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/configure-notifications/template-notifications/language/#functions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/#functions --- -# Reference +# Notification template reference -## Data +By default, Grafana provides predefined templates to format notification messages. -### Alert +You can also customize your notifications with custom templates, which are based on the [Go template language](ref:template-language). -| Name | Kind | Description | Example | -| ------------ | -------- | ----------------------------------------------------------------------------------- | --------------------- | -| Status | `string` | Firing or resolved | `{{ .Status }}` | -| Labels | `KV` | The labels for this alert | `{{ .Labels }}` | -| Annotations | `KV` | The annotations for this alert | `{{ .Annotations }}` | -| Values | `KV` | The values of all expressions, including Classic Conditions | `{{ .Values }}` | -| StartsAt | `Time` | The time the alert fired | `{{ .StartsAt }}` | -| EndsAt | `Time` | | `{{ .EndsAt }}` | -| GeneratorURL | `string` | A link to Grafana, or the source of the alert if using an external alert generator | `{{ .GeneratorURL }}` | -| SilenceURL | `string` | A link to silence the alert | `{{ .SilenceURL }}` | -| DashboardURL | `string` | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation | `{{ .DashboardURL }}` | -| PanelURL | `string` | A link to the panel if the alert has a Panel ID annotation | `{{ .PanelURL }}` | -| Fingerprint | `string` | A unique string that identifies the alert | `{{ .Fingerprint }}` | -| ValueString | `string` | A string that contains the labels and value of each reduced expression in the alert | `{{ .ValueString }}` | +This documentation lists the data available for use in notification templates. -### ExtendedData +## Notification Data -| Name | Kind | Description | Example | -| ----------------- | --------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -| Receiver | `string` | The name of the contact point sending the notification | `{{ .Receiver }}` | -| Status | `string` | The status is `firing` if at least one alert is firing, otherwise `resolved` | `{{ .Status }}` | -| Alerts | `[]Alert` | List of all firing and resolved alerts in this notification | `There are {{ len .Alerts }} alerts` | -| Firing alerts | `[]Alert` | List of all firing alerts in this notification | `There are {{ len .Alerts.Firing }} firing alerts` | -| Resolved alerts | `[]Alert` | List of all resolved alerts in this notification | `There are {{ len .Alerts.Resolved }} resolved alerts` | -| GroupLabels | `KV` | The labels that group these alerts in this | `{{ .GroupLabels }}` | -| CommonLabels | `KV` | The labels common to all alerts in this notification | `{{ .CommonLabels }}` | -| CommonAnnotations | `KV` | The annotations common to all alerts in this notification | `{{ .CommonAnnotations }}` | -| ExternalURL | `string` | A link to Grafana, or the Alertmanager that sent this notification if using an external Alertmanager | `{{ .ExternalURL }}` | +In notification templates, dot (`.`) is initialized with the following data: -### KV +| Name | Type | Description | +| ------------------- | ----------------- | ----------------------------------------------------------------------------------------------------- | +| `Receiver` | string | The name of the contact point sending the notification | +| `Status` | string | The status is `firing` if at least one alert is firing, otherwise `resolved`. | +| `Alerts` | [][Alert](#alert) | List of all firing and resolved alerts in this notification. | +| `Alerts.Firing` | [][Alert](#alert) | List of all firing alerts in this notification. | +| `Alerts.Resolved` | [][Alert](#alert) | List of all resolved alerts in this notification. | +| `GroupLabels` | [KV](#kv) | The labels that group these alerts in this notification based on the `Group by` option. | +| `CommonLabels` | [KV](#kv) | The labels common to all alerts in this notification. | +| `CommonAnnotations` | [KV](#kv) | The annotations common to all alerts in this notification. | +| `ExternalURL` | string | A link to Grafana, or the Alertmanager that sent this notification if using an external Alertmanager. | -`KV` is a set of key value pairs, where each key and value is a string. If a KV happens to contain numbers or bools then these are string representations of the numeric or boolean value. +It's important to remember that [a single notification can group multiple alerts](ref:alert-grouping) to reduce the number of alerts you receive. `Alerts` is an array that includes all the alerts in the notification. -Here is an example of a KV, the annotations of an alert: +Here's an example that prints all available notification data from dot (`.`): -```yaml -summary: 'A summary of the alert' -description: 'A description of the alert' +```go +{{ define "custom_template" }} + {{ .Receiver }} + {{ .Status }} + There are {{ len .Alerts }} alerts + There are {{ len .Alerts.Firing }} firing alerts + There are {{ len .Alerts.Resolved }} resolved alerts + {{ .GroupLabels }} + {{ .CommonLabels }} + {{ .CommonAnnotations }} + {{ .ExternalURL }} +{{ end }} ``` -In addition to iterating over each key value pair, you can sort the pairs, remove keys, and iterate over just the keys or the values. +## Alert -| Name | Description | Arguments | Returns | Example | -| ----------- | ---------------------------------------------- | --------- | ------- | ------------------------------------- | -| SortedPairs | Sorts | | | `{{ .Annotations.SortedPairs }}` | -| Remove | Returns a copy of the KV with the keys removed | []string | | `{{ .Annotations.Remove "summary" }}` | -| Names | A list of the names | | | `{{ .Names }}` | -| Values | A list of the values | | | `{{ .Values }}` | +`Alert` contains data for an individual alert: -### Time +| Name | Type | Description | +| -------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `Status` | string | Firing or resolved. | +| `Labels` | [KV](#kv) | The labels for this alert. | +| `Annotations` | [KV](#kv) | The annotations for this alert. | +| `StartsAt` | [Time](#time) | The time the alert fired | +| `EndsAt` | [Time](#time) | Only set if the end time of an alert is known. Otherwise set to a configurable timeout period from the time since the last alert was received. | +| `GeneratorURL` | string | A link to Grafana, or the source of the alert if using an external alert generator. | +| `Fingerprint` | string | A unique string that identifies the alert. | -Time is from the Go [`time`](https://pkg.go.dev/time#Time) package. +Grafana-managed alerts include these additional properties: -You can format a time in a number of different formats using the `date` function. -For example, to print the time that an alert fired in the format `15:04:05 MST`: +| Name | Type | Description | +| -------------- | --------- | ------------------------------------------------------------------------------------ | +| `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. | +| `SilenceURL` | string | A link to silence the alert. | +| `Values` | [KV](#kv) | The values of all expressions, including Classic Conditions. | +| `ValueString` | string | A string that contains the labels and value of each reduced expression in the alert. | -``` -{{ .StartsAt | date "15:04:05 MST" }} +This example iterates over the list of firing and resolved alerts (`.Alerts`) in the notification and prints the data for each alert: + +```go +{{ define "custom_template" }} +{{ range .Alerts }} + {{ .Status }} + {{ .Labels }} + {{ .Annotations }} + {{ .StartsAt }} + {{ .EndsAt }} + {{ .GeneratorURL }} + {{ .Fingerprint }} + + {{/* Only available for Grafana-managed alerts */}} + {{ .DashboardURL }} + {{ .PanelURL }} + {{ .SilenceURL }} + {{ .Values }} + {{ .ValueString }} +{{ end }} +{{ end }} ``` -You can also use the `tz` function to change the timezone from UTC to a local time. For example: +## KV -``` -{{ .StartsAt | tz "Europe/Paris" | date "15:04:05 MST" }} +`KV` is a set of key value pairs, where each key and value is a string. + +Similarly to accessing variable properties, you can use `.` to retrieve the value of a value. For example: + +```go +{{ define "custom_template" }} + {{ .CommonLabels.grafana_folder }} +{{ end }} ``` -You can find a reference for Go's time format [here](https://pkg.go.dev/time#pkg-constants). +If a KV happens to contain numbers or bools then these are string representations of the numeric or boolean value. + +Additionally, KV provides methods to sort the pairs, remove keys, and iterate over just the keys or values: + +| Method name | Description | Arguments | Returns | +| ----------- | ---------------------------------------------- | --------- | --------- | +| SortedPairs | Returns a sorted list of key/value pairs. | | Pairs | +| Remove | Returns a copy of the KV with the keys removed | []string | [KV](#kv) | +| Names | Return the names of the label names | | []string | +| Values | Return a list of the values | | []string | + +Here's an example of using these methods: + +```go +{{ define "custom_template" }} + {{ .CommonLabels.SortedPairs }} + {{ .CommonLabels.Names }} + {{ .CommonLabels.Values }} + {{ .CommonLabels.Remove (stringSlice "grafana_folder") }} +{{ end }} +``` + +## Time + +Some template functions and properties return a `Time` object, which refers to the [type `Time`](https://pkg.go.dev/time#Time) in Go's time package. + +When accessing a `Time` object, you can use various [`Time` functions](https://pkg.go.dev/time#Time) in your templates as follows. + +```go +{{ define "custom_template" }} + {{ range .Alerts }} + {{ .StartsAt }} + {{ .StartsAt.Add 6000000000000 }} + {{ .StartsAt.Add -6000000000000 }} + {{ .StartsAt.AddDate 1 0 0 }} + {{ .StartsAt.Year }}/{{ .StartsAt.Month }}/{{ .StartsAt.Day }} + {{ .StartsAt.Hour }}:{{ .StartsAt.Minute }}:{{ .StartsAt.Second }} + {{ .StartsAt.YearDay }}-{{ .StartsAt.Weekday }} + {{ .StartsAt.Unix }} {{ .StartsAt.UnixMilli }} + {{ end}} +{{ end }} +``` + +## Functions + +Functions can perform actions in templates such as transforming or formatting data. + +Note that the [functions provided by Go's template language](ref:template-language-functions), such as `index`, `and`, `printf`, and `len`, are available, along with many others. + +In addition, the following functions are also available for templating notifications: + +| Name | Arguments | Returns | Description | +| -------------- | -------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `title` | string | string | Capitalizes the first character of each word. | +| `toUpper` | string | string | Returns all text in uppercase. | +| `toLower` | string | string | Returns all text in lowercase. | +| `trimSpace` | string | string | Removes leading and trailing white spaces. | +| `match` | pattern, text | boolean | Matches the text against a regular expression pattern. | +| `reReplaceAll` | pattern, replacement, text | string | Replaces text matching the regular expression. | +| `join` | sep string, s []string | string | Concatenates the elements of s to create a single string. The separator string sep is placed between elements in the resulting string. | +| `safeHtml` | string | string | Marks string as HTML not requiring auto-escaping. | +| `stringSlice` | ...string | string | Returns the passed strings as a slice of strings. auto-escaping. | +| `date` | string, [Time](#time) | string | Format a time in the specified format. For format options, refer to [Go's time format documentation](https://pkg.go.dev/time#pkg-constants). | +| `tz` | string, [Time](#time) | [Time](#time) | Returns the time in the specified timezone, such as `Europe/Paris`. For available timezone options, refer to the [list of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). | + +Here's an example using some functions to format text: + +```go +{{ define "custom_template" }} + {{ title "hello, world!" }} + {{ toUpper "Hello, world!" }} + {{ toLower "Hello, world!" }} + {{ trimSpace "Hello, world!" }} + {{ match "a.*" "abc" }} + {{ reReplaceAll "localhost:(.*)" "example.com:$1" "localhost:8080" }} + {{ join "-" (stringSlice "a" "b" "c") }} + {{ safeHtml "Text"}} + {{ stringSlice "a" "b" "c" }} +{{ end }} +``` + +`date` and `tz` can format times. For example, to print the time an alert fired in the format `15:04:05 MST`: + +```go +{{ define "custom_template" }} + {{ range .Alerts }} + {{ .StartsAt | date "15:04:05 MST" }} + {{ end}} +{{ end }} +``` + +You can then use `tz` to change the timezone from UTC to local time, such as `Europe/Paris`. + +```go +{{ define "custom_template" }} + {{ range .Alerts }} + {{ .StartsAt | tz "Europe/Paris" }} + {{ .StartsAt | tz "Europe/Paris" | date "15:04:05 MST" }} + {{ end}} +{{ end }} +``` + +```template-output +2024-10-30 21:01:45.227 +0100 CET +21:01:45 CET +``` + +## Differences with annotation and label templates + +In the alert rule, you can also template annotations and labels to include additional information. For example, you might add a `summary` annotation that displays the query value triggering the alert. + +Annotation and label templates add relevant information to individual alert instances, while notification templates inform about a group of alert instances. + +Since both types of templates operate in distinct contexts, the [functions and variables available in annotation and label templates](ref:alert-rule-template-reference) differ from those used in notification templates. diff --git a/docs/sources/alerting/configure-notifications/template-notifications/use-notification-templates.md b/docs/sources/alerting/configure-notifications/template-notifications/use-notification-templates.md deleted file mode 100644 index 3763e22ccf7..00000000000 --- a/docs/sources/alerting/configure-notifications/template-notifications/use-notification-templates.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -aliases: - - ../../manage-notifications/template-notifications/use-notification-templates/ # /docs/grafana//alerting/manage-notifications/template-notifications/use-notification-templates/ -canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/use-notification-templates/ -description: Use notification templates in contact points to customize your notifications -keywords: - - grafana - - alerting - - notifications - - templates - - use templates -labels: - products: - - cloud - - enterprise - - oss -title: Use notification templates -weight: 300 -refs: - create-notification-templates: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/create-notification-templates/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/create-notification-templates/ - using-go-templating-language: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/using-go-templating-language/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/using-go-templating-language/ ---- - -# Use notification templates - -Use templates in contact points to customize your notifications. - -Complete the following steps to add templates to your contact point. - -1. Click an existing contact point or create a new one -1. In **Optional settings**, click any field that contains templates. - - For example, if you are creating an email contact point integration, click **Message** or **Subject**. - -1. Click **Edit**. - A dialog box opens where you can select templates. -1. Click **Select existing template** to select a template and preview it using the default payload. - - You can also copy the selected template and use it in the custom tab. - -1. Click **Enter custom message** to customize and edit the field directly. Note that the title changes depending on the field you are editing. - -1. You can switch between the two tabs to access the list of available templates and copy them across to the customized version. - -1. Click **Save contact point**. diff --git a/docs/sources/alerting/configure-notifications/template-notifications/using-go-templating-language.md b/docs/sources/alerting/configure-notifications/template-notifications/using-go-templating-language.md deleted file mode 100644 index 5999a20001e..00000000000 --- a/docs/sources/alerting/configure-notifications/template-notifications/using-go-templating-language.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -aliases: - - ../../manage-notifications/template-notifications/using-go-templating-language/ # /docs/grafana//alerting/manage-notifications/template-notifications/using-go-templating-language/ -canonical: https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/using-go-templating-language/ -description: Use Go's templating language to create your own notification templates -keywords: - - grafana - - alerting - - notifications - - templates - - write templates -labels: - products: - - cloud - - enterprise - - oss -title: Using Go's templating language -weight: 100 -refs: - create-notification-templates: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/create-notification-templates/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/create-notification-templates/ - extendeddata: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/#extendeddata - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/#extendeddata - reference: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ ---- - -# Using Go's templating language - -You write notification templates in Go's templating language, [text/template](https://pkg.go.dev/text/template). - -Before you start creating your own notification templates, we recommend that you read through this topic, which provides you with an overview of Go's templating language and writing templates in text/template. - -## Dot - -In text/template there is a special cursor called dot, and is written as `.`. You can think of this cursor as a variable whose value changes depending where in the template it is used. For example, at the start of a notification template `.` refers to something called [`ExtendedData`](ref:extendeddata) which contains a number of fields including `Alerts`, `Status`, `GroupLabels`, `CommonLabels`, `CommonAnnotations` and `ExternalURL`. However, dot might refer to something else when used in a range over a list, when used inside a `with`, or when writing feature templates to be used in other templates. You can see examples of this in [Create notification templates](ref:create-notification-templates), and all data and functions in the [Reference](ref:reference). - -## Opening and closing tags - -In text/template, templates start with `{{` and end with `}}` irrespective of whether the template prints a variable or executes control structures such as if statements. This is different from other templating languages such as Jinja where printing a variable uses `{{` and `}}` and control structures use `{%` and `%}`. - -## Print - -To print the value of something use `{{` and `}}`. You can print the value of dot, a field of dot, the result of a function, and the value of a [variable](#variables). For example, to print the `Alerts` field where dot refers to `ExtendedData` you would write the following: - -``` -{{ .Alerts }} -``` - -## Iterate over alerts - -To print just the labels of each alert, rather than all information about the alert, you can use a `range` to iterate the alerts in `ExtendedData`: - -``` -{{ range .Alerts }} -{{ .Labels }} -{{ end }} -``` - -Inside the range dot no longer refers to `ExtendedData`, but to an `Alert`. You can use `{{ .Labels }}` to print the labels of each alert. This works because `{{ range .Alerts }}` changes dot to refer to the current alert in the list of alerts. When the range is finished dot is reset to the value it had before the start of the range, which in this example is `ExtendedData`: - -``` -{{ range .Alerts }} -{{ .Labels }} -{{ end }} -{{/* does not work, .Labels does not exist here */}} -{{ .Labels }} -{{/* works, cursor was reset */}} -{{ .Status }} -``` - -## Iterate over annotations and labels - -Let's write a template to print the labels of each alert in the format `The name of the label is $name, and the value is $value`, where `$name` and `$value` contain the name and value of each label. - -Like in the previous example, use a range to iterate over the alerts in `.Alerts` such that dot refers to the current alert in the list of alerts, and then use a second range on the sorted labels so dot is updated a second time to refer to the current label. Inside the second range use `.Name` and `.Value` to print the name and value of each label: - -``` -{{ range .Alerts }} -{{ range .Labels.SortedPairs }} -The name of the label is {{ .Name }}, and the value is {{ .Value }} -{{ end }} -{{ range .Annotations.SortedPairs }} -The name of the annotation is {{ .Name }}, and the value is {{ .Value }} -{{ end }} -{{ end }} -``` - -## The index function - -To print a specific annotation or label use the `index` function. - -``` -{{ range .Alerts }} -The name of the alert is {{ index .Labels "alertname" }} -{{ end }} -``` - -## If statements - -You can use if statements in templates. For example, to print `There are no alerts` if there are no alerts in `.Alerts` you would write the following: - -``` -{{ if .Alerts }} -There are alerts -{{ else }} -There are no alerts -{{ end }} -``` - -## With - -With is similar to if statements, however unlike if statements, `with` updates dot to refer to the value of the with: - -``` -{{ with .Alerts }} -There are {{ len . }} alert(s) -{{ else }} -There are no alerts -{{ end }} -``` - -## Variables - -Variables in text/template must be created within the template. For example, to create a variable called `$variable` with the current value of dot you would write the following: - -``` -{{ $variable := . }} -``` - -You can use `$variable` inside a range or `with` and it will refer to the value of dot at the time the variable was defined, not the current value of dot. - -For example, you cannot write a template that use `{{ .Labels }}` in the second range because here dot refers to the current label, not the current alert: - -``` -{{ range .Alerts }} -{{ range .Labels.SortedPairs }} -{{ .Name }} = {{ .Value }} -{{/* does not work because in the second range . is a label not an alert */}} -There are {{ len .Labels }} -{{ end }} -{{ end }} -``` - -You can fix this by defining a variable called `$alert` in the first range and before the second range: - -``` -{{ range .Alerts }} -{{ $alert := . }} -{{ range .Labels.SortedPairs }} -{{ .Name }} = {{ .Value }} -{{/* works because $alert refers to the value of dot inside the first range */}} -There are {{ len $alert.Labels }} -{{ end }} -{{ end }} -``` - -## Range with index - -You can get the index of each alert within a range by defining index and value variables at the start of the range: - -``` -{{ $num_alerts := len .Alerts }} -{{ range $index, $alert := .Alerts }} -This is alert {{ $index }} out of {{ $num_alerts }} -{{ end }} -``` - -## Define templates - -You can define templates using `define` and the name of the template in double quotes. You should not define templates with the same name as other templates, including default templates such as `__subject`, `__text_values_list`, `__text_alert_list`, `default.title` and `default.message`. Where a template has been created with the same name as a default template, or a template in another notification template, Grafana might use either template. Grafana does not prevent, or show an error message, when there are two or more templates with the same name. - -``` -{{ define "print_labels" }} -{{ end }} -``` - -## Execute templates - -You can execute defined templates using `template`, the name of the template in double quotes, and the cursor that should be passed to the template: - -``` -{{ template "print_labels" . }} -``` - -## Pass data to templates - -Within a template dot refers to the value that is passed to the template. - -For example, if a template is passed a list of firing alerts then dot refers to that list of firing alerts: - -``` -{{ template "print_alerts" .Alerts }} -``` - -If the template is passed the sorted labels for an alert then dot refers to the list of sorted labels: - -``` -{{ template "print_labels" .SortedLabels }} -``` - -This is useful when writing reusable templates. For example, to print all alerts you might write the following: - -``` -{{ template "print_alerts" .Alerts }} -``` - -Then to print just the firing alerts you could write this: - -``` -{{ template "print_alerts" .Alerts.Firing }} -``` - -This works because both `.Alerts` and `.Alerts.Firing` is a list of alerts. - -``` -{{ define "print_alerts" }} -{{ range . }} -{{ template "print_labels" .SortedLabels }} -{{ end }} -{{ end }} -``` - -## Comments - -You can add comments with `{{/*` and `*/}}`: - -``` -{{/* This is a comment */}} -``` - -To prevent comments from adding line breaks use: - -``` -{{- /* This is a comment with no leading or trailing line breaks */ -}} -``` - -## Indentation - -You can use indentation, both tabs and spaces, and line breaks, to make templates more readable: - -``` -{{ range .Alerts }} - {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} - {{ end }} -{{ end }} -``` - -However, indentation in the template is also present in the text. Next, we look at how to remove it. - -## Remove spaces and line breaks - -In text/template use `{{-` and `-}}` to remove leading and trailing spaces and line breaks. - -For example, when using indentation and line breaks to make a template more readable: - -``` -{{ range .Alerts }} - {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} - {{ end }} -{{ end }} -``` - -The indentation and line breaks will also be present in the text: - -``` - alertname = "Test" - - grafana_folder = "Test alerts" -``` - -You can remove the indentation and line breaks from the text changing `}}` to `-}}` at the start of each range: - -``` -{{ range .Alerts -}} - {{ range .Labels.SortedPairs -}} - {{ .Name }} = {{ .Value }} - {{ end }} -{{ end }} -``` - -The indentation and line breaks in the template are now absent from the text: - -``` -alertname = "Test" -grafana_folder = "Test alerts" -``` diff --git a/docs/sources/alerting/fundamentals/_index.md b/docs/sources/alerting/fundamentals/_index.md index d8ec2362cbd..934f019cfd3 100644 --- a/docs/sources/alerting/fundamentals/_index.md +++ b/docs/sources/alerting/fundamentals/_index.md @@ -13,6 +13,11 @@ menuTitle: Introduction title: Introduction to Alerting weight: 100 refs: + notifications: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/ notification-policies: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/notifications/notification-policies/ @@ -105,11 +110,15 @@ A rule using the PromQL expression above creates as many alert instances as the [Contact points](ref:contact-points) determine the notification message and where notifications are sent. For example, you might have a contact point that sends notifications to an email address, to Slack, to an incident management system (IRM) such as Grafana OnCall or Pagerduty, or to a webhook. +### Notification messages + +By default, notification messages include alert details, such as the number of alerts, their status, and annotations to help responders address alert issues. Notification messages can also be customized. + In the alert rule, you can choose a contact point to receive the alert notifications or use notification policies instead. ### Notification policies -[Notification policies](ref:notification-policies) provide a flexible method to handle alert notifications for larger systems. +[Notification policies](ref:notification-policies) is an advanced option to handle alert notifications for larger systems. Notification policies routes alerts to contact points via label matching. Each notification policy consists of a set of label matchers (0 or more) that specify which alert instances (identified by their labels) they handle. Notification policies are defined in a tree structure, where the root of the notification policy tree is the **Default notification policy**, which ensures all alert instances are handled. @@ -129,12 +138,8 @@ Each notification policy decides where to send the alert (contact point) and whe Grafana Alerting is built on the Prometheus model of designing alerting systems. Prometheus-based alerting systems have two main components: -- An alert generator that evaluates alert rules and sends firing and resolved alerts to the alert receiver. -- An alert receiver (also known as Alertmanager) that receives the alerts and is responsible for handling them and sending their notifications. - -Grafana Alerting doesn’t use Prometheus as its default alert generator because it works with many other data sources; not just Prometheus. - -However, Grafana can also use Prometheus as an alert generator (refer to [alert rule types](ref:alert-rules)), as well as external Alertmanagers. +- An alert generator that [evaluates alert rules](ref:alert-rule-evaluation) and sends firing and resolved alerts to the alert receiver. +- An alert receiver (also known as Alertmanager) that receives the alerts and is responsible for sending their [notifications](ref:notifications). ## Design your Alerting system @@ -147,23 +152,23 @@ Here are some tips on how to create an effective alert management set up for you **Which are the key metrics for your business that you want to monitor and alert on?** - Find events that are important to know about and not so trivial or frequent that recipients ignore them. - - Alerts should only be created for big events that require immediate attention or intervention. - - Consider quality over quantity. -**Which type of Alerting do you want to use?** - -- Choose between Grafana-managed Alerting or Grafana Mimir or Loki-managed Alerting; or both. - **How do you want to organize your alerts and notifications?** -- Be selective about who you set to receive alerts. Consider sending them to whoever is on call or a specific Slack channel. -- Automate as far as possible using the Alerting API or alerts as code (Terraform). +- Be selective about who you set to receive alerts. Consider sending them to the right teams, whoever is on call, and the specific channels. +- Think carefully about priority and severity levels. +- Automate as far as possible provisioning Alerting resources with the API or Terraform. + +**Which information should you include in notifications?** + +- Consider who the alert receivers and responders are. +- Share information that helps responders identify and address potential issues. +- Link alerts to dashboards to guide responders on which data to investigate. **How can you reduce alert fatigue?** - Avoid noisy, unnecessary alerts by using silences, mute timings, or pausing alert rule evaluation. - Continually tune your alert rules to review effectiveness. Remove alert rules to avoid duplication or ineffective alerts. -- Think carefully about priority and severity levels. - Continually review your thresholds and evaluation rules. diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md index 5de512dd012..42b3faa03ee 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md @@ -43,7 +43,7 @@ Alert rules in different groups can be evaluated simultaneously. - **Grafana-managed** alert rules within the same group are evaluated concurrently—they are evaluated at different times over the same evaluation interval but display the same evaluation timestamp. -- **Data-source managed** alert rules within the same group are evaluated sequentially, one after the other—this is necessary to ensure that recording rules are evaluated before alert rules. +- **Data-source managed** alert rules within the same group are evaluated sequentially, one after the other—this is useful to ensure that recording rules are evaluated before alert rules. ## Pending period diff --git a/docs/sources/alerting/fundamentals/alert-rules/_index.md b/docs/sources/alerting/fundamentals/alert-rules/_index.md index 5d9a3219f64..06221de0e68 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rules/_index.md @@ -3,10 +3,7 @@ aliases: - ../fundamentals/data-source-alerting/ # /docs/grafana//alerting/fundamentals/data-source-alerting/ - ../fundamentals/alert-rules/alert-instances/ # /docs/grafana//alerting/fundamentals/alert-rules/alert-instances/ - ../fundamentals/alert-rules/organising-alerts/ # /docs/grafana//alerting/fundamentals/alert-rules/organising-alerts/ - - ../fundamentals/alert-rules/recording-rules/ # /docs/grafana//alerting/fundamentals/alert-rules/recording-rules/ - ../fundamentals/alert-rules/alert-rule-types/ # /docs/grafana//alerting/fundamentals/alert-rules/alert-rule-types/ - - ../unified-alerting/alerting-rules/edit-cortex-loki-namespace-group/ # /docs/grafana//alerting/unified-alerting/alerting-rules/edit-cortex-loki-namespace-group/ - - ../unified-alerting/alerting-rules/edit-mimir-loki-namespace-group/ # /docs/grafana//alerting/unified-alerting/alerting-rules/edit-mimir-loki-namespace-group/ canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rules/ description: Learn about alert rules keywords: @@ -26,9 +23,6 @@ refs: destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/ - 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/ @@ -41,14 +35,19 @@ refs: destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/ create-recording-rules: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-mimir-loki-managed-recording-rule/ + 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 - 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 alert-rule-evaluation: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rules/rule-evaluation/ @@ -69,54 +68,52 @@ Grafana supports two different alert rule types: Grafana-managed alert rules and ## Grafana-managed alert rules -Grafana-managed alert rules are the most flexible alert rule type. They allow you to create alert rules that can act on data from any of the [supported data sources](#supported-data-sources), and use multiple data sources in a single alert rule. You can add [expressions to transform your data](ref:expression-queries) and set custom alert conditions. Using [images in alert notifications](ref:notification-images). is also supported. +Grafana-managed alert rules are the most flexible alert rule type. They allow you to create alert rules that can act on data from any of the [supported data sources](#supported-data-sources), and use multiple data sources in a single alert rule. {{< figure src="/media/docs/alerting/grafana-managed-alerting-architecture.png" max-width="750px" caption="How Grafana-managed alerting works by default" >}} -1. Alert rules are created within Grafana based on one or more data sources. +1. Alert rules are created within Grafana and query one or more data sources. 1. Alert rules are evaluated by the Alert Rule Evaluation Engine from within Grafana. 1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications). ### Supported data sources -Grafana-managed alert rules can query backend data sources if Grafana Alerting is enabled by specifying `{"backend": true, "alerting": true}` in the [plugin.json](https://grafana.com/developers/plugin-tools/reference/plugin-json). +Grafana-managed alert rules can query backend data sources if Grafana Alerting is enabled by specifying `{"backend": true, "alerting": true}` in the `plugin.json` file. Find the public data sources supporting Alerting in the [Grafana Plugins directory](/grafana/plugins/data-source-plugins/?features=alerting). ## Data source-managed alert rules -Data source-managed alert rules can be used for Grafana Mimir or Grafana Loki data sources which have been configured to support rule creation. +Data source-managed alert rules can query Prometheus-based data sources, such as Grafana Mimir or Grafana Loki. Alert rules are stored within the data source when the Ruler API is enabled (e.g., [Mimir Ruler API](/docs/mimir//references/http-api/#ruler) or [Loki Ruler API](/docs/loki//api/#ruler)). -They can improve query performance via [recording rules](#recording-rules) and ensure high-availability and fault tolerance when implementing a distributed architecture. - -They are only supported for Grafana Mimir or Grafana Loki data sources with the Ruler API enabled. For more information, refer to the [Loki Ruler API](/docs/loki//api/#ruler) or [Mimir Ruler API](/docs/mimir//references/http-api/#ruler). +In this setup, the distributed architecture can provide high-availability and fault tolerance. {{< figure src="/media/docs/alerting/mimir-managed-alerting-architecture-v2.png" max-width="750px" caption="Mimir-managed alerting architecture" >}} 1. Alert rules are created and stored within the data source itself. -1. Alert rules can only query Prometheus-based data. It can use either queries or [recording rules](#recording-rules). +1. Alert rules can only query Prometheus-based data. 1. Alert rules are evaluated by the Alert Rule Evaluation Engine. 1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications). ## Recording rules -A recording rule allows you to pre-compute frequently needed or computationally expensive expressions and save their result as a new set of time series. This is useful if you want to run alerts on aggregated data or if you have dashboards that query computationally expensive expressions repeatedly. +A recording rule pre-compute frequently used or computationally expensive queries, and saves the results as a new time series metric. -Querying this new time series is faster, especially for dashboards since they query the same expression every time the dashboards refresh. For more information, refer to [Create recording rules](ref:create-recording-rules). +The new metric can then be used in alert rules and dashboards to optimize their queries. -Alternatively, Grafana Enterprise and Grafana Cloud offer [recorded queries](ref:recorded-queries) that can be executed against any data source. +Similar to alert rules, recording rules are evaluated periodically. For more details, refer to [Create recording rules](ref:create-recording-rules). ## Comparison between alert rule types When choosing which alert rule type to use, consider the following comparison between Grafana-managed and data source-managed alert rules. -|
Feature
|
Grafana-managed alert rule
|
Data source-managed alert rule | -| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Create alert rules based on data from any of the supported data sources | Yes | No. You can only create alert rules that are based on Prometheus-based data. | -| Mix and match data sources | Yes | No | -| Includes support for recording rules | Yes. Only for Grafana OSS users with the `grafanaManagedRecordingRules` feature flag enabled. | Yes | -| Add expressions to transform your data and set alert conditions | Yes | No | -| Use images in alert notifications | Yes | No | -| Organization | Organize and manage access with folders | Use namespaces | -| Scaling | More resource intensive, depend on the database, and are likely to suffer from transient errors. They only scale vertically. | Store alert rules within the data source itself and allow for “infinite” scaling. Generate and send alert notifications from the location of your data. | -| Alert rule evaluation and delivery | Alert rule evaluation and delivery is done from within Grafana, using an external Alertmanager; or both. | Alert rule evaluation and alert delivery is distributed, meaning there is no single point of failure. | +|
Feature
|
Grafana-managed alert rule
|
Data source-managed alert rule | +| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Create alert rules that query [data sources supporting Alerting](#supported-data-sources) | Yes | No. Only query Prometheus-based data sources. | +| Mix and match data sources | Yes | No | +| Add [expressions](ref:expression-queries) to transform your data and set [alert conditions](ref:alert-condition) | Yes | No | +| Use [images in alert notifications](ref:notification-images) | Yes | No | +| Support for [recording rules](#recording-rules) | Yes | Yes | +| Organization | Organize and manage access with folders | Use namespaces | +| Scaling | More resource intensive, depend on the database, and are likely to suffer from transient errors. They only scale vertically. | Store alert rules within the data source itself and allow for “infinite” scaling. Generate and send alert notifications from the location of your data. | +| Alert rule evaluation and delivery | Alert rule evaluation and delivery is done from within Grafana, using an external Alertmanager; or both. | Alert rule evaluation and alert delivery is distributed, meaning there is no single point of failure. | diff --git a/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md b/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md index 270b45d563c..9640d3c3fe0 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md +++ b/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md @@ -26,16 +26,16 @@ refs: destination: /docs/grafana//alerting/fundamentals#alert-instances - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals#alert-instances - create-alerts-from-panel: + link-alert-rules-to-panels: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/alerting-rules/create-grafana-managed-rule#create-alerts-from-panels + destination: /docs/grafana//alerting/alerting-rules/link-alert-rules-to-panels/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule#create-alerts-from-panels + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/ templates: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/notifications/templates/ + destination: /docs/grafana//alerting/fundamentals/templates/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/templates/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/templates/ alert-rule-evaluation: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/ @@ -57,8 +57,8 @@ refs: Labels and annotations add additional information about an alert using key/value pairs: -- [Labels](#labels) are used to differentiate an alert from all other alerts. -- [Annotations](#annotations) are used to provide extra detail on an existing alert. +- [Labels](#labels) are used to differentiate an alert from all other alerts and decide how to manage them. +- [Annotations](#annotations) provide extra details for alert responders to help them understand and address potential issues. ## Labels @@ -83,9 +83,9 @@ An alert's label set can contain three types of labels: **User-configured labels** -Labels that you manually configure in the alert rule to identify the generated alert instances or group them. +Labels that you manually configure in the alert rule to identify the generated alert instances and manage the alerts. Common custom labels, depending on the use case, are: `severity`, `priority`, `team`, and `service`. -You can also use a [template](ref:templates) to customize the label value and generate dynamic values when the rule is evaluated. +Additionally, you can use a [template](ref:templates) to customize the label value and generate dynamic values from query data. **Data source query labels** @@ -97,10 +97,10 @@ Data source query labels labels are also used to generate multiple alert instanc Reserved labels are automatically added by Grafana: -- `alert_name`: the name of the alert rule. +- `alertname`: the name of the alert rule. - `grafana_folder`: the title of the folder containing the alert. -Labels prefixed with `grafana_` are reserved by Grafana for special use. To stop Grafana Alerting from adding a reserved label, you can disable it via the `disabled_labels` option in [unified_alerting.reserved_labels](/docs/grafana//setup-grafana/configure-grafana#unified_alertingreserved_labels) configuration. +Labels prefixed with `grafana_` are reserved by Grafana for special use. You can disable reserved labels via the [`unified_alerting.reserved_labels`](/docs/grafana//setup-grafana/configure-grafana#unified_alertingreserved_labels) option. {{}} @@ -132,13 +132,29 @@ If multiple label keys are sanitized to the same value, the duplicates have a sh ## Annotations -The purpose of annotations is to add additional information to alert instances, such as extra details for notification messages. +Annotations add additional information to alert instances, helping responders identify and address potential issues. -Grafana provides several optional annotations that you can edit for use in notification messages and within Grafana. +Annotations are displayed in Grafana and are included by default in notifications. Grafana provides several optional annotations that you can edit: - `summary`: A short summary of what the alert has detected and why. - `description`: A detailed description of what happened and what the alert does. - `runbook_url`: The runbook page to guide operators managing a potential incident. -- `dashboardUId` and `panelId`: Link the alert to a dashboard and panel. These are automatically set when [creating an alert from panels](ref:create-alerts-from-panel). +- `dashboardUId` and `panelId`: [Link the alert to a dashboard and panel](ref:link-alert-rules-to-panels) to facilitate alert investigation. -Like labels, annotations can use a [template](ref:templates) to customize the label value and generate dynamic values when the rule is evaluated. +For example, you can edit the annotation `summary` to explain why the alert was triggered: + +``` +CPU usage has exceeded 80% for the last 5 minutes. +``` + +And edit the `description` annotation to provide more context and how to respond: + +``` +The web server's CPU has exceeded 80% for more than 5 minutes. + +This indicates that the system is under heavy load and may result in an outage. + +Consider scaling the server's resources and investigating bottlenecks. +``` + +Like labels, annotations can use a [template](ref:templates) to include dynamic data from queries. diff --git a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md index a3caf9f1f07..229816db306 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md +++ b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md @@ -43,7 +43,7 @@ refs: 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. -A query specifies the desired data to retrieve from a data source and applies relevant filters or transformations. Query languages or syntax specific to the chosen data source are utilized for constructing these queries. +A query specifies the data to extract from a data source, with the syntax varying based on the type of data source used. 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. diff --git a/docs/sources/alerting/fundamentals/notifications/_index.md b/docs/sources/alerting/fundamentals/notifications/_index.md index 2017bfef8d8..c26846e069b 100644 --- a/docs/sources/alerting/fundamentals/notifications/_index.md +++ b/docs/sources/alerting/fundamentals/notifications/_index.md @@ -27,9 +27,9 @@ refs: destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/ templates: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/notifications/templates/ + destination: /docs/grafana//alerting/fundamentals/templates/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/templates/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/templates/ configure-alertmanager: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/set-up/configure-alertmanager/ @@ -84,7 +84,9 @@ Start defining your [contact points](ref:contact-points) to specify how to recei [Contact points](ref:contact-points) contain the configuration for sending alert notifications, specifying destinations like email, Slack, OnCall, webhooks, and their notification messages. -A contact point is a list of integrations, each sending a message to a specific destination. They allow the customization of notification messages and the use of notification templates. +A contact point is a list of integrations, each sending a message to a specific destination. + +By default, notification messages include common alert details, such as the number of alerts, alert names, labels, annotations, and other alert information. You can also customize notification messages and use notification templates. First, create the contact point and test the notifications. Then, configure the alert rule to send its notifications to either a contact point or through Notification Policies. diff --git a/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md b/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md index 5c812545f5e..e78920d6e76 100644 --- a/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md +++ b/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md @@ -105,15 +105,15 @@ Consider a notification policy that: - Groups notifications by the `team` label—one group for each distinct `team`. - Sets the Group wait timer to `30s`. -| Time | Incoming alert instance | Notification policy group | Number of instances | | -| ------------------ | ------------------------------- | ------------------------- | ------------------- | ----------------------------------------------------------------------- | -| 00:00 | `alert_name=f1` `team=frontend` | `frontend` | 1 | Starts the group wait timer of the `frontend` group. | -| 00:10 | `alert_name=f2` `team=frontend` | `frontend` | 2 | | -| 00:20 | `alert_name=b1` `team=backend` | `backend` | 1 | Starts the group wait timer of the `backend` group. | -| 00:30\* | | `frontend` | 2 | Group wait elapsed.
Send initial notification reporting 2 alerts. | -| 00:35 | `alert_name=b2` `team=backend` | `backend` | 2 | | -| 00:40 | `alert_name=b3` `team=backend` | `backend` | 3 | | -| 00:50\* | | `backend` | 3 | Group wait elapsed.
Send initial notification reporting 3 alerts. | +| Time | Incoming alert instance | Notification policy group | Number of instances | | +| ------------------ | ------------------------------ | ------------------------- | ------------------- | ----------------------------------------------------------------------- | +| 00:00 | `alertname=f1` `team=frontend` | `frontend` | 1 | Starts the group wait timer of the `frontend` group. | +| 00:10 | `alertname=f2` `team=frontend` | `frontend` | 2 | | +| 00:20 | `alertname=b1` `team=backend` | `backend` | 1 | Starts the group wait timer of the `backend` group. | +| 00:30\* | | `frontend` | 2 | Group wait elapsed.
Send initial notification reporting 2 alerts. | +| 00:35 | `alertname=b2` `team=backend` | `backend` | 2 | | +| 00:40 | `alertname=b3` `team=backend` | `backend` | 3 | | +| 00:50\* | | `backend` | 3 | Group wait elapsed.
Send initial notification reporting 3 alerts. | ### Group interval @@ -134,15 +134,15 @@ Here are the related excerpts from the previous example: And below is the continuation of the example setting the Group interval timer to 5 minutes: -| Time | Incoming alert instance | Notification policy group | Number of instances | | -| ------------------ | ------------------------------- | ------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- | -| 01:30 | `alert_name=f3` `team=frontend` | `frontend` | 3 | | -| 02:30 | `alert_name=f4` `team=frontend` | `frontend` | 4 | | -| 05:30\* | | `frontend` | 4 | Group interval elapsed and resets timer.
Send one notification reporting 4 alerts. | -| 05:50\* | | `backend` | 3 | Group interval elapsed and resets timer.
No group changes, and do not send notification. | -| 08:00 | `alert_name=f4` `team=backend` | `backend` | 4 | | -| 10:30\* | | `frontend` | 4 | Group interval elapsed and resets timer.
No group changes, and do not send notification. | -| 10:50\* | | `backend` | 4 | Group interval elapsed and resets timer.
Send one notification reporting 4 alerts. | +| Time | Incoming alert instance | Notification policy group | Number of instances | | +| ------------------ | ------------------------------ | ------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- | +| 01:30 | `alertname=f3` `team=frontend` | `frontend` | 3 | | +| 02:30 | `alertname=f4` `team=frontend` | `frontend` | 4 | | +| 05:30\* | | `frontend` | 4 | Group interval elapsed and resets timer.
Send one notification reporting 4 alerts. | +| 05:50\* | | `backend` | 3 | Group interval elapsed and resets timer.
No group changes, and do not send notification. | +| 08:00 | `alertname=f4` `team=backend` | `backend` | 4 | | +| 10:30\* | | `frontend` | 4 | Group interval elapsed and resets timer.
No group changes, and do not send notification. | +| 10:50\* | | `backend` | 4 | Group interval elapsed and resets timer.
Send one notification reporting 4 alerts. | **How it works** diff --git a/docs/sources/alerting/fundamentals/templates.md b/docs/sources/alerting/fundamentals/templates.md index 42b0900ff4c..8dabe94816c 100644 --- a/docs/sources/alerting/fundamentals/templates.md +++ b/docs/sources/alerting/fundamentals/templates.md @@ -1,10 +1,10 @@ --- aliases: - - ../fundamentals/notifications/templates/ # /docs/grafana//alerting/fundamentals/notifications/templates/ + - ../fundamentals/notifications/templates/ # /docs/grafana//alerting/fundamentals/templates/ - ../contact-points/message-templating/ # /docs/grafana//alerting/contact-points/message-templating/ - ../alert-rules/message-templating/ # /docs/grafana//alerting/alert-rules/message-templating/ - ../unified-alerting/message-templating/ # /docs/grafana//alerting/unified-alerting/message-templating/ -canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/templates/ +canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/templates/ description: Use templating to customize, format, and reuse alert notification messages. Create more flexible and informative alert notification messages by incorporating dynamic content, such as metric values, labels, and other contextual information. keywords: - grafana @@ -33,24 +33,19 @@ refs: destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#annotations templating-labels-annotations: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/alerting-rules/templating-labels-annotations/ + destination: /docs/grafana//alerting/alerting-rules/templates/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templating-labels-annotations/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/ notification-message-reference: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/configure-notifications/template-notifications/reference/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/ - notification-messages: + template-notifications: - 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/ - create-notification-templates: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/configure-notifications/template-notifications/create-notification-templates/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/create-notification-templates/ --- # Templates @@ -103,7 +98,7 @@ Annotations can contain plain text, but you should template annotations if you n Here’s an example of templating an annotation, which explains where and why the alert was triggered. In this case, the alert triggers when CPU usage exceeds a threshold, and the `summary` annotation provides the relevant details. ``` -CPU usage for {{ index $labels "instance" }} has exceeded 80% ({{ index $values "A" }}) for the last 5 minutes. +CPU usage for {{ $labels.instance }} has exceeded 80% ({{ $values.A.Value }}) for the last 5 minutes. ``` The outcome of this template would be: @@ -120,11 +115,14 @@ For more details on how to template annotations, refer to [Template annotations [Labels](ref:labels) are used to differentiate one alert instance from all other alert instances, as the set of labels uniquely identifies an alert instance. Notification policies and silences use labels to handle alert instances. -Template labels when you need to improve or change how alerts are uniquely identified. This is helpful if the labels you get from your query aren't detailed enough. +You can also template labels based on query results. This is helpful if the labels you get from your query aren't detailed enough. For instance: + +- Add a new label to change how alerts are identified and grouped into different alert groups. +- Add a new label used by notification policies or silences to manage how the alert is handled. Here’s an example of templating a `severity` label based on the query value: -``` +```go {{ if (gt $values.A.Value 90.0) -}} critical {{ else if (gt $values.A.Value 80.0) -}} @@ -136,26 +134,23 @@ low {{- end }} ``` -Avoid using query values in labels, as this may result in the creation of numerous alerts when only one is needed. Use annotation to inform about the query value instead. - For more details on how to template labels, refer to [Template annotations and labels](ref:templating-labels-annotations). ## Template notifications -[Notification templates](ref:notification-messages) allow you to customize the content of your notifications, such as the subject of an email or the body of a Slack message. +[Notification templates](ref:template-notifications) allow you to customize the content of your notifications, such as the subject of an email or the body of a Slack message. Notification templates differ from templating annotations and labels in the following ways: - Notification templates are assigned to the **Contact point**, rather than the alert rule. - If not specified, the contact point uses a default template that includes relevant alert information. -- You can create reusable notification templates and reference them in other templates. - The same template can be shared across multiple contact points, making it easier to maintain and ensuring consistency. -- While both annotation/label templates and notification templates use the same templating language, the available variables and functions differ. For more details, refer to the [notification template reference](ref:notification-message-reference) and [annotation/label template reference](ref:templating-labels-annotations). - Notification templates should not be used to add additional information to individual alerts—use annotations for that purpose. +- While both annotation/label templates and notification templates use the same templating language, the available variables and functions differ. For more details, refer to the [notification template reference](ref:notification-message-reference) and [annotation/label template reference](ref:templating-labels-annotations). Here is an example of a notification template that summarizes all firing and resolved alerts in a notification group: -``` +```go {{ define "alerts.message" -}} {{ if .Alerts.Firing -}} {{ len .Alerts.Firing }} firing alert(s) @@ -184,4 +179,4 @@ The notification message to the contact point would look like this: - The web server web1 has been responding to 5% of HTTP requests with 5xx errors for the last 5 minutes. ``` -For instructions on creating and using notification templates, refer to [Create notification templates.](ref:create-notification-templates) +For more details, refer to [Template notifications](ref:template-notifications). diff --git a/docs/sources/alerting/manage-notifications/_index.md b/docs/sources/alerting/manage-notifications/_index.md index 9adb839afcc..1848df368ed 100644 --- a/docs/sources/alerting/manage-notifications/_index.md +++ b/docs/sources/alerting/manage-notifications/_index.md @@ -10,12 +10,12 @@ labels: - cloud - enterprise - oss -menuTitle: Detect and respond -title: Detect and respond +menuTitle: Monitor status +title: Monitor status weight: 130 --- -# Detect and respond +# Monitor status Use Grafana Alerting to track and generate alerts and send notifications, providing an efficient way for engineers to monitor, respond, and triage issues within their services. diff --git a/docs/sources/alerting/manage-notifications/view-alert-state-on-panels.md b/docs/sources/alerting/manage-notifications/view-alert-state-on-panels.md new file mode 100644 index 00000000000..1daffe011b8 --- /dev/null +++ b/docs/sources/alerting/manage-notifications/view-alert-state-on-panels.md @@ -0,0 +1,54 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/manage-notifications/view-alert-state-on-panels/ +description: View alert rules +keywords: + - grafana + - alerting + - guide + - rules + - view +labels: + products: + - cloud + - enterprise + - oss +title: View alert state on panels +weight: 430 +refs: + time-series-visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/time-series/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series/ + alert-list-panel: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/alert-list/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/alert-list/ + link-alert-rules-to-panels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/link-alert-rules-to-panels/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/ +--- + +# View alert state on panels + +When an [alert rule is linked to a time series panel](ref:link-alert-rules-to-panels), the time series panel displays the alert state and alert events. + +A heart icon near the panel title shows the current alert state: + +- A broken red heart when the alert is in `Alerting` state. +- A green heart when the alert is in `Normal` state. + +Colored annotations indicate changes in alert state, such as pending, alerting, and resolving. + +{{< figure src="/media/docs/alerting/panel-displays-alert-state.png" max-width="1200px" alt="A panel with a firing alert and annotations that display the pending and alerting state changes." >}} + +Additionally, Grafana provides an [alert list panel](ref:alert-list-panel) that you can add to a dashboard to display a list of alerts and their states. + +{{< figure src="/static/img/docs/alert-list-panel/alert-list-panel.png" max-width="850px" alt="Two alert list panels displaying distinct lists of alerts." >}} + +You can configure the alert list panel with various visualization options and filters to control how alerts are displayed. For more details, refer to the [Alert list documentation](ref:alert-list-panel). + +{{< docs/play title="this demo dashboard with alert list panels and linked alert rules" url="https://play.grafana.org/d/000000074/" >}} diff --git a/docs/sources/alerting/set-up/_index.md b/docs/sources/alerting/set-up/_index.md index 2da6e3073d8..41c939ce0e6 100644 --- a/docs/sources/alerting/set-up/_index.md +++ b/docs/sources/alerting/set-up/_index.md @@ -2,90 +2,21 @@ aliases: - unified-alerting/set-up/ # /docs/grafana//alerting/unified-alerting/set-up/ canonical: https://grafana.com/docs/grafana/latest/alerting/set-up/ -description: Set up or upgrade your implementation of Grafana Alerting +description: Additional configuration for Grafana Alerting labels: products: + - cloud + - enterprise - oss -menuTitle: Set up -title: Set up Alerting -weight: 110 -refs: - terraform-provisioning: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/set-up/provision-alerting-resources/terraform-provisioning/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/terraform-provisioning/ - configure-high-availability: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/set-up/configure-high-availability/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/configure-high-availability/ - configure-alertmanager: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/set-up/configure-alertmanager/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/configure-alertmanager/ - data-source-management: - - pattern: /docs/ - destination: /docs/grafana//administration/data-source-management/ - 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 - file-provisioning: - - pattern: /docs/ - destination: /docs/grafana//alerting/set-up/provision-alerting-resources/file-provisioning/ +menuTitle: Additional configuration +title: Additional configuration +weight: 160 --- -# Set up Alerting +# Additional configuration -Set up or upgrade your implementation of Grafana Alerting. - -**Note:** - -These are set-up instructions for Grafana Alerting Open Source. - -## Before you begin - -- Configure your [data sources](ref:data-source-management) -- Check which data sources are compatible with and supported by [Grafana Alerting](ref:data-source-alerting) - -Watch this short video to get started, or follow the [written tutorial](http://grafana.com/tutorials/alerting-get-started/). - -{{< youtube id="6W8Nu4b_PXM" >}} - -## Set up Alerting - -To set up Alerting, you need to: - -1. Configure alert rules - - - Create Grafana-managed or Mimir/Loki-managed alert rules and recording rules - -1. Configure contact points - - - Check the default contact point and update the email address - - - Optional: Add new contact points and integrations - -1. Configure notification policies - - - Check the default notification policy - - - Optional: Add additional nested policies - - - Optional: Add labels and label matchers to control alert routing - -1. Optional: Integrate with [Grafana OnCall](/docs/oncall/latest/integrations/grafana-alerting) - -## Advanced set up options - -Grafana Alerting supports many additional configuration options, from configuring external Alertmanagers to routing Grafana-managed alerts outside of Grafana, to defining your alerting setup as code. +Grafana Alerting offers a variety of advanced configuration options to further tailor your alerting setup. These optional features include configuring up permissions and role-based access control, adding external Alertmanagers, or defining your alerting setup as code. While not essential for basic alerting, these options can enhance security, scalability, and automation in complex environments. The following topics provide you with advanced configuration options for Grafana Alerting. -- [Provision alert rules using file provisioning](ref:file-provisioning) -- [Provision alert rules using Terraform](ref:terraform-provisioning) -- [Add an external Alertmanager](ref:configure-alertmanager) -- [Configure high availability](ref:configure-high-availability) +{{< section >}} diff --git a/docs/sources/alerting/set-up/configure-rbac/_index.md b/docs/sources/alerting/set-up/configure-rbac/_index.md index 6a15d86e724..647ef8ed535 100644 --- a/docs/sources/alerting/set-up/configure-rbac/_index.md +++ b/docs/sources/alerting/set-up/configure-rbac/_index.md @@ -52,7 +52,7 @@ Grafana Alerting has the following permissions. | `alert.provisioning:write` | n/a | Update all Grafana alert rules, notification policies, etc via provisioning API. Permissions to folders and data source are not required. | | `alert.provisioning.provenance:write` | n/a | Set provisioning status for alerting resources. Cannot be used alone. Requires user to have permissions to access resources | -Contact point permissions. To enable these permissions, enable the `alertingApiServer` feature toggle. +Contact point permissions. To enable API and user interface that use these permissions, enable the `alertingApiServer` feature toggle. | Action | Applicable scope | Description | | -------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------- | @@ -64,7 +64,7 @@ Contact point permissions. To enable these permissions, enable the `alertingApiS | `receivers.permissions:read` | `receivers:*`
`receivers:uid:*` | Read permissions for contact points. | | `receivers.permissions:write` | `receivers:*`
`receivers:uid:*` | Manage permissions for contact points. | -Mute time interval permissions. To enable these permissions, enable the `alertingApiServer` feature toggle. +Mute time interval permissions. To enable API and user interface that use these permissions, enable the `alertingApiServer` feature toggle. | Action | Applicable scope | Description | | ------------------------------------------- | ---------------- | -------------------------------------------------- | @@ -80,4 +80,11 @@ Notification template permissions. To enable these permissions, enable the `aler | `alert.notifications.templates:write` | n/a | Create new or update existing templates. | | `alert.notifications.templates:delete` | n/a | Delete existing templates. | +Notification policies permissions. To enable API and user interface that use these permissions, enable the `alertingApiServer` feature toggle. + +| Action | Applicable scope | Description | +| ---------------------------------- | ---------------- | ---------------------------------------------------- | +| `alert.notifications.routes:read` | n/a | Read notification policies. | +| `alert.notifications.routes:write` | n/a | Create new, update and update notification policies. | + To help plan your RBAC rollout strategy, refer to [Plan your RBAC rollout strategy](https://grafana.com/docs/grafana/next/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/). diff --git a/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md b/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md index 2c9f3efa4db..b6bb3145053 100644 --- a/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md +++ b/docs/sources/alerting/set-up/configure-rbac/access-roles/index.md @@ -43,19 +43,19 @@ Fixed roles provide users more granular access to create, view, and update Alert Details of the fixed roles and the access they provide for Grafana Alerting are below. -| Display name in UI / Fixed role | Permissions | Description | -| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Silences Writer: `fixed:alerting.instances:writer` | All permissions from `fixed:alerting.instances:reader` and
`alert.instances:create`
`alert.instances:write` for organization scope
`alert.instances.external:write` for scope `datasources:*` | Add and update silences in Grafana and external providers. | -| Instances and Silences Reader: `fixed:alerting.instances:reader` | `alert.instances:read` for organization scope
`alert.instances.external:read` for scope `datasources:*` | Read alert instances and silences in Grafana and external providers. | -| Notifications Writer: `fixed:alerting.notifications:writer` | All permissions from `fixed:alerting.notifications:reader` and
`alert.notifications:write`for organization scope
`alert.notifications.external:read` for scope `datasources:*` | Add, update, and delete notification policies and contact points in Grafana and external providers. | -| Notifications Reader: `fixed:alerting.notifications:reader` | `alert.notifications:read` for organization scope
`alert.notifications.external:read` for scope `datasources:*` | Read notification policies and contact points in Grafana and external providers. | -| Rules Writer: `fixed:alerting.rules:writer` | All permissions from `fixed:alerting.rules:reader` and
`alert.rule:create`
`alert.rule:write`
`alert.rule:delete`
`alert.silences:create`
`alert.silences:write` for scope `folders:*`
`alert.rules.external:write` for scope `datasources:*` | Create, update, and delete all alert rules and manage rule-specific silences. | -| Rules Reader: `fixed:alerting.rules:reader` | `alert.rule:read`, `alert.silences:read` for scope `folders:*`
`alert.rules.external:read` for scope `datasources:*`
`alert.notifications.time-intervals:read`
`alert.notifications.receivers:list` | Read all alert rules and rule-specific silences in Grafana and external providers. | -| Full access: `fixed:alerting:writer` | All permissions from `fixed:alerting.rules:writer`
`fixed:alerting.instances:writer`
`fixed:alerting.notifications:writer` | Add, update, and delete alert rules, silences, contact points, and notification policies in Grafana and external providers. | -| Full read-only access: `fixed:alerting:reader` | All permissions from `fixed:alerting.rules:reader`
`fixed:alerting.instances:reader`
`fixed:alerting.notifications:reader` | Read alert rules, alert instances, silences, contact points, and notification policies in Grafana and external providers. | -| Read via Provisioning API + Export Secrets: `fixed:alerting.provisioning.secrets:reader` | `alert.provisioning:read` and `alert.provisioning.secrets:read` | Read alert rules, alert instances, silences, contact points, and notification policies using the provisioning API and use export with decrypted secrets. | -| Access to alert rules provisioning API: `fixed:alerting.provisioning:writer` | `alert.provisioning:read` and `alert.provisioning:write` | Manage all alert rules, notification policies, contact points, templates, in the organization using the provisioning API. | -| Set provisioning status: `fixed:alerting.provisioning.status:writer` | `alert.provisioning.provenance:write` | Set provisioning rules for Alerting resources. Should be used together with other regular roles (Notifications Writer and/or Rules Writer.) | +| Display name in UI / Fixed role | Permissions | Description | +| ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Silences Writer: `fixed:alerting.instances:writer` | All permissions from `fixed:alerting.instances:reader` and
`alert.instances:create`
`alert.instances:write` for organization scope
`alert.instances.external:write` for scope `datasources:*` | Add and update silences in Grafana and external providers. | +| Instances and Silences Reader: `fixed:alerting.instances:reader` | `alert.instances:read` for organization scope
`alert.instances.external:read` for scope `datasources:*` | Read alert instances and silences in Grafana and external providers. | +| Notifications Writer: `fixed:alerting.notifications:writer` | All permissions from `fixed:alerting.routes:writer`,
`fixed:alerting.receivers:creator`,
`fixed:alerting.receivers:writer`,
`fixed:alerting.templates:writer`,
`fixed:alerting.time-intervals:writer`and
`alert.notifications:write`for organization scope
`alert.notifications.external:read` for scope `datasources:*` | Add, update, and delete notification policies and contact points in Grafana and external providers. | +| Notifications Reader: `fixed:alerting.notifications:reader` | All permissions from `fixed:alerting.routes:reader`,
`fixed:alerting.receivers:reader`,
`fixed:alerting.templates:reader`,
`fixed:alerting.time-intervals:reader`and
`alert.notifications:read` for organization scope
`alert.notifications.external:read` for scope `datasources:*` | Read notification policies and contact points in Grafana and external providers. | +| Rules Writer: `fixed:alerting.rules:writer` | All permissions from `fixed:alerting.rules:reader` and
`alert.rule:create`
`alert.rule:write`
`alert.rule:delete`
`alert.silences:create`
`alert.silences:write` for scope `folders:*`
`alert.rules.external:write` for scope `datasources:*` | Create, update, and delete all alert rules and manage rule-specific silences. | +| Rules Reader: `fixed:alerting.rules:reader` | `alert.rule:read`, `alert.silences:read` for scope `folders:*`
`alert.rules.external:read` for scope `datasources:*`
`alert.notifications.time-intervals:read`
`alert.notifications.receivers:list` | Read all alert rules and rule-specific silences in Grafana and external providers. | +| Full access: `fixed:alerting:writer` | All permissions from `fixed:alerting.rules:writer`
`fixed:alerting.instances:writer`
`fixed:alerting.notifications:writer` | Add, update, and delete alert rules, silences, contact points, and notification policies in Grafana and external providers. | +| Full read-only access: `fixed:alerting:reader` | All permissions from `fixed:alerting.rules:reader`
`fixed:alerting.instances:reader`
`fixed:alerting.notifications:reader` | Read alert rules, alert instances, silences, contact points, and notification policies in Grafana and external providers. | +| Read via Provisioning API + Export Secrets: `fixed:alerting.provisioning.secrets:reader` | `alert.provisioning:read` and `alert.provisioning.secrets:read` | Read alert rules, alert instances, silences, contact points, and notification policies using the provisioning API and use export with decrypted secrets. | +| Access to alert rules provisioning API: `fixed:alerting.provisioning:writer` | `alert.provisioning:read` and `alert.provisioning:write` | Manage all alert rules, notification policies, contact points, templates, in the organization using the provisioning API. | +| Set provisioning status: `fixed:alerting.provisioning.status:writer` | `alert.provisioning.provenance:write` | Set provisioning rules for Alerting resources. Should be used together with other regular roles (Notifications Writer and/or Rules Writer.) | If you have enabled the `alertingApiServer` feature toggle, an additional set of fixed roles is available. @@ -68,6 +68,8 @@ If you have enabled the `alertingApiServer` feature toggle, an additional set of | Templates Writer: `fixed:alerting.templates:writer` | `alert.notifications.templates:read`, `alert.notifications.templates:write`, `alert.notifications.templates:delete` | Create new and manage existing notification templates. | | Time Intervals Reader: `fixed:alerting.time-intervals:reader` | `alert.notifications.time-intervals:read` | Read all time intervals. | | Time Intervals Writer: `fixed:alerting.time-intervals:writer` | `alert.notifications.time-intervals:read`, `alert.notifications.time-intervals:write`, `alert.notifications.time-intervals:delete` | Create new and manage existing time intervals. | +| Notification Policies Reader: `fixed:alerting.routes:reader` | `alert.notifications.routes:read` | Read all time intervals. | +| Notification Policies Writer: `fixed:alerting.routes:writer` | `alert.notifications.routes:read` `alert.notifications.routes:write` | Create new and manage existing time intervals. | ## Create custom roles diff --git a/docs/sources/alerting/monitor/_index.md b/docs/sources/alerting/set-up/meta-monitoring.md similarity index 96% rename from docs/sources/alerting/monitor/_index.md rename to docs/sources/alerting/set-up/meta-monitoring.md index 1f807e13c52..4c29f6ac4a9 100644 --- a/docs/sources/alerting/monitor/_index.md +++ b/docs/sources/alerting/set-up/meta-monitoring.md @@ -1,8 +1,8 @@ --- aliases: - - ./meta-monitoring/ # /docs/grafana//alerting/meta-monitoring/ - - ./set-up/meta-monitoring/ # /docs/grafana//alerting/set-up/meta-monitoring/ -canonical: https://grafana.com/docs/grafana/latest/alerting/monitor/ + - ../meta-monitoring/ # /docs/grafana//alerting/meta-monitoring/ + - ../monitoring/ # /docs/grafana//alerting/monitoring/ +canonical: https://grafana.com/docs/grafana/latest/alerting/set-up/meta-monitoring description: Monitor your alerting metrics to ensure you identify potential issues before they become critical. keywords: - grafana @@ -12,9 +12,8 @@ labels: products: - enterprise - oss -menuTitle: Monitor title: Meta monitoring -weight: 140 +weight: 700 --- # Meta monitoring diff --git a/docs/sources/alerting/set-up/performance-limitations/index.md b/docs/sources/alerting/set-up/performance-limitations/index.md index 18b631852d7..2d80cdd6dad 100644 --- a/docs/sources/alerting/set-up/performance-limitations/index.md +++ b/docs/sources/alerting/set-up/performance-limitations/index.md @@ -15,7 +15,7 @@ labels: - enterprise - oss title: Performance considerations and limitations -weight: 600 +weight: 800 --- # Performance considerations and limitations diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md index 6e82782b9e4..b5e58fe3eab 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md @@ -69,9 +69,9 @@ refs: destination: /docs/grafana//alerting/set-up/provision-alerting-resources/http-api-provisioning/#span-idroute-get-alert-rule-exportspan-export-an-alert-rule-in-provisioning-file-format-_routegetalertruleexport_ alerting_http_templates: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/set-up/provision-alerting-resources/http-api-provisioning/#templates + destination: /docs/grafana//alerting/set-up/provision-alerting-resources/http-api-provisioning/#notification-template-groups - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/http-api-provisioning/#templates + destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/http-api-provisioning/#notification-template-groups alerting_http_contactpoints: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/set-up/provision-alerting-resources/http-api-provisioning/#contact-points @@ -165,15 +165,15 @@ To export contact points from the Grafana UI, complete the following steps. 1. Click **Copy Code** or **Download**. -### Export templates +### Export notification template groups -Grafana currently doesn't offer an Export UI or [Export endpoint](#export-api-endpoints) for notification templates, unlike other Alerting resources presented in this documentation. +Grafana currently doesn't offer an Export UI or [Export endpoint](#export-api-endpoints) for notification template groups, unlike other Alerting resources presented in this documentation. -However, you can export it by manually copying the content template and title directly from the Grafana UI. +However, you can export it by manually copying the content and name of the notification template group from the Grafana UI. 1. Click **Alerts & IRM** -> **Contact points** -> **Notification templates** tab. -1. Find the template you want to export. -1. Copy the content and title. +1. Find the notification template group you want to export. +1. Copy the content and name. 1. Adjust it for the [file provisioning format](ref:alerting_file_provisioning_template) or [Terraform resource](ref:alerting_tf_provisioning_template). ### Export the notification policy tree @@ -217,8 +217,8 @@ You can use the [Alerting HTTP API](ref:alerting_http_provisioning) to return ex | [Alert rules](ref:alerting_http_alertrules) | /api/v1/provisioning/alert-rules | | [Contact points](ref:alerting_http_contactpoints) | /api/v1/provisioning/contact-points | | [Notification policy tree](ref:alerting_http_notificationpolicies) | /api/v1/provisioning/policies | +| [Notification template groups](ref:alerting_http_templates) | /api/v1/provisioning/templates | | [Mute timings](ref:alerting_http_mutetimings) | /api/v1/provisioning/mute-timings | -| [Templates](ref:alerting_http_templates) | /api/v1/provisioning/templates | However, note the standard endpoints return a JSON format that is not compatible for provisioning through configuration files or Terraform, except the `/export` endpoints listed below. 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 11d92dc2243..b7eb4b841fa 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 @@ -46,9 +46,9 @@ refs: destination: /docs/grafana//administration/provisioning/ export_templates: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-templates + destination: /docs/grafana//alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-notification-template-groups - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-templates + destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-notification-template-groups --- # Use configuration files to provision alerting resources @@ -270,8 +270,8 @@ settings: url: https://discord/webhook # avatar_url: https://my_avatar - # - use_discord_username: Grafana + # + use_discord_username: false # message: | {{ template "default.message" . }} @@ -616,6 +616,16 @@ settings: authorization_credentials: abc123 # maxAlerts: '10' + # + tlsConfig: + # + insecureSkipVerify: false + # + clientCertificate: certificate in PEM format + # + clientKey: key in PEM format + # + caCertificate: CA certificate in PEM format ``` {{< /collapse >}} @@ -639,19 +649,19 @@ settings: {{< /collapse >}} -## Import templates +## Import notification template groups -Create or delete templates using provisioning files in your Grafana instance(s). +Create or delete notification template groups using provisioning files in your Grafana instance(s). -1. Find the notification template in Grafana. -1. [Export](ref:export_templates) a template by copying the template content and title. +1. Find the notification template group in Grafana. +1. [Export](ref:export_templates) a template group by copying the template content and name. 1. Copy the contents into a YAML or JSON configuration file and add it to the `provisioning/alerting` directory of the Grafana instance you want to import the alerting resources to. Example configuration files can be found below. 1. Restart your Grafana instance (or reload the provisioned files using the Admin API). -Here is an example of a configuration file for creating templates. +Here is an example of a configuration file for creating notification template groups. ```yaml # config file version @@ -661,16 +671,16 @@ apiVersion: 1 templates: # organization ID, default = 1 - orgId: 1 - # name of the template, must be unique + # name of the template group, must be unique name: my_first_template - # content of the template + # content of the template group template: | {{ define "my_first_template" }} Custom notification message {{ end }} ``` -Here is an example of a configuration file for deleting templates. +Here is an example of a configuration file for deleting notification template groups. ```yaml # config file version @@ -680,7 +690,7 @@ apiVersion: 1 deleteTemplates: # organization ID, default = 1 - orgId: 1 - # name of the template, must be unique + # name of the template group, must be unique name: my_first_template ``` @@ -853,8 +863,8 @@ In alerting resources, most properties support template variable interpolation, - Alert rule query model: `groups[].rules[].data.model` - Mute timings name: `muteTimes[].name` - Mute timings time intervals: `muteTimes[].time_intervals[]` -- Notification template name: `templates[].name` -- Notification template content: `templates[].template` +- Notification template group name: `templates[].name` +- Notification template group content: `templates[].template` Note for properties that support interpolation, you may unexpectedly substitute template variables when not intended. To avoid this, you can escape the `$variable` with `$$variable`. diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md index 724e4bdf3da..0821f5ec0b4 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md @@ -272,19 +272,19 @@ In this section, we'll create Terraform configurations for each alerting resourc 1. Continue to add more Grafana resources or [use the Terraform CLI for provisioning](#provision-grafana-resources-with-terraform). -### Add and enable templates +### Add and enable notification templates [Notification templates](ref:notification-template) allow customization of alert notifications across multiple contact points. -1. Create or find the notification template you want to import in Grafana. Alternatively, consider writing the resource in code as demonstrated in the example below. +1. Create or find the notification template group you want to import in Grafana. Alternatively, consider writing the resource in code as demonstrated in the example below. -1. [Export](ref:alerting_export) the template as [`grafana_message_template` Terraform resource](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/message_template). +1. [Export](ref:alerting_export) the notification template group as [`grafana_message_template` Terraform resource](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/message_template). - This example is a simple demo template defined as `custom_email.message`. + This example creates a notification template group named `custom_emails` that defines a `custom_email.message` template. ```terraform resource "grafana_message_template" "" { - name = "custom_email.message" + name = "custom_emails" template = <"}}`) within the notification template group. + 1. In the previous contact point, enable the template by setting the `email.message` property as follows. ```terraform @@ -384,9 +386,9 @@ resource "grafana_contact_point" "my_contact_point" { disable_provenance = true } -resource "grafana_message_template" "my_template" { - name = "My Reusable Template" - template = "{{define \"My Reusable Template\" }}\n template content\n{{ end }}" +resource "grafana_message_template" "custom_notification_template_group" { + name = "custom_notification_template_group" + template = "{{define \"template1\" }}Say{{ end }}{{define \"template2\" }}Hi!{{ end }}" disable_provenance = true } diff --git a/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md b/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md index c7862d79151..c9e0edddd0f 100644 --- a/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md +++ b/docs/sources/dashboards/build-dashboards/annotate-visualizations/index.md @@ -188,7 +188,7 @@ Following are some query options specific to the built-in annotation query. You can create new queries to fetch annotations from the built-in annotation query using the `-- Grafana --` data source by setting _Filter by_ to `Tags`. -Grafana v8.1 and later versions also support typeahead of existing tags, provide at least one tag. +Grafana also supports typeahead of existing tags, provide at least one tag. For example, create an annotation query name `outages` and specify a tag `outage`. This query will show all annotations (from any dashboard or via API) with the `outage` tag. If multiple tags are defined in an annotation query, then Grafana will only show annotations matching all the tags. To modify the behavior, enable `Match any`, and Grafana will show annotations that contain any one of the tags you provided. diff --git a/docs/sources/dashboards/create-manage-playlists/index.md b/docs/sources/dashboards/create-manage-playlists/index.md index cae7a433784..20dba1c7cc6 100644 --- a/docs/sources/dashboards/create-manage-playlists/index.md +++ b/docs/sources/dashboards/create-manage-playlists/index.md @@ -24,7 +24,7 @@ A _playlist_ is a list of dashboards that are displayed in a sequence. You might Grafana automatically scales dashboards to any resolution, which makes them perfect for big screens. -You can access the Playlist feature from Grafana's side menu, in the Dashboards submenu. +You can access the **Playlist** feature from Grafana's side menu, in the Dashboards submenu. {{< admonition type="note" >}} You must have at least Editor role permissions to create and manage playlists. @@ -41,12 +41,12 @@ Use the information in this section to access playlists. Start and control the d ### Start a playlist -You can start a playlist in six different view modes. View modes determine how the menus and navigation bar appear on the dashboards as well as how panels are sized. +You can start a playlist in four different view modes. View modes determine how the menus and navigation bar appear on the dashboards as well as how panels are sized. 1. Click **Dashboards** in the main menu. 1. Click **Playlists**. 1. Find the desired playlist and click **Start playlist**. -1. In the dialog box that opens, select one of the [six playlist modes](#playlist-modes) available. +1. In the dialog box that opens, select one of the [four playlist modes](#playlist-modes) available. 1. Disable any dashboard controls that you don't want displayed while the list plays; these controls are enabled and visible by default. Select from: - **Time and refresh** @@ -55,31 +55,28 @@ You can start a playlist in six different view modes. View modes determine how t 1. Click **Start \**. -The playlist displays each dashboard for the time specified in the **Interval** field, set when creating or editing a playlist. After a playlist starts, you can [control](#control-a-playlist) it using the navbar at the top of your screen. +The playlist displays each dashboard for the time specified in the **Interval** field, set when creating or editing a playlist. After a playlist starts, you can [start or stop it](#control-a-playlist) it using the controls at the top of your screen. ### Playlist modes -| Mode | Description | -| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Normal mode |
  • The side menu remains visible.
  • The navbar and dashboard controls appear at the top of the screen.
| -| Normal mode (with auto fit panels) |
  • The side menu remains visible.
  • The navbar and dashboard controls appear at the top of the screen.
  • Dashboard panels automatically adjust to optimize space on screen.
| -| TV mode |
  • The side menu is hidden or removed.
  • The navbar and dashboard controls appear at the top of the screen.
  • Enabled automatically after one minute of user inactivity.
  • Enable it manually using the `d v` sequence shortcut, or by appending the parameter `?inactive` to the dashboard URL.
  • Disable it with any mouse movement or keyboard action.
| -| TV mode (with auto fit panels) |
  • The side menu is hidden or removed.
  • The navbar and dashboard controls appear at the top of the screen.
  • Dashboard panels automatically adjust to optimize space on screen.
    | -| Kiosk mode |
    • The side menu is hidden or removed.
    • The navbar and dashboard controls appear at the top of the screen.
    • You can disable or enable it manually using the `d v` sequence shortcut after the playlist has started.
    | -| Kiosk mode (with auto fit panels) |
    • The side menu is hidden or removed.
    • The navbar and dashboard controls appear at the top of the screen.
    • You can disable or enable it manually using the `d v` sequence shortcut after the playlist has started.
    • Dashboard panels automatically adjust to optimize space on screen.
    | + -### Control a playlist +| Mode | Description | +| ---------------------------------- | ------------------------------------------------------------------------------------ | +| Normal mode |
    • The main menu and navbar remain visible.
    • Dashboard controls are hidden.
    • Playlist controls are displayed at the top of the screen.
      • | +| Normal mode (with auto fit panels) |
        • The main menu and navbar remain visible.
        • Dashboard controls are hidden.
        • Playlist controls are displayed at the top of the screen.
        • Dashboard panels automatically adjust to optimize space on screen.
        | +| Kiosk mode |
        • The main menu, navbar, and dashboard controls are hidden.
        • You can disable the playlist manually by pressing the `Esc` key after the playlist has started. Doing so causes the playlist controls to be displayed at the top of the screen briefly.
        | +| Kiosk mode (with auto fit panels) |
          <
        • The main menu, navbar, and dashboard controls are hidden.
        • You can disable the playlist manually by pressing the `Esc` key after the playlist has started. Doing so causes the playlist controls to be displayed at the top of the screen briefly.
        • Dashboard panels automatically adjust to optimize space on screen.
        | -You can control a playlist in **Normal** or **TV** mode after it's started, using the navigation bar at the top of your screen. Press the Esc key in your keyboard to stop the playlist. + -| Button | Result | -| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| Next (double-right arrow) | Advances to the next dashboard. | -| Back (left arrow) | Returns to the previous dashboard. | -| Stop (square) | Ends the playlist, and exits to the current dashboard. | -| Cycle view mode (monitor icon) | Rotates the display of the dashboards in different view modes. | -| Time range | Displays data within a time range. It can be set to display the last 5 minutes up to 5 years ago, or a custom time range, using the down arrow. | -| Refresh (circle arrow) | Reloads the dashboard, to display the current data. It can be set to reload automatically every 5 seconds to 1 day, using the drop-down arrow. | +### Playlist controls + +You can control a playlist in **Normal** mode after it's started, using the buttons at the top of your screen. Press the `Esc` key to stop the playlist. + +- **Next (double-right arrow)** - Advances to the next dashboard. +- **Back (doublt-left arrow)** - Returns to the previous dashboard. +- **Stop playlist** - Ends the playlist, and exits to the current dashboard. ## Create a playlist diff --git a/docs/sources/dashboards/create-reports/index.md b/docs/sources/dashboards/create-reports/index.md index 272b948d796..d3a15f00404 100644 --- a/docs/sources/dashboards/create-reports/index.md +++ b/docs/sources/dashboards/create-reports/index.md @@ -149,13 +149,13 @@ Only organization administrators can create reports by default. You can customiz ### Save as draft -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 9.1.0 and later and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). You can save a report as a draft at any point during the report creation or update process. You can save a report as a draft even if it's missing required fields. Also, the report won't be sent according to its schedule while it's a draft. ### Choose template variables -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 7.5 and later behind the `reportVariables` feature flag, Grafana Enterprise version 8.0 and later without a feature flag, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). 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. @@ -165,7 +165,7 @@ The query variables saved with a report might become of date if the results of t ### Render a report with panels or rows set to repeat by a variable -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 8.0 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). You can include dynamic dashboards with panels or rows, set to repeat by a variable, into reports. For detailed information about setting up repeating panels or rows in dashboards, refer to [Repeat panels or rows](ref:repeat-panels-or-rows). @@ -233,8 +233,7 @@ This feature relies on the same plugin that supports the [image rendering](ref:i ### Scheduling -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 8.0 and later, and [Grafana Cloud](/docs/grafana-cloud/). -> The scheduler was significantly changed in Grafana Enterprise version 8.1. +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). 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 via the API. @@ -254,7 +253,7 @@ When you schedule a report with a monthly frequency, and set the start date betw #### Send a test email -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 7.0 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). 1. In the report, click **Send test email**. 1. In the **Email** field, enter the email address or addresses that you want to test, separated by a semicolon. @@ -265,19 +264,19 @@ The last saved version of the report will be sent to selected emails. You can us ### Pause a report -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 8.0 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). You can pause sending reports from the report list view by clicking the pause icon. The report will not be sent according to its schedule until it is resumed by clicking the resume button on the report row. ### Add multiple dashboards to a report -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 9.0 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). You can add more than one dashboard to a 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 cannot add the same dashboard to a report multiple times. ### Embed a dashboard as an image into a report -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 9.0 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). 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. @@ -285,7 +284,7 @@ You can send a report email with an image of the dashboard embedded in the email You can generate and save PDF files of any dashboard. -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 6.7 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). 1. In the dashboard that you want to export as PDF, click the **Share** button. 1. On the PDF tab, select a layout option for the exported dashboard: **Portrait** or **Landscape**. @@ -328,21 +327,31 @@ font_regular = DejaVuSansCondensed.ttf font_bold = DejaVuSansCondensed-Bold.ttf # Name of the TrueType font file with italic style font_italic = DejaVuSansCondensed-Oblique.ttf +# Allowed domains to receive reports. Use * to allow all domains. Use a comma-separated list to allow multiple domains. Example: allowed_domains = grafana.com, example.org +allowed_domains = * ``` ## Report settings -> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) version 7.2 and later, and [Grafana Cloud](/docs/grafana-cloud/). +> **Note:** Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). You can configure organization-wide report settings in the **Settings** under **Dashboards > Reporting**. Settings are applied to all the reports for current organization. You can customize the branding options. -Report branding: +### Attachment settings + +#### 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. -Email branding: +- **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 Admin's instance theme, but the preview and exported PDFs will be in the user's instance theme. + +#### Embedded Image + +- **Theme:** Theme of the dashboard image embedded in the email. Defaults to **Dark**. + +### Email branding - **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. - **Email footer:** Toggle to enable the report email footer. Select **Sent by** or **None**. diff --git a/docs/sources/dashboards/share-dashboards-panels/_index.md b/docs/sources/dashboards/share-dashboards-panels/_index.md index 68c8715c570..6df94211f9f 100644 --- a/docs/sources/dashboards/share-dashboards-panels/_index.md +++ b/docs/sources/dashboards/share-dashboards-panels/_index.md @@ -71,7 +71,7 @@ You must have an authorized viewer permission to see an image rendered by a dire The same permission is also required to view embedded links unless you have anonymous access permission enabled for your Grafana instance. {{< admonition type="note" >}} -As of Grafana 8.0, anonymous access permission is not available in Grafana Cloud. +Anonymous access permission is not available in Grafana Cloud. {{< /admonition >}} ## Share dashboards {#share-a-dashboard} @@ -249,13 +249,13 @@ To share a personalized, direct link to your panel within your organization, fol When you click **Render image** in the panel link settings, Grafana generates a PNG image of the panel with the following default parameters: -| Parameter | Description | -| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| width | Width in pixels. Default is 800. | -| height | Height in pixels. Default is 400. | -| tz | Timezone in the format `UTC%2BHH%3AMM` where HH and MM are offset in hours and minutes after UTC. | -| timeout | Number of seconds. The timeout can be increased if the query for the panel needs more than the default 30 seconds. | -| scale | Numeric value to configure device scale factor. Default is 1. Use a higher value to produce more detailed images (higher DPI). Supported in Grafana v7.0+. | +| Parameter | Description | +| --------- | ------------------------------------------------------------------------------------------------------------------------------ | +| width | Width in pixels. Default is 800. | +| height | Height in pixels. Default is 400. | +| tz | Timezone in the format `UTC%2BHH%3AMM` where HH and MM are offset in hours and minutes after UTC. | +| timeout | Number of seconds. The timeout can be increased if the query for the panel needs more than the default 30 seconds. | +| scale | Numeric value to configure device scale factor. Default is 1. Use a higher value to produce more detailed images (higher DPI). | You can also update these parameters in the [image rendering configuration](ref:image-rendering-config). @@ -270,7 +270,7 @@ https://play.grafana.org/render/d-solo/ktMs4D6Mk?from=2024-09-03T11:55:44.442Z&t You can share a panel by embedding it on another website using an iframe. Users must be signed into Grafana to view the panel. {{< admonition type="note" >}} -As of Grafana 8.0, anonymous access permission is no longer available for Grafana Cloud. +Anonymous access permission is no longer available for Grafana Cloud. {{< /admonition >}} To create a panel that can be embedded, follow these steps: diff --git a/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md b/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md index 1c92c0cee32..711127d2db4 100644 --- a/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md +++ b/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md @@ -77,6 +77,8 @@ This feature was previously called **email sharing**. Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud). {{< /admonition >}} +{{< docs/private-preview product="Sharing externally with specific people" >}} + To share a dashboard with specific external users, you can send them a link by email. Use this option when you only want to share your dashboard with specific people. When you share dashboards by email, recipients receive a one-time use link that's valid for **one hour**. Once the link is used, the viewer has access to the shared dashboard for **30 days**. When you share a dashboard with an email link, your organization is billed per user, regardless of how many dashboards are shared. Grafana bills monthly per user until access is revoked. diff --git a/docs/sources/dashboards/use-dashboards/index.md b/docs/sources/dashboards/use-dashboards/index.md index a2be205471a..df3323b38f0 100644 --- a/docs/sources/dashboards/use-dashboards/index.md +++ b/docs/sources/dashboards/use-dashboards/index.md @@ -71,6 +71,16 @@ refs: destination: /docs/grafana//dashboards/share-dashboards-panels/#export-dashboards - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/dashboards/share-dashboards-panels/#export-dashboards + add-ad-hoc-filters: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/add-template-variables/#add-ad-hoc-filters + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/variables/add-template-variables/#add-ad-hoc-filters + shared-dashboards: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/share-dashboards-panels/shared-dashboards/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/share-dashboards-panels/shared-dashboards/ --- # Use dashboards @@ -85,19 +95,18 @@ The dashboard user interface provides a number of features that you can use to c The following image and descriptions highlight all dashboard features. -![An annotated image of a dashboard](/media/docs/grafana/dashboards/screenshot-dashboard-no-toggle-annotated-11.3.png) +![An annotated image of a dashboard](/media/docs/grafana/dashboards/screenshot-dashboard-annotated-v11.3-2.png) -1. **Grafana home** - Click **Home** in the breadcrumb to go to the home page configured in the Grafana instance. 1. **Dashboard folder** - When you click the dashboard folder name, you can search for other dashboards contained in the folder and perform other [folder management tasks](ref:dashboard-folders). 1. **Dashboard title** - You can create your own dashboard titles or have Grafana create them for you using [generative AI features](ref:generative-ai-features). +1. **Kiosk mode** - Click to display the dashboard on a large screen such as a TV or a kiosk. Kiosk mode hides the main menu, navbar, and dashboard controls. Learn more about kiosk mode in our [How to Create Kiosks to Display Dashboards on a TV blog post](https://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/). Press `Esc` to leave kiosk mode. 1. **Mark as favorite** - Mark the dashboard as one of your favorites so it's included in your list of **Starred** dashboards in the main menu. -1. **Public label** - When you share a dashboard externally, it's marked with the **Public** label. +1. **Public label** - When you [share a dashboard externally](ref:shared-dashboards), it's marked with the **Public** label. 1. **Dashboard insights** - Click to view analytics about your dashboard including information about users, activity, query counts. Learn more about [dashboard analytics](ref:dashboard-analytics). 1. **Edit** - Click to leave view-only mode and enter edit mode, where you can make changes directly to the dashboard and access dashboard settings, as well as several panel editing functions. 1. **Export** - Access [dashboard exporting](ref:export-dashboards) options. 1. **Share dashboard** - Access several [dashboard sharing](ref:sharing) options. -1. **Kiosk mode** - Click to display the dashboard on a large screen such as a TV or a kiosk. Kiosk mode hides elements such as navigation menus. Learn more about kiosk mode in our [How to Create Kiosks to Display Dashboards on a TV blog post](https://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/). Press `Enter` to leave kiosk mode. -1. **Variables** - Use [variables](ref:variables) to create more interactive and dynamic dashboards. +1. **Variables** - Use [variables](ref:variables), including ad hoc filters, to create more interactive and dynamic dashboards. 1. **Dashboard links** - Link to other dashboards, panels, and external websites. Learn more about [dashboard links](ref:dashboard-links). 1. **Current dashboard time range and time picker** - Click to select [relative time range](#relative-time-range) options and set custom [absolute time ranges](#absolute-time-range). - You can change the **Timezone** and **Fiscal year** settings from the time range controls by clicking the **Change time settings** button. @@ -272,3 +281,33 @@ Selecting the **Auto** interval schedules a refresh based on the query time rang ### Control the time range using a URL {{< docs/shared lookup="dashboards/time-range-URLs.md" source="grafana" version="" >}} + +## Filter dashboard data + +Once you've [added an ad hoc filter](ref:add-ad-hoc-filters) in the dashboard settings, you can create label/value filter pairs on the dashboard. +These filters are applied to all metric queries that use the specified data source and to all panels on the dashboard. + +To filter dashboard data, follow these steps: + +1. On the dashboard, click in the filter field. +1. Select a label, operator, and value. + + To add multiple values for one label, choose one of the multi-select operators, **One of** (`=|`) or **Not one of** (`!=|`). These operators only appear if the filter data source supports it. + +1. Repeat this process as needed until you have all the filters you need. + + ![Ad hoc filters](/media/docs/grafana/dashboards/screenshot-adhoc-filters-v11.3.png) + +### Edit or delete filters + +To edit or delete filters, follow these steps: + +1. On the dashboard, click anywhere on the filter you want to change. +1. Do one of the following: + + - To edit the operator or value of a filter, click anywhere on the filter and update it. + + ![Editing an ad hoc filter](/media/docs/grafana/dashboards/screenshot-edit-filters-v11.3.png) + + - To change the filter label, you must delete the filter and create a new one. + - To delete a filter, click the **X** next to it. diff --git a/docs/sources/dashboards/variables/add-template-variables/index.md b/docs/sources/dashboards/variables/add-template-variables/index.md index 3d5debf5386..c98f9d09472 100644 --- a/docs/sources/dashboards/variables/add-template-variables/index.md +++ b/docs/sources/dashboards/variables/add-template-variables/index.md @@ -77,9 +77,14 @@ refs: destination: /docs/grafana-cloud/visualizations/dashboards/variables/variable-syntax/#raw add-a-data-source: - pattern: /docs/grafana/ - destination: /docs/grafana//administration/data-source-management/#add-a-data-source + destination: /docs/grafana//datasources/#add-a-data-source - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//administration/data-source-management/#add-a-data-source + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/#add-a-data-source + filter-dashboard: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/use-dashboards/#filter-dashboard-data + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/use-dashboards/#filter-dashboard-data --- # Add variables @@ -101,21 +106,33 @@ The following table lists the types of variables shipped with Grafana. ## Enter General options You must enter general options for any type of variable that you create. +To create a variable, follow these steps: 1. Click **Edit** in the top-right corner of the dashboard. 1. Click **Settings**. 1. Go to the **Variables** tab. -1. Click **Add variable**. +1. Click **Add variable**, or if there are already existing variables, **+ New variable**. +1. Choose an option in the **Select variable type** drop-down list. 1. Enter a **Name** for the variable. -1. In the **Select variable type** drop-down, choose **Query**. -1. (Optional) In **Label**, enter the display name of the variable dropdown. +1. (Optional) In the **Label** field, enter the display name for the variable drop-down list. - If you don't enter a display name, then the dropdown label is the variable name. + If you don't enter a display name, then the drop-down list label is the variable name. 1. Choose a **Show on dashboard** option: - - **Label and value** - The variable drop-down displays the variable **Name** or **Label** value. This is the default. - - **Value:** The variable drop-down only displays the selected variable value and a down arrow. - - **Nothing:** No variable drop-down is displayed on the dashboard. + + - **Label and value** - The variable drop-down list displays the variable **Name** or **Label** value. This is the default. + - **Value:** The variable drop-down list only displays the selected variable value and a down arrow. + - **Nothing:** No variable drop-down list is displayed on the dashboard. + +1. Click one of the following links to complete the steps for adding your selected variable type: + + - [Query](#add-a-query-variable) + - [Custom](#add-a-custom-variable) + - [Textbox](#add-a-text-box-variable) + - [Constant](#add-a-constant-variable) + - [Data source](#add-a-data-source-variable) + - [Interval](#add-an-interval-variable) + - [Ad hoc filters](#add-ad-hoc-filters) ## Add a query variable @@ -130,10 +147,20 @@ Query expressions are different for each data source. For more information, refe {{< /admonition >}} 1. [Enter general options](#enter-general-options). -1. In the **Data source** list, select the target data source for the query. For more information about data sources, refer to [Add a data source](ref:add-a-data-source). -1. In the **Refresh** list, select when the variable should update options. - - **On Dashboard Load:** Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized. - - **On Time Range Change:** Queries the data source every time the dashboard loads and when the dashboard time range changes. Use this option if your variable options query contains a time range filter or is dependent on the dashboard time range. +1. Under the **Query options** section of the page, select a target data source in the **Data source** drop-down list. + + You can also click **Open advanced data source picker** to see more options, including adding a data source (Admins only). + For more information about data sources, refer to [Add a data source](ref:add-a-data-source). + +1. In the **Query type** drop-down list, select one of the following options: + + - **Label names** + - **Label values** + - **Metrics** + - **Query result** + - **Series query** + - **Classic query** + 1. In the **Query** field, enter a query. - The query field varies according to your data source. Some data sources have custom query editors. @@ -142,9 +169,18 @@ Query expressions are different for each data source. For more information, refe - 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. In the **Sort** 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 will be used. -1. (Optional) Enter [Selection Options](#configure-variable-selection-options). -1. In **Preview of values**, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. +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 will be used. +1. Under **Refresh**, select when the variable should update options: + + - **On dashboard load** - Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized. + - **On time range change** - Queries the data source every time the dashboard loads and when the dashboard time range changes. Use this option if your variable options query contains a time range filter or is dependent on the dashboard time range. + +1. (Optional) Configure the settings in the [Selection Options](#configure-variable-selection-options) section: + + - **Multi-value** - Enables multiple values to be selected at the same time. + - **Include All option** - Enables an option to include all variables. + +1. In the **Preview of values** section, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. 1. Click **Save dashboard**. 1. Click **Back to dashboard** and **Exit edit**. @@ -155,9 +191,16 @@ Use a _custom_ variable for a value that does not change, such as a number or a For example, if you have server names or region names that never change, then you might want to create them as custom variables rather than query variables. Because they do not change, you might use them in [chained variables](#chained-variables) rather than other query variables. That would reduce the number of queries Grafana must send when chained variables are updated. 1. [Enter general options](#enter-general-options). -1. In the **Values separated by comma** list, enter the values for this variable in a comma-separated list. You can include numbers, strings, or key/value pairs separated by a space and a colon. For example, `key1 : value1,key2 : value2`. -1. (Optional) Enter [Selection Options](#configure-variable-selection-options). -1. In **Preview of values**, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. +1. Under the **Custom options** section of the page, in the **Values separated by comma** field, enter the values for this variable in a comma-separated list. + + You can include numbers, strings, or key/value pairs separated by a space and a colon. For example, `key1 : value1,key2 : value2`. + +1. (Optional) Configure the settings in the [Selection Options](#configure-variable-selection-options) section: + + - **Multi-value** - Enables multiple values to be selected at the same time. + - **Include All option** - Enables an option to include all variables. + +1. In the **Preview of values** section, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. 1. Click **Save dashboard**. 1. Click **Back to dashboard** and **Exit edit**. @@ -168,8 +211,10 @@ _Text box_ variables display a free text input field with an optional default va For more information about cardinality, refer to [What are cardinality spikes and why do they matter?](https://grafana.com/blog/2022/02/15/what-are-cardinality-spikes-and-why-do-they-matter/) 1. [Enter general options](#enter-general-options). -1. (Optional) In the **Default value** field, select the default value for the variable. If you do not enter anything in this field, then Grafana displays an empty text box for users to type text into. -1. In **Preview of values**, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. +1. (Optional) Under the **Text options** section of the page, in the **Default value** field, enter the default value for the variable. + + If you do not enter anything in this field, then Grafana displays an empty text box for users to type text into. + 1. Click **Save dashboard**. 1. Click **Back to dashboard** and **Exit edit**. @@ -182,8 +227,10 @@ Constant variables are _not_ flexible. Each constant variable only holds one val Constant variables are useful when you have complex values that you need to include in queries but don't want to retype in every query. For example, if you had a server path called `i-0b6a61efe2ab843gg`, then you could replace it with a variable called `$path_gg`. 1. [Enter general options](#enter-general-options). -1. In the **Value** field, enter the variable value. You can enter letters, numbers, and symbols. You can even use wildcards if you use [raw format](ref:raw-format). -1. In **Preview of values**, Grafana displays the current variable value. Review it to ensure it matches what you expect. +1. Under the **Constant options** section of the page, in the **Value** field, enter the variable value. + + You can enter letters, numbers, and symbols. You can even use wildcards if you use [raw format](ref:raw-format). + 1. Click **Save dashboard**. 1. Click **Back to dashboard** and **Exit edit**. @@ -192,13 +239,17 @@ Constant variables are useful when you have complex values that you need to incl _Data source_ variables enable you to quickly change the data source for an entire dashboard. They are useful if you have multiple instances of a data source, perhaps in different environments. 1. [Enter general options](#enter-general-options). -1. In the **Type** list, select the target data source for the variable. +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. - You can also click **Open advanced data source picker** to see more options, including adding a data source (Admins only). For more information about data sources, refer to [Add a data source](ref:add-a-data-source). + Leave this field empty to display all instances. -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. Leave this field empty to display all instances. -1. (Optional) Enter [Selection Options](#configure-variable-selection-options). -1. In **Preview of values**, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. +1. (Optional) Configure the settings in the [Selection Options](#configure-variable-selection-options) section: + + - **Multi-value** - Enables multiple values to be selected at the same time. + - **Include All option** - Enables an option to include all variables. + +1. In the **Preview of values** section, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. 1. Click **Save dashboard**. 1. Click **Back to dashboard** and **Exit edit**. @@ -209,11 +260,18 @@ Use an _interval_ variable to represents time spans such as `1m`,`1h`, `1d`. You You can use an interval variable as a parameter to group by time (for InfluxDB), date histogram interval (for Elasticsearch), or as a summarize function parameter (for Graphite). 1. [Enter general options](#enter-general-options). -1. In the **Values** field, enter the time range intervals that you want to appear in the variable drop-down list. The following time units are supported: `s (seconds)`, `m (minutes)`, `h (hours)`, `d (days)`, `w (weeks)`, `M (months)`, and `y (years)`. You can also accept or edit the default values: `1m,10m,30m,1h,6h,12h,1d,7d,14d,30d`. -1. (Optional) Turn on the **Auto Option** if you want to add the `auto` option to the list. This option allows you to specify how many times the current time range should be divided to calculate the current `auto` time span. If you turn it on, then two more options appear: - - **Step count -** Select the number of times the current time range will be divided to calculate the value, similar to the **Max data points** query option. For example, if the current visible time range is 30 minutes, then the `auto` interval groups the data into 30 one-minute increments. The default value is 30 steps. - - **Min Interval -** The minimum threshold below which the step count intervals will not divide the time. To continue the 30 minute example, if the minimum interval is set to 2m, then Grafana would group the data into 15 two-minute increments. -1. In **Preview of values**, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. +1. Under the **Interval options** section, in the **Values** field, enter the time range intervals that you want to appear in the variable drop-down list. + + The following time units are supported: `s (seconds)`, `m (minutes)`, `h (hours)`, `d (days)`, `w (weeks)`, `M (months)`, and `y (years)`. You can also accept or edit the default values: `1m,10m,30m,1h,6h,12h,1d,7d,14d,30d`. + +1. (Optional) Select on the **Auto option** checkbox if you want to add the `auto` option to the list. + + This option allows you to specify how many times the current time range should be divided to calculate the current `auto` time span. If you turn it on, then two more options appear: + + - **Step count** - Select the number of times the current time range will be divided to calculate the value, similar to the **Max data points** query option. For example, if the current visible time range is 30 minutes, then the `auto` interval groups the data into 30 one-minute increments. The default value is 30 steps. + - **Min interval** - The minimum threshold below which the step count intervals will not divide the time. To continue the 30 minute example, if the minimum interval is set to 2m, then Grafana would group the data into 15 two-minute increments. + +1. In the **Preview of values** section, Grafana displays a list of the current variable values. Review them to ensure they match what you expect. 1. Click **Save dashboard**. 1. Click **Back to dashboard** and **Exit edit**. @@ -233,23 +291,32 @@ groupByNode(summarize(movingAverage(apps.$app.$server.counters.requests.count, 5 ## Add ad hoc filters -_Ad hoc filters_ enable you to add key/value filters that are automatically added to all metric queries that use the specified data source. Unlike other variables, you do not use ad hoc filters in queries. Instead, you use ad hoc filters to write filters for existing queries. +_Ad hoc filters_ are one of the most complex and flexible variable options available. +Instead of a regular list of variable options, this variable allows you to build a dashboard-wide ad hoc query. + +Ad hoc filters let you add label/value filters that are automatically added to all metric queries that use the specified data source. +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" %}} -Not all data sources support ad hoc filters. Examples of those that do include Prometheus, Loki, InfluxDB, and Elasticsearch. +Not all data sources support ad hoc filters. +Examples of those that do include Prometheus, Loki, InfluxDB, and Elasticsearch. {{% /admonition %}} +To create an ad hoc filter, follow these steps: + 1. [Enter general options](#enter-general-options). -1. In the **Data source** list, select the target data source. +1. Under the **Ad-hoc options** section of the page, select a target data source in the **Data source** drop-down list. - You can also click **Open advanced data source picker** to see more options, including adding a data source (Admins only). For more information about data sources, refer to [Add a data source](ref:add-a-data-source). + You can also click **Open advanced data source picker** to see more options, including adding a data source (Admins only). + For more information about data sources, refer to [Add a data source](ref:add-a-data-source). +1. (Optional) To provide the filter dimensions as comma-separated values (CSV), toggle the **Use static key dimensions** switch on, and then enter the values in the space provided. 1. Click **Save dashboard**. +1. Enter an optional description of your dashboard changes, and then click **Save**. 1. Click **Back to dashboard** and **Exit edit**. -### Create ad hoc filters - -Ad hoc filters are one of the most complex and flexible variable options available. Instead of a regular list of variable options, this variable allows you to build a dashboard-wide ad hoc query. Filters you apply in this manner are applied to all panels on the dashboard. +Now you can [filter data on the dashboard](ref:filter-dashboard). ## Configure variable selection options @@ -304,18 +371,12 @@ Grafana has global built-in variables that can be used in expressions in the que ### $\_\_dashboard -In Grafana 7.1, the variable changed from showing the UID of the current dashboard to the name of the current dashboard. - This variable is the name of the current dashboard. ### $\_\_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. -{{% admonition type="note" %}} -This special formatting syntax is only available in Grafana 7.1.2+ -{{% /admonition %}} - | Syntax | Example result | Description | | ------------------------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `${__from}` | 1594671549254 | Unix millisecond epoch | @@ -639,10 +700,6 @@ demo.robustperception.io:9100 ### Filter and modify using named text and value capture groups -{{% admonition type="note" %}} -This feature is available in Grafana 7.4+. -{{% /admonition %}} - Using named capture groups, you can capture separate 'text' and 'value' parts from the options returned by the variable query. This allows the variable drop-down list to contain a friendly name for each value that can be selected. For example, when querying the `node_hwmon_chip_names` Prometheus metric, the `chip_name` is a lot friendlier than the `chip` value. So the following variable query result: diff --git a/docs/sources/dashboards/variables/inspect-variable/index.md b/docs/sources/dashboards/variables/inspect-variable/index.md index 1e0464020b7..bcdb8635141 100644 --- a/docs/sources/dashboards/variables/inspect-variable/index.md +++ b/docs/sources/dashboards/variables/inspect-variable/index.md @@ -46,10 +46,6 @@ In addition to [managing variables](#manage-variables), the **Variables** tab le The variables page lets you easily identify whether a variable is being referenced (or used) in other variables or dashboard. In addition, you can also [add](ref:add) and [manage variables](#manage-variables) on this page. -{{% admonition type="note" %}} -This feature is available in Grafana 7.4 and later versions. -{{% /admonition %}} - ![Variables list](/static/img/docs/variables-templates/variables-list-7-4.png) Any variable that is referenced or used has a green check mark next to it, while unreferenced variables have a orange caution icon next to them. @@ -59,7 +55,3 @@ Any variable that is referenced or used has a green check mark next to it, while In addition, all referenced variables have a dependency icon next to the green check mark. You can click on the icon to view the dependency map. The dependency map can be moved. You can zoom in out with mouse wheel or track pad equivalent.--> ![Variables list](/static/img/docs/variables-templates/dependancy-map-7-4.png) - -{{% admonition type="note" %}} -This feature is available in Grafana 7.4 and later versions. -{{% /admonition %}} diff --git a/docs/sources/datasources/_index.md b/docs/sources/datasources/_index.md index 2cdfc50daa0..89fe11e36e5 100644 --- a/docs/sources/datasources/_index.md +++ b/docs/sources/datasources/_index.md @@ -138,6 +138,8 @@ A data source that uses the result set from another panel in the same dashboard. These built-in core data sources are also included in the Grafana documentation: +{{< column-list >}} + - [Alertmanager]({{< relref "./alertmanager" >}}) - [AWS CloudWatch]({{< relref "./aws-cloudwatch" >}}) - [Azure Monitor]({{< relref "./azure-monitor" >}}) @@ -157,6 +159,8 @@ These built-in core data sources are also included in the Grafana documentation: - [Testdata]({{< relref "./testdata" >}}) - [Zipkin]({{< relref "./zipkin" >}}) +{{< /column-list >}} + ## Add additional data source plugins You can add additional data sources as plugins (that are not available in core Grafana), which you can install or create yourself. diff --git a/docs/sources/datasources/aws-cloudwatch/_index.md b/docs/sources/datasources/aws-cloudwatch/_index.md index 5729ce54e01..6e5d3a0618e 100644 --- a/docs/sources/datasources/aws-cloudwatch/_index.md +++ b/docs/sources/datasources/aws-cloudwatch/_index.md @@ -238,6 +238,10 @@ You can attach these permissions to the IAM role or IAM user you configured in [ } ``` +{{< admonition type="note" >}} +Cross-account observability lets you to retrieve metrics and logs across different accounts in a single region but you can't query EC2 Instance Attributes across accounts because those come from the EC2 API and not the CloudWatch API. +{{< /admonition >}} + ### Configure CloudWatch settings #### Namespaces of Custom Metrics @@ -413,7 +417,7 @@ Each time you select a dimension in the query editor, Grafana issues a `ListMetr Each time you change queries in the query editor, Grafana issues a new request to the `GetMetricData` API. {{% admonition type="note" %}} -Grafana v6.5 and higher replaced all `GetMetricStatistics` API requests with calls to GetMetricData to provide better support for CloudWatch metric math, and enables the automatic generation of search expressions when using wildcards or disabling the `Match Exact` option. +Grafana replaced all `GetMetricStatistics` API requests with calls to GetMetricData to provide better support for CloudWatch metric math, and enables the automatic generation of search expressions when using wildcards or disabling the `Match Exact` option. The `GetMetricStatistics` API qualified for the CloudWatch API free tier, but `GetMetricData` calls don't. {{% /admonition %}} diff --git a/docs/sources/datasources/aws-cloudwatch/query-editor/index.md b/docs/sources/datasources/aws-cloudwatch/query-editor/index.md index 0d688f5c09f..0acd6b7e696 100644 --- a/docs/sources/datasources/aws-cloudwatch/query-editor/index.md +++ b/docs/sources/datasources/aws-cloudwatch/query-editor/index.md @@ -132,10 +132,6 @@ This feature is not available for metrics based on [metric math expressions](#me ### Create a Metric Insights query -{{% admonition type="note" %}} -This query option is available only in Grafana v8.3 and higher. -{{% /admonition %}} - The Metrics Query option in the CloudWatch data source is referred to as **Metric Insights** in the AWS console. It's a fast, flexible, SQL-based query engine that you can use to identify trends and patterns across millions of operational metrics in real time. diff --git a/docs/sources/datasources/google-cloud-monitoring/query-editor/index.md b/docs/sources/datasources/google-cloud-monitoring/query-editor/index.md index 937ff627a37..87e1ee9d85b 100644 --- a/docs/sources/datasources/google-cloud-monitoring/query-editor/index.md +++ b/docs/sources/datasources/google-cloud-monitoring/query-editor/index.md @@ -66,8 +66,17 @@ The metrics query editor helps you select metrics, group and aggregate by labels 1. _(Optional)_ Use the plus and minus icons in the filter and group-by sections to add and remove filters or group-by clauses. Google Cloud Monitoring supports several metrics types, such as `GAUGE`, `DELTA,` and `CUMULATIVE`. -Each supports different aggregation options, such as reducers and aligners. -The metrics query editor lists available aggregation methods for a selected metric, and sets a default reducer and aligner when you select a metric. +Each supports different aggregation options, such as reducers and aligners. Additionally, metrics have specific value types that can be either scalar or a distribution. + +The metrics query editor lists available aggregation methods for a selected metric, and sets a default aggregation, reducer and aligner when you select a metric. + +In the case that the metric value type is a distribution, the aggregation will be set by default to the mean. For scalar value types, there is no aggregation by default. + +The various metrics are documented [here](https://cloud.google.com/monitoring/api/metrics_gcp) and further details on the kinds and types of metrics can be found [here](https://cloud.google.com/monitoring/api/v3/kinds-and-types). + +{{% admonition type="note" %}} +Distribution metrics are typically best visualized as either a heatmap or histogram. When visualizing in this way, aggregation is not necessary. However, for other visualization types, performance degradation may be observed when attempting to query distribution metrics that are not aggregated due to the number of potential buckets that can be returned. For more information on how to visualize distribution metrics refer to [this page](https://cloud.google.com/monitoring/charts/charting-distribution-metrics). +{{% /admonition %}} ### Apply a filter diff --git a/docs/sources/datasources/graphite/template-variables/index.md b/docs/sources/datasources/graphite/template-variables/index.md index 1343e50db7c..67b6cbaf94f 100644 --- a/docs/sources/datasources/graphite/template-variables/index.md +++ b/docs/sources/datasources/graphite/template-variables/index.md @@ -73,7 +73,7 @@ For details, refer to the [Graphite docs on the autocomplete API for tags](http: ### Use multi-value variables in tag queries -Multi-value variables in tag queries use the advanced formatting syntax for variables introduced in Grafana v5.0: `{var:regex}`. +Multi-value variables in tag queries use the advanced formatting syntax for variables: `{var:regex}`. Non-tag queries use the default glob formatting for multi-value variables. #### Tag expression example diff --git a/docs/sources/datasources/influxdb/_index.md b/docs/sources/datasources/influxdb/_index.md index 586cb89a682..c3f722d848f 100644 --- a/docs/sources/datasources/influxdb/_index.md +++ b/docs/sources/datasources/influxdb/_index.md @@ -4,7 +4,7 @@ aliases: - ../data-sources/influxdb/provision-influxdb/ - ../features/datasources/influxdb/ - provision-influxdb/ -description: Guide for using InfluxDB in Grafana +description: InfluxDB data source for Grafana keywords: - grafana - influxdb @@ -19,242 +19,42 @@ menuTitle: InfluxDB title: InfluxDB data source weight: 700 refs: - explore: + annotations: - pattern: /docs/grafana/ - destination: /docs/grafana//explore/ + destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//explore/ - build-dashboards: + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/annotate-visualizations/ + alerting: - pattern: /docs/grafana/ - destination: /docs/grafana//dashboards/build-dashboards/ + destination: /docs/grafana//alerting/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/build-dashboards/ - data-source-management: + destination: /docs/grafana-cloud/alerting-and-irm/alerting/ + transformations: - pattern: /docs/grafana/ - destination: /docs/grafana//administration/data-source-management/ + 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/ --- # InfluxDB data source {{< docs/shared lookup="influxdb/intro.md" source="grafana" version="" >}} -Grafana includes built-in support for InfluxDB. -This topic explains options, variables, querying, and other features specific to the InfluxDB data source, which include -its [feature-rich code editor for queries and visual query builder]({{< relref "./query-editor" >}}). +Grafana includes built-in support for InfluxDB. You do not have to install a plugin to add the InfluxDB 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. -Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning -system. +Grafana offers multiple configuration options for the InfluxDB data source, including a choice of three query languages and a robust query editor that includes both a code editor and a visual query builder. -Once you've added the InfluxDB data source, you can [configure it](#configure-the-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). +## Get started with the InfluxDB data source -## Configure the data source +The following documents will help you get started with the InfluxDB data source in Grafana: -To configure basic settings for the data source, complete the following steps: +- [Get started with Grafana and InfluxDB](/docs/grafana//getting-started/get-started-grafana-influxdb/) +- [Configure the InfluxDB data source](./configure-influxdb-data-source/) +- [InfluxDB query editor](./query-editor/) +- [InfluxDB templates and variables](./template-variables/) -1. Click **Connections** in the left-side menu. -1. Under Your connections, click **Data sources**. -1. Enter `InfluxDB` in the search bar. -1. Select **InfluxDB**. +Once you have configured the data source you can: - The **Settings** tab of the data source is displayed. - -1. Set the data source's basic configuration options carefully: - -| Name | Description | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **Name** | Sets the name you use to refer to the data source in panels and queries. We recommend something like `InfluxDB-InfluxQL`. | -| **Default** | Sets whether the data source is pre-selected for new panels. | -| **URL** | The HTTP protocol, IP address, and port of your InfluxDB API. InfluxDB's default API port is 8086. | -| **Min time interval** | _(Optional)_ Refer to [Min time interval](#configure-min-time-interval). | -| **Max series** | _(Optional)_ Limits the number of series and tables that Grafana processes. Lower this number to prevent abuse, and increase it if you have many small time series and not all are shown. Defaults to 1,000. | - -You can also configure settings specific to the InfluxDB data source. These options are described in the sections below. - -### Min time interval - -The **Min time interval** setting defines a lower limit for the auto group-by time interval. - -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 | - -We recommend setting this value to match your InfluxDB write frequency. -For example, use `1m` if InfluxDB writes data every minute. - -You can also override this setting in a dashboard panel under its data source options. - -### Select a query language - -InfluxDB data source options differ depending on which query language you select: - -- [InfluxQL](https://docs.influxdata.com/influxdb/v1.8/query_language/explore-data/), a SQL-like language for querying - InfluxDB, with statements such as SELECT, FROM, WHERE, and GROUP BY that are familiar to SQL users. - InfluxQL is available in InfluxDB 1.0 onwards. -- [SQL](https://www.influxdata.com/products/sql/) native SQL language with - support [FlightSQL](https://www.influxdata.com/glossary/apache-arrow-flight-sql/). -- [Flux](https://docs.influxdata.com/influxdb/v2.0/query-data/get-started/), which provides significantly broader - functionality than InfluxQL. It supports not only queries but also built-in functions for data shaping, string - manipulation, and joining to non-InfluxDB data sources, but also processing time-series data. - It's similar to JavaScript with a functional style. - -To help choose the best language for your needs, refer to -a [comparison of Flux vs InfluxQL](https://docs.influxdata.com/influxdb/v1.8/flux/flux-vs-influxql/) -and [why InfluxData created Flux](https://www.influxdata.com/blog/why-were-building-flux-a-new-data-scripting-and-query-language/). - -{{% admonition type="note" %}} -Though not required, we recommend that you append your query language choice to the data source's **Name** setting: - -- InfluxDB-InfluxQL -- InfluxDB-SQL -- InfluxDB-Flux - -{{% /admonition %}} - -### Configure InfluxQL - -Configure these options if you select the InfluxQL (classic InfluxDB) query language: - -| Name | Description | -| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Allowed cookies** | Defines which cookies are forwarded to the data source. All other cookies are deleted. | -| **Database** | Sets the ID of the bucket to query. Copy this from the [Buckets page](https://docs.influxdata.com/influxdb/v2.0/organizations/buckets/view-buckets/) of the InfluxDB UI. | -| **User** | Sets the username to sign into InfluxDB. | -| **Password** | Defines the token you use to query the bucket defined in **Database**. Copy this from the [Tokens page](https://docs.influxdata.com/influxdb/v2.0/security/tokens/view-tokens/) of the InfluxDB UI. | -| **HTTP mode** | Sets the HTTP method used to query your data source. The POST verb allows for larger queries that would return an error using the GET verb. Defaults to GET. | - -### Configure SQL - -Configure these options if you select the SQL query language: - -| Name | Description | -| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Database** | Sets the ID of the bucket to query. Copy this from the Buckets page of the InfluxDB UI. | -| **Token** | API token used for SQL queries. It can be generated on InfluxDB Cloud dashboard under [Load Data > API Tokens](https://docs.influxdata.com/influxdb/cloud-serverless/get-started/setup/#create-an-all-access-api-token) menu. | -| **Insecure Connection** | Disable gRPC TLS security. | - -### Configure Flux - -Configure these options if you select the Flux query language: - -| Name | Description | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **Organization** | The [Influx organization](https://v2.docs.influxdata.com/v2.0/organizations/) that will be used for Flux queries. This is also used to for the `v.organization` query macro. | -| **Token** | The authentication token used for Flux queries. With Influx 2.0, use the [influx authentication token to function](https://v2.docs.influxdata.com/v2.0/security/tokens/create-token/). Token must be set as `Authorization` header with the value `Token `. For influx 1.8, the token is `username:password`. | -| **Default bucket** | _(Optional)_ The [Influx bucket](https://v2.docs.influxdata.com/v2.0/organizations/buckets/) that will be used for the `v.defaultBucket` macro in Flux queries. | - -### Provision the data source - -You can define and configure the data source in YAML files as part of Grafana's provisioning system. -For more information about provisioning, and for available configuration options, refer -to [Provisioning Grafana][provisioning-data-sources]. - -{{% admonition type="note" %}} -`database` [field is deprecated](https://github.com/grafana/grafana/pull/58647). -We suggest to use `dbName` field in `jsonData`. Please see the examples below. -No need to change existing provisioning settings. -{{% /admonition %}} - -#### Provisioning examples - -**InfluxDB 1.x example:** - -```yaml -apiVersion: 1 - -datasources: - - name: InfluxDB_v1 - type: influxdb - access: proxy - user: grafana - url: http://localhost:8086 - jsonData: - dbName: site - httpMode: GET - secureJsonData: - password: grafana -``` - -**InfluxDB 2.x for Flux example:** - -```yaml -apiVersion: 1 - -datasources: - - name: InfluxDB_v2_Flux - type: influxdb - access: proxy - url: http://localhost:8086 - jsonData: - version: Flux - organization: organization - defaultBucket: bucket - tlsSkipVerify: true - secureJsonData: - token: token -``` - -**InfluxDB 2.x for InfluxQL example:** - -```yaml -apiVersion: 1 - -datasources: - - name: InfluxDB_v2_InfluxQL - type: influxdb - access: proxy - url: http://localhost:8086 - jsonData: - dbName: site - httpHeaderName1: 'Authorization' - secureJsonData: - httpHeaderValue1: 'Token ' -``` - -**InfluxDB 3.x for SQL example:** - -```yaml -apiVersion: 1 - -datasources: - - name: InfluxDB_v3_InfluxQL - type: influxdb - access: proxy - url: http://localhost:8086 - jsonData: - version: SQL - dbName: site - httpMode: POST - insecureGrpc: false - secureJsonData: - token: '' -``` - -## Query the data source - -The InfluxDB data source's query editor has two modes, InfluxQL and Flux, depending on your choice of query language in -the [data source configuration](#configure-the-data-source): - -For details, refer to the [query editor documentation]({{< relref "./query-editor" >}}). - -## Use template variables - -Instead of hard-coding details such as server, application, and sensor names in metric queries, you can use variables. -Grafana lists these variables in dropdown select boxes at the top of the dashboard to help you change the data displayed -in your dashboard. -Grafana refers to such variables as template variables. - -For details, see the [template variables documentation]({{< relref "./template-variables" >}}). +- Add [annotations](ref:annotations) +- Set up [alerting](ref:alerting) +- Add [transformations](ref:transformations) diff --git a/docs/sources/datasources/influxdb/configure-influxdb-data-source/_index.md b/docs/sources/datasources/influxdb/configure-influxdb-data-source/_index.md new file mode 100644 index 00000000000..f81c0069ce8 --- /dev/null +++ b/docs/sources/datasources/influxdb/configure-influxdb-data-source/_index.md @@ -0,0 +1,259 @@ +--- +aliases: + - ../data-sources/influxdb/ + - ../data-sources/influxdb/provision-influxdb/ + - ../features/datasources/influxdb/ + - provision-influxdb/ +description: Guide for using InfluxDB in Grafana +keywords: + - grafana + - influxdb + - guide + - flux +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Configure the InfluxDB data source +title: Configure the InfluxDB data source +weight: 300 +refs: + provision-grafana: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/provisioning/#provision-grafana + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/provisioning/#provision-grafana +--- + +# Configure the InfluxDB data source + +This document provides instructions for configuring the InfluxDB data source and explains the available configuration options. + +## Before you begin + +To configure the InfluxDB data source you must have the `Administrator` role. + +{{< admonition type="note" >}} +Select the query language you want to use with InfluxDB before adding the InfluxDB data source. Configuration options differ based on query language type. +{{< /admonition >}} + +InfluxData provides three query languages. Some key points to consider: + +- SQL is only available for InfluxDB v3.x. +- Flux is a functional data scripting language for InfluxDB 2.x. Refer to [Query InfluxDB with Flux](https://docs.influxdata.com/influxdb/cloud/query-data/get-started/query-influxdb/) for a basic guide on working with Flux. +- InfluxQL is SQL-like query language developed by InfluxData. It doesn't support more advanced functions such as JOINs. + +To help choose the best language for your needs, refer to +a [comparison of Flux vs InfluxQL](https://docs.influxdata.com/influxdb/v1.8/flux/flux-vs-influxql/) +and [Why InfluxData created Flux](https://www.influxdata.com/blog/why-were-building-flux-a-new-data-scripting-and-query-language/). + +## Add the InfluxDB data source + +Complete the following steps to set up a new InfluxDB data source: + +1. Click **Connections** in the left-side menu. +2. Click **Add new connection**. +3. Type `InfluxDB` in the search bar. +4. Select the **InfluxDB** data source. +5. Click **Add new data source** in the upper right. + +You are taken to the **Settings** tab where you will configure the data source. + +## InfluxDB common configuration options + +The following configuration options apply to **all three query language options**. + +- **Name** - Sets the name you use to refer to the data source in panels and queries. Examples: `InfluxDB-InfluxQL`, `InfluxDB_SQL`. +- **Default** - Toggle to set as the default data source. +- **Query language** - Select the query language for your InfluxDB instance. The three options are: + - **InfluxQL** - SQL-like language for querying InfluxDB, with statements such as SELECT, FROM, WHERE, and GROUP BY that are familiar to SQL users. + - **SQL** - Native SQL language starting with InfluxDB v.3.0. Refer to InfluxData's [SQL reference documentation](https://docs.influxdata.com/influxdb/cloud-serverless/reference/sql/) for a list of supported statements, operators, and functions. + - **Flux** - Flux is a data scripting language developed by InfluxData that allows you to query, analyze, and act on data. Refer to [Get started with Flux](https://docs.influxdata.com/influxdb/cloud/query-data/get-started/) for guidance on using Flux. + +**HTTP section:** + +- **URL** - The HTTP protocol, IP address, and port of your InfluxDB API. InfluxDB’s default API port is `8086`. +- **Allowed cookies** - Defines which cookies are forwarded to the data source. All other cookies are deleted by default. +- **Timeout** - Set an HTTP request timeout in seconds. + +**Auth section:** + +- **Basic auth** - The most common authentication method. Use your InfluxData user name and password to authenticate. Toggling requires you to add the user and password under **Basic auth details**. +- **With credentials** - Toggle to enable credentials such as cookies or auth headers to be sent with cross-site requests. +- **TLS client auth** - Toggle to use client authentication. When enabled, add the `Server name`, `Client cert` and `Client key` under the **TLS/SSL auth details** section. The client provides a certificate that the server validates to establish the client’s trusted identity. The client key encrypts the data between client and server. +- **With CA cert** - Authenticate with a CA certificate. Follow the instructions of your CA (Certificate Authority) to download the certificate file. +- **Skip TLS verify** - Toggle to bypass TLS certificate validation. +- **Forward OAuth identity** - Forward the OAuth access token (and also the OIDC ID token if available) of the user querying the data source. + +**Basic auth details:** + +If you enable **Basic auth** under the Auth section you need to configure the following: + +- **User** - Add the username used to sign in to InfluxDB. +- **Password** - Defines the token you use to query the bucket defined in **Database**. Retrieve this from the [Tokens page](https://docs.influxdata.com/influxdb/v2.0/security/tokens/view-tokens/) in the InfluxDB UI. + +**TLS/SSL auth details:** + +TLS/SSL certificates are encrypted and stored in the Grafana database. + +- **CA cert** - If you toggle **With CA cert** add your self-signed cert here. +- **Server name** - Name of the server. Example: server1.domain.com +- **Client cert** - Add the client certificate. +- **Client key** - Add the client key. + +**Custom HTTP headers:** + +- **Header** - Add a custom HTTP header. Select an option from the drop-down. Allows custom headers to be passed based on the needs of your InfluxDB instance. +- **Value** - The value for the header. + +**Private data source connect:** + +- **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. + +Once you have added your connection settings, click **Save & test** to test the data source connection. + +### InfluxQL-specific configuration section + +The following settings are specific to the InfluxQL query language option. + +**InfluxQL InfluxDB details section:** + +- **Database** - Sets the ID of the bucket to query. Refer to [View buckets](https://docs.influxdata.com/influxdb/v2.0/organizations/buckets/view-buckets/) in InfluxData's documentation on how to locate the list of available buckets and their corresponding IDs. +- **User** - The user name used to sign in to InfluxDB. +- **Password** - Defines the token used to query the bucket defined in **Database**. Retrieve the password from the [Tokens page](https://docs.influxdata.com/influxdb/v2.0/security/tokens/view-tokens/) of the InfluxDB UI. +- **HTTP method** - Sets the HTTP method used to query your data source. The POST method allows for larger queries that would return an error using the GET method. The default method is `POST`. +- **Min time interval** - _(Optional)_ Sets the minimum time interval for auto group-by. Grafana recommends setting this to match the data write frequency. For example, if your data is written every minute, it’s recommended to set this interval to 1 minute, so that each group contains data from each new write. The default is `10s`. Refer to [Min time interval](#min-time-interval) for format examples. +- **Max series** - _(Optional)_ Sets a limit on the maximum number of series or tables that Grafana processes. Set a lower limit to prevent system overload, or increase it if you have many small time series and need to display more of them. The default is `1000`. + +### SQL-specific configuration section + +The following settings are specific to the SQL query language option. + +**SQL InfluxDB details section:** + +- **Database** - Specify the **bucket ID**. Refer to the **Buckets page** in the InfluxDB UI to locate the ID. +- **Token** The API token used for SQL queries. Generated on InfluxDB Cloud dashboard under [Load Data > API Tokens](https://docs.influxdata.com/influxdb/cloud-serverless/get-started/setup/#create-an-all-access-api-token) menu. +- **Insecure Connection** - Toggle to disable gRPC TLS security. +- **Max series** - _(Optional)_ Sets a limit on the maximum number of series or tables that Grafana processes. Set a lower limit to prevent system overload, or increase it if you have many small time series and need to display more of them. The default is `1000`. + +### Flux-specific configuration section + +The following settings are specific to the Flux query language option. + +**Flux InfluxDB details section:** + +- **Organization** - The [Influx organization](https://v2.docs.influxdata.com/v2.0/organizations/) used for Flux queries. Also used for the `v.organization` query macro. +- **Token** - The authentication token used for Flux queries. With Influx 2.0, use the [influx authentication token to function](https://v2.docs.influxdata.com/v2.0/security/tokens/create-token/). Token must be set as `Authorization` header with the value `Token `. For Influx 1.8, the token is `username:password`. +- **Default bucket** - _(Optional)_ The [Influx bucket](https://v2.docs.influxdata.com/v2.0/organizations/buckets/) used for the `v.defaultBucket` macro in Flux queries. +- **Min time interval** - Sets the minimum time interval for auto group-by. Grafana recommends aligning this setting with the data write frequency. For example, if data is written every minute, set the interval to 1 minute to ensure each group includes data from every new write. The default is `10s`. +- **Max series** - Sets a limit on the maximum number of series or tables that Grafana processes. Set a lower limit to prevent system overload, or increase it if you have many small time series and need to display more of them. The default is `1000`. + +### Min time interval + +The **Min time interval** setting defines a lower limit for the auto group-by time interval. + +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 | + +You can also override this setting in a dashboard panel under its data source options. + +## Provision the InfluxDB data source + +You can define and configure the data source in YAML files as part of Grafana's provisioning system. +For more information about provisioning, and for available configuration options, refer +to [Provision Grafana](ref:provision-grafana). + +{{% admonition type="note" %}} +The `database` [field is deprecated](https://github.com/grafana/grafana/pull/58647). +Grafana recommends using the `dbName` field in `jsonData`. There is no need to change existing provisioning settings. +{{% /admonition %}} + +### Provisioning examples + +Provisioning differs based on query language. + +**InfluxDB 1.x example:** + +```yaml +apiVersion: 1 + +datasources: + - name: InfluxDB_v1 + type: influxdb + access: proxy + user: grafana + url: http://localhost:8086 + jsonData: + dbName: site + httpMode: GET + secureJsonData: + password: grafana +``` + +**InfluxDB 2.x for Flux example:** + +```yaml +apiVersion: 1 + +datasources: + - name: InfluxDB_v2_Flux + type: influxdb + access: proxy + url: http://localhost:8086 + jsonData: + version: Flux + organization: organization + defaultBucket: bucket + tlsSkipVerify: true + secureJsonData: + token: token +``` + +**InfluxDB 2.x for InfluxQL example:** + +```yaml +apiVersion: 1 + +datasources: + - name: InfluxDB_v2_InfluxQL + type: influxdb + access: proxy + url: http://localhost:8086 + jsonData: + dbName: site + httpHeaderName1: 'Authorization' + secureJsonData: + httpHeaderValue1: 'Token ' +``` + +**InfluxDB 3.x for SQL example:** + +```yaml +apiVersion: 1 + +datasources: + - name: InfluxDB_v3_InfluxQL + type: influxdb + access: proxy + url: http://localhost:8086 + jsonData: + version: SQL + dbName: site + httpMode: POST + insecureGrpc: false + secureJsonData: + token: '' +``` diff --git a/docs/sources/datasources/influxdb/query-editor/index.md b/docs/sources/datasources/influxdb/query-editor/index.md index 76f1a50d733..14b3c1294d4 100644 --- a/docs/sources/datasources/influxdb/query-editor/index.md +++ b/docs/sources/datasources/influxdb/query-editor/index.md @@ -2,14 +2,15 @@ aliases: - ../../data-sources/influxdb/query-editor/ - influxdb-flux/ -description: Guide for Flux in Grafana +description: This topic describes the InfluxDB query editor, modes and querying the InfluxDB data source. labels: products: - cloud - enterprise - oss -title: Query Editor -weight: 200 +title: InfluxDB query Editor +menuTitle: Query editor +weight: 400 refs: explore: - pattern: /docs/grafana/ @@ -31,45 +32,80 @@ refs: destination: /docs/grafana//panels-visualizations/visualizations/logs/ - pattern: /docs/grafana-cloud/ destination: grafana-cloud/visualizations/panels-visualizations/visualizations/logs/ + 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 + build-dashboards: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/ + data-source-management: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/data-source-management/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/data-source-management/ + annotations: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/annotate-visualizations/ + configure-influxdb-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/influxdb/configure-influxdb-data-source/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//datasources/influxdb/configure-influxdb-data-source/ --- # InfluxDB query editor -This topic explains querying specific to the InfluxDB data source. -For general documentation on querying data sources in Grafana, see [Query and transform data](ref:query-transform-data). +Grafana's query editors are unique to 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 InfluxDB query editor is located on the [Explore page](ref:explore). You can also access the InfluxDB query editor from a dashboard panel. Click the ellipsis in the upper right of the panel and select **Edit**. + +You can also use the query editor to retrieve [log data](#query-logs) and [annotate](#apply-annotations) visualizations. ## Choose a query editing mode -The InfluxDB data source's query editor has two modes depending on your choice of query language in -the [data source configuration]({{< relref "../#configure-the-data-source" >}}): +The InfluxDB data source has three different types of query editors, each corresponding to the query language selected in the [data source configuration](https://grafana.com/docs/grafana//datasources/influxdb/configure-influxdb-data-source/#influxdb-configuration-options): - [InfluxQL](#influxql-query-editor) - [SQL](#sql-query-editor) - [Flux](#flux-query-editor) -You also use the query editor to retrieve [log data](#query-logs) and [annotate](#apply-annotations) visualizations. +Editor options vary based on query language. ## InfluxQL query editor -The InfluxQL query editor helps you select metrics and tags to create InfluxQL queries. +The InfluxQL query editor helps you select metrics and tags to create InfluxQL queries. There are two modes: `visual editor mode` and `raw query mode`. To switch between the two modes click the **pencil icon** in the upper right. -**To enter edit mode:** +Visual query editor mode contains the following components: -1. Hover over any part of the panel to display the actions menu on the top right corner. -1. Click the menu and select **Edit**. +- **FROM** - Select a measurement to query. +- **WHERE** - Select filters by clicking the **+ sign**. +- **SELECT** - Select fields and functions from the drop-down. You can add multiple fields and functions by clicking the **+ sign**. +- **GROUP BY** - Select a tag from the drop-down menu. +- **TIMEZONE** - _Optional_ Group data by a specific timezone. +- **ORDER BY TIME** - Sort data by time in either ascending or descending order. +- **LIMIT** - _Optional_ Limits the number of rows returned by the query. +- **SLIMIT** - _Optional_ Limits the number of series returned by the query. Refer to [SLIMIT clause](https://docs.influxdata.com/influxdb/cloud/query-data/influxql/explore-data/limit-and-slimit/#slimit-clause) for more information on this option. +- **FORMAT AS** - Select a format option from the drop-down menu. +- **ALIAS** - Add an alias. Refer to [Alias patterns](#alias-patterns) for more information. + +### Raw query editor mode + +You can write raw InfluxQL queries by switching to raw query mode. Click the pencil in the upper right of the query editor to switch modes. Note that when you switch to visual editor mode, you will lose any changes made in raw query mode. + +If you use raw query mode, your query must include `WHERE $timeFilter`. You should also provide a group by time and an aggregation function. Otherwise, InfluxDB may return hundreds of thousands of data points, potentially causing your browser to hang. ![InfluxQL query editor](/static/img/docs/influxdb/influxql-query-editor-8-0.png) -### Filter data (WHERE) - -To add a tag filter, click the plus icon to the right of the `WHERE` condition. - -To remove tag filters, click the tag key, then select **--remove tag filter--**. - -#### Match by regular expressions +### Match by regular expressions You can enter regular expressions for metric names or tag filter values. -Wrap the regex pattern in forward slashes (`/`). +Wrap the regex pattern in forward slashes (`/`), as shown in this example: `/measurement/`. Grafana automatically adjusts the filter tag condition to use the InfluxDB regex match condition operator (`=~`). @@ -77,56 +113,35 @@ Grafana automatically adjusts the filter tag condition to use the InfluxDB regex In the `SELECT` row, you can specify which fields and functions to use. -If you have a group by time, you must have an aggregation function. -Some functions like `derivative` also require an aggregation function. +If you **group by time** you must use an aggregation function. Certain functions such as `derivative` also require an aggregation function. -The editor helps you build this part of the query. -For example: +If you have the following: ![](/static/img/docs/influxdb/select_editor.png) -This query editor input generates an InfluxDB `SELECT` clause: +The query editor input generates an InfluxDB `SELECT` clause: ```sql SELECT derivative(mean("value"), 10s) / 10 AS "REQ/s" FROM.... ``` -**To select multiple fields:** +You can also use a \* in a SELECT statement to select all fields. -1. Click the plus button. -1. Select **Field > field** to add another `SELECT` clause. +```sql +SELECT * FROM +``` - You can also `SELECT` an asterisk (`*`) to select all fields. +### GROUP BY results -### Group query results +To group results by a tag, specify the tag in the **GROUP BY** row: -To group results by a tag, define it in a "Group By". +1. Click the **+ sign** in the GROUP BY row. +1. Select a tag from the drop-down. -**To group by a tag:** +You can GROUP BY multiple options. -1. Click the plus icon at the end of the GROUP BY row. -1. Select a tag from the dropdown that appears. - -**To remove the "Group By":** - -1. Click the tag. -1. Click the "x" icon. - -### Text editor mode (RAW) - -You can write raw InfluxQL queries by switching the editor mode. -However, be careful when writing queries manually. - -If you use raw query mode, your query _must_ include at least `WHERE $timeFilter`. - -Also, _always_ provide a group by time and an aggregation function. -Otherwise, InfluxDB can easily return hundreds of thousands of data points that can hang your browser. - -**To switch to raw query mode:** - -1. Click the hamburger icon. -1. Toggle **Switch editor mode**. +To remove a GROUP BY option click the **X icon** next to the option. ### Alias patterns @@ -138,20 +153,21 @@ Otherwise, InfluxDB can easily return hundreds of thousands of data points that | `$col` | Column name. | | `$tag_exampletag` | The value of the `exampletag` tag. The syntax is `$tag*yourTagName` and must start with `$tag*`. To use your tag as an alias in the ALIAS BY field, you must use the tag to group by in the query. | -You can also use `[[tag_hostname]]` pattern replacement syntax. +You can also use the `[[tag_hostname]]` pattern replacement syntax. -For example, entering the value `Host: [[tag_hostname]]` in the ALIAS BY field replaces it with the `hostname` tag value -for each legend value. -An example legend value would be `Host: server1`. +For example, entering the value `Host: [[tag_hostname]]` in the ALIAS BY field replaces it with the `hostname` tag value for each legend value. + +An example legend value is `Host: server1`. ## SQL query editor -Grafana support [SQL querying language](https://docs.influxdata.com/influxdb/cloud-serverless/query-data/sql/) -with [InfluxDB v3.0](https://www.influxdata.com/blog/introducing-influxdb-3-0/) and higher. +Grafana supports the [SQL query language](https://docs.influxdata.com/influxdb/cloud-serverless/query-data/sql/) in [InfluxDB v3.0](https://www.influxdata.com/blog/introducing-influxdb-3-0/) and higher. + +You construct your SQL query directly in the query editor. ### Macros -You can use macros within the query to replace them with the values from Grafana's context. +You can use macros in your query to automatically substitute them with values from Grafana's context. | Macro example | Replaced with | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -179,12 +195,12 @@ Examples: ## Flux query editor Grafana supports Flux when running InfluxDB v1.8 and higher. -If your data source is [configured for Flux]({{< relref "./#configure-the-data-source" >}}), you can use -the [Flux query and scripting language](https://www.influxdata.com/products/flux/) in the query editor, which serves as +If your data source is [configured for Flux](ref:configure-influxdb-data-source), you can use +the [Flux](https://docs.influxdata.com/flux/v0/) in the query editor, which serves as a text editor for raw Flux queries with macro support. For more information and connection details, refer -to [1.8 compatibility](https://github.com/influxdata/influxdb-client-go/#influxdb-18-api-compatibility). +to [InfluxDB 1.8 API compatibility](https://github.com/influxdata/influxdb-client-go/#influxdb-18-api-compatibility). ### Use macros @@ -199,7 +215,7 @@ Macros support copying and pasting from [Chronograf](https://www.influxdata.com/ | `v.defaultBucket` | The data source configuration's "Default Bucket" setting. | | `v.organization` | The data source configuration's "Organization" setting. | -For example, the query editor interpolates this query: +For example, consider the following Flux query: ```flux from(bucket: v.defaultBucket) @@ -210,8 +226,7 @@ from(bucket: v.defaultBucket) |> yield(name: "mean") ``` -Into this query to send to InfluxDB, with interval and time period values changing according to the active time -selection: +This Flux query is interpolated into the following query and sent to InfluxDB, with the interval and time period values changing according to the active time selection: ```flux from(bucket: "grafana") @@ -222,33 +237,20 @@ from(bucket: "grafana") |> yield(name: "mean") ``` -To view the interpolated version of a query with the query inspector, refer to [Panel Inspector](ref:panel-inspector). +To view the interpolated version of a query with the Query inspector, refer to [Panel Inspector](ref:panel-inspector). ## Query logs -You can query and display log data from InfluxDB in [Explore](ref:explore) and with the [Logs panel](ref:logs) for dashboards. +You can query and display log data from InfluxDB in [Explore](ref:explore) and in the dashboard [Logs panel](ref:logs). -Select the InfluxDB data source, then enter a query to display your logs. +Select an InfluxDB data source in the Query editor. Under the **Select measurement field** next to the **FROM** section, choose a measurement containing your log data, then choose the appropriate fields that will display the log message. Add any additional filters by clicking the **+ sign** next to the **WHERE** field. Add additional conditions in the GROUP BY, ORDER BY and the rest of the options. -### Create log queries - -The Logs Explorer next to the query field, accessed by the **Measurements/Fields** button, lists measurements and -fields. -Choose the desired measurement that contains your log data, then choose which field to use to display the log message. - -Once InfluxDB returns the result, the log panel lists log rows and displays a bar chart, where the x axis represents the -time and the y axis represents the frequency/count. - -### Filter search - -To add a filter, click the plus icon to the right of the **Measurements/Fields** button, or next to a condition. - -To remove tag filters, click the first select, then choose **--remove filter--**. +After InfluxDB returns the results, the log panel displays log rows along with a bar chart. The x-axis represents time, while the y-axis shows the frequency or count. ## Apply annotations -[Annotations][annotate-visualizations] overlay rich event information on top of graphs. -You can add annotation queries in the Dashboard menu's Annotations view. +[Annotations](ref:annotations) overlay rich event information on top of graphs. +You can add annotation queries in the dashboard menu's **Annotations view**. For InfluxDB, your query **must** include `WHERE $timeFilter`. diff --git a/docs/sources/datasources/influxdb/template-variables/index.md b/docs/sources/datasources/influxdb/template-variables/index.md index 5351258ed1b..72d48bdccbf 100644 --- a/docs/sources/datasources/influxdb/template-variables/index.md +++ b/docs/sources/datasources/influxdb/template-variables/index.md @@ -17,7 +17,7 @@ labels: - oss menuTitle: Template variables title: InfluxDB template variables -weight: 300 +weight: 600 refs: add-template-variables-chained-variables: - pattern: /docs/grafana/ @@ -39,57 +39,62 @@ refs: destination: /docs/grafana//dashboards/variables/add-template-variables/#add-ad-hoc-filters - pattern: /docs/grafana-cloud/ destination: /docs/grafana//dashboards/variables/add-template-variables/#add-ad-hoc-filters - add-template-variables-adds-a-query-variable: + add-template-variables-add-a-query-variable: - pattern: /docs/grafana/ destination: /docs/grafana//dashboards/variables/add-template-variables/#add-a-query-variable - pattern: /docs/grafana-cloud/ destination: /docs/grafana//dashboards/variables/add-template-variables/#add-a-query-variable + variable-best-practices: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/#variable-best-practices + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/variables/#variable-best-practices --- # InfluxDB template variables -Instead of hard-coding details such as server, application, and sensor names in metric queries, you can use variables. -Grafana lists these variables in dropdown select boxes at the top of the dashboard to help you change the data displayed in your dashboard. -Grafana refers to such variables as template variables. +Instead of hard-coding details such as server, application, and sensor names in metric queries, you can use variables. Grafana displays these variables in drop-down select boxes at the top of the dashboard to help you change the data displayed in your dashboard. Grafana refers to such variables as template variables. -For an introduction to templating and template variables, refer to the [Templating](ref:variables) and [Add and manage variables](ref:add-template-variables) documentation. +For additional information using variables and templates, refer to the following documentation: + +- [Variables](ref:variables) +- [Templates](ref:variables) +- [Add and manage variables](ref:add-template-variables) +- [Variable best practices](ref:variable-best-practices) ## Use query variables -If you add a query template variable, you can write an InfluxDB exploration (metadata) query. -These queries can return results like measurement names, key names, or key values. +By adding a query template variable, you can write an InfluxDB metadata exploration query. These queries return results such as measurement names, key names, and key values. -For more information, refer to [Add query variable](ref:add-template-variables-adds-a-query-variable). +For more information, refer to [Add a query variable](ref:add-template-variables-add-a-query-variable). -For example, to create a variable that contains all values for tag `hostname`, specify a query like this in the query variable **Query**: +To create a variable containing all values for the `hostname` tag, use the following query format in the **Query** variable: ```sql SHOW TAG VALUES WITH KEY = "hostname" ``` -### Chain or nest variables +## Chain or nest variables You can also create nested variables, sometimes called [chained variables](ref:add-template-variables-chained-variables). -For example, if you had a variable called `region`, you could have the `hosts` variable show only hosts from the selected region with a query like: +For example, if you have a variable named `region`, you can configure the `hosts` variable to display only hosts from the selected region using the following query: ```sql SHOW TAG VALUES WITH KEY = "hostname" WHERE region = '$region' ``` -You can fetch key names for a given measurement: +You can also fetch key names for a given measurement: ```sql SHOW TAG KEYS [FROM ] ``` -If you have a variable with key names, you can use this variable in a group-by clause. -This helps you change group-by using the variable list at the top of the dashboard. +If you have a variable containing key names, you can use it in a **GROUP BY** clause. This allows you to adjust the grouping by selecting from the variable list at the top of the dashboard -### Use ad hoc filters +## Use ad hoc filters -InfluxDB supports the special **Ad hoc filters** variable type. -You can use this variable type to specify any number of key/value filters, and Grafana applies them automatically to all of your InfluxDB queries. +InfluxDB supports the **Ad hoc filters** variable type. This variable type allows you to define multiple key/value filters, which Grafana then automatically applies to all your InfluxDB queries. For more information, refer to [Add ad hoc filters](ref:add-template-variables-add-ad-hoc-filters). @@ -97,13 +102,13 @@ For more information, refer to [Add ad hoc filters](ref:add-template-variables-a The InfluxDB data source supports two variable syntaxes for use in the **Query** field: -- `$`, which is easier to read and write but does not allow you to use a variable in the middle of a word. +- `$` - This syntax is easy to read and write but does not allow you to use a variable in the middle of a word or expression. ```sql SELECT mean("value") FROM "logins" WHERE "hostname" =~ /^$host$/ AND $timeFilter GROUP BY time($__interval), "hostname" ``` -- `${varname}` +- `${varname}` - Use this syntax when you want to interpolate a variable in the middle of an expression. ```sql SELECT mean("value") FROM "logins" WHERE "hostname" =~ /^[[host]]$/ AND $timeFilter GROUP BY time($__interval), "hostname" @@ -113,4 +118,4 @@ When you enable the **Multi-value** or **Include all value** options, Grafana co ### Templated dashboard example -To view an example templated dashboard, refer to this [InfluxDB templated dashboard](https://play.grafana.org/d/f62a0410-5abb-4dd8-9dfc-caddfc3e2ffd/eccb2445-b0a2-5e83-8e0f-6d5ea53ad575). +To view an example of a templated dashboard, refer to this [InfluxDB example dashboard](https://play.grafana.org/d/f62a0410-5abb-4dd8-9dfc-caddfc3e2ffd/eccb2445-b0a2-5e83-8e0f-6d5ea53ad575). diff --git a/docs/sources/datasources/loki/configure-loki-data-source.md b/docs/sources/datasources/loki/configure-loki-data-source.md index 5bd23377868..8dc6eb1670c 100644 --- a/docs/sources/datasources/loki/configure-loki-data-source.md +++ b/docs/sources/datasources/loki/configure-loki-data-source.md @@ -114,9 +114,13 @@ Each derived field consists of the following: - **Type** - Defines the type of the derived field. It can be either: - - **Regex**: A regular expression to parse a part of the log message and capture it as the value of the new field. Can contain only one capture group. +{{% admonition type="caution" %}} +Using complex regular expressions in either type can impact browser performance when processing large volumes of logs. Consider using simpler patterns when possible. +{{% /admonition %}} - - **Label**: A label from the selected log line. This can be any type of label - indexed, parsed or structured metadata. The label's value will be used as the value of the derived field. +- **Regex**: A regular expression to parse a part of the log message and capture it as the value of the new field. Can contain only one capture group. + +- **Label**: A label from the selected log line. This can be any type of label - indexed, parsed or structured metadata. When using this type, the input will match as a regular expression against label keys, allowing you to match variations like `traceid` and `trace_id` with a single regex pattern like `trace[_]?id`. The value of the matched label will be used as the value of the derived field. - **URL/query** Sets the full link URL if the link is external, or a query for the target data source if the link is internal. You can interpolate the value from the field with the `${__value.raw}` macro. @@ -124,6 +128,8 @@ Each derived field consists of the following: - **Internal link** - Toggle on to define an internal link. For internal links, you can select the target data source from a selector. This supports only tracing data sources. +- **Open in new tab** - Toggle on to open the link in a new tab or window. + - **Show example log message** - Click to paste an example log line to test the regular expression of your derived fields. Click **Save & test** to test your connection. diff --git a/docs/sources/datasources/mssql/_index.md b/docs/sources/datasources/mssql/_index.md index 7b568ee4cf8..cdc2a64812f 100644 --- a/docs/sources/datasources/mssql/_index.md +++ b/docs/sources/datasources/mssql/_index.md @@ -89,7 +89,7 @@ To configure basic settings for the data source, complete the following steps: | **Encrypt** | Determines whether or to which extent a secure SSL TCP/IP connection will be negotiated with the server. Options include: `disable` - data sent between client and server is not encrypted; `false` - data sent between client and server is not encrypted beyond the login packet; `true` - data sent between client and server is encrypted. Default is `false`. | | **Max open** | Sets the maximum number of open connections to the database. Default is `100`. | | **Max idle** | Sets the maximum number of connections in the idle connection pool. Default is `100`. | -| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections (Grafana v9.5.1+). Default is `true`. | +| **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** | Sets the maximum number of seconds that the data source can reuse a connection. Default is `14400` (4 hours). | You can also configure settings specific to the Microsoft SQL Server data source. These options are described in the sections below. @@ -171,11 +171,11 @@ datasources: user: grafana jsonData: database: grafana - maxOpenConns: 100 # Grafana v5.4+ - maxIdleConns: 100 # Grafana v5.4+ - maxIdleConnsAuto: true # Grafana v9.5.1+ - connMaxLifetime: 14400 # Grafana v5.4+ - connectionTimeout: 0 # Grafana v9.3+ + maxOpenConns: 100 + maxIdleConns: 100 + maxIdleConnsAuto: true + connMaxLifetime: 14400 + connectionTimeout: 0 encrypt: 'false' secureJsonData: password: 'Password!' diff --git a/docs/sources/datasources/mssql/query-editor/index.md b/docs/sources/datasources/mssql/query-editor/index.md index 85ac61234b0..d582f7be4b3 100644 --- a/docs/sources/datasources/mssql/query-editor/index.md +++ b/docs/sources/datasources/mssql/query-editor/index.md @@ -129,6 +129,8 @@ You can select an optional aggregation function for the column in the **Aggregat To add more value columns, click the plus (`+`) button to the right of the column's row. +{{< 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. @@ -169,18 +171,16 @@ To simplify syntax and to allow for dynamic components, such as date range filte | `$__timeGroup(dateColumn,'5m'[, fillvalue])` | An expression usable in GROUP BY clause. Providing a _fillValue_ of _NULL_ or _floating value_ will automatically fill empty series in timerange with that value.
        For example, _CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)\*300_. | | `$__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. | | `$__timeGroup(dateColumn,'5m', NULL)` | Same as above but NULL will be used as value for missing points. | -| `$__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 available in Grafana 5.3+). | -| `$__timeGroupAlias(dateColumn,'5m')` | Same as `$__timeGroup` but with an added column alias (only available in Grafana 5.3+). | +| `$__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. | +| `$__timeGroupAlias(dateColumn,'5m')` | Same as `$__timeGroup` but with an added column alias. | | `$__unixEpochFilter(dateColumn)` | A time range filter using the specified column name with times represented as Unix timestamp. For example, _dateColumn > 1494410783 AND dateColumn < 1494497183_ | | `$__unixEpochFrom()` | The start of the currently active time selection as Unix timestamp. For example, _1494410783_ | | `$__unixEpochTo()` | The end of the currently active time selection as Unix timestamp. For example, _1494497183_ | | `$__unixEpochNanoFilter(dateColumn)` | A time range filter using the specified column name with times represented as nanosecond timestamp. For example, _dateColumn > 1494410783152415214 AND dateColumn < 1494497183142514872_ | | `$__unixEpochNanoFrom()` | The start of the currently active time selection as nanosecond timestamp. For example, _1494410783152415214_ | | `$__unixEpochNanoTo()` | 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 (only available in Grafana 5.3+). | -| `$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])` | Same as above but also adds a column alias (only available in Grafana 5.3+). | - -To suggest more macros, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. +| `$__unixEpochGroup(dateColumn,'5m', [fillmode])` | Same as `$__timeGroup` but for times stored as Unix timestamp. | +| `$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])` | Same as above but also adds a column alias. | ### View the interpolated query @@ -358,12 +358,12 @@ You can add annotation queries in the Dashboard menu's Annotations view. **Columns:** -| 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. (Grafana v6.6+) | -| `text` | Event description field. | -| `tags` | Optional field name to use for event tags as a comma separated string. | +| 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. | **Example database tables:** diff --git a/docs/sources/datasources/mssql/template-variables/index.md b/docs/sources/datasources/mssql/template-variables/index.md index 63bd278847a..cab7113d84b 100644 --- a/docs/sources/datasources/mssql/template-variables/index.md +++ b/docs/sources/datasources/mssql/template-variables/index.md @@ -77,9 +77,7 @@ SELECT hostname FROM host WHERE region IN ($region) ## Using variables in queries -> From Grafana 4.3.0 to 4.6.0, template variables are always quoted automatically so if it is a string value do not wrap them in quotes in where clauses. -> -> From Grafana 5.0.0, template variable values are only quoted when the template variable is a `multi-value`. +> 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. diff --git a/docs/sources/datasources/mysql/_index.md b/docs/sources/datasources/mysql/_index.md index a64d62c5e49..72923a252ae 100644 --- a/docs/sources/datasources/mysql/_index.md +++ b/docs/sources/datasources/mysql/_index.md @@ -97,11 +97,11 @@ Administrators can also [configure the data source via YAML](#provision-the-data | **User** | Database user's login/username | | **Password** | Database user's password | | **Session Timezone** | Specifies the timezone used in the database session, such as `Europe/Berlin` or `+02:00`. Required if the timezone of the database (or the host of the database) is set to something other than UTC. Set this to `+00:00` so Grafana can handle times properly. Set the value used in the session with `SET time_zone='...'`. If you leave this field empty, the timezone will not be updated. For more information, refer to [MySQL Server Time Zone Support](https://dev.mysql.com/doc/en/time-zone-support.html). | -| **Max open** | The maximum number of open connections to the database, default `100` (Grafana v5.4+). | -| **Max idle** | The maximum number of connections in the idle connection pool, default `100` (Grafana v5.4+). | +| **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)** | Toggle to set the maximum number of idle connections to the number of maximum open connections. Default is `true`. | | **Allow cleartext passwords** | Allows the use of the [cleartext client side plugin](https://dev.mysql.com/doc/en/cleartext-pluggable-authentication.html) as required by a specific type of account, such as one defined with the [PAM authentication plugin](https://dev.mysql.com/doc/en/pam-pluggable-authentication.html).
        **Sending passwords in clear text may be a security problem in some configurations**. To avoid password issues, it is recommended that clients connect to a MySQL server using a method that protects the password. Possibilities include [TLS / SSL](https://github.com/go-sql-driver/mysql#tls), IPsec, or a private network. Default is `false`. | -| **Max lifetime** | The maximum amount of time in seconds a connection may be reused. This should always be lower than configured [wait_timeout](https://dev.mysql.com/doc/en/server-system-variables.html#sysvar_wait_timeout) in MySQL (Grafana v5.4+). The default is `14400` or 4 hours. | +| **Max lifetime** | The maximum amount of time in seconds a connection may be reused. This should always be lower than configured [wait_timeout](https://dev.mysql.com/doc/en/server-system-variables.html#sysvar_wait_timeout) in MySQL. The default is `14400` or 4 hours. | ### Min time interval @@ -160,10 +160,10 @@ datasources: user: grafana jsonData: database: grafana - maxOpenConns: 100 # Grafana v5.4+ - maxIdleConns: 100 # Grafana v5.4+ - maxIdleConnsAuto: true # Grafana v9.5.1+ - connMaxLifetime: 14400 # Grafana v5.4+ + maxOpenConns: 100 + maxIdleConns: 100 + maxIdleConnsAuto: true + connMaxLifetime: 14400 secureJsonData: password: ${GRAFANA_MYSQL_PASSWORD} ``` @@ -181,10 +181,10 @@ datasources: jsonData: tlsAuth: true database: grafana - maxOpenConns: 100 # Grafana v5.4+ - maxIdleConns: 100 # Grafana v5.4+ - maxIdleConnsAuto: true # Grafana v9.5.1+ - connMaxLifetime: 14400 # Grafana v5.4+ + maxOpenConns: 100 + maxIdleConns: 100 + maxIdleConnsAuto: true + connMaxLifetime: 14400 secureJsonData: password: ${GRAFANA_MYSQL_PASSWORD} tlsClientCert: ${GRAFANA_TLS_CLIENT_CERT} @@ -205,10 +205,10 @@ datasources: tlsAuth: true tlsSkipVerify: true database: grafana - maxOpenConns: 100 # Grafana v5.4+ - maxIdleConns: 100 # Grafana v5.4+ - maxIdleConnsAuto: true # Grafana v9.5.1+ - connMaxLifetime: 14400 # Grafana v5.4+ + maxOpenConns: 100 + maxIdleConns: 100 + maxIdleConnsAuto: true + connMaxLifetime: 14400 secureJsonData: password: ${GRAFANA_MYSQL_PASSWORD} tlsClientCert: ${GRAFANA_TLS_CLIENT_CERT} @@ -247,6 +247,8 @@ Using the dropdown, select a column to include in the data. You can also specify 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. @@ -294,7 +296,7 @@ To simplify syntax and to allow for dynamic parts, like date range filters, the | `$__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 (only available in Grafana 5.3+). | +| `$__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_ | @@ -304,10 +306,6 @@ To simplify syntax and to allow for dynamic parts, like date range filters, the | `$__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). | -We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. - -The query editor has a link named `Generated SQL` that shows up after a query has been executed, while in panel edit mode. Click on it and it will expand and show the raw interpolated SQL string that was executed. - ## 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. @@ -492,8 +490,6 @@ SELECT hostname FROM my_host WHERE region IN($region) #### Using `__searchFilter` to filter results in Query Variable -> Available from Grafana 6.5 and above - 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 `%`. @@ -509,9 +505,7 @@ SELECT hostname FROM my_host WHERE hostname LIKE '$__searchFilter' ### Using Variables in Queries -From Grafana 4.3.0 to 4.6.0, template variables are always quoted automatically so if it is a string value do not wrap them in quotes in where clauses. - -From Grafana 4.7.0, template variable values are only quoted when the template variable is a `multi-value`. +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. @@ -593,12 +587,12 @@ 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. (Grafana v6.6+) | -| `text` | Event description field. | -| `tags` | Optional field name to use for event tags as a comma separated string. | +| 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 diff --git a/docs/sources/datasources/postgres/_index.md b/docs/sources/datasources/postgres/_index.md index f426fd074f3..3489b760f70 100644 --- a/docs/sources/datasources/postgres/_index.md +++ b/docs/sources/datasources/postgres/_index.md @@ -81,23 +81,23 @@ To configure basic settings for the data source, complete the following steps: 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. Grafana v7.5+ | -| **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` (Grafana v5.4+). | -| **Max idle** | The maximum number of connections in the idle connection pool, default `100` (Grafana v5.4+). | -| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections (Grafana v9.5.1+). Default is `true`. | -| **Max lifetime** | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours (Grafana v5.4+). | -| **Version** | Determines which functions are available in the query builder (only available in Grafana 5.3+). | -| **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 (only available in Grafana 5.3+). For more information, see [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana). | +| 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 @@ -155,6 +155,8 @@ Using the dropdown, select a column to include in the data. You can also specify 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. @@ -196,10 +198,10 @@ datasources: jsonData: database: grafana sslmode: 'disable' # disable/require/verify-ca/verify-full - maxOpenConns: 100 # Grafana v5.4+ - maxIdleConns: 100 # Grafana v5.4+ - maxIdleConnsAuto: true # Grafana v9.5.1+ - connMaxLifetime: 14400 # Grafana v5.4+ + 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 ``` @@ -250,8 +252,6 @@ Macros can be used within a query to simplify syntax and allow for dynamic parts | `$__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). | -We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. - ## 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. @@ -413,8 +413,6 @@ SELECT hostname FROM host WHERE region IN($region) #### Using `__searchFilter` to filter results in Query Variable -> Available from Grafana 6.5 and above - 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 `%`. @@ -430,9 +428,7 @@ SELECT hostname FROM my_host WHERE hostname LIKE '$__searchFilter' ### Using Variables in Queries -From Grafana 4.3.0 to 4.6.0, template variables are always quoted automatically. If your template variables are strings, do not wrap them in quotes in where clauses. - -From Grafana 4.7.0, template variable values are only quoted when the template variable is a `multi-value`. +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. @@ -512,12 +508,12 @@ 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. (Grafana v6.6+) | -| `text` | Event description field. | -| `tags` | Optional field name to use for event tags as a comma separated string. | +| 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 diff --git a/docs/sources/datasources/prometheus/_index.md b/docs/sources/datasources/prometheus/_index.md index 42c3015fdfc..cd5cbb04ac1 100644 --- a/docs/sources/datasources/prometheus/_index.md +++ b/docs/sources/datasources/prometheus/_index.md @@ -163,17 +163,7 @@ For details about these metrics, refer to [Internal Grafana metrics](ref:set-up- ## Amazon Managed Service for Prometheus -The Prometheus data source works with Amazon Managed Service for Prometheus. - -If you use an AWS Identity and Access Management (IAM) policy to control access to your Amazon Elasticsearch Service domain, you must use AWS Signature Version 4 (AWS SigV4) to sign all requests to that domain. - -For details on AWS SigV4, refer to the [AWS documentation](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html). - -### AWS Signature Version 4 authentication - -To connect the Prometheus data source to Amazon Managed Service for Prometheus using SigV4 authentication, refer to the AWS guide to [Set up Grafana open source or Grafana Enterprise for use with AMP](https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-onboard-query-standalone-grafana.html). - -If you run Grafana in an Amazon EKS cluster, follow the AWS guide to [Query using Grafana running in an Amazon EKS cluster](https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-onboard-query-grafana-7.3.html). +The Prometheus data source with Amazon Managed Service for Prometheus is deprecated. Please use the [Amazon Managed service for Prometheus data source](https://grafana.com/grafana/plugins/grafana-amazonprometheus-datasource/). Migrations steps are detailed in the link. ## Azure authentication settings @@ -199,7 +189,7 @@ Exemplars associate higher-cardinality metadata from a specific event with tradi Available in Prometheus v2.26 and higher with Grafana v7.4 and higher. {{% /admonition %}} -Grafana 7.4 and higher can show exemplars data alongside a metric both in Explore and in Dashboards. +Grafana can show exemplars data alongside a metric both in Explore and in Dashboards. {{< figure src="/static/img/docs/v74/exemplars.png" class="docs-image--no-shadow" caption="Screenshot showing the detail window of an Exemplar" >}} diff --git a/docs/sources/datasources/pyroscope/query-profile-data.md b/docs/sources/datasources/pyroscope/query-profile-data.md index d85ebfd9d70..bdc2624ffed 100644 --- a/docs/sources/datasources/pyroscope/query-profile-data.md +++ b/docs/sources/datasources/pyroscope/query-profile-data.md @@ -135,6 +135,8 @@ Using **Group by**, you can group metric data by a specified label. Without any **Group by** label, metric data aggregates over all the labels into single time series. You can use multiple labels to group by. Group by only effects the metric data and doesn't change the profile data results. +In conjunction with **Group by**, you can set a positive number in the **Limit** input to limit the maximum number of time series returned by the data source. The series returned are always ordered by descending value for the total aggregated data over the time period. + ## Profiles query results Profiles can be visualized in a flame graph. diff --git a/docs/sources/developers/angular_deprecation/_index.md b/docs/sources/developers/angular_deprecation/_index.md index 060e3931dfe..7b596b88308 100644 --- a/docs/sources/developers/angular_deprecation/_index.md +++ b/docs/sources/developers/angular_deprecation/_index.md @@ -60,7 +60,7 @@ We encourage you to locate the repository of the corresponding plugin and create ### Links -- [Migrate Angular to React](https://grafana.com/developers/plugin-tools/migration-guides/migrate-angularjs-to-react) +- [Migrate Angular to React](https://grafana.com/developers/plugin-tools/migration-guides/angular-react/) - [Build a panel plugin](https://grafana.com/tutorials/build-a-panel-plugin/) - [Build a data source plugin](https://grafana.com/tutorials/build-a-data-source-plugin/) - [List of current Angular plugins]({{< relref "./angular-plugins/" >}}) diff --git a/docs/sources/developers/http_api/_index.md b/docs/sources/developers/http_api/_index.md index 51b5d2cab61..13f1ca5aac9 100644 --- a/docs/sources/developers/http_api/_index.md +++ b/docs/sources/developers/http_api/_index.md @@ -20,12 +20,12 @@ weight: 100 # HTTP API reference -The Grafana backend exposes an HTTP API, which is the same API that is used by the frontend to do everything from saving +The Grafana backend exposes an HTTP API, which is the same API that's used by the frontend to do everything from saving dashboards, creating users, and updating data sources. -Since version 8.4, HTTP API details are [specified](https://editor.swagger.io/?url=https://raw.githubusercontent.com/grafana/grafana/main/public/api-merged.json) using OpenAPI v2. +HTTP API details are [specified](https://editor.swagger.io/?url=https://raw.githubusercontent.com/grafana/grafana/main/public/api-merged.json) using OpenAPI v2. -Starting from version 9.1, there is also a [OpenAPI v3 specification](https://editor.swagger.io/?url=https://raw.githubusercontent.com/grafana/grafana/main/public/openapi3.json) (generated by the v2 one). +There is also an [OpenAPI v3 specification](https://editor.swagger.io/?url=https://raw.githubusercontent.com/grafana/grafana/main/public/openapi3.json) (generated by the v2 one). Users can browser and try out both via the Swagger UI editor (served by the Grafana server) by navigating to `/swagger-ui`. @@ -84,6 +84,7 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk Grafana Enterprise includes all of the Grafana OSS APIs as well as the following APIs: - [Data source permissions API]({{< relref "datasource_permissions/" >}}) +- [Group attribute sync API]({{< relref "group_attribute_sync/" >}}) - [License API]({{< relref "licensing/" >}}) - [Query and resource caching API]({{< relref "query_and_resource_caching/" >}}) - [Reporting API]({{< relref "reporting/" >}}) diff --git a/docs/sources/developers/http_api/annotations.md b/docs/sources/developers/http_api/annotations.md index 365c717062e..974b0c6c6d5 100644 --- a/docs/sources/developers/http_api/annotations.md +++ b/docs/sources/developers/http_api/annotations.md @@ -257,8 +257,6 @@ Content-Type: application/json ## Patch Annotation -> This is available in Grafana 6.0.0-beta2 and above. - `PATCH /api/annotations/:id` Updates one or more properties of an annotation that matches the specified id. diff --git a/docs/sources/developers/http_api/auth.md b/docs/sources/developers/http_api/auth.md index 2f3dd915dcb..be88ae5ccd0 100644 --- a/docs/sources/developers/http_api/auth.md +++ b/docs/sources/developers/http_api/auth.md @@ -22,7 +22,7 @@ title: 'Authentication HTTP API ' The Authentication HTTP API is used to manage API keys. {{% admonition type="note" %}} -If you use Grafana v9.1 or newer, use service accounts instead of API keys. For more information, refer to [Grafana service account API reference]({{< relref "./serviceaccount/" >}}). +Grafana recommends using service accounts instead of API keys. For more information, refer to [Grafana service account API reference]({{< relref "./serviceaccount/" >}}). {{% /admonition %}} > If you are running Grafana Enterprise, for some endpoints you would need to have relevant permissions. Refer to [Role-based access control permissions]({{< relref "../../administration/roles-and-permissions/access-control/custom-role-actions-scopes/" >}}) for more information. diff --git a/docs/sources/developers/http_api/datasource_lbac_rules.md b/docs/sources/developers/http_api/datasource_lbac_rules.md index 002d43fe4f2..7f541c37d44 100644 --- a/docs/sources/developers/http_api/datasource_lbac_rules.md +++ b/docs/sources/developers/http_api/datasource_lbac_rules.md @@ -26,7 +26,7 @@ LBAC (Label-Based Access Control) rules can be set for teams. ## Get LBAC rules for a data source -`GET /api/datasources/:uid/lbac/teams` +`GET /api/datasources/uid/:uid/lbac/teams` Gets all existing LBAC rules for the data source with the given `uid`. @@ -41,15 +41,40 @@ Gets all existing LBAC rules for the data source with the given `uid`. **Example request:** ``` -GET /api/datasources/:uid/lbac/teams HTTP/1.1 +GET /api/datasources/uid/my_datasource/lbac/teams HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk ``` +**Example response:** + +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 131 + +{ + "rules": [ + { + "teamId": "1", + "rules": [ + "{ service_name=\"bigquery-sync-mysql\" }" + ] + }, + { + "teamId": "2", + "rules": [ + "{ service_name=\"api\" }" + ] + } + ] +} +``` + ## Update LBAC rules for a data source -`PUT /api/datasources/:uid/lbac/teams` +`PUT /api/datasources/uid/:uid/lbac/teams` Updates LBAC rules for teams associated with the data source with the given `uid`. Here you submit a list of teams and the rules for each team. Deleting a team from the list will remove the team's LBAC rules. You have to submit all teams and their rules to be updated, to remove a team's rules, you have to submit the current list of rules without the team. @@ -66,17 +91,24 @@ Deleting a team from the list will remove the team's LBAC rules. You have to sub **Example request:** ```http -PUT /api/datasources/my_datasource/lbac/teams +PUT /api/datasources/uid/my_datasource/lbac/teams Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk { - "teamId": 1, "rules": [ { - "header": "X-Prom-Label-Policy", - "value": "18042:{ foo=\"bar\" }" + "teamId": "1", + "rules": [ + "{ service_name=\"bigquery-sync-mysql\" }" + ] + }, + { + "teamId": "2", + "rules": [ + "{ service_name=\"api\" }" + ] } ] } @@ -90,20 +122,23 @@ Content-Type: application/json; charset=UTF-8 Content-Length: 35 { - "message": "Data source LBAC rules updated", "id": 1, - "uid": "my_datasource", - "name": "My Data Source", - "lbacRules": [ + "message": "Data source LBAC rules updated", + "name": "loki", + "rules": [ { - "teamId": 1, + "teamId": "1", "rules": [ - { - "header": "X-Prom-Label-Policy", - "value": "18042:{ foo=\"bar\" }" - } + "{ service_name=\"bigquery-sync-mysql\" }" + ] + }, + { + "teamId": "2", + "rules": [ + "{ service_name=\"api\" }" ] } - ] + ], + "uid": "ee1nm1t7spog0e", } ``` diff --git a/docs/sources/developers/http_api/group_attribute_sync.md b/docs/sources/developers/http_api/group_attribute_sync.md new file mode 100644 index 00000000000..82f4f67c0f8 --- /dev/null +++ b/docs/sources/developers/http_api/group_attribute_sync.md @@ -0,0 +1,209 @@ +--- +description: Grafana Group Attribute Sync HTTP API +keywords: + - grafana + - http + - documentation + - api + - group + - member + - enterprise +labels: + products: + - enterprise + - oss +title: Group Attribute Sync HTTP API +--- + +# Group attribute sync API + +The Group Attribute Sync API allows you to configure [group attribute sync feature](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync). This API is useful when you want to manage user roles based on group membership in an external system. + +> **Note:** Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](https://grafana.com/docs/grafana-cloud/) + +{{% admonition type="note" %}} +This feature is behind the `groupAttributeSync` feature toggle. +You can enable feature toggles through configuration file or environment variables. See configuration [docs](/docs/grafana//setup-grafana/configure-grafana/#feature_toggles) for details. +{{% /admonition %}} + +## List group mappings + +`GET /api/groupsync/groups` + +**Required permissions** + +| Action | Scope | +| ----------------------- | ----- | +| groupsync.mappings:read | n/a | + +**Example Request**: + +```http +GET /api/groupsync/groups HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +[ + { + "groups": [ + { + "groupID": "group 1", + "mappings": { + "1": { + "roles": [ + "fixed_nzVQoNSDSn0fg1MDgO6XnZX2RZI", + "my_custom_role", + ] + } + } + }, + { + "groupID": "group 2", + "mappings": { + "1": { + "roles": [ + "another_role", + ] + } + } + } + ], + "total": 2 + } +] +``` + +Status Codes: + +- **200** - Ok +- **400** - Bad request +- **401** - Unauthorized +- **403** - Permission denied +- **500** - Internal server error + +## Create group mappings + +`POST /api/groupsync/groups/:groupID` + +**Required permissions** + +| Action | Scope | +| ------------------------ | ----- | +| groupsync.mappings:write | n/a | + +**Example Request**: + +```http +POST /api/groupsync/groups/my_group_id HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt + +{ + "roles": [ + "fixed_nzVQoNSDSn0fg1MDgO6XnZX2RZI", + "my_custom_role_uid" + ] +} +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "message": "Group mappings created." +} +``` + +Status Codes: + +- **201** - Ok +- **400** - Bad request +- **401** - Unauthorized +- **403** - Permission denied +- **500** - Internal server error + +## Update group mappings + +`PUT /api/groupsync/groups/:groupID` + +This endpoint will replace the existing mappings for the group with the new mappings provided in the request. + +**Required permissions** + +| Action | Scope | +| ------------------------ | ----- | +| groupsync.mappings:write | n/a | + +**Example Request**: + +```http +PUT /api/groupsync/groups/my_group_id HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt + +{ + "roles": [ + "fixed_nzVQoNSDSn0fg1MDgO6XnZX2RZI", + "my_custom_role_uid" + ] +} +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "message": "Group mappings set." +} +``` + +Status Codes: + +- **201** - Ok +- **400** - Bad request +- **401** - Unauthorized +- **403** - Permission denied +- **500** - Internal server error + +## Remove group mappings + +`DELETE /api/groupsync/groups/:groupID` + +**Required permissions** + +| Action | Scope | +| ------------------------ | ----- | +| groupsync.mappings:write | n/a | + +**Example Request**: + +```http +DELETE /api/groupsync/groups/my_group_id HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt +``` + +Status Codes: + +- **204** - Ok +- **400** - Bad request +- **401** - Unauthorized +- **403** - Permission denied +- **500** - Internal server error diff --git a/docs/sources/developers/http_api/serviceaccount.md b/docs/sources/developers/http_api/serviceaccount.md index 15e424cf151..680c3472647 100644 --- a/docs/sources/developers/http_api/serviceaccount.md +++ b/docs/sources/developers/http_api/serviceaccount.md @@ -445,10 +445,13 @@ Content-Type: application/json Authorization: Basic YWRtaW46YWRtaW4= { - "name": "grafana" + "name": "grafana", + "secondsToLive": 604800 } ``` +Default value for the `secondsToLive` is 0, which means that the service account token will never expire. + **Example Response**: ```http diff --git a/docs/sources/developers/http_api/team.md b/docs/sources/developers/http_api/team.md index c3bfab47411..f7eec3d6260 100644 --- a/docs/sources/developers/http_api/team.md +++ b/docs/sources/developers/http_api/team.md @@ -147,7 +147,7 @@ Status Codes: ## Add Team -The Team `name` needs to be unique. `name` is required and `email`,`orgId` is optional. +The Team `name` needs to be unique. `name` is required and `email` is optional. `POST /api/teams` @@ -170,7 +170,6 @@ Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt { "name": "MyTestTeam", "email": "email@test.com", - "orgId": 2 } ``` diff --git a/docs/sources/developers/http_api/user.md b/docs/sources/developers/http_api/user.md index bde419c26b5..03e418e0511 100644 --- a/docs/sources/developers/http_api/user.md +++ b/docs/sources/developers/http_api/user.md @@ -570,14 +570,14 @@ Content-Type: application/json ## Star a dashboard -`POST /api/user/stars/dashboard/:dashboardId` +`POST /api/user/stars/dashboard/uid/:uid` Stars the given Dashboard for the actual user. **Example Request**: ```http -POST /api/user/stars/dashboard/1 HTTP/1.1 +POST /api/user/stars/dashboard/uid/BqokFhx7z HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk @@ -594,14 +594,14 @@ Content-Type: application/json ## Unstar a dashboard -`DELETE /api/user/stars/dashboard/:dashboardId` +`DELETE /api/user/stars/dashboard/uid/:uid` Deletes the starring of the given Dashboard for the actual user. **Example Request**: ```http -DELETE /api/user/stars/dashboard/1 HTTP/1.1 +DELETE /api/user/stars/dashboard/uid/BqokFhx7z HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk diff --git a/docs/sources/developers/plugins/plugin.schema.json b/docs/sources/developers/plugins/plugin.schema.json index 4a98dc1ea47..91896ef88f0 100644 --- a/docs/sources/developers/plugins/plugin.schema.json +++ b/docs/sources/developers/plugins/plugin.schema.json @@ -234,6 +234,10 @@ "type": "boolean", "description": "If the plugin has a backend component." }, + "buildMode": { + "type": "string", + "description": "The build mode of the plugin. This field is set automatically at build time, so it should not be provided manually." + }, "builtIn": { "type": "boolean", "description": "[internal only] Indicates whether the plugin is developed and shipped as part of Grafana. Also known as a 'core plugin'." diff --git a/docs/sources/explore/simplified-exploration/_index.md b/docs/sources/explore/simplified-exploration/_index.md index 2162eb693ce..60612ae02ba 100644 --- a/docs/sources/explore/simplified-exploration/_index.md +++ b/docs/sources/explore/simplified-exploration/_index.md @@ -40,7 +40,10 @@ The Grafana Explore apps are designed for effortless data exploration through in Easily explore telemetry signals with these specialized tools, tailored specifically for the Grafana databases to provide quick and accurate insights. -To learn more, read [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/). +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/). {{< youtube id="MSHeWWsHaIA" >}} diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index a8373cffba8..ccd311d14fe 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -34,12 +34,14 @@ Grafana Enterprise includes integrations with more ways to authenticate your use Supported auth providers: - [Auth Proxy]({{< relref "../setup-grafana/configure-security/configure-authentication/auth-proxy#team-sync-enterprise-only" >}}) -- [Azure AD OAuth]({{< relref "../setup-grafana/configure-security/configure-authentication/azuread#team-sync-enterprise-only" >}}) -- [GitHub OAuth]({{< relref "../setup-grafana/configure-security/configure-authentication/github#configure-team-synchronization" >}}) -- [GitLab OAuth]({{< relref "../setup-grafana/configure-security/configure-authentication/gitlab#configure-team-synchronization" >}}) -- [LDAP]({{< relref "../setup-grafana/configure-security/configure-authentication/enhanced-ldap#ldap-group-synchronization-for-teams" >}}) -- [Okta]({{< relref "../setup-grafana/configure-security/configure-authentication/okta#configure-team-synchronization-enterprise-only" >}}) -- [SAML]({{< relref "../setup-grafana/configure-security/configure-authentication/saml#configure-team-sync" >}}) +- [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) ### Enhanced LDAP integration @@ -108,4 +110,4 @@ With a Grafana Enterprise license, you also get access to premium data sources, ## Try Grafana Enterprise -To purchase or obtain a trial license, contact the Grafana Labs [Sales Team](/contact?about=support&topic=Grafana%20Enterprise). +To purchase or obtain a trial license, contact the Grafana Labs [Sales Team](/contact?about=grafana-enterprise-stack). diff --git a/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md b/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md index 5b780c99376..7e5d2a61cd6 100644 --- a/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md +++ b/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md @@ -286,6 +286,8 @@ In the field mapping specify: Grafana builds value mappings from your query result and applies them to the real data query results. You should see values being mapped and colored according to the config query results. +> **Note:** When you use this transformation for thresholds, the visualization continues to use the panel's base threshold. + ### Convert field type Use this transformation to modify the field type of a specified field. @@ -338,6 +340,7 @@ Use this transformation to select a source of data and extract content from it i - **Format** - Choose one of the following: - **JSON** - Parse JSON content from the source. - **Key+value pairs** - Parse content in the format 'a=b' or 'c:d' from the source. + - **RegExp** - Parse content using a regular expression with [named capturing group(s)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) like `/(?.*)/`. - **Auto** - Discover fields automatically. - **Replace All Fields** - (Optional) Select this option to hide all other fields and display only your calculated field in the visualization. - **Keep Time** - (Optional) Available only if **Replace All Fields** is true. Keeps the time field in the output. @@ -1215,8 +1218,6 @@ Select this option to transform the time series data frame from the long format | 2023-01-01 00:00:00 | 10 | 20 | | 2023-01-01 01:00:00 | 15 | 25 | -> **Note:** This transformation is available in Grafana 7.5.10+ and Grafana 8.0.6+. - ### Reduce Use this transformation to apply a calculation to each field in the data frame and return a single value. This transformation is particularly useful for consolidating multiple time series data into a more compact, summarized format. Time fields are removed when applying this transformation. @@ -1387,8 +1388,6 @@ Here is the result after applying the Series to rows transformation. This transformation facilitates the consolidation of results from multiple time series queries, providing a streamlined and unified dataset for efficient analysis and visualization in a tabular format. -> **Note:** This transformation is available in Grafana 7.1+. - ### Sort by Use this transformation to sort each frame within a query result based on a specified field, making your data easier to understand and analyze. By configuring the desired field for sorting, you can control the order in which the data is presented in the table or visualization. diff --git a/docs/sources/panels-visualizations/visualizations/bar-chart/index.md b/docs/sources/panels-visualizations/visualizations/bar-chart/index.md index e129f65d473..3d7f982d554 100644 --- a/docs/sources/panels-visualizations/visualizations/bar-chart/index.md +++ b/docs/sources/panels-visualizations/visualizations/bar-chart/index.md @@ -37,6 +37,11 @@ refs: destination: /docs/grafana//panels-visualizations/configure-data-links/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-data-links/ + time-series: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/time-series/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series/ --- # Bar chart @@ -44,7 +49,7 @@ refs: A bar chart is a visual representation that uses rectangular bars, where the length of each bar represents each value. You can use the bar chart visualization when you want to compare values over different categories or time periods. The visualization can display the bars horizontally or vertically, and can be customized to group or stack bars for more complex data analysis. -{{< figure src="/static/img/docs/bar-chart-panel/barchart_small_example.png" max-width="1000px" alt="Bar chart" >}} +![Bar chart visualizations](/media/docs/grafana/panels-visualizations/screenshot-bar-charts-v11.3.png) You can use the bar chart visualization if you need to show: @@ -89,170 +94,98 @@ If your dataset contains multiple rows, the visualization displays multiple bar ![Bar chart multiple row example](/media/docs/grafana/panels-visualizations/screenshot-grafana-11.1-barchart-example2.png 'Bar chart multiple row example') -While the first field can be time-based and you can use a bar chart to plot time-series data, for large amounts of time-series data, we recommend that you use the [time series visualization](https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/time-series/) and configure it to be displayed as bars. +While the first field can be time-based and you can use a bar chart to plot time-series data, for large amounts of time-series data, we recommend that you use the [time series visualization](ref:time-series) and configure it to be displayed as bars. We recommend that you only use one dataset in a bar chart because using multiple datasets can result in unexpected behavior. -## Panel options +## Configuration options + +{{< docs/shared lookup="visualizations/config-options-intro.md" source="grafana" version="" >}} + +### Panel options {{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} -## Bar chart options +### Bar chart options Use these options to refine your visualization. -### X Axis + -Specify which field is used for the x-axis. +| Option | Description | +| -------------------------------- | ------------------------------------------------------------------------------------------------------- | +| X Axis | Specify which field is used for the x-axis. | +| Orientation | Choose from:
        • **Auto** - Grafana decides the bar orientation based on the panel dimensions.
        • **Horizontal** - Will make the X axis the category axis.
        • **Vertical** - Will make the Y axis the category axis.
        | +| Rotate x-axis tick labels | When the graph is vertically oriented, this setting rotates the labels under the bars. This setting is useful when bar chart labels are long and overlap. | +| X-axis tick label max length | Sets the maximum length of bar chart labels. Labels longer than the maximum length are truncated, and appended with `...`. | +| X-axis labels minimum spacing | Sets the minimum spacing between x-axis labels. Depending on your choice, you can select the **RTL** checkbox to require space from the right side. Choose from:
        • **None** - All tick marks are shown.
        • **Small** - 100 px of space is required between labels.
        • **Medium** - 200 px of space is required between labels.
        • **Large** - 300 px of space is required between labels.
        | +| Show values | This controls whether values are shown. Values are shown on top or to the left of bars. Choose from:
        • **Auto** Values will be shown if there is space.
        • **Always** Always show values.
        • **Never** Never show values.
        | +| Stacking | Controls bar chart stacking. Choose from:
        • **Off**: Bars will not be stacked.
        • **Normal**: Bars will be stacked on each other.
        • **Percent**: Bars will be stacked on each other, and the height of each bar is the percentage of the total height of the stack.
        | +| Group width | Controls the width of groups. 1 = Max with, 0 = Min width. | +| Bar width | Controls the width of bars. 1 = Max width, 0 = Min width. | +| Bar radius | Controls the radius of the bars. Choose from:
        • 0 = Minimum radius
        • 0.5 = Maximum radius
        | +| Highlight full area on cover | Controls if the entire surrounding area of the bar is highlighted when you hover over the bar. | +| Color by field | Use the color value for a sibling field to color each bar value. | +| Line width | Controls line width of the bars. | +| Fill opacity | Controls the fill opacity bars. | +| [Gradient mode](#gradient-mode) | Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. Gradient appearance is influenced by the **Fill opacity** setting. | -### Orientation + -- **Auto** - Grafana decides the bar orientation based on what the panel dimensions. -- **Horizontal** - Will make the X axis the category axis. -- **Vertical** - Will make the Y axis the category axis. +#### Gradient mode -### Rotate x-axis tick labels +Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. Gradient appearance is influenced by the **Fill opacity** setting. Choose from: -When the graph is vertically oriented, this setting rotates the labels under the bars. This setting is useful when bar chart labels are long and overlap. +- **None** - No gradient fill. This is the default setting. +- **Opacity** - Transparency of the gradient is calculated based on the values on the y-axis. Opacity of the fill is increasing with the values on the Y-axis. +- **Hue** - Gradient color is generated based on the hue of the line color. +- **Scheme** - The bar receives a gradient color defined by the **Standard options > Color scheme** selection. -### X-axis tick label maximum length + - **From thresholds** - If the **Color scheme** selection is **From thresholds (by value)**, then each bar is the color of the defined threshold. -Sets the maximum length of bar chart labels. Labels longer than the maximum length are truncated, and appended with `...`. + {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-colors-by-thresholds-v11.3.png" alt="Color scheme From thresholds" caption="Color scheme: From thresholds" >}} -### Bar labels minimum spacing + - **Gradient color schemes** - The following image shows a bar chart with the **Green-Yellow-Red (by value)** color scheme option selected. -Sets the minimum spacing between bar labels. + {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-colors-by-value-v11.3.png" alt="Color scheme Green-Yellow-Red" caption="Color scheme: Green-Yellow-Red" >}} -### Show values +### Tooltip options -This controls whether values are shown on top or to the left of bars. +{{< docs/shared lookup="visualizations/tooltip-options-1.md" source="grafana" version="" leveloffset="+1" >}} -- **Auto** Values will be shown if there is space -- **Always** Always show values. -- **Never** Never show values. - -### Stacking - -Controls bar chart stacking. - -- **Off**: Bars will not be stacked. -- **Normal**: Bars will be stacked on each other. -- **Percent**: Bars will be stacked on each other, and the height of each bar is the percentage of the total height of the stack. - -### Group width - -Controls the width of groups. 1 = max with, 0 = min width. - -### Bar width - -Controls the width of bars. 1 = Max width, 0 = Min width. - -### Bar radius - -Controls the radius of the bars. - -- 0 = Minimum radius -- 0.5 = Maximum radius - -### Highlight full area on cover - -Controls if the entire surrounding area of the bar is highlighted when you hover over the bar. - -### Line width - -Controls line width of the bars. - -### Fill opacity - -Controls the fill opacity bars. - -### Gradient mode - -Set the mode of the gradient fill. Fill gradient is based on the line color. To change the color, use the standard color scheme field option. - -Gradient appearance is influenced by the **Fill opacity** setting. - -#### None - -No gradient fill. This is the default setting. - -#### Opacity - -Transparency of the gradient is calculated based on the values on the y-axis. Opacity of the fill is increasing with the values on the Y-axis. - -#### Hue - -Gradient color is generated based on the hue of the line color. - -#### Scheme gradient mode - -The **Gradient mode** option located under the **Graph styles** has a mode named **Scheme**. When you enable **Scheme**, the bar receives a gradient color defined from the selected **Color scheme**. - -##### From thresholds - -If the **Color scheme** is set to **From thresholds (by value)** and **Gradient mode** is set to **Scheme**, then the bar color changes as they cross the defined thresholds. - -{{< figure src="/static/img/docs/time-series-panel/gradient_mode_scheme_thresholds_bars.png" max-width="1200px" caption="Color scheme: From thresholds" >}} - -##### Gradient color schemes - -The following image shows a bar chart with the **Green-Yellow-Red (by value)** color scheme option selected. - -{{< figure src="/static/img/docs/time-series-panel/gradient_mode_scheme_bars.png" max-width="1200px" caption="Color scheme: Green-Yellow-Red" >}} - -## Tooltip options - -{{< docs/shared lookup="visualizations/tooltip-options-1.md" source="grafana" version="" >}} - -## Legend options +### Legend options {{< docs/shared lookup="visualizations/legend-options-1.md" source="grafana" version="" >}} -## Text size +### Text size Enter a **Value** to change the size of the text on your bar chart. -## Axis +### Axis Use the following field settings to refine how your axes display. +For guidance on configuring more than one y-axis, refer to [Multiple y-axes](#multiple-y-axes). + Some field options will not affect the visualization until you click outside of the field option box you are editing or press Enter. -### Placement + -Select the placement of the Y-axis. +| Option | Description | +| ------ | ----------- | +| Placement | Select the placement of the Y-axis. Choose from:
        • **Auto** - Grafana automatically assigns Y-axis to the series. When there are two or more series with different units, then Grafana assigns the left axis to the first unit and right to the following units.
        • **Left** - Display all Y-axes on the left side.
        • **Right** - Display all Y-axes on the right side.
        • **Hidden** - Hide all axes. To selectively hide axes, [add a field override](ref:add-a-field-override) that targets specific fields.
        | +| Label | Set a Y-axis text label. If you have more than one Y-axis, then you can assign different labels with an override. | +| Width | Set a fixed width of the axis. By default, Grafana dynamically calculates the width of an axis.

        By setting the width of the axis, data whose axes types are different can share the same display proportions. This makes it easier to compare more than one graph’s worth of data because the axes are not shifted or stretched within visual proximity of each other. | +| Show grid lines | Set whether grid lines are displayed in the chart. Choose from:
        • **Auto** - Grafana automatically determines whether grid lines are displayed.
        • **On** - Grid lines are always displayed.
        • **Off** - Grid lines are never displayed
        | +| Color | Choose whether the axis color is the **Text** or **Series** color. | +| Show border | Toggle the switch to hide or display the border. | +| Scale | Set how the y-axis is split. Choose from:
        • **Linear**
        • **Logarithmic** - Choose between log base 2 or log base 10.
        • **Symlog** - Uses a symmetrical logarithmic scale. Choose between log base 2 or log base 10, allowing for negative values.
        | +| Centered zero | Set the y-axis so it's centered on zero. | +| [Soft min and soft max](#soft-min-and-soft-max) | Set a **Soft min** or **soft max** option for better control of Y-axis limits. By default, Grafana sets the range for the Y-axis automatically based on the dataset. | -#### Auto + -Grafana automatically assigns Y-axis to the series. When there are two or more series with different units, then Grafana assigns the left axis to the first unit and right to the following units. - -#### Left - -Display all Y-axes on the left side. - -#### Right - -Display all Y-axes on the right side. - -#### Hidden - -Hide all axes. - -To selectively hide axes, [Add a field override](ref:add-a-field-override) that targets specific fields. - -### Label - -Set a Y-axis text label. - -If you have more than one Y-axis, then you can give assign different labels with an override. - -### Width - -Set a fixed width of the axis. By default, Grafana dynamically calculates the width of an axis. - -By setting the width of the axis, data whose axes types are different can share the same display proportions. This makes it easier to compare more than one graph’s worth of data because the axes are not shifted or stretched within visual proximity of each other. - -### Soft min and soft max +#### Soft min and soft max Set a **Soft min** or **soft max** option for better control of Y-axis limits. By default, Grafana sets the range for the Y-axis automatically based on the dataset. @@ -260,24 +193,24 @@ Set a **Soft min** or **soft max** option for better control of Y-axis limits. B You can set standard min/max options to define hard limits of the Y-axis. For more information, refer to [Standard options definitions](ref:standard-options-definitions). -{{< docs/shared lookup="visualizations/multiple-y-axes.md" source="grafana" version="" leveloffset="+2" >}} +{{< docs/shared lookup="visualizations/multiple-y-axes.md" source="grafana" version="" leveloffset="+3" >}} -## Standard options +### Standard options {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -## Data links +### Data links {{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} -## Thresholds - -{{< docs/shared lookup="visualizations/thresholds-options-1.md" source="grafana" version="" >}} - -## Value mappings +### Value mappings {{< docs/shared lookup="visualizations/value-mappings-options.md" source="grafana" version="" >}} -## Field overrides +### Thresholds + +{{< docs/shared lookup="visualizations/thresholds-options-1.md" source="grafana" version="" >}} + +### Field overrides {{< docs/shared lookup="visualizations/overrides-options.md" source="grafana" version="" >}} diff --git a/docs/sources/panels-visualizations/visualizations/canvas/index.md b/docs/sources/panels-visualizations/visualizations/canvas/index.md index 00a5196a6e6..4dab56e9f61 100644 --- a/docs/sources/panels-visualizations/visualizations/canvas/index.md +++ b/docs/sources/panels-visualizations/visualizations/canvas/index.md @@ -43,6 +43,12 @@ With all of these dynamic elements, there's almost no limit to what a canvas can We'd love your feedback on the canvas visualization. Please check out the [open Github issues](https://github.com/grafana/grafana/issues?page=1&q=is%3Aopen+is%3Aissue+label%3Aarea%2Fpanel%2Fcanvas) and [submit a new feature request](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request,area%2Fpanel%2Fcanvas&title=Canvas:&projects=grafana-dataviz&template=1-feature_requests.md) as needed. {{< /admonition >}} +## Configure a canvas visualization + +The following video shows you how to create and configure a canvas visualization: + +{{< youtube id="b7AYKoFcPpY" >}} + ## Supported data formats The canvas visualization is unique in that it doesn't have any specific data requirements. You can even start adding and configuring visual elements without providing any data. However, any data you plan to consume should be accessible through supported Grafana data sources and structured in a way that ensures smooth integration with your custom elements. @@ -85,7 +91,23 @@ The text element lets you easily add text to the canvas. The element also suppor ### Icon -The icon element lets you add a supported icon to the canvas. Icons can have their color set based on thresholds / value mappings. +The icon element lets you add a supported icon to the canvas. Icons can have their color set based on thresholds or value mappings. + +#### Add a custom icon + +You can add a custom icon by referencing an SVG file. To add a custom icon, follow these steps: + +1. Under **Icon > SVG Path**, if it's not already selected, select **Fixed** as your file source. +1. Click **Select a value** in the field below. +1. In the dialog box that opens, click the **URL** tab. +1. Enter the URL in the field below the **URL** tab. + + {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-canvas-custom-image-v11.3.png" max-width="300px" alt="Add a custom image URL" >}} + +1. Click **Select**. +1. (Optional) Add a background image to your icon with the **Background (icon)** option by following the steps to [add a custom image](#add-custom-images-to-elements). + +If you don't have an SVG file, you can use a rectangle element instead of an icon and set its background image to an image file type. To add a custom image for another element type, follow the steps to [add a custom image](#add-custom-images-to-elements). ### Server @@ -105,6 +127,25 @@ A button click will only trigger an API call when [inline editing](#inline-editi {{< docs/play title="Canvas Visualization: Buttons" url="https://play.grafana.org/d/c9ea65f5-ed5a-45cf-8fb7-f82af7c3afdf/" >}} +## Add custom images to elements + +You can add custom background images to all elements except **Button** by referencing an image URL. +The image must be hosted at a URL that allows requests from your Grafana instance. + +To upload a custom image, follow these steps: + +1. Under **Background (\)**, if it's not already selected, select **Fixed** as your image source. + + {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-canvas-custom-image-src-v11.3.png" max-width="300px" alt="Custom image source selection" >}} + +1. Click **Select a value** in the field below. +1. In the dialog box that opens, click the **URL** tab. +1. Enter the URL in the field below the **URL** tab. + + {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-canvas-custom-image-v11.3.png" max-width="300px" alt="Add a custom image URL" >}} + +1. Click **Select**. + ## Connections When building a canvas, you can connect elements together to create more complex visualizations. Connections are created by dragging from the connection anchor of one element to the connection anchor of another element. You can also create connections to the background of the canvas. Connection anchors are displayed when you hover over an element and inline editing is turned on. diff --git a/docs/sources/panels-visualizations/visualizations/logs/index.md b/docs/sources/panels-visualizations/visualizations/logs/index.md index c2d73352c8a..00f0163f55f 100644 --- a/docs/sources/panels-visualizations/visualizations/logs/index.md +++ b/docs/sources/panels-visualizations/visualizations/logs/index.md @@ -32,7 +32,7 @@ _Logs_ are structured records of events or messages generated by a system or app The logs visualization displays these records from data sources that support logs, such as Elastic, Influx, and Loki. The logs visualization has colored indicators of log status, as well as collapsible log events that help you analyze the information generated. -{{< figure src="/static/img/docs/v64/logs-panel.png" max-width="1025px" alt="Logs panel" >}} +![Logs visualization](/media/docs/grafana/panels-visualizations/screenshot-logs-v11.3.png) {{< docs/play title="Logs Panel" url="https://play.grafana.org/d/6NmftOxZz/" >}} @@ -46,7 +46,7 @@ The following video provides a walkthrough of creating a logs visualization. You ## Supported data formats -The logs visualization works best with log-type datasets such as queries from data sources like Loki, Elastic, and InlfuxDB. +The logs visualization works best with log-type datasets such as queries from data sources like Loki, Elastic, and InfluxDB. You can also build log-formatted data from other data sources as long as the first field is a time type followed by string, number, and time fields. The leading time field is used to sort and timestamp the logs and if the data contains other time-type fields, they’re included as elements of the logged record. @@ -66,27 +66,33 @@ To limit the number of log lines rendered in the visualization, you can use the ![Logs Example](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-logs-example.png 'Logs Example') -## Panel options - -{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} - ## Log level For logs where a **level** label is specified, we use the value of the label to determine the log level and update color accordingly. If the log doesn't have a level label specified, we try to find out if its content matches any of the supported expressions (see below for more information). The log level is always determined by the first match. In case Grafana is not able to determine a log level, it will be visualized with **unknown** log level. See [supported log levels and mappings of log level abbreviation and expressions](ref:log-levels). -## Log details +## Configuration options -Each log row has an extendable area with its labels and detected fields, for more robust interaction. Each field or label has a stats icon to display ad-hoc statistics in relation to all displayed logs. +{{< docs/shared lookup="visualizations/config-options-intro.md" source="grafana" version="" >}} -### Display options +### Panel options + +{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} + +### Logs options Use these settings to refine your visualization: -- **Time -** Show or hide the time column. This is the timestamp associated with the log line as reported from the data source. -- **Unique labels -** Show or hide the unique labels column, which shows only non-common labels. -- **Common labels -** Show or hide the common labels. -- **Wrap lines -** Toggle line wrapping. -- **Prettify JSON -** Set this to `true` to pretty print all JSON logs. This setting does not affect logs in any format other than JSON. -- **Enable log details -** Toggle option to see the log details view for each log row. The default setting is true. -- **Deduplication -** Hides log messages that are duplicates of others shown according to your selected criteria. Choose from: **Exact** (ignoring ISO datetimes), **Numerical** (ignoring only those that differ by numbers such as IPs or latencies), or **Signatures** (removing successive lines with identical punctuation and white space). -- **Order -** Display results in descending or ascending time order. The default is **Descending**, showing the newest logs first. Set to **Ascending** to show the oldest log lines first. + + +| Option | Description | +| ------ | ----------- | +| Time | Show or hide the time column. This is the timestamp associated with the log line as reported from the data source. | +| Unique labels | Show or hide the unique labels column, which shows only non-common labels. | +| Common labels | Show or hide the common labels. | +| Wrap lines | Turn line wrapping on or off. | +| Prettify JSON | Toggle the switch on to pretty print all JSON logs. This setting does not affect logs in any format other than JSON. | +| Enable log details | Toggle the switch on to see an extendable area with log details including labels and detected fields. Each field or label has a stats icon to display ad-hoc statistics in relation to all displayed logs. The default setting is on. | +| Deduplication | Hide log messages that are duplicates of others shown, according to your selected criteria. Choose from:
        • **Exact** - Ignoring ISO datetimes.
        • **Numerical** - Ignoring only those that differ by numbers such as IPs or latencies.
        • **Signatures** - Removing successive lines with identical punctuation and white space.
        | +| Order | Set whether to show results **Newest first** or **Oldest first**. | + + diff --git a/docs/sources/panels-visualizations/visualizations/node-graph/index.md b/docs/sources/panels-visualizations/visualizations/node-graph/index.md index a57b4304de2..7f488c8a40f 100644 --- a/docs/sources/panels-visualizations/visualizations/node-graph/index.md +++ b/docs/sources/panels-visualizations/visualizations/node-graph/index.md @@ -25,7 +25,7 @@ Node graphs are useful when you need to visualize elements that are related to e Node graphs display useful information about each node, as well as the relationships between them, allowing you to visualize complex infrastructure maps, hierarchies, or execution diagrams. -![Node graph visualization](/media/docs/grafana/data-sources/tempo/query-editor/tempo-ds-query-node-graph.png 'Node graph') +![Node graph visualization](/media/docs/grafana/panels-visualizations/screenshot-node-graph-v11.3.png 'Node graph') The appearance of nodes and edges can also be customized in several ways including color, borders, and line style. @@ -79,30 +79,7 @@ Similar to the nodes dataset, the edges dataset needs one unique ID field for ea If a node lacks edge connections, it’s displayed on its own outside of the network. -## Panel options - -{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} - -## Nodes options - -The **Nodes** options section provides configurations for node behaviors. - -- **Main stat unit** - Choose which unit the main stat displays in the graph's nodes. -- **Secondary stat unit** - Choose which unit the secondary stat displays in the graph's nodes. -- **Arc sections** - Configure which fields define the size of the colored circle around the node and select a color for each. You can add multiple fields. - -{{< admonition type="note" >}} -Defining arc sections overrides the automatic detection of `arc__*` and `color` fields described in the **Optional fields** section of [Nodes data frame structure](#nodes-data-frame-structure). -{{< /admonition >}} - -## Edges options - -The **Edges** options section provides configurations for node edges behaviors. - -- **Main stat unit** - Choose which unit the main stat displays in the graph's edges. -- **Secondary stat unit** - Choose which unit the secondary stat displays in the graph's edges. - -## Data requirements +### Data requirements A node graph requires a specific shape of the data to be able to display its nodes and edges. This means not every data source or query can be visualized with this graph. If you want to use this as a data source developer see the section about data API. @@ -113,7 +90,7 @@ A node graph consists of _nodes_ and _edges_. Both nodes and edges can have associated metadata or statistics. The data source defines what information and values is shown, so different data sources can show different type of values or not show some values. -### Nodes +#### Nodes {{% admonition type="note" %}} Node graphs can show only 1,500 nodes. If this limit is crossed a warning will be visible in upper right corner, and some nodes will be hidden. You can expand hidden parts of the graph by clicking on the "Hidden nodes" markers in the graph. @@ -125,47 +102,88 @@ For example, you can have the percentage of errors represented by a red portion Additional details can be displayed in a context menu which is displayed when you click on the node. There also can be additional links in the context menu that can target either other parts of Grafana or any external link. -![Node graph navigation](/media/docs/grafana/data-sources/tempo/query-editor/node-graph-navigation.png 'Node graph navigation') +![Node context menu](/media/docs/grafana/panels-visualizations/screenshot-node-links-v11.3.png 'Node context menu') -### Edges +#### Edges Edges can also show statistics when you hover over the edge. Similar to nodes, you can open a context menu with additional details and links by clicking on the edge. The first data source supporting this visualization is X-Ray data source for its Service map feature. For more information, refer to the [X-Ray plugin documentation](https://grafana.com/grafana/plugins/grafana-x-ray-datasource). -## Navigating the node graph +## Node graph navigation -You can pan and zoom in or out a node graph. +You can use pan, zoom, and other functions to navigate a node graph. ### Pan You can pan the view by clicking outside any node or edge and dragging your mouse. -### Zoom in or out +### Zoom -Use the buttons in the upper left corner or use the mouse wheel, touchpad scroll, together with either Ctrl or Cmd key to zoom in or out. +Use the buttons in the lower right corner to zoom in or out. You can also use the mouse wheel or touchpad scroll, together with either Ctrl or Cmd key to do so. -### Explore hidden nodes +### Hidden nodes The number of nodes shown at a given time is limited to maintain a reasonable visualization performance. Nodes that are not currently visible are hidden behind clickable markers that show an approximate number of hidden nodes that are connected by a particular edge. You can click on the marker to expand the graph around that node. -![Node graph exploration](/static/img/docs/node-graph/node-graph-exploration-8-0.png 'Node graph exploration') +![Node graph exploration](/media/docs/grafana/panels-visualizations/node-graph-exploration-8.0-2.png 'Node graph exploration') ### Grid view You can switch to the grid view to have a better overview of the most interesting nodes in the graph. Grid view shows nodes in a grid without edges and can be sorted by stats shown inside the node or by stats represented by the a colored border of the nodes. - - -![Node graph grid](/media/docs/grafana/data-sources/tempo/query-editor/node-graph-grid-view.png 'Node graph grid') +![Node graph grid](/media/docs/grafana/panels-visualizations/screenshot-node-graph-grid-v11.3.png 'Node graph grid') To sort the nodes, click on the stats inside the legend. The marker next to the stat name shows which stat is currently used for sorting and sorting direction. -![Node graph legend](/static/img/docs/node-graph/node-graph-legend-8-0.png 'Node graph legend') +![Node graph legend](/media/docs/grafana/panels-visualizations/screenshot-node-graph-legend-v11.3.png 'Node graph legend') Click on the node and select "Show in Graph layout" option to switch back to graph layout and focus on the selected node, to show it in context of the full graph. -![Node graph grid to default](/static/img/docs/node-graph/node-graph-grid-to-default-8-0.png 'Node graph grid to default') +![Node graph grid to default](/media/docs/grafana/panels-visualizations/screenshot-node-graph-view-v11.3.png 'Node graph grid to default') + +## Configuration options + +{{< docs/shared lookup="visualizations/config-options-intro.md" source="grafana" version="" >}} + +### Panel options + +{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} + +### Node graph options + +Use the following options to refine your node graph visualization. + +- **Zoom mode** - Choose how the node graph should handle zoom and scroll events. + +### Nodes options + +The **Nodes** options section provides configurations for node behaviors. + +- **Main stat unit** - Choose which unit the main stat displays in the graph's nodes. +- **Secondary stat unit** - Choose which unit the secondary stat displays in the graph's nodes. +- **Arc sections** - Configure which fields define the size of the colored circle around the node and select a color for each. You can add multiple fields. + +{{< admonition type="note" >}} +Defining arc sections overrides the automatic detection of `arc__*` and `color` fields described in the **Optional fields** section of [Nodes data frame structure](#nodes-data-frame-structure). +{{< /admonition >}} + +### Edges options + +The **Edges** options section provides configurations for node edges behaviors. + +- **Main stat unit** - Choose which unit the main stat displays in the graph's edges. +- **Secondary stat unit** - Choose which unit the secondary stat displays in the graph's edges. + +### Data links + +{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} + +In node graphs, some data fields may have pre-configured data links. To add a different data link in those cases, use a [field override](#field-overrides). + +### Field overrides + +{{< docs/shared lookup="visualizations/overrides-options.md" source="grafana" version="" >}} ## Data API @@ -211,15 +229,15 @@ Required fields: Optional fields: -| Field name | Type | Description | -| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| title | string | Name of the node visible in just under the node. | -| subtitle | string | Additional, name, type or other identifier shown under the title. | -| mainstat | string/number | First stat shown inside the node itself. It can either be a string showing the value as is or a number. If it is a number, any unit associated with that field is also shown. | -| secondarystat | string/number | Same as mainStat, but shown under it inside the node. | -| arc\_\_\* | number | Any field prefixed with `arc__` will be used to create the color circle around the node. All values in these fields should add up to 1. You can specify color using `config.color.fixedColor`. | -| detail\_\_\* | string/number | Any field prefixed with `detail__` will be shown in the header of context menu when clicked on the node. Use `config.displayName` for more human readable label. | -| color | string/number | Can be used to specify a single color instead of using the `arc__` fields to specify color sections. It can be either a string which should then be an acceptable HTML color string or it can be a number in which case the behaviour depends on `field.config.color.mode` setting. This can be for example used to create gradient colors controlled by the field value. | -| icon | string | Name of the icon to show inside the node instead of the default stats. Only Grafana built in icons are allowed (see the available icons [here](https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview)). | -| nodeRadius | number | Radius value in pixels. Used to manage node size. | -| highlighted | boolean | Sets whether the node should be highlighted. Useful for example to represent a specific path in the graph by highlighting several nodes and edges. Default: `false` | +| Field name | Type | Description | +| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| title | string | Name of the node visible in just under the node. | +| subtitle | string | Additional, name, type or other identifier shown under the title. | +| mainstat | string/number | First stat shown inside the node itself. It can either be a string showing the value as is or a number. If it is a number, any unit associated with that field is also shown. | +| secondarystat | string/number | Same as mainStat, but shown under it inside the node. | +| arc\_\_\* | number | Any field prefixed with `arc__` will be used to create the color circle around the node. All values in these fields should add up to 1. You can specify color using `config.color.fixedColor`. | +| detail\_\_\* | string/number | Any field prefixed with `detail__` will be shown in the header of context menu when clicked on the node. Use `config.displayName` for more human readable label. | +| color | string/number | Can be used to specify a single color instead of using the `arc__` fields to specify color sections. It can be either a string which should then be an acceptable HTML color string or it can be a number in which case the behavior depends on `field.config.color.mode` setting. This can be for example used to create gradient colors controlled by the field value. | +| icon | string | Name of the icon to show inside the node instead of the default stats. Only Grafana [built in icons](https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview)) are allowed. | +| nodeRadius | number | Radius value in pixels. Used to manage node size. | +| highlighted | boolean | Sets whether the node should be highlighted. Useful for example to represent a specific path in the graph by highlighting several nodes and edges. Default: `false` | diff --git a/docs/sources/panels-visualizations/visualizations/stat/index.md b/docs/sources/panels-visualizations/visualizations/stat/index.md index ad6da7f2889..eb989ae6d4e 100644 --- a/docs/sources/panels-visualizations/visualizations/stat/index.md +++ b/docs/sources/panels-visualizations/visualizations/stat/index.md @@ -23,6 +23,11 @@ refs: destination: /docs/grafana//panels-visualizations/query-transform-data/calculation-types/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/calculation-types/ + create-dashboard: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/create-dashboard/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/create-dashboard/ --- # Stat @@ -35,7 +40,7 @@ A stat visualization displays your data in single values of interest—such For example, if you're monitoring the utilization of various services, you can use a stat visualization to show their latest usage: -{{< figure src="/static/img/docs/v66/stat_panel_dark3.png" max-width="1025px" alt="A stat panel showing latest usage of various services" >}} +![A stat panel showing latest usage of various services](/media/docs/grafana/panels-visualizations/screenshot-stat-visualization-v11.3.png) Use a stat visualization when you need to: @@ -47,7 +52,7 @@ Use a stat visualization when you need to: ## Configure a stat visualization -Once you've [created a dashboard](https://grafana.com/docs/grafana//dashboards/build-dashboards/create-dashboard/), the following video shows you how to configure a stat visualization: +Once you've [created a dashboard](ref:create-dashboard), the following video shows you how to configure a stat visualization: {{< youtube id="yNRnLyVntUw" start="1048" >}} @@ -73,9 +78,9 @@ The following tables are examples of the type of data you need for a stat visual | 59 | | 40 | -The data is visualized as follows, with the last value displayed, along with a sparkline and [percentage change](#show-percent-change): +The data is visualized as follows, with the last value displayed, along with a sparkline and [percentage change](#value-options): -{{< figure src="/static/img/docs/stat-panel/stat_panel_single.png" max-width="1025px" alt="A stat panel showing the latest number of high priority bugs" >}} +![A stat panel showing the latest number of high priority bugs](/media/docs/grafana/panels-visualizations/screenshot-stat-single-value-v11.3.png) #### Time-series data @@ -89,7 +94,7 @@ The data is visualized as follows, with the last value displayed, along with a s The data is visualized as follows, with the mean value displayed for each room, along with the room name, sparkline, and unit of measurement: -{{< figure src="/static/img/docs/stat-panel/stat_panel_multiple.png" max-width="1025px" alt="A stat panel showing some statistics for each room in square meters" >}} +![A stat panel showing some statistics for each room in square meters](/media/docs/grafana/panels-visualizations/screenshot-stat-multiple-values-v11.3.png) By default, a stat displays one of the following: @@ -98,131 +103,87 @@ By default, a stat displays one of the following: You can use the [**Text mode**](#text-mode) to control how the text is displayed. -## Automatic layout adjustment +## Configuration options -The panel automatically adjusts the layout depending on available width and height in the dashboard. It automatically hides the graph (sparkline) if the panel becomes too small. +{{< docs/shared lookup="visualizations/config-options-intro.md" source="grafana" version="" >}} -## Panel options +### Panel options {{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}} -## Value options +### Value options Use the following options to refine how your visualization displays its values: -### Show + +| Option | Description | +| ------ | ----------- | +| Show | Display a single value per column or series, or show values for each row. Choose from:
        • **Calculate** - Display a calculated value based on all rows.
        • **All values** - Show a separate stat for every row. If you select this option, then you can also limit the number of rows to display.
        • | +| Calculation | This option is displayed when you select **Calculate** as your **Show** option. Select a reducer function that Grafana will use to reduce many fields to a single value. For a list of available calculations, refer to [Calculation types](ref:calculation-types). | +| Limit | This option is displayed when you select **All values** as your **Show** option. Set the maximum number of rows to display. Default is 5,000. | +| Fields | Select the fields displayed in the visualization. | -Display a single value per column or series, or show values for each row. + -#### Calculate +### Stat styles -Display a calculated value based on all rows. +The stat visualization automatically adjusts the layout depending on available width and height in the dashboard, but you can also use the following options to further style the visualization. -- **Calculation -** Select a reducer function that Grafana will use to reduce many fields to a single value. For a list of available calculations, refer to [Calculation types](ref:calculation-types). -- **Fields -** Select the fields display in the visualization. + +| Option | Description | +| ------ | ----------- | +| Orientation | Select a stacking direction. Choose from:
          • **Auto** - Grafana selects the ideal orientation.
          • **Horizontal** - Bars stretch horizontally, left to right.
          • **Vertical** - Bars stretch vertically, top to bottom.
          | +| [Text mode](#text-mode) | You can use the **Text mode** option to control what text the visualization renders. If the value is not important, only the name and color is, then change the **Text mode** to **Name**. The value will still be used to determine color and is displayed in a tooltip. | +| [Wide layout](#wide-layout) | Set whether wide layout is enabled or not. Wide layout is enabled by default. This option is only applicable when **Text mode** is set to **Value and name**. | +| Color mode | Select a color mode. Choose from:
          • **None** - No color applied to the value.
          • **Value** - Applies color to the value and graph area.
          • **Background Gradient** - Applies color to the value, graph area, and background, with a slight background gradient.
          • **Background Solid** - Applies color to the value, graph area, and background, with a solid background color.
          | +| Graph mode | Select a graph sparkline mode. Choose from:
          • **None** - Hides the graph sparkline and only shows the value.
          • **Area** - Shows the graph sparkline below the value. This requires that your query returns a time column.
          The graph sparkline is automatically hidden if the panel becomes too small.| +| Text alignment | Select an alignment mode. Choose from:
          • **Auto** - If only a single value is shown (no repeat), then the value is centered. If multiple series or rows are shown, then the value is left-aligned.
          • **Center** - Stat value is centered.
          | +| Show percent change | Set whether percent change is displayed or not. Disabled by default. This option is applicable when the **Show** setting, under **Value options**, is set to **Calculate**. | +| Percent change color mode | This option is only displayed when **Show percent change** is enabled. Choose from:
          • **Standard** - Green if the percent change is positive, red if the percent change is negative.
          • **Inverted** - Red if the percent change is positive, green if the percent change is negative.
          • **Same as Value** - Use the same color as the value.
          | + -#### All values - -Show a separate stat for every row. If you select this option, then you can also limit the number of rows to display. - -- **Limit -** The maximum number of rows to display. Default is 5,000. -- **Fields -** Select the fields display in the visualization. - -## Stat styles - -Style your visualization. - -### Orientation - -Choose a stacking direction. - -- **Auto -** Grafana selects what it thinks is the best orientation. -- **Horizontal -** Bars stretch horizontally, left to right. -- **Vertical -** Bars stretch vertically, top to bottom. - -### Text mode +#### Text mode You can use the Text mode option to control what text the visualization renders. If the value is not important, only the name and color is, then change the **Text mode** to **Name**. The value will still be used to determine color and is displayed in a tooltip. -- **Auto -** If the data contains multiple series or fields, show both name and value. -- **Value -** Show only value, never name. Name is displayed in the hover tooltip instead. -- **Value and name -** Always show value and name. -- **Name -** Show name instead of value. Value is displayed in the hover tooltip. -- **None -** Show nothing (empty). Name and value are displayed in the hover tooltip. +- **Auto** - If the data contains multiple series or fields, show both name and value. +- **Value** - Show only value, never name. Name is displayed in the hover tooltip instead. +- **Value and name** - Always show value and name. +- **Name** - Show name instead of value. Value is displayed in the hover tooltip. +- **None** - Show nothing (empty). Name and value are displayed in the hover tooltip. -### Wide layout +#### Wide layout Set whether wide layout is enabled or not. Wide layout is enabled by default. -- **On -** Wide layout is enabled. -- **Off -** Wide layout is disabled. +- **On** - Wide layout is enabled. +- **Off** - Wide layout is disabled. -{{< admonition type="note" >}} This option is only applicable when **Text mode** is set to **Value and name**. When wide layout is enabled, the value and name are displayed side-by-side with the value on the right, if the panel is wide enough. When wide layout is disabled, the value is always rendered underneath the name. -{{< /admonition >}} -### Color mode - -Select a color mode. - -- **None -** No color applied to the value. -- **Value -** Applies color to the value and graph area. -- **Background Gradient -** Applies color to the value, graph area, and background, with a slight background gradient. -- **Background Solid -** Applies color to the value, graph area, and background, with a solid background color. - -### Graph mode - -Select a graph and sparkline mode. - -- **None -** Hides the graph and only shows the value. -- **Area -** Shows the area graph below the value. This requires that your query returns a time column. - -### Text alignment - -Choose an alignment mode. - -- **Auto -** If only a single value is shown (no repeat), then the value is centered. If multiple series or rows are shown, then the value is left-aligned. -- **Center -** Stat value is centered. - -### Show percent change - -Set whether percent change is displayed or not. Disabled by default. - -{{< admonition type="note" >}} -This option is not applicable when the **Show** setting, under **Value options**, is set to **All values**. -{{< /admonition >}} - -### Percent change color mode - -This option is only displayed when **Show percent change** is enabled. Choose from: - -- **Standard -** Green if the percent change is positive, red if the percent change is negative. -- **Inverted -** Red if the percent change is positive, green if the percent change is negative. -- **Same as Value -** Use the same color as the value. - -## Text size +### Text size Adjust the sizes of the gauge text. -- **Title -** Enter a numeric value for the gauge title size. -- **Value -** Enter a numeric value for the gauge value size. +- **Title** - Enter a numeric value for the gauge title size. +- **Value** - Enter a numeric value for the gauge value size. -## Standard options +### Standard options {{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="" >}} -## Data links +### Data links {{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="" >}} -## Value mappings +### Value mappings {{< docs/shared lookup="visualizations/value-mappings-options.md" source="grafana" version="" >}} -## Thresholds +### Thresholds {{< docs/shared lookup="visualizations/thresholds-options-2.md" source="grafana" version="" >}} -## Field overrides +### Field overrides {{< docs/shared lookup="visualizations/overrides-options.md" source="grafana" version="" >}} diff --git a/docs/sources/panels-visualizations/visualizations/table/index.md b/docs/sources/panels-visualizations/visualizations/table/index.md index e1b6ecdaaaf..69bbc7edc47 100644 --- a/docs/sources/panels-visualizations/visualizations/table/index.md +++ b/docs/sources/panels-visualizations/visualizations/table/index.md @@ -68,9 +68,11 @@ refs: # Table -Tables are a highly flexible visualization designed to display data in columns and rows. They support various data types, including tables, time series, annotations, and raw JSON data. The table visualization can even take multiple data sets and provide the option to switch between them. With this versatility, it's the preferred visualization for viewing multiple data types, aiding in your data analysis needs. +Tables are a highly flexible visualization designed to display data in columns and rows. +The table visualization can take multiple datasets and provide the option to switch between them. +With this versatility, it's the preferred visualization for viewing multiple data types, aiding in your data analysis needs. -{{< figure src="/static/img/docs/tables/table_visualization.png" max-width="1200px" lightbox="true" alt="Table visualization" >}} +![Basic table visualization](/media/docs/grafana/panels-visualizations/screenshot-basic-table-v11.3.png) You can use a table visualization to show datasets such as: @@ -81,36 +83,43 @@ You can use a table visualization to show datasets such as: Any information you might want to put in a spreadsheet can often be best visualized in a table. -Tables also provide different styles to visualize data inside the table cells such as colored text and cell backgrounds, gauges, sparklines, data links, JSON code, and images. - -## Configure a table visualization - -The following video provides a visual walkthrough of the options you can set in a table visualization. If you want to see a configuration in action, check out the video: - -{{< youtube id="PCY7O8EJeJY" >}} - -{{< docs/play title="Table Visualizations in Grafana" url="https://play.grafana.org/d/OhR1ID6Mk/" >}} +Tables also provide different styles to visualize data inside the table cells, such as colored text and cell backgrounds, gauges, sparklines, data links, JSON code, and images. {{< admonition type="note" >}} Annotations and alerts are not currently supported for tables. {{< /admonition >}} +## Configure a table visualization + +The following video provides a visual walkthrough of the options you can set in a table visualization. +If you want to see a configuration in action, check out the video: + +{{< youtube id="PCY7O8EJeJY" >}} + +{{< docs/play title="Table Visualizations in Grafana" url="https://play.grafana.org/d/OhR1ID6Mk/" >}} + ## Supported data formats The table visualization supports any data that has a column-row structure. +{{< admonition type="note" >}} +If you’re using a cell type such as sparkline or JSON, the data requirements may differ in a way that’s specific to that type. For more info refer to [Cell type](#cell-type). +{{< /admonition >}} + ### Example -``` +This example shows a basic dataset in which there's data for every table cell: + +```csv Column1, Column2, Column3 value1 , value2 , value3 value4 , value5 , value6 value7 , value8 , value9 ``` -If a cell is missing or the table cell-row structure is not complete, the table visualization won’t display any of the data: +If a cell is missing or the table column-row structure is not complete, as in the following example, the table visualization won’t display any of the data: -``` +```csv Column1, Column2, Column3 value1 , value2 , value3 gap1 , gap2 @@ -119,60 +128,67 @@ value4 , value5 , value6 If you need to hide columns, you can do so using [data transformations](ref:data-transformation), [field overrides](#field-overrides), or by [building a query](ref:build-query) that returns only the needed columns. -If you’re using a cell type such as sparkline or JSON, the data requirements may differ in a way that’s specific to that type. For more info refer to [Cell type](#cell-type). +## Column filtering -## Debugging in tables +You can temporarily change how column data is displayed using column filtering. +For example, you can show or hide specific values. -The table visualization helps with debugging when you need to know exactly what results your query is returning and why other visualizations might not be working. This functionality is also accessible in most visualizations by toggling on the **Table view** switch at the top of the panel: +### Turn on column filtering -![The Table view switch](/media/docs/grafana/panels-visualizations/screenshot-table-view-on-11.2.png) - -## Turn on column filtering +To turn on column filtering, follow these steps: 1. In Grafana, navigate to the dashboard with the table with the columns that you want to filter. -1. On the table panel you want to filter, open the panel editor. -1. Expand the the **Table** options section. +1. Hover over any part of the panel to which you want to add the link to display the actions menu on the top right corner. +1. Click the menu and select **Edit**. +1. In the panel editor pane, expand the **Table** options section. 1. Toggle on the [**Column filter** switch](#table-options). -A filter icon appears next to each column title. +A filter icon (funnel) appears next to each column title. {{< figure src="/static/img/docs/tables/column-filter-with-icon.png" max-width="350px" alt="Column filtering turned on" class="docs-image--no-shadow" >}} ### Filter column values -To filter column values, click the filter (funnel) icon next to a column title. Grafana displays the filter options for that column. +To filter column values, follow these steps: -{{< figure src="/static/img/docs/tables/filter-column-values.png" max-width="300px" alt="Filter column values" class="docs-image--no-shadow" >}} +1. Click the filter icon (funnel) next to a column title. -Click the check box next to the values that you want to display. Enter text in the search field at the top to show those values in the display so that you can select them rather than scroll to find them. + Grafana displays the filter options for that column. -Choose from several operators to display column values: + {{< figure src="/static/img/docs/tables/filter-column-values.png" max-width="300px" alt="Filter column values" class="docs-image--no-shadow" >}} -- **Contains** - Matches a regex pattern (operator by default). -- **Expression** - Evaluates a boolean expression. The character `$` represents the column value in the expression (for example, "$ >= 10 && $ <= 12"). -- The typical comparison operators: `=`, `!=`, `<`, `<=`, `>`, `>=`. +1. Click the checkbox next to the values that you want to display or click **Select all**. +1. Enter text in the search field at the top to show those values in the display so that you can select them rather than scroll to find them. +1. Choose from several operators to display column values: -Click the check box above the **Ok** and **Cancel** buttons to add or remove all displayed values to/from the filter. + - **Contains** - Matches a regex pattern (operator by default). + - **Expression** - Evaluates a boolean expression. The character `$` represents the column value in the expression (for example, "$ >= 10 && $ <= 12"). + - The typical comparison operators: `=`, `!=`, `<`, `<=`, `>`, `>=`. + +1. Click the checkbox above the **Ok** and **Cancel** buttons to add or remove all displayed values to and from the filter. ### Clear column filters -Columns with filters applied have a blue funnel displayed next to the title. +Columns with filters applied have a blue filter displayed next to the title. {{< figure src="/static/img/docs/tables/filtered-column.png" max-width="100px" alt="Filtered column" class="docs-image--no-shadow" >}} -To remove the filter, click the blue funnel icon and then click **Clear filter**. +To remove the filter, click the blue filter icon and then click **Clear filter**. ## Sort columns -Click a column title to change the sort order from default to descending to ascending. Each time you click, the sort order changes to the next option in the cycle. You can sort multiple columns by holding the `shift` key and clicking the column name. +Click a column title to change the sort order from default to descending to ascending. +Each time you click, the sort order changes to the next option in the cycle. +You can sort multiple columns by holding the `Shift` key and clicking the column name. {{< figure src="/static/img/docs/tables/sort-descending.png" max-width="350px" alt="Sort descending" class="docs-image--no-shadow" >}} ## Dataset selector If the data queried contains multiple datasets, a table displays a drop-down list at the bottom, so you can select the dataset you want to visualize. +This option is only available when you're editing the panel. -{{< figure src="/media/docs/grafana/panels-visualizations/TablePanelMultiSet.png" max-width="650px" alt="Table visualization with multiple datasets" class="docs-image--no-shadow" >}} +{{< figure src="/media/docs/grafana/panels-visualizations/screenshot-table-multi-dataset-v11.3.png" max-width="650px" alt="Table visualization with multiple datasets" >}} ## Configuration options @@ -182,66 +198,70 @@ If the data queried contains multiple datasets, a table displays a drop-down lis ### Table options -{{% admonition type="note" %}} -If you are using a table created before Grafana 7.0, then you need to migrate to the new table version in order to see these options. To migrate, on the Panel tab, click **Table** visualization. Grafana updates the table version and you can then access all table options. -{{% /admonition %}} - -| Option | Description | -| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Show table header | Show or hide column names imported from your data source. | -| Cell height | Set the height of the cell. Choose from **Small**, **Medium**, and **Large**. | -| Enable pagination | Toggle the switch to control how many table rows are visible at once. When switched on, the page size automatically adjusts to the height of the table. This option doesn't affect queries. | -| Minimum column width | Define the lower limit of the column width, in pixels. By default, the minimum width of the table column is 150 pixels. For small-screen devices, such as smartphones or tablets, reduce the default `150` pixel value to `50` to allow table-based panels to render correctly in dashboards. | -| Column width | Define a column width, in pixels, rather than allowing the width to be set automatically. By default, Grafana calculates the column width based on the table size and the minimum column width. | -| Column alignment | Set how Grafana should align cell contents. Choose from: **Auto** (default), **Left**, **Center**, and **Right**. | -| Column filter | Temporarily change how column data is displayed. For example, you can order values from highest to lowest or hide specific values. For more information, refer to [Filter table columns](#filter-table-columns). | +| Option | Description | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Show table header | Show or hide column names imported from your data source. | +| Cell height | Set the height of the cell. Choose from **Small**, **Medium**, or **Large**. | +| Enable pagination | Toggle the switch to control how many table rows are visible at once. When switched on, the page size automatically adjusts to the height of the table. This option doesn't affect queries. | +| Minimum column width | Define the lower limit of the column width, in pixels. By default, the minimum width of the table column is 150 pixels. For small-screen devices, such as mobile phones or tablets, reduce the value to `50` to allow table-based panels to render correctly in dashboards. | +| Column width | Define a column width, in pixels, rather than allowing the width to be set automatically. By default, Grafana calculates the column width based on the table size and the minimum column width. | +| Column alignment | Set how Grafana should align cell contents. Choose from: **Auto** (default), **Left**, **Center**, or **Right**. | +| Column filter | Temporarily change how column data is displayed. For example, show or hide specific values. For more information, refer to [Column filtering](#column-filtering). | ### Table footer options -Toggle the **Show table footer** switch on and off to control the display of the footer. When the toggle is switched on, you can use the table footer to show [calculations](ref:calculations) on fields. +Toggle the **Show table footer** switch on and off to control the display of the footer. +When the toggle is switched on, you can use the table footer to show [calculations](ref:calculations) on fields. -After you activate the table footer, make selections in the following options: +After you activate the table footer, make selections for the following options: - **Calculation** - The calculation that you want to apply. -- **Fields** - The fields to which you want to apply the calculations. The system applies the calculation to all numeric fields if you do not select a field. -- **Count rows** - This options is displayed if you select the **Count** calculation. If you want to show the number of rows in the dataset instead of the number of values in the selected fields, toggle on the **Count rows** switch. +- **Count rows** - This option is displayed if you select the **Count** calculation. If you want to show the number of rows in the dataset instead of the number of values in the selected fields, toggle on the **Count rows** switch. +- **Fields** - The fields to which you want to apply the calculation. Grafana applies the calculation to all numeric fields if you don't select a field. ### Cell options Cell options allow you to control how data is displayed in a table. +The options are: + +- [Cell type](#cell-type) - Control the default cell display settings. +- [Wrap text](#wrap-text) - Wrap text in the cell that contains the longest content in your table. +- [Cell value inspect](#cell-value-inspect) - Enables value inspection from table cells. #### Cell type -By default, Grafana automatically chooses display settings. You can override the settings by choosing one of the following options to set the default for all fields. Additional configuration is available for some cell types. +By default, Grafana automatically chooses display settings. +You can override these settings by choosing one of the following cell types to control the default display for all fields. +Additional configuration is available for some cell types. -{{% admonition type="note" %}} -If you set these in the Field tab, then the type will apply to all fields, including the time field. Many options will work best if you set them in the Override tab so that they can be restricted to one or more fields. -{{% /admonition %}} +If you want to apply a cell type to only some fields instead of all fields, you can do so using the **Cell options > Cell type** field override. -| Cell type | Description | -| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Auto | The **Auto** cell type automatically displays values, with sensible defaults applied. | -| [Sparkline](#sparkline) | Shows values rendered as a sparkline. | -| [Colored text](#colored-text) | If thresholds are set, then the field text is displayed in the appropriate threshold color. | -| [Colored background](#colored-background) | If thresholds are set, then the field background is displayed in the appropriate threshold color. | -| [Gauge](#gauge) | Cells can be displayed as a graphical gauge, with several different presentation types. You can set the [Gauge display mode](#gauge-display-mode) and the [Value display](#value-display) options. | -| Data links | If you've configured data links, when the cell type is **Auto** mode, the cell text becomes clickable. If you change the cell type to **Data links**, the cell text reflects the titles of the configured data links. To control the application of data link text more granularly use a **Cell option > Cell type > Data links** field override. | -| [JSON View](#json-view) | Shows value formatted as code. | -| [Image](#image) | If you have a field value that is an image URL or a base64 encoded image you can configure the table to display it as an image. | +| Cell type | Description | +| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Auto | Automatically displays values with sensible defaults applied. | +| [Sparkline](#sparkline) | Shows values rendered as a sparkline. | +| [Colored text](#colored-text) | If thresholds are set, then the field text is displayed in the appropriate threshold color. | +| [Colored background](#colored-background) | If thresholds are set, then the field background is displayed in the appropriate threshold color. | +| [Gauge](#gauge) | Cells can be displayed as a graphical gauge, with several different presentation types. You can set the [Gauge display mode](#gauge-display-mode) and the [Value display](#value-display) options. | +| Data links | If you've configured data links, when the cell type is **Auto**, the cell text becomes clickable. If you change the cell type to **Data links**, the cell text reflects the titles of the configured data links. To control the application of data link text more granularly, use a **Cell option > Cell type > Data links** field override. | +| [JSON View](#json-view) | Shows values formatted as code. | +| [Image](#image) | If the field value is an image URL or a base64 encoded image, the table displays the image. | ##### Sparkline -Shows values rendered as a sparkline. You can show sparklines using the [Time series to table transformation](ref:time-series-to-table-transformation) on data with multiple time series to process it into a format the table can show. +This cell type shows values rendered as a sparkline. +To show sparklines on data with multiple time series, use the [Time series to table transformation](ref:time-series-to-table-transformation) to process it into a format the table can show. -{{< figure src="/static/img/docs/tables/sparkline2.png" max-width="500px" alt="Sparkline" class="docs-image--no-shadow" >}} +![Table using sparkline cell type](/media/docs/grafana/panels-visualizations/screenshot-table-as-sparkline-v11.3.png) -You can customize sparklines with many of the same options as the [time series visualization](ref:time-series-panel) including line style and width, fill opacity, gradient mode, and more. You can also change the color of the sparkline by updating the [color scheme](ref:color-scheme) in the **Standard options** section of the panel configuration. +You can customize sparklines with many of the same options as the [time series visualization](ref:time-series-panel) including line style and width, fill opacity, gradient mode, and more. +You can also change the color of the sparkline by updating the [color scheme](ref:color-scheme) in the **Standard options** section of the panel configuration. ##### Colored text -If thresholds are set, then the field text is displayed in the appropriate threshold color. +If thresholds are set, with this cell type, the field text is displayed in the appropriate threshold color. -{{< figure src="/static/img/docs/tables/color-text.png" max-width="500px" alt="Color text" class="docs-image--no-shadow" >}} +![Table with colored text cell type](/media/docs/grafana/panels-visualizations/screenshot-table-colored-text-v11.3-2.png) {{< admonition type="note" >}} This is an experimental feature. @@ -249,71 +269,66 @@ This is an experimental feature. ##### Colored background -If thresholds are set, then the field background is displayed in the appropriate threshold color. +If thresholds are set, with this cell type, the field background is displayed in the appropriate threshold color. -{{< figure src="/static/img/docs/tables/color-background.png" max-width="500px" alt="Color background" class="docs-image--no-shadow" >}} +![Table with colored background cell type](/media/docs/grafana/panels-visualizations/screenshot-table-colored-bkgrnd-v11.3-2.png) -Choose between **Basic** and **Gradient** to set the **Background display mode**. +- **Background display mode** - Choose between **Basic** and **Gradient**. +- **Apply to entire row** - Toggle the switch on to apply the background color that's configured for the cell to the whole row. -Toggle the **Apply to entire row** switch, to apply the background color that's configured for the cell to the whole row. - -{{< figure src="/static/img/docs/tables/colored-rows.png" max-width="500px" alt="Colored row background" class="docs-image--no-shadow" >}} +![Table with background cell color applied to row](/media/docs/grafana/panels-visualizations/screenshot-table-colored-row-v11.3.png) ##### Gauge -Cells can be displayed as a graphical gauge, with several different presentation types controlled by the gauge display mode and the value display. +With this cell type, cells can be displayed as a graphical gauge, with several different presentation types controlled by the [gauge display mode](#gauge-display-mode) and the [value display](#value-display). {{< admonition type="note" >}} -The maximum and minimum values of the gauges are configured automatically from the smallest and largest values in your whole data set. If you don't want the max/min values to be pulled from the whole data set, you can configure them for each column with field overrides. +The maximum and minimum values of the gauges are configured automatically from the smallest and largest values in your whole dataset. +If you don't want the max/min values to be pulled from the whole dataset, you can configure them for each column using [field overrides](#field-overrides). {{< /admonition >}} ###### Gauge display mode You can set three gauge display modes. -- **Basic** - Shows a simple gauge with the threshold levels defining the color of gauge. + - {{< figure src="/static/img/docs/tables/basic-gauge.png" max-width="500px" alt="Gradient gauge" class="docs-image--no-shadow" >}} +| Option | Description | +| ------ | ----------- | +| Basic | Shows a simple gauge with the threshold levels defining the color of gauge. {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-gauge-mode-basic-v11.3.png" alt="Table cell with basic gauge mode" >}} | +| Gradient | The threshold levels define a gradient. {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-gauge-mode-gradient-v11.3.png" alt="Table cell with gradient gauge mode" >}} | +| Retro LCD | The gauge is split up in small cells that are lit or unlit. {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-gauge-mode-retro-v11.3.png" alt="Table cell with retro LCD gauge mode" >}} | -- **Gradient** - The threshold levels define a gradient. - - {{< figure src="/static/img/docs/tables/gradient-gauge.png" max-width="500px" alt="Gradient gauge" class="docs-image--no-shadow" >}} - -- **Retro LCD** - The gauge is split up in small cells that are lit or unlit. - - {{< figure src="/static/img/docs/tables/lcd-gauge.png" max-width="500px" alt="LCD gauge" class="docs-image--no-shadow" >}} + ###### Value display Labels displayed alongside of the gauges can be set to be colored by value, match the theme text color, or be hidden. -- **Value color** + - {{< figure src="/static/img/docs/tables/value-color-mode.png" max-width="500px" alt="Color Label by Value" class="docs-image--no-shadow" >}} +| Option | Description | +| ------ | ----------- | +| Value color | Labels are colored by value. {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-labels-value-color-v11.3.png" alt="Table with labels in value color" >}} | +| Text color | Labels match the theme text color. {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-labels-text-color-v11.3.png" alt="Table with labels in theme color" >}} | +| Hidden | Labels are hidden. {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-labels-hidden-v11.3.png" alt="Table with labels hidden" >}} | -- **Text color** - - {{< figure src="/static/img/docs/tables/text-color-mode.png" max-width="500px" alt="Color Label by theme color" class="docs-image--no-shadow" >}} - -- **Hidden** - - {{< figure src="/static/img/docs/tables/hidden-mode.png" max-width="500px" alt="Hide Label" class="docs-image--no-shadow" >}} + ##### JSON View -Shows value formatted as code. If a value is an object the JSON view allowing browsing the JSON object will appear on hover. +This cell type shows values formatted as code. +If a value is an object the JSON view allowing browsing the JSON object will appear on hover. {{< figure src="/static/img/docs/tables/json-view.png" max-width="350px" alt="JSON view" class="docs-image--no-shadow" >}} ##### Image -{{< admonition type="note" >}} -Only available in Grafana 7.3+ -{{< /admonition >}} +If you have a field value that is an image URL or a base64 encoded image, this cell type displays it as an image. -If you have a field value that is an image URL or a base64 encoded image you can configure the table to display it as an image. +![Table with image cell type](/media/docs/grafana/panels-visualizations/screenshot-table-cell-image-v11.3.png) -{{< figure src="/static/img/docs/v73/table_hover.gif" max-width="900px" alt="Table hover" >}} +Set the following options: - **Alt text** - Set the alternative text of an image. The text will be available for screen readers and in cases when images can't be loaded. - **Title text** - Set the text that's displayed when the image is hovered over with a cursor. @@ -321,22 +336,26 @@ If you have a field value that is an image URL or a base64 encoded image you can #### Wrap text {{< admonition type="note" >}} -Text wrapping is in [public preview](https://grafana.com/docs/release-life-cycle/#public-preview), however, it’s available to use by default. We’d love hear from you about how this new feature is working. To provide feedback, you can open an issue in the [Grafana GitHub repository](https://github.com/grafana/grafana). +Text wrapping is in [public preview](https://grafana.com/docs/release-life-cycle/#public-preview), however, it’s available to use by default. +We’d love hear from you about how this new feature is working. To provide feedback, you can open an issue in the [Grafana GitHub repository](https://github.com/grafana/grafana). {{< /admonition >}} -Toggle the **Wrap text** switch to wrap text in the cell with the longest content in your table. To wrap the text in a specific column only, use the Wrap Text option in a [field override](ref:field-override). +Toggle the **Wrap text** switch to wrap text in the cell that contains the longest content in your table. +To wrap the text in a specific column only, use the Wrap Text option in a [field override](ref:field-override). -This option isn't available when you set the cell type to **Gauge** or Data links,JSON View, Image. +This option is available for the following cell types: **Auto**, **Colored text**, and **Colored background**. #### Cell value inspect Enables value inspection from table cells. When the **Cell inspect value** switch is toggled on, clicking the inspect icon in a cell opens the **Inspect value** drawer. -The **Inspect value** drawer has two tabs, **Plain text** and **Code editor**. Grafana attempts to automatically detect the type of data in the cell and opens the drawer with the associated tab showing. However, you can switch back and forth between tabs. +The **Inspect value** drawer has two tabs, **Plain text** and **Code editor**. +Grafana attempts to automatically detect the type of data in the cell and opens the drawer with the associated tab showing. +However, you can switch back and forth between tabs. -Cell value inspection is only available when the **Cell type** selection is **Auto**, **Colored text**, **Colored background**, or **JSON View**. +This option is available for the following cell types: **Auto**, **Colored text**, **Colored background**, and **JSON View**. -This option isn't available when you set the cell type to **Gauge** or Data links, Image, . +If you want to apply this setting to only some fields instead of all fields, you can do so using the **Cell options > Cell value inspect** field override. ### Standard options diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index ca45c37396a..bd96e5514a2 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -93,10 +93,6 @@ export GF_FEATURE_TOGGLES_ENABLE=newNavigation ## Variable expansion -{{% admonition type="note" %}} -Only available in Grafana 7.1+. -{{% /admonition %}} - If any of your options contains the expression `$__{}` or `${}`, then they will be processed by Grafana's variable expander. The expander runs the provider with the provided argument @@ -693,7 +689,9 @@ Set the policy template that will be used when adding the `Content-Security-Poli ### actions_allow_post_url -Sets API paths to be accessible between plugins using the POST verb. This is a comma separated list, and uses glob matching. +Sets API paths to be accessible between plugins using the POST verb. If the value is empty, you can only pass remote requests through the proxy. If the value is set, you can also send authenticated POST requests to the local server. You typically use this to enable backend communication between plugins. + +This is a comma-separated list which uses glob matching. This will allow access to all plugins that have a backend: @@ -768,7 +766,7 @@ Number dashboard versions to keep (per dashboard). Default: `20`, Minimum: `1`. This feature prevents users from setting the dashboard refresh interval to a lower value than a given interval value. The default interval value is 5 seconds. The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. `30s` or `1m`. -As of Grafana v7.3, this also limits the refresh interval options in Explore. +This also limits the refresh interval options in Explore. ### default_home_dashboard_path @@ -824,9 +822,8 @@ that this organization already exists. Default is 1. ### auto_assign_org_role -The `auto_assign_org_role` setting determines the default role assigned to new users -in the main organization (if `auto_assign_org` setting is set to true). -The available options are `Viewer` (default), `Admin`, `Editor`, and `None`. For example: +The `auto_assign_org_role` setting determines the default role assigned to new users in the main organization if `auto_assign_org` setting is set to `true`. +You can set this to one of the following roles: (`Viewer` (default), `Admin`, `Editor`, and `None`). For example: `auto_assign_org_role = Viewer` @@ -1069,14 +1066,10 @@ Limit of API key seconds to live before expiration. Default is -1 (unlimited). ### sigv4_auth_enabled -> Only available in Grafana 7.3+. - Set to `true` to enable the AWS Signature Version 4 Authentication option for HTTP-based datasources. Default is `false`. ### sigv4_verbose_logging -> Only available in Grafana 8.4+. - Set to `true` to enable verbose request signature logging when AWS Signature Version 4 Authentication is enabled. Default is `false`.
          @@ -1481,8 +1474,6 @@ Syslog tag. By default, the process's `argv[0]` is used. ## [log.frontend] -**Note:** This feature is available in Grafana 7.4+. - ### enabled Faro javascript agent is initialized. Default is `false`. @@ -1521,7 +1512,7 @@ Turns on tracing instrumentation. Only affects Grafana Javascript Agent. ### api_key -If `custom_endpoint` required authentication, you can set the api key here. Only relevant for Grafana Javascript Agent provider. +If `custom_endpoint` required authentication, you can set the API key here. Only relevant for Grafana Javascript Agent provider.
          diff --git a/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md b/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md index 4d39ca9b09e..8a16c6e293d 100644 --- a/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md +++ b/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md @@ -32,9 +32,9 @@ With custom branding, you have the ability to modify the following elements: - Side menu top logo - Footer and help menu links - Fav icon (shown in browser tab) -- Login title (will not appear if a login logo is set, Grafana v7.0+) -- Login subtitle (will not appear if a login logo is set, Grafana v7.0+) -- Login box background (Grafana v7.0+) +- Login title (will not appear if a login logo is set) +- Login subtitle (will not appear if a login logo is set) +- Login box background - Loading logo > You will have to host your logo and other images used by the custom branding feature separately. Make sure Grafana can access the URL where the assets are stored. diff --git a/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md b/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md index 8b641608ecd..5aaaa2253a6 100644 --- a/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md +++ b/docs/sources/setup-grafana/configure-grafana/enterprise-configuration/index.md @@ -23,32 +23,24 @@ Defaults to `/license.jwt`. ### license_text -{{% admonition type="note" %}} -Available in Grafana Enterprise version 7.4 and later. -{{% /admonition %}} - When set to the text representation (i.e. content of the license file) of the license, Grafana will evaluate and apply the given license to the instance. ### auto_refresh_license -{{% admonition type="note" %}} -Available in Grafana Enterprise version 7.4 and later. -{{% /admonition %}} - When enabled, Grafana will send the license and usage statistics to the license issuer. If the license has been updated on the issuer's side to be valid for a different number of users or a new duration, your Grafana instance will be updated with the new terms automatically. Defaults to `true`. -### license_validation_type - {{% admonition type="note" %}} -Available in Grafana Enterprise version 8.3 and later. +The license only automatically updates once per day. To immediately update the terms for a license, use the Grafana UI to renew your license token. {{% /admonition %}} +### license_validation_type + When set to `aws`, Grafana will validate its license status with Amazon Web Services (AWS) instead of with Grafana Labs. Only use this setting if you purchased an Enterprise license from AWS Marketplace. Defaults to empty, which means that by default Grafana Enterprise will validate using a license issued by Grafana Labs. For details about licenses issued by AWS, refer to [Activate a Grafana Enterprise license purchased through AWS Marketplace]({{< relref "../../../administration/enterprise-licensing/activate-aws-marketplace-license" >}}). ## [white_labeling] @@ -182,6 +174,10 @@ Name of the TrueType font file with italic style. Maximum number of panel rendering request retries before returning an error. To disable the retry feature, enter `0`. This is available in public preview and requires the `reportingRetries` feature toggle. +### allowed_domains + +Allowed domains to receive reports. Use an asterisk (`*`) to allow all domains. Use a comma-separated list to allow multiple domains. Example: allowed_domains = grafana.com, example.org + ## [auditing] [Auditing]({{< relref "../../configure-security/audit-grafana" >}}) allows you to track important changes to your Grafana instance. By default, audit logs are logged to file but the auditing feature also supports sending logs directly to Loki. @@ -368,10 +364,6 @@ New duration for renewed tokens. Vault may be configured to ignore this value an ## [security.egress] -{{% admonition type="note" %}} -Available in Grafana Enterprise version 7.4 and later. -{{% /admonition %}} - Security egress makes it possible to control outgoing traffic from the Grafana server. ### host_deny_list @@ -398,10 +390,6 @@ Encryption algorithm used to encrypt secrets stored in the database and cookies. ## [caching] -{{% admonition type="note" %}} -Available in Grafana Enterprise version 7.5 and later. -{{% /admonition %}} - When query caching is enabled, Grafana can temporarily store the results of data source queries and serve cached responses to similar requests. ### backend diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index b2e28ff7d7a..a9199b6ea11 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -21,99 +21,103 @@ For more information about feature release stages, refer to [Release life cycle Most [generally available](https://grafana.com/docs/release-life-cycle/#general-availability) features are enabled by default. You can disable these feature by setting the feature flag to "false" in the configuration. -| Feature toggle name | Description | Enabled by default | -| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -| `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | | -| `publicDashboards` | [Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version. | Yes | -| `publicDashboardsScene` | Enables public dashboard rendering using scenes | Yes | -| `featureHighlights` | Highlight Grafana Enterprise features | | -| `correlations` | Correlations page | Yes | -| `autoMigrateXYChartPanel` | Migrate old XYChart panel to new XYChart2 model | Yes | -| `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes | -| `accessControlOnCall` | Access control primitives for OnCall | Yes | -| `nestedFolders` | Enable folder nesting | Yes | -| `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view | Yes | -| `lokiQuerySplitting` | Split large interval queries into subqueries with smaller time intervals | Yes | -| `prometheusMetricEncyclopedia` | Adds the metrics explorer component to the Prometheus query builder as an option in metric select | Yes | -| `influxdbBackendMigration` | Query InfluxDB InfluxQL without the proxy | Yes | -| `lokiMetricDataplane` | Changes metric responses from Loki to be compliant with the dataplane specification. | Yes | -| `dataplaneFrontendFallback` | Support dataplane contract field name change for transformations and field name matchers where the name is different | Yes | -| `recordedQueriesMulti` | Enables writing multiple items from a single query within Recorded Queries | Yes | -| `logsExploreTableVisualisation` | A table visualisation for logs in Explore | Yes | -| `transformationsRedesign` | Enables the transformations redesign | Yes | -| `traceQLStreaming` | Enables response streaming of TraceQL queries of the Tempo data source | | -| `awsAsyncQueryCaching` | Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled | Yes | -| `prometheusConfigOverhaulAuth` | Update the Prometheus configuration page with the new auth component | Yes | -| `alertingNoDataErrorExecution` | Changes how Alerting state manager handles execution of NoData/Error | Yes | -| `angularDeprecationUI` | Display Angular warnings in dashboards and panels | Yes | -| `dashgpt` | Enable AI powered features in dashboards | Yes | -| `alertingInsights` | Show the new alerting insights landing page | Yes | -| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes | -| `formatString` | Enable format string transformer | Yes | -| `transformationsVariableSupport` | Allows using variables in transformations | Yes | -| `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s | Yes | -| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes | -| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes | -| `managedPluginsInstall` | Install managed plugins directly from plugins catalog | Yes | -| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes | -| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes | -| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes | -| `dashboardSceneSolo` | Enables rendering dashboards using scenes for solo panels | Yes | -| `dashboardScene` | Enables dashboard rendering using scenes for all roles | Yes | -| `ssoSettingsApi` | Enables the SSO settings API and the OAuth configuration UIs in Grafana | Yes | -| `logsInfiniteScrolling` | Enables infinite scrolling for the Logs panel in Explore and Dashboards | Yes | -| `exploreMetrics` | Enables the new Explore Metrics core app | Yes | -| `alertingSimplifiedRouting` | Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule | Yes | -| `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected | Yes | -| `lokiQueryHints` | Enables query hints for Loki | Yes | -| `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | | -| `promQLScope` | In-development feature that will allow injection of labels into prometheus queries. | Yes | -| `groupToNestedTableTransformation` | Enables the group to nested table transformation | Yes | -| `tlsMemcached` | Use TLS-enabled memcached in the enterprise caching feature | Yes | -| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes | -| `newDashboardSharingComponent` | Enables the new sharing drawer design | | -| `notificationBanner` | Enables the notification banner UI and API | Yes | -| `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | | -| `pinNavItems` | Enables pinning of nav items | Yes | -| `openSearchBackendFlowEnabled` | Enables the backend query flow for Open Search datasource plugin | Yes | -| `cloudWatchRoundUpEndTime` | Round up end time for metric queries to the next minute to avoid missing data | Yes | +| Feature toggle name | Description | Enabled by default | +| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | +| `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | | +| `publicDashboardsScene` | Enables public dashboard rendering using scenes | Yes | +| `featureHighlights` | Highlight Grafana Enterprise features | | +| `correlations` | Correlations page | Yes | +| `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes | +| `accessControlOnCall` | Access control primitives for OnCall | Yes | +| `nestedFolders` | Enable folder nesting | Yes | +| `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view | Yes | +| `lokiQuerySplitting` | Split large interval queries into subqueries with smaller time intervals | Yes | +| `prometheusMetricEncyclopedia` | Adds the metrics explorer component to the Prometheus query builder as an option in metric select | Yes | +| `influxdbBackendMigration` | Query InfluxDB InfluxQL without the proxy | Yes | +| `lokiMetricDataplane` | Changes metric responses from Loki to be compliant with the dataplane specification. | Yes | +| `dataplaneFrontendFallback` | Support dataplane contract field name change for transformations and field name matchers where the name is different | Yes | +| `unifiedRequestLog` | Writes error logs to the request logger | Yes | +| `recordedQueriesMulti` | Enables writing multiple items from a single query within Recorded Queries | Yes | +| `logsExploreTableVisualisation` | A table visualisation for logs in Explore | Yes | +| `transformationsRedesign` | Enables the transformations redesign | Yes | +| `traceQLStreaming` | Enables response streaming of TraceQL queries of the Tempo data source | | +| `awsAsyncQueryCaching` | Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled | Yes | +| `prometheusConfigOverhaulAuth` | Update the Prometheus configuration page with the new auth component | Yes | +| `alertingNoDataErrorExecution` | Changes how Alerting state manager handles execution of NoData/Error | Yes | +| `angularDeprecationUI` | Display Angular warnings in dashboards and panels | Yes | +| `dashgpt` | Enable AI powered features in dashboards | Yes | +| `alertingInsights` | Show the new alerting insights landing page | Yes | +| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes | +| `formatString` | Enable format string transformer | Yes | +| `transformationsVariableSupport` | Allows using variables in transformations | Yes | +| `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s | Yes | +| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes | +| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes | +| `managedPluginsInstall` | Install managed plugins directly from plugins catalog | Yes | +| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes | +| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes | +| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes | +| `dashboardSceneSolo` | Enables rendering dashboards using scenes for solo panels | Yes | +| `dashboardScene` | Enables dashboard rendering using scenes for all roles | Yes | +| `ssoSettingsApi` | Enables the SSO settings API and the OAuth configuration UIs in Grafana | Yes | +| `logsInfiniteScrolling` | Enables infinite scrolling for the Logs panel in Explore and Dashboards | Yes | +| `exploreMetrics` | Enables the new Explore Metrics core app | Yes | +| `alertingSimplifiedRouting` | Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule | Yes | +| `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected | Yes | +| `lokiQueryHints` | Enables query hints for Loki | Yes | +| `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | | +| `promQLScope` | In-development feature that will allow injection of labels into prometheus queries. | Yes | +| `groupToNestedTableTransformation` | Enables the group to nested table transformation | Yes | +| `tlsMemcached` | Use TLS-enabled memcached in the enterprise caching feature | Yes | +| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes | +| `accessActionSets` | Introduces action sets for resource permissions. Also ensures that all folder editors and admins can create subfolders without needing any additional permissions. | Yes | +| `newDashboardSharingComponent` | Enables the new sharing drawer design | | +| `notificationBanner` | Enables the notification banner UI and API | Yes | +| `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | | +| `pinNavItems` | Enables pinning of nav items | Yes | +| `openSearchBackendFlowEnabled` | Enables the backend query flow for Open Search datasource plugin | Yes | +| `cloudWatchRoundUpEndTime` | Round up end time for metric queries to the next minute to avoid missing data | Yes | +| `cloudwatchMetricInsightsCrossAccount` | Enables cross account observability for Cloudwatch Metric Insights query builder | Yes | +| `singleTopNav` | Unifies the top search bar and breadcrumb bar into one | Yes | +| `azureMonitorDisableLogLimit` | Disables the log limit restriction for Azure Monitor when true. The limit is enabled by default. | | +| `preinstallAutoUpdate` | Enables automatic updates for pre-installed plugins | Yes | +| `alertingUIOptimizeReducer` | Enables removing the reducer from the alerting UI when creating a new alert rule and using instant query | Yes | ## Public preview feature toggles [Public preview](https://grafana.com/docs/release-life-cycle/#public-preview) features are supported by our Support teams, but might be limited to enablement, configuration, and some troubleshooting. -| Feature toggle name | Description | -| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `panelTitleSearch` | Search for dashboards using panel title | -| `autoMigrateOldPanels` | Migrate old angular panels to supported versions (graph, table-old, worldmap, etc) | -| `autoMigrateGraphPanel` | Migrate old graph panel to supported time series panel - broken out from autoMigrateOldPanels to enable granular tracking | -| `autoMigrateTablePanel` | Migrate old table panel to supported table panel - broken out from autoMigrateOldPanels to enable granular tracking | -| `autoMigratePiechartPanel` | Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking | -| `autoMigrateWorldmapPanel` | Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking | -| `autoMigrateStatPanel` | Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking | -| `disableAngular` | Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime. | -| `grpcServer` | Run the GRPC server | -| `alertingNoNormalState` | Stop maintaining state of alerts that are not firing | -| `renderAuthJWT` | Uses JWT-based auth for rendering instead of relying on remote cache | -| `refactorVariablesTimeRange` | Refactor time range variables flow to reduce number of API calls made when query variables are chained | -| `faroDatasourceSelector` | Enable the data source selector within the Frontend Apps section of the Frontend Observability | -| `enableDatagridEditing` | Enables the edit functionality in the datagrid panel | -| `sqlDatasourceDatabaseSelection` | Enables previous SQL data source dataset dropdown behavior | -| `reportingRetries` | Enables rendering retries for the reporting feature | -| `externalServiceAccounts` | Automatic service account and token setup for plugins | -| `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches | -| `teamHttpHeaders` | Enables LBAC for datasources to apply LogQL filtering of logs to the client requests for users in teams | -| `pdfTables` | Enables generating table data as PDF in reporting | -| `canvasPanelPanZoom` | Allow pan and zoom in canvas panel | -| `regressionTransformation` | Enables regression analysis transformation | -| `onPremToCloudMigrations` | Enable the Grafana Migration Assistant, which helps you easily migrate on-prem dashboards, folders, and data source configurations to your Grafana Cloud stack. | -| `newPDFRendering` | New implementation for the dashboard-to-PDF rendering | -| `ssoSettingsSAML` | Use the new SSO Settings API to configure the SAML connector | -| `accessActionSets` | Introduces action sets for resource permissions. Also ensures that all folder editors and admins can create subfolders without needing any additional permissions. | -| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | -| `ssoSettingsLDAP` | Use the new SSO Settings API to configure LDAP | -| `cloudwatchMetricInsightsCrossAccount` | Enables cross account observability for Cloudwatch Metric Insights query builder | -| `useSessionStorageForRedirection` | Use session storage for handling the redirection after login | +| Feature toggle name | Description | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `panelTitleSearch` | Search for dashboards using panel title | +| `autoMigrateOldPanels` | Migrate old angular panels to supported versions (graph, table-old, worldmap, etc) | +| `autoMigrateGraphPanel` | Migrate old graph panel to supported time series panel - broken out from autoMigrateOldPanels to enable granular tracking | +| `autoMigrateTablePanel` | Migrate old table panel to supported table panel - broken out from autoMigrateOldPanels to enable granular tracking | +| `autoMigratePiechartPanel` | Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking | +| `autoMigrateWorldmapPanel` | Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking | +| `autoMigrateStatPanel` | Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking | +| `disableAngular` | Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime. | +| `grpcServer` | Run the GRPC server | +| `alertingNoNormalState` | Stop maintaining state of alerts that are not firing | +| `renderAuthJWT` | Uses JWT-based auth for rendering instead of relying on remote cache | +| `refactorVariablesTimeRange` | Refactor time range variables flow to reduce number of API calls made when query variables are chained | +| `faroDatasourceSelector` | Enable the data source selector within the Frontend Apps section of the Frontend Observability | +| `enableDatagridEditing` | Enables the edit functionality in the datagrid panel | +| `sqlDatasourceDatabaseSelection` | Enables previous SQL data source dataset dropdown behavior | +| `reportingRetries` | Enables rendering retries for the reporting feature | +| `externalServiceAccounts` | Automatic service account and token setup for plugins | +| `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches | +| `teamHttpHeaders` | Enables LBAC for datasources to apply LogQL filtering of logs to the client requests for users in teams | +| `pdfTables` | Enables generating table data as PDF in reporting | +| `canvasPanelPanZoom` | Allow pan and zoom in canvas panel | +| `regressionTransformation` | Enables regression analysis transformation | +| `onPremToCloudMigrations` | Enable the Grafana Migration Assistant, which helps you easily migrate on-prem dashboards, folders, and data source configurations to your Grafana Cloud stack. | +| `newPDFRendering` | New implementation for the dashboard-to-PDF rendering | +| `ssoSettingsSAML` | Use the new SSO Settings API to configure the SAML connector | +| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | +| `ssoSettingsLDAP` | Use the new SSO Settings API to configure LDAP | +| `useSessionStorageForRedirection` | Use session storage for handling the redirection after login | +| `reportingUseRawTimeRange` | Uses the original report or dashboard time range instead of making an absolute transformation | ## Experimental feature toggles @@ -135,6 +139,7 @@ Experimental features might be changed or removed without prior notice. | `mysqlParseTime` | Ensure the parseTime flag is set for MySQL driver | | `alertingBacktesting` | Rule backtesting API for alerting | | `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files | +| `lokiShardSplitting` | Use stream shards to split queries into smaller subqueries | | `lokiQuerySplittingConfig` | Give users the option to configure split durations for Loki queries | | `individualCookiePreferences` | Support overriding cookie preferences per user | | `influxqlStreamingParser` | Enable streaming JSON parser for InfluxDB datasource InfluxQL query language | @@ -143,17 +148,16 @@ Experimental features might be changed or removed without prior notice. | `alertStateHistoryLokiSecondary` | Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations. | | `alertStateHistoryLokiPrimary` | Enable a remote Loki instance as the primary source for state history reads. | | `alertStateHistoryLokiOnly` | Disable Grafana alerts from emitting annotations when a remote Loki instance is available. | -| `unifiedRequestLog` | Writes error logs to the request logger | | `extraThemes` | Enables extra themes | | `lokiPredefinedOperations` | Adds predefined query operations to Loki query editor | | `pluginsFrontendSandbox` | Enables the plugins frontend sandbox | | `frontendSandboxMonitorOnly` | Enables monitor only in the plugin frontend sandbox (if enabled) | | `pluginsDetailsRightPanel` | Enables right panel for the plugins details page | -| `vizAndWidgetSplit` | Split panels between visualizations and widgets | | `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | | `mlExpressions` | Enable support for Machine Learning in server-side expressions | | `metricsSummary` | Enables metrics summary queries in the Tempo data source | | `datasourceAPIServers` | Expose some datasources as apiservers. | +| `provisioning` | Next generation provisioning... and git | | `permissionsFilterRemoveSubquery` | Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder | | `aiGeneratedDashboardChanges` | Enable AI powered features for dashboards to auto-summary changes when saving | | `sseGroupByDatasource` | Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch. | @@ -179,11 +183,13 @@ Experimental features might be changed or removed without prior notice. | `alertmanagerRemotePrimary` | Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager. | | `alertmanagerRemoteOnly` | Disable the internal Alertmanager and only use the external one defined. | | `extractFieldsNameDeduplication` | Make sure extracted field names are unique in the dataframe | +| `dashboardNewLayouts` | Enables experimental new dashboard layouts | | `pluginsSkipHostEnvVars` | Disables passing host environment variable to plugin processes | | `tableSharedCrosshair` | Enables shared crosshair in table panel | | `kubernetesFeatureToggles` | Use the kubernetes API for feature toggle management in the frontend | | `newFolderPicker` | Enables the nested folder picker without having nested folders enabled | | `onPremToCloudMigrationsAlerts` | Enables the migration of alerts and its child resources to your Grafana Cloud stack. Requires `onPremToCloudMigrations` to be enabled in conjunction. | +| `onPremToCloudMigrationsAuthApiMig` | Enables the use of auth api instead of gcom for internal token services. Requires `onPremToCloudMigrations` to be enabled in conjunction. | | `sqlExpressions` | Enables using SQL and DuckDB functions as Expressions. | | `nodeGraphDotLayout` | Changed the layout algorithm for the node graph | | `kubernetesAggregator` | Enable grafana's embedded kube-aggregator | @@ -194,13 +200,13 @@ Experimental features might be changed or removed without prior notice. | `alertingListViewV2` | Enables the new alert list view design | | `dashboardRestore` | Enables deleted dashboard restore feature | | `alertingCentralAlertHistory` | Enables the new central alert history. | +| `sqlQuerybuilderFunctionParameters` | Enables SQL query builder function parameters | | `failWrongDSUID` | Throws an error if a datasource has an invalid UIDs | | `alertingApiServer` | Register Alerting APIs with the K8s API server | | `dataplaneAggregator` | Enable grafana dataplane aggregator | | `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture | | `lokiSendDashboardPanelNames` | Send dashboard and panel names to Loki when querying | | `alertingPrometheusRulesPrimary` | Uses Prometheus rules as the primary source of truth for ruler-enabled data sources | -| `singleTopNav` | Unifies the top search bar and breadcrumb bar into one | | `exploreLogsShardSplitting` | Used in Explore Logs to split queries into multiple queries based on the number of shards | | `exploreLogsAggregatedMetrics` | Used in Explore Logs to query by aggregated metrics | | `exploreLogsLimitedTimeRange` | Used in Explore Logs to limit the time range | @@ -210,6 +216,17 @@ Experimental features might be changed or removed without prior notice. | `rolePickerDrawer` | Enables the new role picker drawer design | | `pluginsSriChecks` | Enables SRI checks for plugin assets | | `unifiedStorageBigObjectsSupport` | Enables to save big objects in blob storage | +| `timeRangeProvider` | Enables time pickers sync | +| `prometheusUsesCombobox` | Use new combobox component for Prometheus query editor | +| `userStorageAPI` | Enables the user storage API | +| `dashboardSchemaV2` | Enables the new dashboard schema version 2, implementing changes necessary for dynamic dashboards and dashboards as code. | +| `playlistsWatcher` | Enables experimental watcher for playlists | +| `enableExtensionsAdminPage` | Enables the extension admin page regardless of development mode | +| `zipkinBackendMigration` | Enables querying Zipkin data source without the proxy | +| `enableSCIM` | Enables SCIM support for user and group management | +| `crashDetection` | Enables browser crash detection reporting to Faro. | +| `jaegerBackendMigration` | Enables querying the Jaeger data source without the proxy | +| `alertingNotificationsStepMode` | Enables simplified step mode in the notifications section | ## Development feature toggles diff --git a/docs/sources/setup-grafana/configure-security/audit-grafana.md b/docs/sources/setup-grafana/configure-security/audit-grafana.md index 9f421becc5f..9bafd16630e 100644 --- a/docs/sources/setup-grafana/configure-security/audit-grafana.md +++ b/docs/sources/setup-grafana/configure-security/audit-grafana.md @@ -26,7 +26,7 @@ To enable sending Grafana Cloud audit logs to your Grafana Cloud Logs instance, Only API requests or UI actions that trigger an API request generate an audit log. {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) version 7.3 and later, and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} ## Audit logs @@ -326,6 +326,17 @@ external group. | Set licensing token | `{"action": "create", "requestUri": "/api/licensing/token"}` | | Save billing information | `{"action": "billing-information"}` | +#### Cloud migration management + +{{< docs/public-preview product="Cloud Migration Assistant" featureFlag="onPremToCloudMigrations" >}} + +| Action | Distinguishing fields | +| -------------------------------- | ----------------------------------------------------------- | +| Connect to a cloud instance | `{"action": "connect-instance"}` | +| Disconnect from a cloud instance | `{"action": "disconnect-instance"}` | +| Build a snapshot | `{"action": "build", "resources": [{"type": "snapshot"}]}` | +| Upload a snapshot | `{"action": "upload", "resources": [{"type": "snapshot"}]}` | + #### Generic actions In addition to the actions listed above, any HTTP request (`POST`, `PATCH`, `PUT`, and `DELETE`) diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md index c8590e8c111..1c6b0ce9256 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md @@ -17,12 +17,14 @@ weight: 200 Grafana provides many ways to authenticate users. Some authentication integrations also enable syncing user permissions and org memberships. -The following table shows all supported authentication providers and the features available for them. [Team sync]({{< relref "../configure-team-sync" >}}) and [active sync]({{< relref "./enhanced-ldap#active-ldap-synchronization" >}}) are only available in Grafana Enterprise. +The following table shows all supported authentication methods and the features available for them. [Team sync]({{< relref "../configure-team-sync" >}}) and [active sync]({{< relref "./enhanced-ldap#active-ldap-synchronization" >}}) are only available in Grafana Enterprise. -| Provider | Multi Org Mapping | Enforce Sync | Role Mapping | Grafana Admin Mapping | Team Sync | Allowed groups | Active Sync | Skip OrgRole mapping | Auto Login | Single Logout | +| Authentication method | Multi Org Mapping | Enforce Sync | Role Mapping | Grafana Admin Mapping | Team Sync | Allowed groups | Active Sync | Skip OrgRole mapping | Auto Login | Single Logout | | :---------------------------------------------------- | :---------------- | :----------- | :----------- | :-------------------- | :-------- | :------------- | :---------- | :------------------- | :--------- | :------------ | +| [Anonymous access]({{< relref "./anonymous-auth" >}}) | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | | [Auth Proxy]({{< relref "./auth-proxy" >}}) | no | yes | yes | no | yes | no | N/A | no | N/A | N/A | | [Azure AD OAuth]({{< relref "./azuread" >}}) | yes | yes | yes | yes | yes | yes | N/A | yes | yes | yes | +| [Basic auth]({{< relref "./grafana" >}}) | yes | N/A | yes | yes | N/A | N/A | N/A | N/A | N/A | N/A | | [Generic OAuth]({{< relref "./generic-oauth" >}}) | yes | yes | yes | yes | yes | no | N/A | yes | yes | yes | | [GitHub OAuth]({{< relref "./github" >}}) | yes | yes | yes | yes | yes | yes | N/A | yes | yes | yes | | [GitLab OAuth]({{< relref "./gitlab" >}}) | yes | yes | yes | yes | yes | yes | N/A | yes | yes | yes | @@ -33,17 +35,9 @@ The following table shows all supported authentication providers and the feature | [LDAP]({{< relref "./ldap" >}}) | yes | yes | yes | yes | yes | yes | yes | no | N/A | N/A | | [JWT Proxy]({{< relref "./jwt" >}}) | no | yes | yes | yes | no | no | N/A | no | N/A | N/A | -N/A = Not applicable +Fields explanation: -## Auth Proxy - -| Feature | Supported? | -| :---------------- | :--------- | -| Multi Org Mapping | no | -| Enforce Sync | N/A | -| Role Mapping | yes | - -**Multi Org Mapping:** Able to add a user and role map him to multiple orgs +**Multi Org Mapping:** Able to add a user and map roles to multiple organizations **Enforce Sync:** If the information provided by the identity provider is empty, does the integration skip setting that user’s field or does it enforce a default. @@ -82,15 +76,15 @@ If users want to use the same email address with multiple identity providers (fo To enable this option, refer to the [Enable email lookup](#enable-email-lookup) section. -## Grafana Auth +## Multi-factor authentication (MFA/2FA) -Grafana of course has a built in user authentication system with password authentication enabled by default. You can -disable authentication by enabling anonymous access. You can also hide the login form and only allow login through an auth -provider (listed above). There are also options for allowing self sign up. +Grafana and the Grafana Cloud portal currently do not include built-in support for multi-factor authentication (MFA). -### Login and short-lived tokens +We strongly recommend integrating an external identity provider (IdP) that supports MFA, such as Okta, Azure AD, or Google Workspace. By configuring your Grafana instances to use an external IdP, you can leverage MFA to protect your accounts and resources effectively. -> The following applies when using Grafana's built in user authentication, LDAP (without Auth proxy) or OAuth integration. +## Login and short-lived tokens + +> The following applies when using Grafana's basic authentication, LDAP (without Auth proxy) or OAuth integration. Grafana uses short-lived tokens as a mechanism for verifying authenticated users. These short-lived tokens are rotated on an interval specified by `token_rotation_interval_minutes` for active authenticated users. @@ -99,11 +93,6 @@ Inactive authenticated users will remain logged in for a duration specified by ` This means that a user can close a Grafana window and return before `now + login_maximum_inactive_lifetime_duration` to continue their session. This is true as long as the time since last user login is less than `login_maximum_lifetime_duration`. -#### Remote logout - -You can logout from other devices by removing login sessions from the bottom of your profile page. If you are -a Grafana admin user, you can also do the same for any user from the Server Admin / Edit User view. - ## Settings Example: @@ -130,54 +119,19 @@ api_key_max_seconds_to_live = -1 oauth_allow_insecure_email_lookup = false ``` -### Anonymous authentication - -You can make Grafana accessible without any login required by enabling anonymous access in the configuration file. - -Example: - -```bash -[auth.anonymous] -enabled = true - -# Organization name that should be used for unauthenticated users -org_name = Main Org. - -# Role for unauthenticated users, other valid values are `Editor` and `Admin` -org_role = Viewer -``` - -If you change your organization name in the Grafana UI, this setting needs to be updated to match the new name. - -### Basic authentication - -Basic auth is enabled by default and works with the built-in Grafana user-password authentication system and LDAP -authentication integration. - -To disable basic auth: - -```bash -[auth.basic] -enabled = false -``` - -### Disable login form - -Hide the Grafana login form using the below configuration settings. - -```bash -[auth] -disable_login_form = true -``` +## Extended authentication settings ### Enable email lookup -Enable user lookup based on email in addition to using unique ID provided by IdPs. +By default, Grafana identifies users based on the unique ID provided by the identity provider (IdP). +In certain cases, however, enabling user lookups by email can be a feasible option, such as when: -By default, Grafana relies on the user unique ID provided by the identity provider. -Looking up users by email can be safe for some identity providers (for example, when they are single tenants and unique non-editable, validated emails are provided), as well as in some infrastructures. +- The identity provider is a single-tenant setup. +- Unique, validated, and non-editable emails are provided by the IdP. +- The infrastructure allows email-based identification without compromising security. -We strongly recommend against enabling email lookups, however it is possible to do with the following configuration. +**Important note**: While it is possible to configure Grafana to allow email-based user lookups, we strongly recommend against this approach in most cases due to potential security risks. +If you still choose to proceed, the following configuration can be applied to enable email lookup. ```bash [auth] @@ -211,10 +165,19 @@ Defaults to `false`. auto_login = true ``` -### Avoid automatic OAuth login +### Avoid automatic login -To sign in with a username and password and avoid automatic OAuth login, add the `disableAutoLogin` parameter to your login URL. -For example: `grafana.example.com/login?disableAutoLogin` or `grafana.example.com/login?disableAutoLogin=true` +The `disableAutoLogin=true` URL parameter allows users to bypass the automatic login feature in scenarios where incorrect configuration changes prevent normal login functionality. +This feature is especially helpful when you need to access the login screen to troubleshoot and fix misconfigurations. + +#### How to use + +1. Add `disableAutoLogin=true` as a query parameter to your Grafana URL. + - Example: `grafana.example.net/login?disableAutoLogin=true` or `grafana.example.net/login?disableAutoLogin` +1. This will redirect you to the standard login screen, bypassing the automatic login mechanism. +1. Fix any configuration issues and test your login setup. + +This feature is available for both for OAuth and SAML. Ensure that after fixing the issue, you remove the parameter or revert the configuration to re-enable the automatic login feature, if desired. ### Hide sign-out menu @@ -236,6 +199,11 @@ Example for Generic OAuth: signout_redirect_url = ``` +### Remote logout + +You can log out from other devices by removing login sessions from the bottom of your profile page. If you are +a Grafana admin user, you can also do the same for any user from the Server Admin / Edit User view. + ### Protected roles {{% admonition type="note" %}} diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/anonymous-auth/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/anonymous-auth/index.md new file mode 100644 index 00000000000..2d33683a7de --- /dev/null +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/anonymous-auth/index.md @@ -0,0 +1,61 @@ +--- +aliases: + - ../../../auth/anonymous-auth/ +description: Learn how to configure anonymous access in Grafana +labels: + products: + - enterprise + - oss +menuTitle: Anonymous access +title: Configure anonymous access +weight: 250 +--- + +# Anonymous authentication + +You can make Grafana accessible without any login required by enabling anonymous access in the configuration file. + +{{< admonition type="note" >}} +Anonymous users are charged as active users in Grafana Enterprise +{{< /admonition >}} + +## Anonymous devices + +The anonymous devices feature enhances the management and monitoring of anonymous access within your Grafana instance. This feature is part of ongoing efforts to provide more control and transparency over anonymous usage. + +Users can now view anonymous usage statistics, including the count of devices and users over the last 30 days. + +- Go to **Administration -> Users** to access the anonymous devices tab. +- A new stat for the usage stats page -> Usage & Stats page shows the active anonymous devices last 30 days. + +The number of anonymous devices is not limited by default. The configuration option `device_limit` allows you to enforce a limit on the number of anonymous devices. This enables you to have greater control over the usage within your Grafana instance and keep the usage within the limits of your environment. Once the limit is reached, any new devices that try to access Grafana will be denied access. + +To display anonymous users and devices for versions 10.2, 10.3, 10.4, you need to enable the feature toggle `displayAnonymousStats` + +```bash +[feature_toggles] +enable = displayAnonymousStats +``` + +## Configuration + +Example: + +```bash +[auth.anonymous] +enabled = true + +# Organization name that should be used for unauthenticated users +org_name = Main Org. + +# Role for unauthenticated users, other valid values are `Editor` and `Admin` +org_role = Viewer + +# Hide the Grafana version text from the footer and help tooltip for unauthenticated users (default: false) +hide_version = true + +# Setting this limits the number of anonymous devices in your instance. Any new anonymous devices added after the limit has been reached will be denied access. +device_limit = +``` + +If you change your organization name in the Grafana UI this setting needs to be updated to match the new name. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md index c71da95bc53..4c4d8d6e81e 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md @@ -12,12 +12,12 @@ labels: - cloud - enterprise - oss -menuTitle: Azure AD OAuth2 -title: Configure Azure AD OAuth2 authentication +menuTitle: Azure AD/Entra ID OAuth +title: Configure Azure AD/Entra ID OAuth authentication weight: 800 --- -# Configure Azure AD OAuth2 authentication +# Configure Azure AD/Entra ID OAuth authentication The Azure AD authentication allows you to use a Microsoft Entra ID (formerly known as Azure Active Directory) tenant as an identity provider for Grafana. You can use Entra ID application roles to assign users and groups to Grafana roles from the Azure Portal. @@ -27,7 +27,7 @@ If Users use the same email address in Microsoft Entra ID that they use with oth ## Create the Microsoft Entra ID application -To enable the Azure AD OAuth2, register your application with Entra ID. +To enable the Azure AD/Entra ID OAuth, register your application with Entra ID. 1. Log in to [Azure Portal](https://portal.azure.com), then click **Microsoft Entra ID** in the side menu. @@ -51,7 +51,11 @@ To enable the Azure AD OAuth2, register your application with Entra ID. - Description: Grafana OAuth - Expires: Select an expiration period -1. Click **Add** then copy the key value. This is the OAuth client secret. +1. Click **Add** then copy the key **Value**. This is the OAuth client secret. + +{{% admonition type="note" %}} +Make sure that you copy the string in the **Value** field, rather than the one in the **Secret ID** field. +{{% /admonition %}} 1. Define the required application roles for Grafana [using the Azure Portal](#configure-application-roles-for-grafana-in-the-azure-portal) or [using the manifest file](#configure-application-roles-for-grafana-in-the-manifest-file). @@ -62,6 +66,10 @@ To enable the Azure AD OAuth2, register your application with Entra ID. 1. Click **Users and Groups**. 1. Click **Add user/group** to add a user or group to the Grafana roles. +{{% admonition type="note" %}} +When assigning a group to a Grafana role, ensure that users are direct members of the group. Users in nested groups will not have access to Grafana due to limitations within Azure AD/Entra ID side. For more information, see [Microsoft Entra service limits and restrictions](https://learn.microsoft.com/en-us/entra/identity/users/directory-service-limits-restrictions). +{{% /admonition %}} + ### Configure application roles for Grafana in the Azure Portal This section describes setting up basic application roles for Grafana within the Azure Portal. For more information, see [Add app roles to your application and receive them in the token](https://learn.microsoft.com/en-us/entra/identity-platform/howto-add-app-roles-in-apps). @@ -173,7 +181,7 @@ Ensure that you have followed the steps in [Create the Microsoft Entra ID applic Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure your Azure AD OAuth2 client from within Grafana using the Grafana UI. To do this, navigate to the **Administration > Authentication > Azure AD** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. +As a Grafana Admin, you can configure your Azure AD/Entra ID OAuth client from within Grafana using the Grafana UI. To do this, navigate to the **Administration > Authentication > Azure AD** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. After you have filled in the form, click **Save** to save the configuration. If the save was successful, Grafana will apply the new configurations. @@ -345,14 +353,14 @@ This setting is ignored if multiple auth providers are configured to use auto lo auto_login = true ``` -### Team Sync (Enterprise only) +### Group sync (Enterprise only) -With Team Sync you can map your Entra ID groups to teams in Grafana so that your users will automatically be added to -the correct teams. +With group sync you can map your Entra ID groups to teams and roles in Grafana. This allows users to automatically be added to +the correct teams and be granted the correct roles in Grafana. You can reference Entra ID groups by group object ID, like `8bab1c86-8fba-33e5-2089-1d1c80ec267d`. -To learn more, refer to the [Team Sync]({{< relref "../../configure-team-sync" >}}) documentation. +To learn more about group synchronization, refer to [Configure team sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-team-sync) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync). ## Common troubleshooting @@ -361,8 +369,6 @@ configuring Azure AD authentication in Grafana. ### Users with over 200 Group assignments -> Supported in Grafana v8.5 and later versions. - To ensure that the token size doesn't exceed HTTP header size limits, Entra ID limits the number of object IDs that it includes in the groups claim. If a user is member of more groups than the @@ -439,3 +445,37 @@ See [Configure Grafana]({{< relref "../../../configure-grafana#authazuread" >}}) # prevents the sync of org roles from AzureAD skip_org_role_sync = true ``` + +## Configuration options + +The following table outlines the various Azure AD/Entra ID configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). + +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | +| `enabled` | No | Yes | Enables Azure AD/Entra ID authentication. | `false` | +| `name` | No | Yes | Name that refers to the Azure AD/Entra ID authentication from the Grafana user interface. | `OAuth` | +| `icon` | No | Yes | Icon used for the Azure AD/Entra ID authentication in the Grafana user interface. | `signin` | +| `client_id` | Yes | Yes | Client ID of the App (`Application (client) ID` on the **App registration** dashboard). | | +| `client_secret` | Yes | Yes | Client secret of the App. | | +| `auth_url` | Yes | Yes | Authorization endpoint of the Azure AD/Entra ID OAuth2 provider. | | +| `token_url` | Yes | Yes | Endpoint used to obtain the OAuth2 access token. | | +| `auth_style` | No | Yes | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` | +| `scopes` | No | Yes | List of comma- or space-separated OAuth2 scopes. | `openid email profile` | +| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Azure AD/Entra ID login. Only existing Grafana users can log in with Azure AD/Entra ID if set to `false`. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Map roles]({{< relref "#map-roles" >}}). | `false` | +| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `allow_assign_grafana_admin` | No | No | Set to `true` to automatically sync the Grafana server administrator role. When enabled, if the Azure AD/Entra ID user's App role is `GrafanaAdmin`, Grafana grants the user server administrator privileges and the organization administrator role. If disabled, the user will only receive the organization administrator role. For more details on user role mapping, refer to [Map roles]({{< relref "#map-roles" >}}). | `false` | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | +| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure Azure AD/Entra ID to include the `groups` claim following [Configure group membership claims on the Azure Portal]({{< relref "#configure-group-membership-claims-on-the-azure-portal" >}}). | | +| `allowed_organizations` | No | Yes | List of comma- or space-separated Azure tenant identifiers. The user should be a member of at least one tenant to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | +| `use_refresh_token` | No | Yes | Enables the use of refresh tokens and checks for access token expiration. When enabled, Grafana automatically adds the `offline_access` scope to the list of scopes. | `true` | +| `force_use_graph_api` | No | Yes | Set to `true` to always fetch groups from the Microsoft Graph API instead of the `id_token`. If a user belongs to more than 200 groups, the Microsoft Graph API will be used to retrieve the groups regardless of this setting. | `false` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/enhanced-ldap/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/enhanced-ldap/index.md index fd02e51fcb8..a8992e015d6 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/enhanced-ldap/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/enhanced-ldap/index.md @@ -28,18 +28,16 @@ The enhanced LDAP integration adds additional functionality on top of the [LDAP > To control user access with role-based permissions, refer to [role-based access control]({{< relref "../../../../administration/roles-and-permissions/access-control" >}}). -## LDAP group synchronization for teams +## LDAP group synchronization -With enhanced LDAP integration, you can set up synchronization between LDAP groups and teams. This enables LDAP users that are members -of certain LDAP groups to automatically be added or removed as members to certain teams in Grafana. +With enhanced LDAP integration, you can set up synchronization between LDAP groups and Grafana teams and roles. This enables users that are members +of certain LDAP groups to automatically be added to teams and gain roles in Grafana. + +The below example shows an LDAP group member mapped to a Grafana team. ![LDAP group synchronization](/static/img/docs/enterprise/team_members_ldap.png) -Grafana keeps track of all synchronized users in teams, and you can see which users have been synchronized from LDAP in the team members list, see `LDAP` label in screenshot. -This mechanism allows Grafana to remove an existing synchronized user from a team when its LDAP group membership changes. This mechanism also allows you to manually add -a user as member of a team, and it will not be removed when the user signs in. This gives you flexibility to combine LDAP group memberships and Grafana team memberships. - -[Learn more about team sync.]({{< relref "../../configure-team-sync" >}}) +To learn more about group synchronization, refer to [Configure team sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-team-sync) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync).
          @@ -47,7 +45,7 @@ a user as member of a team, and it will not be removed when the user signs in. T In the open source version of Grafana, user data from LDAP is synchronized only during the login process when authenticating using LDAP. -With active LDAP synchronization, available in Grafana Enterprise version 6.3 and later, you can configure Grafana to actively sync users with LDAP servers in the background. Only users that have logged into Grafana at least once are synchronized. +With active LDAP synchronization, you can configure Grafana to actively sync users with LDAP servers in the background. Only users that have logged into Grafana at least once are synchronized. Users with updated role and team membership will need to refresh the page to get access to the new features. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md index 5b038280047..2d6eb5fd39d 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/generic-oauth/ -description: Configure generic OAuth2 authentication +description: Configure Generic OAuth authentication keywords: - grafana - configuration @@ -12,12 +12,12 @@ labels: - cloud - enterprise - oss -menuTitle: Generic OAuth2 -title: Configure generic OAuth2 authentication +menuTitle: Generic OAuth +title: Configure Generic OAuth authentication weight: 700 --- -# Configure generic OAuth2 authentication +# Configure Generic OAuth authentication {{< docs/shared lookup="auth/intro.md" source="grafana" version="" >}} @@ -31,9 +31,9 @@ Grafana provides OAuth2 integrations for the following auth providers: - [Keycloak OAuth]({{< relref "../keycloak" >}}) - [Okta OAuth]({{< relref "../okta" >}}) -If your OAuth2 provider is not listed, you can use generic OAuth2 authentication. +If your OAuth2 provider is not listed, you can use Generic OAuth authentication. -This topic describes how to configure generic OAuth2 authentication using different methods and includes [examples of setting up generic OAuth2]({{< relref "#examples-of-setting-up-generic-oauth2" >}}) with specific OAuth2 providers. +This topic describes how to configure Generic OAuth authentication using different methods and includes [examples of setting up Generic OAuth]({{< relref "#examples-of-setting-up-generic-oauth2" >}}) with specific OAuth2 providers. ## Before you begin @@ -53,7 +53,7 @@ If Users use the same email address in Azure AD that they use with other authent Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure Generic OAuth2 client from within Grafana using the Generic OAuth UI. To do this, navigate to **Administration > Authentication > Generic OAuth** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. +As a Grafana Admin, you can configure Generic OAuth client from within Grafana using the Generic OAuth UI. To do this, navigate to **Administration > Authentication > Generic OAuth** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. After you have filled in the form, click **Save** to save the configuration. If the save was successful, Grafana will apply the new configurations. @@ -98,7 +98,7 @@ Ensure that you have access to the [Grafana configuration file]({{< relref "../. ### Steps -To integrate your OAuth2 provider with Grafana using our generic OAuth2 authentication, follow these steps: +To integrate your OAuth2 provider with Grafana using our Generic OAuth authentication, follow these steps: 1. Create an OAuth2 application in your chosen OAuth2 provider. 1. Set the callback URL for your OAuth2 app to `http://:/login/generic_oauth`. @@ -114,9 +114,9 @@ To integrate your OAuth2 provider with Grafana using our generic OAuth2 authenti | `client_id`, `client_secret` | These values must match the client ID and client secret from your OAuth2 app. | | `auth_url` | The authorization endpoint of your OAuth2 provider. | | `api_url` | The user information endpoint of your OAuth2 provider. Information returned by this endpoint must be compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | - | `enabled` | Enables generic OAuth2 authentication. Set this value to `true`. | + | `enabled` | Enables Generic OAuth authentication. Set this value to `true`. | - Review the list of other generic OAuth2 [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. + Review the list of other Generic OAuth [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. 1. Optional: [Configure a refresh token]({{< relref "#configure-a-refresh-token" >}}): @@ -127,10 +127,10 @@ To integrate your OAuth2 provider with Grafana using our generic OAuth2 authenti c. Enable the refresh token on the provider if required. 1. [Configure role mapping]({{< relref "#configure-role-mapping" >}}). -1. Optional: [Configure team synchronization]({{< relref "#configure-team-synchronization" >}}). +1. Optional: [Configure group synchronization]({{< relref "#configure-group-synchronization" >}}). 1. Restart Grafana. - You should now see a generic OAuth2 login button on the login page and be able to log in or sign up with your OAuth2 provider. + You should now see a Generic OAuth login button on the login page and be able to log in or sign up with your OAuth2 provider. ### Configure login @@ -185,7 +185,7 @@ When a user logs in using an OAuth2 provider, Grafana verifies that the access t Grafana uses a refresh token to obtain a new access token without requiring the user to log in again. If a refresh token doesn't exist, Grafana logs the user out of the system after the access token has expired. -To configure generic OAuth2 to use a refresh token, set `use_refresh_token` configuration option to `true` and perform one or both of the following steps, if required: +To configure Generic OAuth to use a refresh token, set `use_refresh_token` configuration option to `true` and perform one or both of the following steps, if required: 1. Extend the `scopes` field of `[auth.generic_oauth]` section in Grafana configuration file with additional scopes. 1. Enable the refresh token on the provider. @@ -328,19 +328,21 @@ org_attribute_path = info.roles org_mapping = org_foo:org_foo:Viewer org_bar:org_bar:Editor *:org_baz:Editor ``` -### Configure team synchronization +## Configure group synchronization -> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud/). +{{< admonition type="note" >}} +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). +{{< /admonition >}} -By using Team Sync, you can link your OAuth2 groups to teams within Grafana. This will automatically assign users to the appropriate teams. -Teams for each user are synchronized when the user logs in. +Grafana supports synchronization of OAuth2 groups with Grafana teams and roles. This allows automatically assigning users to the appropriate teams or automatically granting them the mapped roles. +Teams and roles get synchronized when the user logs in. -Generic OAuth2 groups can be referenced by group ID, such as `8bab1c86-8fba-33e5-2089-1d1c80ec267d` or `myteam`. +Generic OAuth groups can be referenced by group ID, such as `8bab1c86-8fba-33e5-2089-1d1c80ec267d` or `myteam`. For information on configuring OAuth2 groups with Grafana using the `groups_attribute_path` configuration option, refer to [configuration options]({{< relref "#configuration-options" >}}). -To learn more about Team Sync, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}). +To learn more about group synchronization, refer to [Configure team sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-team-sync) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync). -#### Team synchronization example +#### Group attribute synchronization example Configuration: @@ -367,55 +369,60 @@ Payload: ## Configuration options -The following table outlines the various generic OAuth2 configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. +The following table outlines the various Generic OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | -| `enabled` | No | Enables generic OAuth2 authentication. | `false` | -| `name` | No | Name that refers to the generic OAuth2 authentication from the Grafana user interface. | `OAuth` | -| `icon` | No | Icon used for the generic OAuth2 authentication in the Grafana user interface. | `signin` | -| `client_id` | Yes | Client ID provided by your OAuth2 app. | | -| `client_secret` | Yes | Client secret provided by your OAuth2 app. | | -| `auth_url` | Yes | Authorization endpoint of your OAuth2 provider. | | -| `token_url` | Yes | Endpoint used to obtain the OAuth2 access token. | | -| `api_url` | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | | -| `auth_style` | No | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` | -| `scopes` | No | List of comma- or space-separated OAuth2 scopes. | `user:email` | -| `empty_scopes` | No | Set to `true` to use an empty scope during authentication. | `false` | -| `allow_sign_up` | No | Controls Grafana user creation through the generic OAuth2 login. Only existing Grafana users can log in with generic OAuth if set to `false`. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `id_token_attribute_name` | No | The name of the key used to extract the ID token from the returned OAuth2 token. | `id_token` | -| `login_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user login lookup from the user ID token. For more information on how user login is retrieved, refer to [Configure login]({{< relref "#configure-login" >}}). | | -| `name_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user name lookup from the user ID token. This name will be used as the user's display name. For more information on how user display name is retrieved, refer to [Configure display name]({{< relref "#configure-display-name" >}}). | | -| `email_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user email lookup from the user information. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | | -| `email_attribute_name` | No | Name of the key to use for user email lookup within the `attributes` map of OAuth2 ID token. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | `email:primary` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `org_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | -| `groups_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a string array of groups. | | -| `allowed_groups` | No | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | | -| `allowed_organizations` | No | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | | -| `allowed_domains` | No | List comma- or space-separated domains. The user should belong to at least one domain to log in. | | -| `team_ids` | No | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | | -| `team_ids_attribute_path` | No | The [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana team ID lookup within the results returned by the `teams_url` endpoint. | | -| `teams_url` | No | The URL used to query for team IDs. If not set, the default value is `/teams`. If you configure `teams_url`, you must also configure `team_ids_attribute_path`. | | -| `tls_skip_verify_insecure` | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | -| `tls_client_cert` | No | The path to the certificate. | | -| `tls_client_key` | No | The path to the key. | | -| `tls_client_ca` | No | The path to the trusted certificate authority list. | | -| `use_pkce` | No | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `false` | -| `use_refresh_token` | No | Set to `true` to use refresh token and check access token expiration. | `false` | +{{< admonition type="note" >}} +If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"` +{{< /admonition >}} -## Examples of setting up generic OAuth2 +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `enabled` | No | Yes | Enables Generic OAuth authentication. | `false` | +| `name` | No | Yes | Name that refers to the Generic OAuth authentication from the Grafana user interface. | `OAuth` | +| `icon` | No | Yes | Icon used for the Generic OAuth authentication in the Grafana user interface. | `signin` | +| `client_id` | Yes | Yes | Client ID provided by your OAuth2 app. | | +| `client_secret` | Yes | Yes | Client secret provided by your OAuth2 app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your OAuth2 provider. | | +| `token_url` | Yes | Yes | Endpoint used to obtain the OAuth2 access token. | | +| `api_url` | Yes | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | | +| `auth_style` | No | Yes | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` | +| `scopes` | No | Yes | List of comma- or space-separated OAuth2 scopes. | `user:email` | +| `empty_scopes` | No | Yes | Set to `true` to use an empty scope during authentication. | `false` | +| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Generic OAuth login. Only existing Grafana users can log in with Generic OAuth if set to `false`. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `id_token_attribute_name` | No | Yes | The name of the key used to extract the ID token from the returned OAuth2 token. | `id_token` | +| `login_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user login lookup from the user ID token. For more information on how user login is retrieved, refer to [Configure login]({{< relref "#configure-login" >}}). | | +| `name_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user name lookup from the user ID token. This name will be used as the user's display name. For more information on how user display name is retrieved, refer to [Configure display name]({{< relref "#configure-display-name" >}}). | | +| `email_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user email lookup from the user information. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | | +| `email_attribute_name` | No | Yes | Name of the key to use for user email lookup within the `attributes` map of OAuth2 ID token. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | `email:primary` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | +| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `groups_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a string array of groups. | | +| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | | +| `allowed_organizations` | No | Yes | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | +| `team_ids` | No | Yes | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | | +| `team_ids_attribute_path` | No | Yes | The [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana team ID lookup within the results returned by the `teams_url` endpoint. | | +| `teams_url` | No | Yes | The URL used to query for team IDs. If not set, the default value is `/teams`. If you configure `teams_url`, you must also configure `team_ids_attribute_path`. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `false` | +| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. | `false` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | -This section includes examples of setting up generic OAuth2 integration. +## Examples of setting up Generic OAuth + +This section includes examples of setting up Generic OAuth integration. ### Set up OAuth2 with Descope -To set up generic OAuth2 authentication with Descope, follow these steps: +To set up Generic OAuth authentication with Descope, follow these steps: 1. Create a Descope Project [here](https://app.descope.com/gettingStarted), and go through the Getting Started Wizard to configure your authentication. You can skip step if you already have Descope project set up. @@ -449,7 +456,11 @@ To set up generic OAuth2 authentication with Descope, follow these steps: ### Set up OAuth2 with Auth0 -To set up generic OAuth2 authentication with Auth0, follow these steps: +{{< admonition type="note" >}} +Support for the Auth0 "audience" feature is not currently available in Grafana. For roles and permissions, the available options are described [here]({{< relref "../../../../administration/roles-and-permissions/" >}}). +{{< /admonition >}} + +To set up Generic OAuth authentication with Auth0, follow these steps: 1. Create an Auth0 application using the following parameters: @@ -482,7 +493,7 @@ To set up generic OAuth2 authentication with Auth0, follow these steps: ### Set up OAuth2 with Bitbucket -To set up generic OAuth2 authentication with Bitbucket, follow these steps: +To set up Generic OAuth authentication with Bitbucket, follow these steps: 1. Navigate to **Settings > Workspace setting > OAuth consumers** in BitBucket. @@ -517,7 +528,7 @@ By default, a refresh token is included in the response for the **Authorization ### Set up OAuth2 with OneLogin -To set up generic OAuth2 authentication with OneLogin, follow these steps: +To set up Generic OAuth authentication with OneLogin, follow these steps: 1. Create a new Custom Connector in OneLogin with the following settings: @@ -553,7 +564,7 @@ To set up generic OAuth2 authentication with OneLogin, follow these steps: ### Set up OAuth2 with Dex -To set up generic OAuth2 authentication with [Dex IdP](https://dexidp.io/), follow these +To set up Generic OAuth authentication with [Dex IdP](https://dexidp.io/), follow these steps: 1. Add Grafana as a client in the Dex config YAML file: diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md index 4c820d9bbe1..5792c3a4a70 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/github/ -description: Configure GitHub OAuth2 authentication +description: Configure GitHub OAuth authentication keywords: - grafana - configuration @@ -12,16 +12,16 @@ labels: - cloud - enterprise - oss -menuTitle: GitHub OAuth2 -title: Configure GitHub OAuth2 authentication +menuTitle: GitHub OAuth +title: Configure GitHub OAuth authentication weight: 900 --- -# Configure GitHub OAuth2 authentication +# Configure GitHub OAuth authentication {{< docs/shared lookup="auth/intro.md" source="grafana" version="" >}} -This topic describes how to configure GitHub OAuth2 authentication. +This topic describes how to configure GitHub OAuth authentication. {{% admonition type="note" %}} If Users use the same email address in GitHub that they use with other authentication providers (such as Grafana.com), you need to do additional configuration to ensure that the users are matched correctly. Please refer to the [Using the same email address to login with different identity providers]({{< relref "../../configure-authentication#using-the-same-email-address-to-login-with-different-identity-providers" >}}) documentation for more information. @@ -31,13 +31,23 @@ If Users use the same email address in GitHub that they use with other authentic Ensure you know how to create a GitHub OAuth app. Consult GitHub's documentation on [creating an OAuth app](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) for more information. +### Create a GitHub OAuth App + +1. Log in to your GitHub account. + In **Profile > Settings > Developer settings**, select **OAuth Apps**. +1. Click **New OAuth App**. +1. Fill out the fields, using your Grafana homepage URL when appropriate. + In the **Authorization callback URL** field, enter the following: `https:///login/github` . +1. Note your client ID. +1. Generate, then note, your client secret. + ## Configure GitHub authentication client using the Grafana UI {{% admonition type="note" %}} Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure GitHub OAuth2 client from within Grafana using the GitHub UI. To do this, navigate to **Administration > Authentication > GitHub** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. +As a Grafana Admin, you can configure GitHub OAuth client from within Grafana using the GitHub UI. To do this, navigate to **Administration > Authentication > GitHub** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. After you have filled in the form, click **Save** . If the save was successful, Grafana will apply the new configurations. @@ -100,7 +110,7 @@ To configure GitHub authentication with Grafana, follow these steps: Review the list of other GitHub [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. 1. [Configure role mapping]({{< relref "#configure-role-mapping" >}}). -1. Optional: [Configure team synchronization]({{< relref "#configure-team-synchronization" >}}). +1. Optional: [Configure group synchronization]({{< relref "#configure-group-synchronization" >}}). 1. Restart Grafana. You should now see a GitHub login button on the login page and be able to log in or sign up with your GitHub accounts. @@ -156,7 +166,18 @@ All other users are granted the `Viewer` role. role_attribute_path = contains(groups[*], '@my-github-organization/my-github-team') && 'Editor' || 'Viewer' ``` -#### Map server administrator role +##### Map roles using multiple GitHub teams + +In this example, the users from GitHub teams `admins` and `devops` have been granted the `Admin` role, +the users from GitHub teams `engineers` and `managers` have been granted the `Editor` role, +the users from GitHub team `qa` have been granted the `Viewer` role and +all other users are granted the `None` role. + +```bash +role_attribute_path = contains(groups[*], '@my-github-organization/admins') && 'Admin' || contains(groups[*], '@my-github-organization/devops') && 'Admin' || contains(groups[*], '@my-github-organization/engineers') && 'Editor' || contains(groups[*], '@my-github-organization/managers') && 'Editor' || contains(groups[*], '@my-github-organization/qa') && 'Viewer' || 'None' +``` + +##### Map server administrator role In this example, the user with login `octocat` has been granted the `Admin` organization role as well as the Grafana server admin role. All other users are granted the `Viewer` role. @@ -195,14 +216,14 @@ allowed_domains = mycompany.com mycompany.org role_attribute_path = [login=='octocat'][0] && 'GrafanaAdmin' || 'Viewer' ``` -## Configure team synchronization +## Configure group synchronization {{< admonition type="note" >}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and Grafana Cloud. +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). {{< /admonition >}} -By using Team Sync, you can map teams from your GitHub organization to teams within Grafana. This will automatically assign users to the appropriate teams. -Teams for each user are synchronized when the user logs in. +Grafana supports synchronization of teams from your GitHub organization with Grafana teams and roles. This allows automatically assigning users to the appropriate teams or granting them the mapped roles. +Teams and roles get synchronized when the user logs in. GitHub teams can be referenced in two ways: @@ -211,34 +232,39 @@ GitHub teams can be referenced in two ways: Examples: `https://github.com/orgs/grafana/teams/developers` or `@grafana/developers`. -To learn more about Team Sync, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}). +To learn more about group synchronization, refer to [Configure team sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-team-sync) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync). ## Configuration options -The table below describes all GitHub OAuth configuration options. Like any other Grafana configuration, you can apply these options as environment variables. +The table below describes all GitHub OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| `enabled` | No | Whether GitHub OAuth authentication is allowed. | `false` | -| `name` | No | Name used to refer to the GitHub authentication in the Grafana user interface. | `GitHub` | -| `icon` | No | Icon used for GitHub authentication in the Grafana user interface. | `github` | -| `client_id` | Yes | Client ID provided by your GitHub OAuth app. | | -| `client_secret` | Yes | Client secret provided by your GitHub OAuth app. | | -| `auth_url` | Yes | Authorization endpoint of your GitHub OAuth provider. | `https://github.com/login/oauth/authorize` | -| `token_url` | Yes | Endpoint used to obtain GitHub OAuth access token. | `https://github.com/login/oauth/access_token` | -| `api_url` | Yes | Endpoint used to obtain GitHub user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://api.github.com/user` | -| `scopes` | No | List of comma- or space-separated GitHub OAuth scopes. | `user:email,read:org` | -| `allow_sign_up` | No | Whether to allow new Grafana user creation through GitHub login. If set to `false`, then only existing Grafana users can log in with GitHub OAuth. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the user information obtained from the UserInfo endpoint. If no role is found, Grafana creates a JSON data with `groups` key that maps to GitHub teams obtained from GitHub's [`/api/user/teams`](https://docs.github.com/en/rest/teams/teams#list-teams-for-the-authenticated-user) endpoint, and evaluates the expression using this data. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | `false` | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. | `false` | -| `allowed_organizations` | No | List of comma- or space-separated organizations. User must be a member of at least one organization to log in. | | -| `allowed_domains` | No | List of comma- or space-separated domains. User must belong to at least one domain to log in. | | -| `team_ids` | No | Integer list of team IDs. If set, user has to be a member of one of the given teams to log in. | | -| `tls_skip_verify_insecure` | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | -| `tls_client_cert` | No | The path to the certificate. | | -| `tls_client_key` | No | The path to the key. | | -| `tls_client_ca` | No | The path to the trusted certificate authority list. | | +{{< admonition type="note" >}} +If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"` +{{< /admonition >}} + +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| `enabled` | No | Yes | Whether GitHub OAuth authentication is allowed. | `false` | +| `name` | No | Yes | Name used to refer to the GitHub authentication in the Grafana user interface. | `GitHub` | +| `icon` | No | Yes | Icon used for GitHub authentication in the Grafana user interface. | `github` | +| `client_id` | Yes | Yes | Client ID provided by your GitHub OAuth app. | | +| `client_secret` | Yes | Yes | Client secret provided by your GitHub OAuth app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your GitHub OAuth provider. | `https://github.com/login/oauth/authorize` | +| `token_url` | Yes | Yes | Endpoint used to obtain GitHub OAuth access token. | `https://github.com/login/oauth/access_token` | +| `api_url` | Yes | Yes | Endpoint used to obtain GitHub user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://api.github.com/user` | +| `scopes` | No | Yes | List of comma- or space-separated GitHub OAuth scopes. | `user:email,read:org` | +| `allow_sign_up` | No | Yes | Whether to allow new Grafana user creation through GitHub login. If set to `false`, then only existing Grafana users can log in with GitHub OAuth. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the user information obtained from the UserInfo endpoint. If no role is found, Grafana creates a JSON data with `groups` key that maps to GitHub teams obtained from GitHub's [`/api/user/teams`](https://docs.github.com/en/rest/teams/teams#list-teams-for-the-authenticated-user) endpoint, and evaluates the expression using this data. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | `false` | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. | `false` | +| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `allowed_organizations` | No | Yes | List of comma- or space-separated organizations. User must be a member of at least one organization to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. User must belong to at least one domain to log in. | | +| `team_ids` | No | Yes | Integer list of team IDs. If set, user has to be a member of one of the given teams to log in. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md index be61bbb796c..d1503808910 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/gitlab/ -description: Grafana OAuthentication Guide +description: Grafana GitLab OAuth Guide keywords: - grafana - configuration @@ -12,16 +12,16 @@ labels: - cloud - enterprise - oss -menuTitle: GitLab OAuth2 -title: Configure GitLab OAuth2 authentication +menuTitle: GitLab OAuth +title: Configure GitLab OAuth authentication weight: 1000 --- -# Configure GitLab OAuth2 authentication +# Configure GitLab OAuth authentication {{< docs/shared lookup="auth/intro.md" source="grafana" version="" >}} -This topic describes how to configure GitLab OAuth2 authentication. +This topic describes how to configure GitLab OAuth authentication. {{% admonition type="note" %}} If Users use the same email address in GitLab that they use with other authentication providers (such as Grafana.com), you need to do additional configuration to ensure that the users are matched correctly. Please refer to the [Using the same email address to login with different identity providers]({{< relref "../../configure-authentication#using-the-same-email-address-to-login-with-different-identity-providers" >}}) documentation for more information. @@ -31,13 +31,23 @@ If Users use the same email address in GitLab that they use with other authentic Ensure you know how to create a GitLab OAuth application. Consult GitLab's documentation on [creating a GitLab OAuth application](https://docs.gitlab.com/ee/integration/oauth_provider.html) for more information. +### Create a GitLab OAuth Application + +1. Log in to your GitLab account and go to **Profile > Preferences > Applications**. +1. Click **Add new application**. +1. Fill out the fields. + - In the **Redirect URI** field, enter the following: `https:///login/gitlab` and check `openid`, `email`, `profile` in the **Scopes** list. + - Leave the **Confidential** checkbox checked. +1. Click **Save application**. +1. Note your **Application ID** (this is the `Client Id`) and **Secret** (this is the `Client Secret`). + ## Configure GitLab authentication client using the Grafana UI {{% admonition type="note" %}} Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure GitLab OAuth2 client from within Grafana using the GitLab UI. To do this, navigate to **Administration > Authentication > GitLab** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. +As a Grafana Admin, you can configure GitLab OAuth client from within Grafana using the GitLab UI. To do this, navigate to **Administration > Authentication > GitLab** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. After you have filled in the form, click **Save** to save the configuration. If the save was successful, Grafana will apply the new configurations. @@ -97,10 +107,10 @@ To configure GitLab authentication with Grafana, follow these steps: 1. Refer to the following table to update field values located in the `[auth.gitlab]` section of the Grafana configuration file: - | Field | Description | - | ---------------------------- | -------------------------------------------------------------------------------------------- | - | `client_id`, `client_secret` | These values must match the client ID and client secret from your GitLab OAuth2 application. | - | `enabled` | Enables GitLab authentication. Set this value to `true`. | + | Field | Description | + | ---------------------------- | --------------------------------------------------------------------------------------------- | + | `client_id`, `client_secret` | These values must match the `Application ID` and `Secret` from your GitLab OAuth application. | + | `enabled` | Enables GitLab authentication. Set this value to `true`. | Review the list of other GitLab [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. @@ -109,7 +119,7 @@ To configure GitLab authentication with Grafana, follow these steps: a. Set `use_refresh_token` to `true` in `[auth.gitlab]` section in Grafana configuration file. 1. [Configure role mapping]({{< relref "#configure-role-mapping" >}}). -1. Optional: [Configure team synchronization]({{< relref "#configure-team-synchronization" >}}). +1. Optional: [Configure group synchronization]({{< relref "#configure-group-synchronization" >}}). 1. Restart Grafana. You should now see a GitLab login button on the login page and be able to log in or sign up with your GitLab accounts. @@ -232,46 +242,53 @@ use_pkce = true use_refresh_token = true ``` -## Configure team synchronization +## Configure group synchronization -> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud/). +{{< admonition type="note" >}} +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). +{{< /admonition >}} -By using Team Sync, you can map GitLab groups to teams within Grafana. This will automatically assign users to the appropriate teams. -Teams for each user are synchronized when the user logs in. +Grafana supports synchronization of GitLab groups with Grafana teams and roles. This allows automatically assigning users to the appropriate teams or granting them the mapped roles. +Teams and roles get synchronized when the user logs in. GitLab groups are referenced by the group name. For example, `developers`. To reference a subgroup `frontend`, use `developers/frontend`. Note that in GitLab, the group or subgroup name does not always match its display name, especially if the display name contains spaces or special characters. Make sure you always use the group or subgroup name as it appears in the URL of the group or subgroup. -To learn more about Team Sync, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}). +To learn more about group synchronization, refer to [Configure team sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-team-sync) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync). ## Configuration options -The table below describes all GitLab OAuth configuration options. Like any other Grafana configuration, you can apply these options as environment variables. +The table below describes all GitLab OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -| `enabled` | Yes | Whether GitLab OAuth authentication is allowed. | `false` | -| `client_id` | Yes | Client ID provided by your GitLab OAuth app. | | -| `client_secret` | Yes | Client secret provided by your GitLab OAuth app. | | -| `auth_url` | Yes | Authorization endpoint of your GitLab OAuth provider. If you use your own instance of GitLab instead of gitlab.com, adjust `auth_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/authorize` | -| `token_url` | Yes | Endpoint used to obtain GitLab OAuth access token. If you use your own instance of GitLab instead of gitlab.com, adjust `token_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/token` | -| `api_url` | No | Grafana uses `/user` endpoint to obtain GitLab user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://gitlab.com/api/v4` | -| `name` | No | Name used to refer to the GitLab authentication in the Grafana user interface. | `GitLab` | -| `icon` | No | Icon used for GitLab authentication in the Grafana user interface. | `gitlab` | -| `scopes` | No | List of comma or space-separated GitLab OAuth scopes. | `openid email profile` | -| `allow_sign_up` | No | Whether to allow new Grafana user creation through GitLab login. If set to `false`, then only existing Grafana users can log in with GitLab OAuth. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the GitLab OAuth token. If no role is found, Grafana creates a JSON data with `groups` key that maps to groups obtained from GitLab's `/oauth/userinfo` endpoint, and evaluates the expression using this data. Finally, if a valid role is still not found, the expression is evaluated against the user information retrieved from `api_url/users` endpoint and groups retrieved from `api_url/groups` endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana role cannot be extracted using `role_attribute_path`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. | `false` | -| `allowed_domains` | No | List of comma or space-separated domains. User must belong to at least one domain to log in. | | -| `allowed_groups` | No | List of comma or space-separated groups. The user should be a member of at least one group to log in. | | -| `tls_skip_verify_insecure` | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | -| `tls_client_cert` | No | The path to the certificate. | | -| `tls_client_key` | No | The path to the key. | | -| `tls_client_ca` | No | The path to the trusted certificate authority list. | | -| `use_pkce` | No | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | -| `use_refresh_token` | No | Set to `true` to use refresh token and check access token expiration. The `accessTokenExpirationCheck` feature toggle should also be enabled to use refresh token. | `true` | +{{< admonition type="note" >}} +If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"` +{{< /admonition >}} + +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| `enabled` | Yes | Yes | Whether GitLab OAuth authentication is allowed. | `false` | +| `client_id` | Yes | Yes | Client ID provided by your GitLab OAuth app. | | +| `client_secret` | Yes | Yes | Client secret provided by your GitLab OAuth app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your GitLab OAuth provider. If you use your own instance of GitLab instead of gitlab.com, adjust `auth_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/authorize` | +| `token_url` | Yes | Yes | Endpoint used to obtain GitLab OAuth access token. If you use your own instance of GitLab instead of gitlab.com, adjust `token_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/token` | +| `api_url` | No | Yes | Grafana uses `/user` endpoint to obtain GitLab user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://gitlab.com/api/v4` | +| `name` | No | Yes | Name used to refer to the GitLab authentication in the Grafana user interface. | `GitLab` | +| `icon` | No | Yes | Icon used for GitLab authentication in the Grafana user interface. | `gitlab` | +| `scopes` | No | Yes | List of comma or space-separated GitLab OAuth scopes. | `openid email profile` | +| `allow_sign_up` | No | Yes | Whether to allow new Grafana user creation through GitLab login. If set to `false`, then only existing Grafana users can log in with GitLab OAuth. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the GitLab OAuth token. If no role is found, Grafana creates a JSON data with `groups` key that maps to groups obtained from GitLab's `/oauth/userinfo` endpoint, and evaluates the expression using this data. Finally, if a valid role is still not found, the expression is evaluated against the user information retrieved from `api_url/users` endpoint and groups retrieved from `api_url/groups` endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana role cannot be extracted using `role_attribute_path`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. | `false` | +| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `allowed_domains` | No | Yes | List of comma or space-separated domains. User must belong to at least one domain to log in. | | +| `allowed_groups` | No | Yes | List of comma or space-separated groups. The user should be a member of at least one group to log in. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | +| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. The `accessTokenExpirationCheck` feature toggle should also be enabled to use refresh token. | `true` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md index 338b95a7644..9e4740c6914 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md @@ -1,20 +1,20 @@ --- aliases: - ../../../auth/google/ -description: Grafana OAuthentication Guide +description: Grafana Google OAuth Guide labels: products: - cloud - enterprise - oss -menuTitle: Google OAuth2 -title: Configure Google OAuth2 authentication +menuTitle: Google OAuth +title: Configure Google OAuth authentication weight: 1100 --- -# Configure Google OAuth2 authentication +# Configure Google OAuth authentication -To enable Google OAuth2 you must register your application with Google. Google will generate a client ID and secret key for you to use. +To enable Google OAuth you must register your application with Google. Google will generate a client ID and secret key for you to use. {{% admonition type="note" %}} If Users use the same email address in Google that they use with other authentication providers (such as Grafana.com), you need to do additional configuration to ensure that the users are matched correctly. Please refer to the [Using the same email address to login with different identity providers]({{< relref "../../configure-authentication#using-the-same-email-address-to-login-with-different-identity-providers" >}}) documentation for more information. @@ -25,13 +25,21 @@ If Users use the same email address in Google that they use with other authentic First, you need to create a Google OAuth Client: 1. Go to https://console.developers.google.com/apis/credentials. +1. Create a new project if you don't have one already. + 1. Enter a project name. The **Organization** and **Location** fields should both be set to your organization's information. + 1. In **OAuth consent screen** select the **External** User Type. Click **CREATE**. + 1. Fill out the requested information using the URL of your Grafana Cloud instance. + 1. Accept the defaults, or customize the consent screen options. 1. Click **Create Credentials**, then click **OAuth Client ID** in the drop-down menu 1. Enter the following: - - Application Type: Web Application - - Name: Grafana - - Authorized JavaScript Origins: https://grafana.mycompany.com - - Authorized Redirect URLs: https://grafana.mycompany.com/login/google - - Replace https://grafana.mycompany.com with the URL of your Grafana instance. + - **Application Type**: Web application + - **Name**: Grafana + - **Authorized JavaScript origins**: `https://` + - **Authorized redirect URIs**: `https:///login/google` + - Replace `` with the URL of your Grafana instance. + {{< admonition type="note" >}} + The URL you enter is the one for your Grafana instance home page, not your Grafana Cloud portal URL. + {{< /admonition >}} 1. Click Create 1. Copy the Client ID and Client Secret from the 'OAuth Client' modal @@ -41,7 +49,7 @@ First, you need to create a Google OAuth Client: Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure Google OAuth2 client from within Grafana using the Google UI. To do this, navigate to **Administration > Authentication > Google** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. +As a Grafana Admin, you can configure Google OAuth client from within Grafana using the Google UI. To do this, navigate to **Administration > Authentication > Google** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. After you have filled in the form, click **Save**. If the save was successful, Grafana will apply the new configurations. @@ -130,7 +138,9 @@ introduces "proof key for code exchange" (PKCE) which provides additional protection against some forms of authorization code interception attacks. PKCE will be required in [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-03). -> You can disable PKCE in Grafana by setting `use_pkce` to `false` in the`[auth.google]` section. +{{% admonition type="note" %}} +You can disable PKCE in Grafana by setting `use_pkce` to `false` in the`[auth.google]` section. +{{% /admonition %}} #### Configure refresh token @@ -155,9 +165,15 @@ This setting is ignored if multiple auth providers are configured to use auto lo auto_login = true ``` -### Configure team sync for Google OAuth +### Configure group synchronization -With team sync, you can easily add users to teams by utilizing their Google groups. To set up team sync for Google OAuth, refer to the following example. +{{< admonition type="note" >}} +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). +{{< /admonition >}} + +Grafana supports syncing users to teams and roles based on their Google groups. + +To set up group sync for Google OAuth: 1. Enable the Google Cloud Identity API on your [organization's dashboard](https://console.cloud.google.com/apis/api/cloudidentity.googleapis.com/). @@ -171,10 +187,9 @@ With team sync, you can easily add users to teams by utilizing their Google grou scopes = openid email profile https://www.googleapis.com/auth/cloud-identity.groups.readonly ``` -1. Configure team sync in your Grafana team's `External group sync` tab. - The external group ID for a Google group is the group's email address, such as `dev@grafana.com`. +The external group ID for a Google group is the group's email address, such as `dev@grafana.com`. -To learn more about Team Sync, refer to [Configure Team Sync]({{< relref "../../configure-team-sync" >}}). +To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. #### Configure allowed groups @@ -183,7 +198,9 @@ to a comma or space separated list of groups. Google groups are referenced by the group email key. For example, `developers@google.com`. -> Note: Add the `https://www.googleapis.com/auth/cloud-identity.groups.readonly` scope to your Grafana `[auth.google]` scopes configuration to retrieve groups +{{% admonition type="note" %}} +Add the `https://www.googleapis.com/auth/cloud-identity.groups.readonly` scope to your Grafana `[auth.google]` scopes configuration to retrieve groups. +{{% /admonition %}} #### Configure role mapping @@ -196,8 +213,9 @@ If no valid role is found, the user is assigned the role specified by [the `auto You can disable this default role assignment by setting `role_attribute_strict = true`. This setting denies user access if no role or an invalid role is returned after evaluating the `role_attribute_path` and the `org_mapping` expressions. To ease configuration of a proper JMESPath expression, go to [JMESPath](http://jmespath.org/) to test and evaluate expressions with custom payloads. - -> By default skip_org_role_sync is enabled. skip_org_role_sync will default to false in Grafana v10.3.0 and later versions. +{{% admonition type="note" %}} +By default skip_org_role_sync is enabled. skip_org_role_sync will default to false in Grafana v10.3.0 and later versions. +{{% /admonition %}} ##### Role mapping examples @@ -237,7 +255,9 @@ role_attribute_path = contains(groups[*], 'example-group@google.com') && 'Editor skip_org_role_sync = false ``` -> Note: Add the `https://www.googleapis.com/auth/cloud-identity.groups.readonly` scope to your Grafana `[auth.google]` scopes configuration to retrieve groups +{{% admonition type="note" %}} +Add the `https://www.googleapis.com/auth/cloud-identity.groups.readonly` scope to your Grafana `[auth.google]` scopes configuration to retrieve groups. +{{% /admonition %}} ###### Map server administrator role @@ -258,3 +278,39 @@ In this example, all users will be assigned `Viewer` role regardless of the user role_attribute_path = "'Viewer'" skip_org_role_sync = false ``` + +## Configuration options + +The following table outlines the various Google OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). + +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | +| `enabled` | No | Yes | Enables Google authentication. | `false` | +| `name` | No | Yes | Name that refers to the Google authentication from the Grafana user interface. | `Google` | +| `icon` | No | Yes | Icon used for the Google authentication in the Grafana user interface. | `google` | +| `client_id` | Yes | Yes | Client ID of the App. | | +| `client_secret` | Yes | Yes | Client secret of the App. | | +| `auth_url` | Yes | Yes | Authorization endpoint of the Google OAuth provider. | `https://accounts.google.com/o/oauth2/v2/auth` | +| `token_url` | Yes | Yes | Endpoint used to obtain the OAuth2 access token. | `https://oauth2.googleapis.com/token` | +| `api_url` | Yes | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://openidconnect.googleapis.com/v1/userinfo` | +| `auth_style` | No | Yes | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` | +| `scopes` | No | Yes | List of comma- or space-separated OAuth2 scopes. | `openid email profile` | +| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Google login. Only existing Grafana users can log in with Google if set to `false`. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `hosted_domain` | No | Yes | Specifies the domain to restrict access to users from that domain. This value is appended to the authorization request using the `hd` parameter. | | +| `validate_hd` | No | Yes | Set to `false` to disable the validation of the `hd` parameter from the Google ID token. For more informatiion, refer to [Enable Google OAuth in Grafana]({{< relref "#enable-google-oauth-in-grafana" >}}). | `true` | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `allow_assign_grafana_admin` | No | No | Set to `true` to automatically sync the Grafana server administrator role. When enabled, if the Google user's App role is `GrafanaAdmin`, Grafana grants the user server administrator privileges and the organization administrator role. If disabled, the user will only receive the organization administrator role. For more details on user role mapping, refer to [Map roles]({{< relref "#map-roles" >}}). | `false` | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | +| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure Google to include the `groups` claim following [Configure allowed groups]({{< relref "#configure-allowed-groups" >}}). | | +| `allowed_organizations` | No | Yes | List of comma- or space-separated Azure tenant identifiers. The user should be a member of at least one tenant to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | +| `use_refresh_token` | No | Yes | Enables the use of refresh tokens and checks for access token expiration. When enabled, Grafana automatically adds the `promp=consent` and `access_type=offline` parameters to the authorization request. | `true` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/grafana/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/grafana/index.md index 3d74d1bd6fe..c0d97fcbb16 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/grafana/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/grafana/index.md @@ -1,127 +1,32 @@ --- aliases: - ../../../auth/grafana/ -description: Grafana OAuthentication Guide +description: Learn how to configure basic authentication in Grafana labels: products: - enterprise - oss menuTitle: Basic auth -title: Configure Grafana authentication +title: Configure basic authentication weight: 200 --- -## Configure Grafana authentication +# Configure basic authentication -Grafana of course has a built in user authentication system with password authentication enabled by default. You can -disable authentication by enabling anonymous access. You can also hide login form and only allow login through an auth -provider (listed above). There is also options for allowing self sign up. +Grafana provides a basic authentication system with password authentication enabled by default. This document details configuration options to manage and enhance basic authentication. -### Login and short-lived tokens +## Disable basic authentication -> The following applies when using Grafana's built in user authentication, LDAP (without Auth proxy) or OAuth integration. - -Grafana uses short-lived tokens as a mechanism for verifying authenticated users. -These short-lived tokens are rotated each `token_rotation_interval_minutes` for an active authenticated user. - -An active authenticated user that gets it token rotated will extend the `login_maximum_inactive_lifetime_duration` time from "now" that Grafana will remember the user. -This means that a user can close its browser and come back before `now + login_maximum_inactive_lifetime_duration` and still being authenticated. -This is true as long as the time since user login is less than `login_maximum_lifetime_duration`. - -#### Remote logout - -You can logout from other devices by removing login sessions from the bottom of your profile page. If you are -a Grafana admin user you can also do the same for any user from the Server Admin / Edit User view. - -## Settings - -Example: - -```bash -[auth] - -# Login cookie name -login_cookie_name = grafana_session - -# The lifetime (days) an authenticated user can be inactive before being required to login at next visit. Default is 7 days. -login_maximum_inactive_lifetime_duration = 7d - -# The maximum lifetime (days) an authenticated user can be logged in since login time before being required to login. Default is 30 days. -login_maximum_lifetime_duration = 30d - -# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes. -token_rotation_interval_minutes = 10 - -# The maximum lifetime (seconds) an api key can be used. If it is set all the api keys should have limited lifetime that is lower than this value. -api_key_max_seconds_to_live = -1 -``` - -### Anonymous authentication - -You can make Grafana accessible without any login required by enabling anonymous access in the configuration file. For more information, refer to [Anonymous authentication]({{< relref "../../configure-authentication#anonymous-authentication" >}}). - -#### Anonymous devices - -The anonymous devices feature enhances the management and monitoring of anonymous access within your Grafana instance. This feature is part of ongoing efforts to provide more control and transparency over anonymous usage. - -Users can now view anonymous usage statistics, including the count of devices and users over the last 30 days. - -- Go to **Administration -> Users** to access the anonymous devices tab. -- A new stat for the usage stats page -> Usage & Stats page shows the active anonymous devices last 30 days. - -The number of anonymous devices is not limited by default. The configuration option `device_limit` allows you to enforce a limit on the number of anonymous devices. This enables you to have greater control over the usage within your Grafana instance and keep the usage within the limits of your environment. Once the limit is reached, any new devices that try to access Grafana will be denied access. - -To display anonymous users and devices for versions 10.2, 10.3, 10.4, you need to enable the feature toggle `displayAnonymousStats` - -```bash -[feature_toggles] -enable = displayAnonymousStats -``` - -#### Anonymous users - -{{< admonition type="note" >}} -Anonymous users are charged as active users in Grafana Enterprise -{{< /admonition >}} - -#### Configuration - -Example: - -```bash -[auth.anonymous] -enabled = true - -# Organization name that should be used for unauthenticated users -org_name = Main Org. - -# Role for unauthenticated users, other valid values are `Editor` and `Admin` -org_role = Viewer - -# Hide the Grafana version text from the footer and help tooltip for unauthenticated users (default: false) -hide_version = true - -# Setting this limits the number of anonymous devices in your instance. Any new anonymous devices added after the limit has been reached will be denied access. -device_limit = -``` - -If you change your organization name in the Grafana UI this setting needs to be updated to match the new name. - -### Basic authentication - -Basic auth is enabled by default and works with the built in Grafana user password authentication system and LDAP -authentication integration. - -To disable basic auth: +To disable basic authentication, use the following configuration: ```bash [auth.basic] enabled = false ``` -### Strong password policy +## Password policy -By default, the password policy for all basic auth users is set to a minimum of four characters. You can enable a stronger password policy with the `password_policy` configuration option. +By default, Grafana’s password policy requires a minimum of four characters for basic auth users. For a stronger password policy, enable the `password_policy` configuration option. With the `password_policy` option enabled, new and updated passwords must meet the following criteria: @@ -137,63 +42,16 @@ password_policy = true ``` {{% admonition type="note" %}} -Existing passwords that don't comply with the new password policy will not be impacted until the user updates their password. +Existing passwords that do not comply with the new password policy will not be affected until the user updates their password. {{% /admonition %}} -### Disable login form +## Disable login form -You can hide the Grafana login form using the below configuration settings. +To hide the Grafana login form, use the following configuration setting: ```bash [auth] disable_login_form = true ``` -### Automatic OAuth login - -Set to true to attempt login with specific OAuth provider automatically, skipping the login screen. -This setting is ignored if multiple auth providers are configured to use auto login. -Defaults to `false`. - -```bash -[auth.generic_oauth] -auto_login = true -``` - -### Hide sign-out menu - -Set the option detailed below to true to hide sign-out menu link. Useful if you use an auth proxy or JWT authentication. - -```bash -[auth] -disable_signout_menu = true -``` - -### URL redirect after signing out - -The URL to redirect the user to after signing out from Grafana can be configured under `[auth]` or under a specific OAuth provider section (for example, `[auth.generic_oauth]`). The URL configured under a specific OAuth provider section takes precedence over the URL configured in `[auth]` section. This can, for example, enable signout from the OAuth provider. - -```bash -[auth.generic_oauth] -signout_redirect_url = - -[auth] -signout_redirect_url = -``` - -### Protected roles - -{{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud]({{< relref "../../../../introduction/grafana-cloud" >}}). -{{% /admonition %}} - -By default, after you configure an authorization provider, Grafana will adopt existing users into the new authentication scheme. For example, if you have created a user with basic authentication having the login `jsmith@example.com`, then set up SAML authentication where `jsmith@example.com` is an account, the user's authentication type will be changed to SAML if they perform a SAML sign-in. - -You can disable this user adoption for certain roles using the `protected_roles` property: - -```bash -[auth.security] -protected_roles = server_admins org_admins -``` - -The value of `protected_roles` should be a list of roles to protect, separated by spaces. Valid roles are `viewers`, `editors`, `org_admins`, `server_admins`, and `all` (a superset of the other roles). +This can be helpful in setups where authentication is handled entirely through external mechanisms or single sign-on (SSO). diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md index c8ed2b24a89..beffabd14f7 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md @@ -23,6 +23,10 @@ This method of authentication is useful for integrating with other systems that use JWKS but can't directly integrate with Grafana or if you want to use pass-through authentication in an app embedding Grafana. +{{% admonition type="note" %}} +Grafana does not currently support refresh tokens. +{{% /admonition %}} + ## Enable JWT To use JWT authentication: @@ -183,7 +187,8 @@ key_id = my-key-id By default, only `"exp"`, `"nbf"` and `"iat"` claims are validated. -You might also want to validate that other claims are really what you expect them to be. +Consider validating that other claims match your expectations by using the `expect_claims` configuration option. +Token claims must match exactly the values set here. ```ini # This can be seen as a required "subset" of a JWT Claims Set. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md index ebf55c834dc..323e6e1614d 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/keycloak/ -description: Keycloak Grafana OAuthentication Guide +description: Grafana Keycloak Guide keywords: - grafana - keycloak @@ -94,7 +94,7 @@ roles ``` {{% admonition type="warning" %}} -these scopes do not add group claims to the id_token. Without group claims, teamsync will not work. Teamsync is covered further down in this document. +These scopes do not add group claims to the id_token. Without group claims, group synchronization will not work. Group synchronization is covered further down in this document. {{% /admonition %}} 3. For role mapping to work with the example configuration above, @@ -106,16 +106,18 @@ editor viewer ``` -## Teamsync +## Group synchronization -{{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud/). -{{% /admonition %}} +{{< admonition type="note" >}} +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). +{{< /admonition >}} -[Teamsync]({{< relref "../../configure-team-sync" >}}) is a feature that allows you to map groups from your identity provider to Grafana teams. This is useful if you want to give your users access to specific dashboards or folders based on their group membership. +By using group synchronization, you can link your Keycloak groups to teams and roles within Grafana. This allows automatically assigning users to the appropriate teams or granting them the mapped roles. +This is useful if you want to give your users access to specific resources based on their group membership. +Teams and roles get synchronized when the user logs in. -To enable teamsync, you need to add a `groups` mapper to the client configuration in Keycloak. -This will add the `groups` claim to the id_token. You can then use the `groups` claim to map groups to teams in Grafana. +To enable group synchronization, you need to add a `groups` mapper to the client configuration in Keycloak. +This will add the `groups` claim to the id_token. You can then use the `groups` claim to map groups to teams and roles in Grafana. 1. In the client configuration, head to `Mappers` and create a mapper with the following settings: @@ -141,6 +143,8 @@ If you use nested groups containing special characters such as quotes or colons, groups_attribute_path = reverse("Global:department") ``` +To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. + ## Enable Single Logout To enable Single Logout, you need to add the following option to the configuration of Grafana: diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md index 2aaef56a35c..39d77ca3f7f 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md @@ -49,7 +49,7 @@ To follow this guide, ensure you have permissions in your Okta workspace to crea - **API URL** For example: https://.okta.com/oauth2/v1/userinfo -### Configure Okta to Grafana Cloud role mapping +### Configure Okta to Grafana role mapping 1. In the **Okta Admin Console**, select **Directory > Profile Editor**. 1. Select the Okta Application Profile you created previously (the default name for this is ` User`). @@ -82,6 +82,10 @@ To follow this guide, ensure you have permissions in your Okta workspace to crea 1. Include the `groups` scope in the **Scopes** field in Grafana of the Okta integration. For Terraform or in the Grafana configuration file, include the `groups` scope in `scopes` field. +{{% admonition type="note" %}} +If you configure the `groups` claim differently, ensure that the `groups` claim is a string array. +{{% /admonition %}} + #### Optional: Add the role attribute to the User (default) Okta profile If you want to configure the role for all users in the Okta directory, you can add the role attribute to the User (default) Okta profile. @@ -165,7 +169,7 @@ To integrate your Okta OIDC provider with Grafana using our Okta OIDC integratio 1. Optional: [Configure a refresh token]({{< relref "#configure-a-refresh-token" >}}). 1. [Configure role mapping]({{< relref "#configure-role-mapping" >}}). -1. Optional: [Configure team synchronization]({{< relref "#configure-team-synchronization-enterprise-only" >}}). +1. Optional: [Configure group synchronization]({{< relref "#configure-group-synchronization-enterprise-only" >}}). 1. Restart Grafana. You should now see a Okta OIDC login button on the login page and be able to log in or sign up with your OIDC provider. @@ -204,7 +208,9 @@ At the configuration file, extend the `scopes` in `[auth.okta]` section with `of ### Configure role mapping -> **Note:** Unless `skip_org_role_sync` option is enabled, the user's role will be set to the role retrieved from the auth provider upon user login. +{{% admonition type="note" %}} +Unless `skip_org_role_sync` option is enabled, the user's role will be set to the role retrieved from the auth provider upon user login. +{{% /admonition %}} The user's role is retrieved using a [JMESPath](http://jmespath.org/examples.html) expression from the `role_attribute_path` configuration option against the `api_url` (`/userinfo` OIDC endpoint) endpoint payload. @@ -224,6 +230,10 @@ To learn about adding custom claims to the user info in Okta, refer to [add cust #### Org roles mapping example +{{% admonition type="note" %}} +Available in on-premise Grafana installations. +{{% /admonition %}} + In this example, the `org_mapping` uses the `groups` attribute as the source (`org_attribute_path`) to map the current user to different organizations and roles. The user has been granted the role of a `Viewer` in the `org_foo` org if they are a member of the `Group 1` group, the role of an `Editor` in the `org_bar` org if they are a member of the `Group 2` group, and the role of an `Editor` in the `org_baz`(OrgID=3) org. Config: @@ -233,42 +243,47 @@ org_attribute_path = groups org_mapping = ["Group 1:org_foo:Viewer", "Group 2:org_bar:Editor", "*:3:Editor"] ``` -### Configure team synchronization (Enterprise only) +### Configure group synchronization (Enterprise only) -> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud]({{< relref "../../../../introduction/grafana-cloud" >}}). +{{< admonition type="note" >}} +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). +{{< /admonition >}} -By using Team Sync, you can link your Okta groups to teams within Grafana. This will automatically assign users to the appropriate teams. - -Map your Okta groups to teams in Grafana so that your users will automatically be added to -the correct teams. +By using group synchronization, you can link your Okta groups to teams and roles within Grafana. This allows automatically assigning users to the appropriate teams or granting them the mapped roles. +Teams and roles get synchronized when the user logs in. Okta groups can be referenced by group names, like `Admins` or `Editors`. -To learn more about Team Sync, refer to [Configure Team Sync]({{< relref "../../configure-team-sync" >}}). +To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. ## Configuration options -The following table outlines the various Okta OIDC configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. +The following table outlines the various Okta OIDC configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | -| `enabled` | No | Enables Okta OIDC authentication. | `false` | -| `name` | No | Name that refers to the Okta OIDC authentication from the Grafana user interface. | `Okta` | -| `icon` | No | Icon used for the Okta OIDC authentication in the Grafana user interface. | `okta` | -| `client_id` | Yes | Client ID provided by your Okta OIDC app. | | -| `client_secret` | Yes | Client secret provided by your Okta OIDC app. | | -| `auth_url` | Yes | Authorization endpoint of your Okta OIDC provider. | | -| `token_url` | Yes | Endpoint used to obtain the Okta OIDC access token. | | -| `api_url` | Yes | Endpoint used to obtain user information. | | -| `scopes` | No | List of comma- or space-separated Okta OIDC scopes. | `openid profile email groups` | -| `allow_sign_up` | No | Controls Grafana user creation through the Okta OIDC login. Only existing Grafana users can log in with Okta OIDC if set to `false`. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the Okta OIDC ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `org_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | -| `allowed_groups` | No | List of comma- or space-separated groups. The user should be a member of at least one group to log in. | | -| `allowed_domains` | No | List comma- or space-separated domains. The user should belong to at least one domain to log in. | | -| `use_pkce` | No | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | -| `use_refresh_token` | No | Set to `true` to use refresh token and check access token expiration. | `false` | +{{< admonition type="note" >}} +If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"` +{{< /admonition >}} + +| Setting | Required | Supported on Cloud | Description | Default | +| ----------------------- | -------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | +| `enabled` | No | Yes | Enables Okta OIDC authentication. | `false` | +| `name` | No | Yes | Name that refers to the Okta OIDC authentication from the Grafana user interface. | `Okta` | +| `icon` | No | Yes | Icon used for the Okta OIDC authentication in the Grafana user interface. | `okta` | +| `client_id` | Yes | Yes | Client ID provided by your Okta OIDC app. | | +| `client_secret` | Yes | Yes | Client secret provided by your Okta OIDC app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your Okta OIDC provider. | | +| `token_url` | Yes | Yes | Endpoint used to obtain the Okta OIDC access token. | | +| `api_url` | Yes | Yes | Endpoint used to obtain user information. | | +| `scopes` | No | Yes | List of comma- or space-separated Okta OIDC scopes. | `openid profile email groups` | +| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Okta OIDC login. Only existing Grafana users can log in with Okta OIDC if set to `false`. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the Okta OIDC ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | +| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | +| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. | `false` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md index b3baddc683b..1be5e2d993e 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml-ui/index.md @@ -87,6 +87,8 @@ Sign in to Grafana and navigate to **Administration > Authentication > Configure For more information, refer to an [example on how to generate SAML credentials]({{< relref "../saml#generate-private-key-for-saml-authentication" >}}). + Alternatively, you can generate a new private key and certificate pair directly from the UI. Click on the `Generate key and certificate` button to open a form where you enter some information you want to be embedded into the new certificate. + 1. Choose which signature algorithm should be used. The SAML standard recommends using a digital signature for some types of messages, like authentication or logout requests to avoid [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). @@ -111,8 +113,8 @@ Sign in to Grafana and navigate to **Administration > Authentication > Configure 1. If you wish to [map user information from SAML assertions]({{< relref "../saml#assertion-mapping" >}}), complete the **Assertion attributes mappings** section. - You also need to configure the **Groups attribute** field if you want to use team sync. Team sync automatically maps users to Grafana teams based on their SAML group membership. - Learn more about [team sync]({{< relref "../../configure-team-sync" >}}) and [configuring team sync for SAML]({{< relref "../saml#configure-team-sync" >}}). + You also need to configure the **Groups attribute** field if you want to use group synchronization. Group sync allows you to automatically map users to Grafana teams or role-based access control roles based on their SAML group membership. + To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. 1. If you want to automatically assign users' roles based on their SAML roles, complete the **Role mapping** section. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/index.md index ca30f1e473f..230edd20cb4 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/index.md @@ -93,6 +93,7 @@ Related links: - [`assertion_attribute_email`]({{< relref "../../../configure-grafana/enterprise-configuration#assertion_attribute_email" >}}) - [`assertion_attribute_name`]({{< relref "../../../configure-grafana/enterprise-configuration#assertion_attribute_name" >}}) - [`assertion_attribute_groups`]({{< relref "../../../configure-grafana/enterprise-configuration#assertion_attribute_groups" >}}) +1. (Optional) Set the `name` parameter in the `[auth.saml]` section in the Grafana configuration file. This parameter replaces SAML in the Grafana user interface in locations such as the sign-in button. 1. Save the configuration file and then restart the Grafana server. When you are finished, the Grafana configuration might look like this example: @@ -103,6 +104,7 @@ root_url = https://grafana.example.com [auth.saml] enabled = true +name = My IdP auto_login = false private_key_path = "/path/to/private_key.pem" certificate_path = "/path/to/certificate.cert" @@ -282,68 +284,31 @@ Grafana supports user authentication through Okta, which is useful when you want 1. Log in to the [Okta portal](https://login.okta.com/). 1. Go to the Admin Console in your Okta organization by clicking **Admin** in the upper-right corner. If you are in the Developer Console, then click **Developer Console** in the upper-left corner and then click **Classic UI** to switch over to the Admin Console. 1. In the Admin Console, navigate to **Applications** > **Applications**. -1. Click **Add Application**. -1. Click **Create New App** to start the Application Integration Wizard. -1. Choose **Web** as a platform. -1. Select **SAML 2.0** in the Sign on method section. +1. Click **Create App Integration** to start the Application Integration Wizard. +1. Choose **SAML 2.0** as the **Sign-in method**. 1. Click **Create**. 1. On the **General Settings** tab, enter a name for your Grafana integration. You can also upload a logo. 1. On the **Configure SAML** tab, enter the SAML information related to your Grafana instance: - In the **Single sign on URL** field, use the `/saml/acs` endpoint URL of your Grafana instance, for example, `https://grafana.example.com/saml/acs`. - - In the **Audience URI (SP Entity ID)** field, use the `/saml/metadata` endpoint URL, for example, `https://grafana.example.com/saml/metadata`. + - In the **Audience URI (SP Entity ID)** field, use the `/saml/metadata` endpoint URL, by default it is the `/saml/metadata` endpoint of your Grafana instance (for example `https://example.grafana.com/saml/metadata`). This could be configured differently, but the value here must match the `entity_id` setting of the SAML settings of Grafana. - Leave the default values for **Name ID format** and **Application username**. + {{% admonition type="note" %}} + If you plan to enable SAML Single Logout, consider setting the **Name ID format** to `EmailAddress` or `Persistent`. This must match the `name_id_format` setting of the Grafana instance. + {{% /admonition %}} - In the **ATTRIBUTE STATEMENTS (OPTIONAL)** section, enter the SAML attributes to be shared with Grafana. The attribute names in Okta need to match exactly what is defined within Grafana, for example: - | Attribute name (in Grafana) | Name and value (in Okta profile) | - | --------------------------- | -------------------------------------------------- | - | Login | Login `user.login` | - | Email | Email `user.email` | - | DisplayName | DisplayName `user.firstName + " " + user.lastName` | + | Attribute name (in Grafana) | Name and value (in Okta profile) | Grafana configuration (under `auth.saml`) | + | --------------------------- | ---------------------------------------------------- | ----------------------------------------- | + | Login | Login - `user.login` | `assertion_attribute_login = Login` | + | Email | Email - `user.email` | `assertion_attribute_email = Email` | + | DisplayName | DisplayName - `user.firstName + " " + user.lastName` | `assertion_attribute_name = DisplayName` | - - In the **GROUP ATTRIBUTE STATEMENTS (OPTIONAL)** section, enter a group attribute name (for example, `Group`) and set filter to `Matches regex .*` to return all user groups. + - In the **GROUP ATTRIBUTE STATEMENTS (OPTIONAL)** section, enter a group attribute name (for example, `Group`, ensure it matches the `asssertion_attribute_groups` setting in Grafana) and set filter to `Matches regex .*` to return all user groups. 1. Click **Next**. 1. On the final Feedback tab, fill out the form and then click **Finish**. -## Configure SAML authentication in Grafana - -The table below describes all SAML configuration options. Continue reading below for details on specific options. Like any other Grafana configuration, you can apply these options as [environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). - -| Setting | Required | Description | Default | -| ---------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | -| `enabled` | No | Whether SAML authentication is allowed | `false` | -| `name` | No | Name used to refer to the SAML authentication in the Grafana user interface. | `SAML` | -| `single_logout` | No | Whether SAML Single Logout enabled | `false` | -| `allow_sign_up` | No | Whether to allow new Grafana user creation through SAML login. If set to `false`, then only existing Grafana users can log in with SAML. | `true` | -| `auto_login` | No | Whether SAML auto login is enabled | `false` | -| `allow_idp_initiated` | No | Whether SAML IdP-initiated login is allowed | `false` | -| `certificate` or `certificate_path` | Yes | Base64-encoded string or Path for the SP X.509 certificate | | -| `private_key` or `private_key_path` | Yes | Base64-encoded string or Path for the SP private key | | -| `signature_algorithm` | No | Signature algorithm used for signing requests to the IdP. Supported values are rsa-sha1, rsa-sha256, rsa-sha512. | | -| `idp_metadata`, `idp_metadata_path`, or `idp_metadata_url` | Yes | Base64-encoded string, Path or URL for the IdP SAML metadata XML | | -| `max_issue_delay` | No | Duration, since the IdP issued a response and the SP is allowed to process it | `90s` | -| `metadata_valid_duration` | No | Duration, for how long the SP metadata is valid | `48h` | -| `relay_state` | No | Relay state for IdP-initiated login. Should match relay state configured in IdP | | -| `assertion_attribute_name` | No | Friendly name or name of the attribute within the SAML assertion to use as the user name. Alternatively, this can be a template with variables that match the names of attributes within the SAML assertion. | `displayName` | -| `assertion_attribute_login` | No | Friendly name or name of the attribute within the SAML assertion to use as the user login handle | `mail` | -| `assertion_attribute_email` | No | Friendly name or name of the attribute within the SAML assertion to use as the user email | `mail` | -| `assertion_attribute_groups` | No | Friendly name or name of the attribute within the SAML assertion to use as the user groups | | -| `assertion_attribute_role` | No | Friendly name or name of the attribute within the SAML assertion to use as the user roles | | -| `assertion_attribute_org` | No | Friendly name or name of the attribute within the SAML assertion to use as the user organization | | -| `allowed_organizations` | No | List of comma- or space-separated organizations. User should be a member of at least one organization to log in. | | -| `org_mapping` | No | List of comma- or space-separated Organization:OrgId:Role mappings. Organization can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. | | -| `role_values_none` | No | List of comma- or space-separated roles which will be mapped into the None role | | -| `role_values_viewer` | No | List of comma- or space-separated roles which will be mapped into the Viewer role | | -| `role_values_editor` | No | List of comma- or space-separated roles which will be mapped into the Editor role | | -| `role_values_admin` | No | List of comma- or space-separated roles which will be mapped into the Admin role | | -| `role_values_grafana_admin` | No | List of comma- or space-separated roles which will be mapped into the Grafana Admin (Super Admin) role | | -| `name_id_format` | No | The Name ID Format to request within the SAML assertion | `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | -| `client_id` | No | Client ID of the IdP service application used to retrieve more information about the user from the IdP. | | -| `client_secret` | No | Client secret of the IdP service application used to retrieve more information about the user from the IdP. | | -| `token_url` | No | URL to retrieve the access token from the IdP. | | -| `force_use_graph_api` | No | Whether to use the IdP service application retrieve more information about the user from the IdP. | `false` | - ### Signature algorithm The SAML standard recommends using a digital signature for some types of messages, like authentication or logout requests. If the `signature_algorithm` option is configured, Grafana will put a digital signature into SAML requests. Supported signature types are `rsa-sha1`, `rsa-sha256`, `rsa-sha512`. This option should match your IdP configuration, otherwise, signature validation will fail. Grafana uses key and certificate configured with `private_key` and `certificate` options for signing SAML requests. @@ -356,10 +321,13 @@ By default, this is set to `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` The following list includes valid configuration field values: -- `urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified` -- `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` -- `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` -- `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent` +| `name_id_format` value in the configuration file or Terraform | `Name identifier format` on the UI | +| ------------------------------------------------------------- | ---------------------------------- | +| `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | Default | +| `urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified` | Unspecified | +| `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` | Email address | +| `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent` | Persistent | +| `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | Transient | ### IdP metadata @@ -442,12 +410,15 @@ This setting is ignored if multiple auth providers are configured to use auto lo auto_login = true ``` -### Configure team sync +### Configure group synchronization -To use SAML Team sync, set [`assertion_attribute_groups`]({{< relref "../../../configure-grafana/enterprise-configuration#assertion_attribute_groups" >}}) to the attribute name where you store user groups. Then Grafana will use attribute values extracted from SAML assertion to add user into the groups with the same name configured on the External group sync tab. +Group synchronization allows you to map user groups from an identity provider to Grafana teams and roles. + +To use SAML group synchronization, set [`assertion_attribute_groups`]({{< relref "../../../configure-grafana/enterprise-configuration#assertion_attribute_groups" >}}) to the attribute name where you store user groups. +Then Grafana will use attribute values extracted from SAML assertion to add user to Grafana teams and grant them roles. {{% admonition type="note" %}} -Teamsync allows you sync users from SAML to Grafana teams. It does not automatically create teams in Grafana. You need to create teams in Grafana before you can use this feature. +Team sync allows you sync users from SAML to Grafana teams. It does not automatically create teams in Grafana. You need to create teams in Grafana before you can use this feature. {{% /admonition %}} Given the following partial SAML assertion: @@ -477,12 +448,12 @@ The configuration would look like this: assertion_attribute_groups = groups ``` -The following `External Group ID`s would be valid for input in the desired team's _External group sync_ tab: +The following `External Group ID`s would be valid for configuring team sync or role sync in Grafana: - `admins_group` - `division_1` -[Learn more about Team Sync]({{< relref "../../configure-team-sync" >}}) +To learn more about how to configure group synchronization, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}) and [Configure group attribute sync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-group-attribute-sync) documentation. ### Configure role sync @@ -719,3 +690,43 @@ cookie_secure = true ``` Ensure cookie_secure is set to true to ensure that cookies are only sent over HTTPS. + +## Configure SAML authentication in Grafana + +The table below describes all SAML configuration options. Continue reading below for details on specific options. Like any other Grafana configuration, you can apply these options as [environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). + +| Setting | Required | Description | Default | +| ---------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | +| `enabled` | No | Whether SAML authentication is allowed. | `false` | +| `name` | No | Name used to refer to the SAML authentication in the Grafana user interface. | `SAML` | +| `entity_id` | No | The entity ID of the service provider. This is the unique identifier of the service provider. | `https://{Grafana URL}/saml/metadata` | +| `single_logout` | No | Whether SAML Single Logout is enabled. | `false` | +| `allow_sign_up` | No | Whether to allow new Grafana user creation through SAML login. If set to `false`, then only existing Grafana users can log in with SAML. | `true` | +| `auto_login` | No | Whether SAML auto login is enabled. | `false` | +| `allow_idp_initiated` | No | Whether SAML IdP-initiated login is allowed. | `false` | +| `certificate` or `certificate_path` | Yes | Base64-encoded string or Path for the SP X.509 certificate. | | +| `private_key` or `private_key_path` | Yes | Base64-encoded string or Path for the SP private key. | | +| `signature_algorithm` | No | Signature algorithm used for signing requests to the IdP. Supported values are rsa-sha1, rsa-sha256, rsa-sha512. | | +| `idp_metadata`, `idp_metadata_path`, or `idp_metadata_url` | Yes | Base64-encoded string, Path or URL for the IdP SAML metadata XML. | | +| `max_issue_delay` | No | Maximum time allowed between the issuance of an AuthnRequest by the SP and the processing of the Response. | `90s` | +| `metadata_valid_duration` | No | Duration for which the SP metadata remains valid. | `48h` | +| `relay_state` | No | Relay state for IdP-initiated login. This should match the relay state configured in the IdP. | | +| `assertion_attribute_name` | No | Friendly name or name of the attribute within the SAML assertion to use as the user name. Alternatively, this can be a template with variables that match the names of attributes within the SAML assertion. | `displayName` | +| `assertion_attribute_login` | No | Friendly name or name of the attribute within the SAML assertion to use as the user login handle. | `mail` | +| `assertion_attribute_email` | No | Friendly name or name of the attribute within the SAML assertion to use as the user email. | `mail` | +| `assertion_attribute_groups` | No | Friendly name or name of the attribute within the SAML assertion to use as the user groups. | | +| `assertion_attribute_role` | No | Friendly name or name of the attribute within the SAML assertion to use as the user roles. | | +| `assertion_attribute_org` | No | Friendly name or name of the attribute within the SAML assertion to use as the user organization | | +| `allowed_organizations` | No | List of comma- or space-separated organizations. User should be a member of at least one organization to log in. | | +| `org_mapping` | No | List of comma- or space-separated Organization:OrgId:Role mappings. Organization can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. | | +| `role_values_none` | No | List of comma- or space-separated roles which will be mapped into the None role. | | +| `role_values_viewer` | No | List of comma- or space-separated roles which will be mapped into the Viewer role. | | +| `role_values_editor` | No | List of comma- or space-separated roles which will be mapped into the Editor role. | | +| `role_values_admin` | No | List of comma- or space-separated roles which will be mapped into the Admin role. | | +| `role_values_grafana_admin` | No | List of comma- or space-separated roles which will be mapped into the Grafana Admin (Super Admin) role. | | +| `skip_org_role_sync` | No | Whether to skip organization role synchronization. | `false` | +| `name_id_format` | No | Specifies the format of the requested NameID element in the SAML AuthnRequest. | `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` | +| `client_id` | No | Client ID of the IdP service application used to retrieve more information about the user from the IdP. (Microsoft Entra ID only) | | +| `client_secret` | No | Client secret of the IdP service application used to retrieve more information about the user from the IdP. (Microsoft Entra ID only) | | +| `token_url` | No | URL to retrieve the access token from the IdP. (Microsoft Entra ID only) | | +| `force_use_graph_api` | No | Whether to use the IdP service application retrieve more information about the user from the IdP. (Microsoft Entra ID only) | `false` | diff --git a/docs/sources/setup-grafana/configure-security/configure-group-attribute-sync.md b/docs/sources/setup-grafana/configure-security/configure-group-attribute-sync.md new file mode 100644 index 00000000000..0ebe9f3db6c --- /dev/null +++ b/docs/sources/setup-grafana/configure-security/configure-group-attribute-sync.md @@ -0,0 +1,89 @@ +--- +description: Learn how to use group attribute sync to synchronize between groups in your authentication provider and Grafana RBAC roles. +labels: + products: + - cloud + - enterprise +title: Configure group attribute sync +weight: 1000 +--- + +# Configure group attribute sync + +Group attribute sync allows you to manage user permissions in Grafana based on group membership sourced from the user's identity provider (IdP). +Groups are mapped to [fixed](https://grafana.com/docs/grafana//administration/roles-and-permissions/access-control#fixed-roles) and [custom](https://grafana.com/docs/grafana//administration/roles-and-permissions/access-control#custom-roles) role-based access control roles in Grafana. + +> **Note:** Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud/). + +{{% admonition type="note" %}} +This feature is behind the `groupAttributeSync` feature toggle. +You can enable feature toggles through configuration file or environment variables. See configuration [docs](/docs/grafana//setup-grafana/configure-grafana/#feature_toggles) for details. +{{% /admonition %}} + +When a user logs in, Grafana checks the user's external group memberships and the configured group to role mappings to assign the corresponding roles to the user. +If the user's group memberships change or a new mapping is created, the user's role assignments are updated the next time the user logs in. +If a group mapping is removed, the role assignment to users for the group mapping is revoked immediately. + +Role mappings are tied to organizations, so you can have different mappings for different organizations. + +## Supported providers + +- [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) + +## Create role mappings for a new group + +For information about creating group mappings via the API, refer to [create group mappings reference](https://grafana.com/docs/grafana//developers/http_api/group_attribute_sync#create-group-mappings). + +### Before you begin + +Ensure you have permission to create and update group mappings. By default, the organization administrator role is required to create and edit group mappings. For more information about user permissions, refer to [roles and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions). + +### To create mappings between an external group and RBAC roles + +1. Sign in to Grafana and click **Administration** in the left-side menu. +1. Click **Users and access**. +1. Click **External group sync**. +1. Click **New**. +1. Insert the group identifier for the group that you want to map. +1. Use the role picker to select the roles that you want to map to the group and click **Update**. +1. Click **Save**. + +## Update role mappings for a group + +For information about updating group mappings via the API, refer to [update group mappings reference](https://grafana.com/docs/grafana//developers/http_api/group_attribute_sync#update-group-mappings). + +### Before you begin + +Ensure you have permission to update group mappings. By default, the organization administrator role is required to edit group mappings. For more information about user permissions, refer to [roles and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions). + +### To update role mappings for an external group + +1. Sign in to Grafana and click **Administration** in the left-side menu. +1. Click **Users and access**. +1. Click **External group sync**. +1. Find the group whose mappings you want to update. +1. Click on the role picker corresponding to the group and select the roles that you want to map. +1. Click **Apply**. + +## Remove role mappings for a group + +For information about deleting group mappings via the API, refer to [delete group mappings reference](https://grafana.com/docs/grafana//developers/http_api/group_attribute_sync#delete-group-mappings). + +### Before you begin + +Ensure you have permission to update group mappings. By default, the organization administrator role is required to edit group mappings. For more information about user permissions, refer to [roles and permissions](https://grafana.com/docs/grafana//administration/roles-and-permissions). + +### To remove role mappings for an external group + +1. Sign in to Grafana and click **Administration** in the left-side menu. +1. Click **Users and access**. +1. Click **External group sync**. +1. Find the group whose mappings you want to remove. +1. Click on the trash bin icon corresponding to the group mappings you want to remove. diff --git a/docs/sources/setup-grafana/configure-security/configure-request-security.md b/docs/sources/setup-grafana/configure-security/configure-request-security.md index 437786ed07f..3a5c393a22d 100644 --- a/docs/sources/setup-grafana/configure-security/configure-request-security.md +++ b/docs/sources/setup-grafana/configure-security/configure-request-security.md @@ -19,7 +19,7 @@ Request security allows you to limit requests from the Grafana server by targeti This can be used to limit access to internal systems that the server Grafana runs on can access but that users of Grafana should not be able to access. This feature does not affect traffic from the Grafana users browser. {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). {{% /admonition %}} {{% admonition type="note" %}} diff --git a/docs/sources/setup-grafana/configure-security/configure-team-sync.md b/docs/sources/setup-grafana/configure-security/configure-team-sync.md index dd506e4fc61..f5d50cff311 100644 --- a/docs/sources/setup-grafana/configure-security/configure-team-sync.md +++ b/docs/sources/setup-grafana/configure-security/configure-team-sync.md @@ -21,21 +21,22 @@ Team sync lets you set up synchronization between your auth providers teams and Grafana keeps track of all synchronized users in teams, and you can see which users have been synchronized in the team members list, see `LDAP` label in screenshot. This mechanism allows Grafana to remove an existing synchronized user from a team when its group membership changes. This mechanism also enables you to manually add a user as member of a team, and it will not be removed when the user signs in. This gives you flexibility to combine LDAP group memberships and Grafana team memberships. -> Currently the synchronization only happens when a user logs in, unless LDAP is used with the active background synchronization that was added in Grafana 6.3. +> Currently the synchronization only happens when a user logs in, unless LDAP is used with the active background synchronization.
          ## Supported providers - [Auth Proxy]({{< relref "./configure-authentication/auth-proxy#team-sync-enterprise-only" >}}) -- [Azure AD]({{< relref "./configure-authentication/azuread#team-sync-enterprise-only" >}}) -- [Generic OAuth integration]({{< relref "./configure-authentication/generic-oauth#configure-team-synchronization" >}}) -- [GitHub OAuth]({{< relref "./configure-authentication/github#configure-team-synchronization" >}}) -- [GitLab OAuth]({{< relref "./configure-authentication/gitlab#configure-team-synchronization" >}}) -- [Google OAuth]({{< relref "./configure-authentication/google#configure-team-sync-for-google-oauth" >}}) -- [LDAP]({{< relref "./configure-authentication/enhanced-ldap#ldap-group-synchronization-for-teams" >}}) -- [Okta]({{< relref "./configure-authentication/okta#configure-team-synchronization-enterprise-only" >}}) -- [SAML]({{< relref "./configure-authentication/saml#configure-team-sync" >}}) +- [Azure AD](https://grafana.com/docs/grafana//configure-authentication/azuread#group-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) ## Synchronize a Grafana team with an external group diff --git a/docs/sources/setup-grafana/configure-security/export-logs.md b/docs/sources/setup-grafana/configure-security/export-logs.md index ba942393530..56c6dfc9831 100644 --- a/docs/sources/setup-grafana/configure-security/export-logs.md +++ b/docs/sources/setup-grafana/configure-security/export-logs.md @@ -18,7 +18,7 @@ weight: 900 # Export logs of usage insights {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). {{% /admonition %}} By exporting usage logs to Loki, you can directly query them and create dashboards of the information that matters to you most, such as dashboard errors, most active organizations, or your top-10 most-used queries. This configuration is done for you in Grafana Cloud, with provisioned dashboards. Read about them in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/). diff --git a/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md b/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md index 46979fc11ab..92ac9f1c5f3 100644 --- a/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md +++ b/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md @@ -136,7 +136,7 @@ Service account access tokens inherit permissions from the service account. ### API keys {{% admonition type="note" %}} -If you use Grafana v8.5 or newer, you should use service accounts instead of API keys. API keys will be deprecated in the near future. For more information, refer to [Grafana service accounts]({{< relref ".#service-accounts" >}}). +Grafana recommends using service accounts instead of API keys. API keys will be deprecated in the near future. For more information, refer to [Grafana service accounts]({{< relref ".#service-accounts" >}}). {{% /admonition %}} You can use Grafana API keys to interact with data sources via HTTP APIs. diff --git a/docs/sources/setup-grafana/image-rendering/_index.md b/docs/sources/setup-grafana/image-rendering/_index.md index 47a74351af8..639f00b6f79 100644 --- a/docs/sources/setup-grafana/image-rendering/_index.md +++ b/docs/sources/setup-grafana/image-rendering/_index.md @@ -35,11 +35,17 @@ Alert notifications can include images, but rendering many images at the same ti ## Install Grafana Image Renderer plugin {{% admonition type="note" %}} -Starting from Grafana v7.0.0, all PhantomJS support has been removed. Please use the Grafana Image Renderer plugin or remote rendering service. +All PhantomJS support has been removed. Instead, use the Grafana Image Renderer plugin or remote rendering service. {{% /admonition %}} To install the plugin, refer to the [Grafana Image Renderer Installation instructions](/grafana/plugins/grafana-image-renderer/?tab=installation#installation). +### Memory requirements + +Rendering images requires a lot of memory, mainly because Grafana creates browser instances in the background for the actual rendering. Grafana recommends a minimum of 16GB of free memory on the system rendering images. + +Rendering multiple images in parallel requires an even bigger memory footprint. You can use the remote rendering service in order to render images on a remote system, so your local system resources are not affected. + ## Configuration The Grafana Image Renderer plugin has a number of configuration options that are used in plugin or remote rendering modes. diff --git a/docs/sources/setup-grafana/installation/_index.md b/docs/sources/setup-grafana/installation/_index.md index 2f852fc3f22..dd6ad422c61 100644 --- a/docs/sources/setup-grafana/installation/_index.md +++ b/docs/sources/setup-grafana/installation/_index.md @@ -76,6 +76,11 @@ Grafana supports the versions of these databases that are officially supported b PostgreSQL versions 10.9, 11.4, and 12-beta2 are affected by a bug (tracked by the PostgreSQL project as [bug #15865](https://www.postgresql.org/message-id/flat/15865-17940eacc8f8b081%40postgresql.org)) which prevents those versions from being used with Grafana. The bug has been fixed in more recent versions of PostgreSQL. {{% /admonition %}} +{{% admonition type="note" %}} +Grafana binaries and images might not work with unsupported databases, even if they claim to be drop-in or replicate the API to their best. +Binaries and images built with [BoringCrypto](https://pkg.go.dev/crypto/internal/boring) may have different problems than other distributions of Grafana. +{{% /admonition %}} + > Grafana can report errors when relying on read-only MySQL servers, such as in high-availability failover scenarios or serverless AWS Aurora MySQL. This is a known issue; for more information, see [issue #13399](https://github.com/grafana/grafana/issues/13399). ## Supported web browsers diff --git a/docs/sources/setup-grafana/set-up-grafana-live.md b/docs/sources/setup-grafana/set-up-grafana-live.md index 3c5e8385e76..db747f4652b 100644 --- a/docs/sources/setup-grafana/set-up-grafana-live.md +++ b/docs/sources/setup-grafana/set-up-grafana-live.md @@ -19,9 +19,7 @@ weight: 1100 # Set up Grafana Live -Grafana Live is a real-time messaging engine introduced in Grafana v8.0. - -With Grafana Live, you can push event data to a frontend as soon as an event occurs. +Grafana Live is a real-time messaging engine you can use to push event data to a frontend as soon as an event occurs. This could be notifications about dashboard changes, new frames for rendered data, and so on. Live features can help eliminate a page reload or polling in many places, it can stream Internet of things (IoT) sensors or any other real-time data to panels. diff --git a/docs/sources/setup-grafana/set-up-grafana-monitoring.md b/docs/sources/setup-grafana/set-up-grafana-monitoring.md index f76ffea3ba0..5422e4f7325 100644 --- a/docs/sources/setup-grafana/set-up-grafana-monitoring.md +++ b/docs/sources/setup-grafana/set-up-grafana-monitoring.md @@ -81,9 +81,11 @@ These instructions assume you have already added Prometheus as a data source in ``` 1. Restart Prometheus. Your new job should appear on the Targets tab. -1. In Grafana, hover your mouse over the **Configuration** (gear) icon on the left sidebar and then click **Data Sources**. +1. In Grafana, click **Connections** in the left-side menu. +1. Under your connections, click **Data Sources**. 1. Select the **Prometheus** data source. -1. On the Dashboards tab, **Import** the Grafana metrics dashboard. All scraped Grafana metrics are available in the dashboard. +1. Under the name of your data source, click **Dashboards**. +1. On the Dashboards tab, click **Import** in the _Grafana metrics_ row to import the Grafana metrics dashboard. All scraped Grafana metrics are available in the dashboard. ### View Grafana metrics in Graphite diff --git a/docs/sources/shared/alerts/alerting_provisioning.md b/docs/sources/shared/alerts/alerting_provisioning.md index b5fe4c23eac..13b99f0aaa9 100644 --- a/docs/sources/shared/alerts/alerting_provisioning.md +++ b/docs/sources/shared/alerts/alerting_provisioning.md @@ -287,6 +287,46 @@ policies: - weekends ``` +### Notification template groups + +Template groups enable you to define multiple notification templates (`{{ define "" }}`) within a single group. They can be managed from the Grafana Alerting UI. + +| Method | URI | Name | Summary | +| ------ | ------------------------------------ | ----------------------------------------------- | ----------------------------------------------- | +| DELETE | /api/v1/provisioning/templates/:name | [route delete template](#route-delete-template) | Delete a notification template group. | +| GET | /api/v1/provisioning/templates/:name | [route get template](#route-get-template) | Get a notification template group. | +| GET | /api/v1/provisioning/templates | [route get template](#route-get-templates) | Get all notification template groups. | +| PUT | /api/v1/provisioning/templates/:name | [route put template](#route-put-template) | Create or update a notification template group. | + +**Example Request for all notification template groups:** + +```http +GET /api/v1/provisioning/templates +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response:** + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +[ + { + "name": "custom_email.message", + "template": "{{ define \"custom_email.message\" }}\n Custom alert!\n{{ end }}", + "provenance": "file" + }, + { + "name": "custom_email.subject", + "template": "{{ define \"custom_email.subject\" }}\n{{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s)\n{{ end }}", + "provenance": "file" + } +] +``` + ### Mute timings | Method | URI | Name | Summary | @@ -331,44 +371,6 @@ Content-Type: application/json ] ``` -### Templates - -| Method | URI | Name | Summary | -| ------ | ------------------------------------ | ----------------------------------------------- | ----------------------------------------- | -| DELETE | /api/v1/provisioning/templates/:name | [route delete template](#route-delete-template) | Delete a template. | -| GET | /api/v1/provisioning/templates/:name | [route get template](#route-get-template) | Get a notification template. | -| GET | /api/v1/provisioning/templates | [route get templates](#route-get-templates) | Get all notification templates. | -| PUT | /api/v1/provisioning/templates/:name | [route put template](#route-put-template) | Create or update a notification template. | - -**Example Request for all notification templates:** - -```http -GET /api/v1/provisioning/templates -Accept: application/json -Content-Type: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk -``` - -**Example Response:** - -```http -HTTP/1.1 200 OK -Content-Type: application/json - -[ - { - "name": "custom_email.message", - "template": "{{ define \"custom_email.message\" }}\n Custom alert!\n{{ end }}", - "provenance": "file" - }, - { - "name": "custom_email.subject", - "template": "{{ define \"custom_email.subject\" }}\n{{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s)\n{{ end }}", - "provenance": "file" - } -] -``` - ### Edit resources in the Grafana UI By default, you cannot edit API-provisioned alerting resources in Grafana. To enable editing these resources in the Grafana UI, add the `X-Disable-Provenance` header to the following requests in the API: @@ -489,7 +491,7 @@ Status: Conflict [GenericPublicError](#generic-public-error) -### Delete a template. (_RouteDeleteTemplate_) +### Delete a notification template group. (_RouteDeleteTemplate_) ``` DELETE /api/v1/provisioning/templates/:name @@ -499,7 +501,7 @@ DELETE /api/v1/provisioning/templates/:name | Name | Source | Type | Go type | Separator | Required | Default | Description | | ------- | ------- | ------ | -------- | --------- | :------: | ------- | ------------------------------------------------------------------------------------------------------------- | -| name | `path` | string | `string` | | ✓ | | Template Name | +| name | `path` | string | `string` | | ✓ | | Name of the template group | | version | `query` | string | `string` | | | | Current version of the resource. Used for optimistic concurrency validation. Keep empty to bypass validation. | #### All responses @@ -1047,7 +1049,7 @@ Status: Not Found [NotFound](#not-found) -### Get a notification template. (_RouteGetTemplate_) +### Get a notification template group. (_RouteGetTemplate_) ``` GET /api/v1/provisioning/templates/:name @@ -1055,9 +1057,9 @@ GET /api/v1/provisioning/templates/:name #### Parameters -| Name | Source | Type | Go type | Separator | Required | Default | Description | -| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ------------- | -| name | `path` | string | `string` | | ✓ | | Template Name | +| Name | Source | Type | Go type | Separator | Required | Default | Description | +| ---- | ------ | ------ | -------- | --------- | :------: | ------- | -------------------------- | +| name | `path` | string | `string` | | ✓ | | Name of the template group | #### All responses @@ -1082,7 +1084,7 @@ Status: OK ###### Schema -### Get all notification templates. (_RouteGetTemplates_) +### Get all notification template groups. (_RouteGetTemplates_) ``` GET /api/v1/provisioning/templates @@ -1152,6 +1154,8 @@ Status: Bad Request POST /api/v1/provisioning/contact-points ``` +When creating a contact point, the `EmbeddedContactPoint.name` property determines if the new contact point is added to an existing one. In the UI, contact points with the same name are grouped together under a single contact point. + #### Parameters {{% responsive-table %}} @@ -1454,7 +1458,7 @@ Status: Bad Request [ValidationError](#validation-error) -### Create or update a notification template. (_RoutePutTemplate_) +### Create or update a notification template group. (_RoutePutTemplate_) ``` PUT /api/v1/provisioning/templates/:name @@ -1466,7 +1470,7 @@ PUT /api/v1/provisioning/templates/:name | Name | Source | Type | Go type | Separator | Required | Default | Description | | -------------------------- | -------- | ------------------------------------------------------------- | ------------------------------------ | --------- | :------: | ------- | --------------------------------------------------------- | -| name | `path` | string | `string` | | ✓ | | Template Name | +| name | `path` | string | `string` | | ✓ | | Name of the template group | | X-Disable-Provenance: true | `header` | string | `string` | | | | Allows editing of provisioned resources in the Grafana UI | | Body | `body` | [NotificationTemplateContent](#notification-template-content) | `models.NotificationTemplateContent` | | | | | @@ -1658,23 +1662,22 @@ Status: Accepted ### EmbeddedContactPoint -> EmbeddedContactPoint is the contact point type that is used -> by grafanas embedded alertmanager implementation. +EmbeddedContactPoint is the contact point type used by Grafana-managed alerts. + +When creating a contact point, the `EmbeddedContactPoint.name` property determines if the new contact point is added to an existing one. In the UI, contact points with the same name are grouped together under a single contact point. **Properties** {{% responsive-table %}} -| Name | Type | Go type | Required | Default | Description | Example | -| ------------------------------------ | ----------------------- | -------- | :------: | ------- | ----------------------------------------------------------------- | --------- | -| disableResolveMessage | boolean | `bool` | | | | `false` | -| name | string | `string` | | | Name is used as grouping key in the UI. Contact points with the | -| same name will be grouped in the UI. | `webhook_1` | -| provenance | string | `string` | | | | | -| settings | [JSON](#json) | `JSON` | ✓ | | | | -| type | string | `string` | ✓ | | | `webhook` | -| uid | string | `string` | | | UID is the unique identifier of the contact point. The UID can be | -| set by the user. | `my_external_reference` | +| Name | Type | Go type | Required | Default | Description | Example | +| --------------------- | ------------- | -------- | :------: | ------- | ---------------------------------------------------------------------------------- | ----------------------- | +| disableResolveMessage | boolean | `bool` | | | | `false` | +| name | string | `string` | | | `name` groups multiple contact points with the same name in the UI. | `webhook_1` | +| provenance | string | `string` | | | | | +| settings | [JSON](#json) | `JSON` | ✓ | | | | +| type | string | `string` | ✓ | | | `webhook` | +| uid | string | `string` | | | UID is the unique identifier of the contact point. The UID can be set by the user. | `my_external_reference` | {{% /responsive-table %}} @@ -1807,24 +1810,24 @@ Status: Accepted {{% responsive-table %}} -| Name | Type | Go type | Required | Default | Description | Example | -| ------------ | ---------------------------- | ------------------- | :------: | ------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| annotations | map of string | `map[string]string` | | | | `{"runbook_url":"https://supercoolrunbook.com/page/13"}` | -| condition | string | `string` | ✓ | | | `A` | -| data | [][AlertQuery](#alert-query) | `[]*AlertQuery` | ✓ | | | `[{"datasourceUid":"__expr__","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}]` | -| execErrState | string | `string` | ✓ | | | | -| folderUID | string | `string` | ✓ | | | `project_x` | -| for | [Duration](#duration) | `Duration` | ✓ | | | | -| id | int64 (formatted integer) | `int64` | | | | | -| isPaused | boolean | `bool` | | | | `false` | -| labels | map of string | `map[string]string` | | | | `{"team":"sre-team-1"}` | -| noDataState | string | `string` | ✓ | | | | -| orgID | int64 (formatted integer) | `int64` | ✓ | | | | -| provenance | [Provenance](#provenance) | `Provenance` | | | | | -| ruleGroup | string | `string` | ✓ | | | `eval_group_1` | -| title | string | `string` | ✓ | | | `Always firing` | -| uid | string | `string` | | | | | -| updated | date-time (formatted string) | `strfmt.DateTime` | | | | | +| Name | Type | Go type | Required | Default | Description | Example | +| ------------ | ---------------------------- | ------------------- | :------: | ------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| annotations | map of string | `map[string]string` | | | Optional key-value pairs. `dashboardUId` and `panelId` must be set together; one cannot be set without the other. | `{"runbook_url":"https://supercoolrunbook.com/page/13"}` | +| condition | string | `string` | ✓ | | | `A` | +| data | [][AlertQuery](#alert-query) | `[]*AlertQuery` | ✓ | | | `[{"datasourceUid":"__expr__","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"type":"__expr__","uid":"__expr__"},"expression":"1 == 1","hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A","type":"math"},"queryType":"","refId":"A","relativeTimeRange":{"from":0,"to":0}}]` | +| execErrState | string | `string` | ✓ | | | | +| folderUID | string | `string` | ✓ | | | `project_x` | +| for | [Duration](#duration) | `Duration` | ✓ | | | | +| id | int64 (formatted integer) | `int64` | | | | | +| isPaused | boolean | `bool` | | | | `false` | +| labels | map of string | `map[string]string` | | | | `{"team":"sre-team-1"}` | +| noDataState | string | `string` | ✓ | | | | +| orgID | int64 (formatted integer) | `int64` | ✓ | | | | +| provenance | [Provenance](#provenance) | `Provenance` | | | | | +| ruleGroup | string | `string` | ✓ | | | `eval_group_1` | +| title | string | `string` | ✓ | | | `Always firing` | +| uid | string | `string` | | | | | +| updated | date-time (formatted string) | `strfmt.DateTime` | | | | | {{% /responsive-table %}} diff --git a/docs/sources/shared/alerts/template-language.md b/docs/sources/shared/alerts/template-language.md new file mode 100644 index 00000000000..44af4f5668b --- /dev/null +++ b/docs/sources/shared/alerts/template-language.md @@ -0,0 +1,237 @@ +--- +title: 'Template language' +--- + +## If + +You can use `if` statements in templates. For example, you can print `Variable empty` when a variable is empty: + +```go +{{ if $element }} +Element value: {{$element}} +{{ else }} +Element is empty +{{ end }} +``` + +## With + +`with` is similar to `if` statements, but unlike `if`, it updates dot(`.`) to refer to the value of the expression in `with`: + +```go +{{ with $array }} +There are {{ len . }} item(s) +{{ else }} +There are no alerts +{{ end }} +``` + +## Range + +`range` iterates over an array or map, and dot (`.`) is set to the current element of the array: + +```go +{{ range $array }} +{{ .itemPropertyName }} +{{ end }} +``` + +Optionally, you can handle empty objects using `else`: + +```go +{{ range $array }} + {{ .itemPropertyName }} +{{ else }} + Empty array +{{ end }} +``` + +You can also get the index of each item within a range by defining index and value variables at the start of the range: + +```go +{{ $num_items := len $array }} +{{ range $index, $item := $array }} +This is item {{ $index }} out of {{ $num_items }} +{{ end }} +``` + +Additionally, you can use `{{break}}` to stop the remaining iterations or `{{continue}}` to stop the current iteration and continue with the next one. + +## Functions + +The global functions available in `text/template` are: + +| Function | Description | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `and` | Returns the boolean AND of its arguments by returning the first empty argument or the last argument. | +| `call` | Returns the result of calling the first argument, which must be a function, with the remaining arguments as parameters. | +| `html` | Returns the escaped HTML equivalent of the textual representation of its arguments. | +| `index` | Returns the result of indexing its first argument by the following arguments, e.g., `{{ index $labels "instance" }}` returns the `instance` key in the `$labels` map variable. | +| `slice` | Returns the result of slicing its first argument by the remaining arguments. | +| `js` | Returns the escaped JavaScript equivalent of the textual representation of its arguments. | +| `len` | Returns the integer length of its argument, e.g., `{{ len $array }}` | +| `not` | Returns the boolean negation of its single argument. | +| `or` | Returns the boolean OR of its arguments by returning the first non-empty argument or the last argument. | +| `print` | An alias for fmt.Sprint | +| `printf` | An alias for fmt.Sprintf | +| `println` | An alias for fmt.Sprintln | +| `urlquery` | Returns the escaped value of the textual representation of its arguments in a form suitable for embedding in a URL query | + +For more details, refer to the official documentation on [functions in `text/template`](https://pkg.go.dev/text/template#hdr-Functions). + +## Comparison operators + +Boolean comparison operators are also available in `text/template`: + +| Function | Description | +| -------- | ----------------------------------------- | +| `eq` | Returns the boolean truth of arg1 == arg2 | +| `ne` | Returns the boolean truth of arg1 != arg2 | +| `lt` | Returns the boolean truth of arg1 < arg2 | +| `le` | Returns the boolean truth of arg1 <= arg2 | +| `gt` | Returns the boolean truth of arg1 > arg2 | +| `ge` | Returns the boolean truth of arg1 >= arg2 | + +## Variables + +Variables in `text/template` must be created within the template. For example, you can create a variable with the current value of dot (`.`) and assign a string or another object to the variable like this: + +```go +{{ $variable := . }} +{{ $variable := "This is a test" }} +{{ $variable }} +``` + +This template outputs: + +``` +This is a test +``` + +## Templates + +You can create reusable templates that can be executed from other templates or within the same template. + +Define templates using `define` and the name of the template in double quotes: + +```go +{{ define "print_labels" }} +{{ end }} +``` + +You should not define templates with the same name as other templates, including default templates such as `__subject`, `__text_values_list`, `__text_alert_list`, `default.title` and `default.message`. Where a template has been created with the same name as a default template, or a template in another notification template, Grafana might use either template. Grafana does not prevent, or show an error message, when there are two or more templates with the same name. + +### Execute templates + +You can execute defined templates using `template`, the name of the template in double quotes, and the cursor that should be passed to the template: + +```go +{{ template "print_labels" . }} +``` + +Within a template dot refers to the value that is passed to the template. + +For example, if a template is passed a list of firing alerts then dot refers to that list of firing alerts: + +```go +{{ template "print_alerts" .Alerts }} +``` + +If the template is passed the sorted labels for an alert then dot refers to the list of sorted labels: + +```go +{{ template "print_labels" .SortedLabels }} +``` + +This is useful when writing reusable templates. For example, to print all alerts you might write the following: + +```go +{{ template "print_alerts" .Alerts }} +``` + +Then to print just the firing alerts you could write this: + +```go +{{ template "print_alerts" .Alerts.Firing }} +``` + +This works because both `.Alerts` and `.Alerts.Firing` is a list of alerts. + +```go +{{ define "print_alerts" }} +{{ range . }} +{{ template "print_labels" .SortedLabels }} +{{ end }} +{{ end }} +``` + +{{% admonition type="note" %}} +You cannot create independent, reusable templates for labels and annotations as you can with notification templates. In alert rule templates, you need to write each template inline within the label or annotation field. +{{% /admonition %}} + +## Comments + +You can add comments with `{{/*` and `*/}}`: + +```go +{{/* This is a comment */}} +``` + +To avoid adding line breaks, use: + +```go +{{- /* This is a comment with no leading or trailing line breaks */ -}} +``` + +## Indentation + +You can use indentation, both tabs and spaces, and line breaks, to make templates more readable: + +```go +{{ range .Alerts }} + {{ range .Labels.SortedPairs }} + {{ .Name }} = {{ .Value }} + {{ end }} +{{ end }} +``` + +However, indentation in the template is also present in the text. + +### Remove spaces and line breaks + +In text/template use `{{-` and `-}}` to remove leading and trailing spaces and line breaks. + +For example, when using indentation and line breaks to make a template more readable: + +```go +{{ range .Alerts }} + {{ range .Labels.SortedPairs }} + {{ .Name }} = {{ .Value }} + {{ end }} +{{ end }} +``` + +The indentation and line breaks are also present in the text: + +``` + alertname = "Test" + + grafana_folder = "Test alerts" +``` + +You can remove the indentation and line breaks from the text changing `}}` to `-}}` at the start of each range: + +```go +{{ range .Alerts -}} + {{ range .Labels.SortedPairs -}} + {{ .Name }} = {{ .Value }} + {{ end }} +{{ end }} +``` + +The indentation and line breaks in the template are now absent from the text: + +``` +alertname = "Test" +grafana_folder = "Test alerts" +``` diff --git a/docs/sources/shared/basics/grafana-enterprise.md b/docs/sources/shared/basics/grafana-enterprise.md index 59be3d70fcd..b85af8d53da 100644 --- a/docs/sources/shared/basics/grafana-enterprise.md +++ b/docs/sources/shared/basics/grafana-enterprise.md @@ -10,4 +10,4 @@ title: Grafana Enterprise Grafana Enterprise is the 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, Grafana Enterprise adds enterprise data sources, advanced authentication options, more permission controls, 24x7x365 support, and training from the core Grafana team. -[Learn more about Grafana Enterprise](https://grafana.com/enterprise) and contact the Grafana Labs [Sales Team](https://grafana.com/contact?about=support&topic=Grafana%20Enterprise) to purchase an Enterprise license. You can also obtain a trial license before the purchase. +[Learn more about Grafana Enterprise](https://grafana.com/enterprise) and contact the Grafana Labs [Sales Team](https://grafana.com/contact?about=grafana-enterprise-stack) to purchase an Enterprise license. You can also obtain a trial license before the purchase. diff --git a/docs/sources/shared/basics/what-is-grafana.md b/docs/sources/shared/basics/what-is-grafana.md index bc449432b45..9d49eafcd5c 100644 --- a/docs/sources/shared/basics/what-is-grafana.md +++ b/docs/sources/shared/basics/what-is-grafana.md @@ -12,16 +12,20 @@ Grafana open source is open source visualization and analytics software. It allo ### Grafana Loki -Grafana Loki is an open source, set of components that can be composed into a fully featured logging stack. For more information, refer to [Loki documentation](/docs/loki/latest/). +Grafana Loki is an open-source set of components that can be composed into a fully featured logging stack. For more information, refer to [Loki documentation](https://grafana.com/docs/loki//). ### Grafana Tempo -Grafana Tempo is an open source, easy-to-use and high-volume distributed tracing backend. For more information, refer to [Tempo documentation](/docs/tempo/latest/?pg=oss-tempo&plcmt=hero-txt/). +Grafana Tempo is an open source, easy-to-use and high-volume distributed tracing backend. For more information, refer to [Tempo documentation](https://grafana.com/docs/tempo//). ### Grafana Mimir -Grafana Mimir is an open source software project that provides a scalable long-term storage for Prometheus. For more information about Grafana Mimir, refer to [Grafana Mimir documentation](/docs/mimir/latest/). +Grafana Mimir is an open source software project that provides a scalable long-term storage for Prometheus. For more information about Grafana Mimir, refer to [Grafana Mimir documentation](https://grafana.com/docs/mimir//). + +### Grafana Pyroscope + +Grafana Pyroscope is an open source software project for aggregating continuous profiling data. Continuous profiling is an observability signal that helps you understand your workload’s resources usage. For more information, refer to [Grafana Pyroscope documentation](https://grafana.com/docs/pyroscope//). ### Grafana Oncall -Grafana OnCall is an open source incident response management tool built to help teams improve their collaboration and resolve incidents faster. For more information about Grafana OnCall, refer to [Grafana OnCall documentation](/docs/oncall/latest/). +Grafana OnCall is an open source incident response management tool built to help teams improve their collaboration and resolve incidents faster. For more information about Grafana OnCall, refer to [Grafana OnCall documentation](https://grafana.com/docs/oncall//). diff --git a/docs/sources/shared/datasources/sql-query-builder-macros.md b/docs/sources/shared/datasources/sql-query-builder-macros.md new file mode 100644 index 00000000000..10425a73ab4 --- /dev/null +++ b/docs/sources/shared/datasources/sql-query-builder-macros.md @@ -0,0 +1,22 @@ +--- +headless: true +labels: + products: + - enterprise + - oss +--- + +#### Macros + +You can enable macros support in the select clause to create time-series queries. + +{{< docs/experimental product="Macros support in visual query builder" featureFlag="`sqlQuerybuilderFunctionParameters`" >}} + +Use the **Data operations** drop-down to select a macro like `$__timeGroup` or `$__timeGroupAlias`. +Select a time column from the **Column** drop-down and a time interval from the **Interval** drop-down to create a time-series query. + +{{< figure src="/media/docs/grafana/data-sources/screenshot-sql-builder-time-series-query.png" class="docs-image--no-shadow" caption="SQL query builder time-series query" >}} + +You can also add custom value to the **Data operations**. +For example, a function that's not in the drop-down list. +This allows you to add any number of parameters. diff --git a/docs/sources/tutorials/alerting-get-started-pt2/index.md b/docs/sources/tutorials/alerting-get-started-pt2/index.md index 340c27144d0..e0564de272c 100644 --- a/docs/sources/tutorials/alerting-get-started-pt2/index.md +++ b/docs/sources/tutorials/alerting-get-started-pt2/index.md @@ -2,8 +2,7 @@ Feedback Link: https://github.com/grafana/tutorials/issues/new categories: - alerting -description: This is part 2 of the Get started with Grafana Alerting tutorials. Learn how to leverage alert instances, and set up a notification policy that routes alert notifications based on labels to a specific contact point. -id: alerting-get-started-pt2 +description: Learn to use alert instances and route notifications by labels to contacts, building on your alerting skills in Grafana for more advanced workflows — Part 2. labels: products: - enterprise @@ -13,11 +12,18 @@ tags: - beginner title: Get started with Grafana Alerting - Part 2 weight: 50 +killercoda: + title: Get started with Grafana Alerting - Part 2 + description: Learn to use alert instances and route notifications by labels to contacts, building on your alerting skills in Grafana for more advanced workflows — Part 2. + backend: + imageid: ubuntu --- + + # Get started with Grafana Alerting - Part 2 -This is part 2 of the [Get Started with Grafana Alerting tutorial](http://grafana.com/tutorials/alerting-get-started/). +The Get started with Grafana Alerting tutorial Part 2 is a continuation of [Get started with Grafana Alerting tutorial Part 1](http://www.grafana.com/tutorials/alerting-get-started/). In this guide, we dig into more complex yet equally fundamental elements of Grafana Alerting: **alert instances** and **notification policies**. @@ -29,6 +35,107 @@ After introducing each component, you will learn how to: Learning about alert instances and notification policies is useful if you have more than one contact point in your organization, or if your alert rule returns a number of metrics that you want to handle separately by routing each alert instance to a specific contact point. The tutorial will introduce each concept, followed by how to apply both concepts in a real-world scenario. + + + + + +{{< docs/ignore >}} + +## Set up the Grafana stack + +{{< /docs/ignore >}} + +## Before you begin + +There are different ways you can follow along with this tutorial. + +- **Grafana Cloud** + + - As a Grafana Cloud user, you don't have to install anything. [Create your free account](http://www.grafana.com/auth/sign-up/create-user). + + Continue to [Alert instances](#alert-instances). + +- **Interactive learning environment** + + - Alternatively, you can try out this example in our interactive learning environment: [Get started with Grafana Alerting - Part 2](https://killercoda.com/grafana-labs/course/grafana/alerting-get-started-pt2/). It's a fully configured environment with all the dependencies already installed. + +- **Grafana OSS** + + - If you opt to run a Grafana stack locally, ensure you have the following applications installed: + + - [Docker Compose](https://docs.docker.com/get-docker/) (included in Docker for Desktop for macOS and Windows) + - [Git](https://git-scm.com/) + +### Set up the Grafana stack (OSS users) + + + +To demonstrate the observation of data using the Grafana stack, download and run the following files. + +1. Clone the [tutorial environment repository](https://www.github.com/grafana/tutorial-environment). + + + + ``` + git clone https://github.com/grafana/tutorial-environment.git + ``` + + + +1. Change to the directory where you cloned the repository: + + + + ``` + cd tutorial-environment + ``` + + + +1. Run the Grafana stack: + + + + ``` + docker compose up -d + ``` + + + + {{< docs/ignore >}} + + + + ```bash + docker-compose up -d + ``` + + + + {{< /docs/ignore >}} + + The first time you run `docker compose up -d`, Docker downloads all the necessary resources for the tutorial. This might take a few minutes, depending on your internet connection. + + + + {{< admonition type="note" >}} + If you already have Grafana, Loki, or Prometheus running on your system, you might see errors, because the Docker image is trying to use ports that your local installations are already using. If this is the case, stop the services, then run the command again. + {{< /admonition >}} + + + + {{< docs/ignore >}} + + NOTE: + + If you already have Grafana, Loki, or Prometheus running on your system, you might see errors, because the Docker image is trying to use ports that your local installations are already using. If this is the case, stop the services, then run the command again. + + {{< /docs/ignore >}} + + + + ## Alert instances An [alert instance](https://grafana.com/docs/grafana/latest/alerting/fundamentals/#alert-instances) is an event that matches a metric returned by an alert rule query. @@ -37,10 +144,13 @@ Let's consider a scenario where you're monitoring website traffic using Grafana. If the query returns more than one time-series, each time-series represents a different metric or aspect being monitored. In this case, the alert rule is applied individually to each time-series. -{{< figure alt="Screenshot displaying alert instances in the context of an alert rule, highlighting the specific alerts triggered by the rule and their respective statuses" src="/media/docs/alerting/get-started-digram-instance-grey.png" max-width="1200px" caption="Alert Instances in the Context of an Alert Rule" >}} +{{< figure alt="Screenshot displaying alert instances in the context of an alert rule, highlighting the specific alerts triggered by the rule and their respective statuses" src="/media/docs/alerting/alert-instance-flow.jpg" max-width="1200px" caption="Alert Instances in the Context of an Alert Rule" >}} In this scenario, each time-series is evaluated independently against the alert rule. It results in the creation of an alert instance for each time-series. The time-series corresponding to the desktop page views meets the threshold and, therefore, results in an alert instance in **Firing** state for which an alert notification is sent. The mobile alert instance state remains **Normal**. + + + ## Notification policies [Notification policies](https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/notification-policies/) route alerts to different communication channels, reducing alert noise and providing control over when and how alerts are sent. For example, you might use notification policies to ensure that critical alerts about server downtime are sent immediately to the on-call engineer. Another use case could be routing performance alerts to the development team for review and action. @@ -54,10 +164,19 @@ Key Characteristics: In the above diagram, alert instances and notification policies are matched by labels. For instance, the label `team=operations` matches the alert instance “**Pod stuck in CrashLoop**” and “**Disk Usage -80%**” to child policies that send alert notifications to a particular contact point (operations@grafana.com). + + + ## Create notification policies Create a notification policy if you want to handle metrics returned by alert rules separately by routing each alert instance to a specific contact point. In Grafana, click on the icon at the top left corner of the screen to access the navigation menu. + + +1. In your browser, **sign in** to your Grafana Cloud account. + + OSS and interactive learning environment users: To log in, navigate to [http://localhost:3000](http://localhost:3000), where Grafana should be running. + 1. Navigate to **Alerts & IRM > Alerting > Notification policies**. 1. In the Default policy, click **+ New child policy**. 1. In the field **Label** enter `device`, and in the field **Value** enter `desktop`. @@ -71,20 +190,35 @@ Create a notification policy if you want to handle metrics returned by alert rul 1. **Repeat the steps above to create a second child policy** to match another alert instance. For labels use: `device=mobile`. Use the Webhook integration for the contact point. Alternatively, experiment by using a different Webhook endpoint or a [different integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#list-of-supported-integrations). + + +{{< docs/ignore >}} + +1. Visit [http://localhost:3000](http://localhost:3000), where Grafana should be running +1. Navigate to **Alerts & IRM > Alerting > Notification policies**. +1. In the Default policy, click **+ New child policy**. +1. In the field **Label** enter `device`, and in the field **Value** enter `desktop`. +1. From the **Contact point** drop-down, choose **Webhook**. + + If you don’t have any contact points, add a [Contact point](https://grafana.com/tutorials/alerting-get-started/#create-a-contact-point). + +1. Click **Save Policy**. + + This new child policy routes alerts that match the label `device=desktop` to the Webhook contact point. + +1. **Repeat the steps above to create a second child policy** to match another alert instance. For labels use: `device=mobile`. Use the Webhook integration for the contact point. Alternatively, experiment by using a different Webhook endpoint or a [different integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/#list-of-supported-integrations). + +{{< /docs/ignore >}} + + + + ## Create an alert rule that returns alert instances The alert rule that you are about to create is meant to monitor web traffic page views. The objective is to explore what an alert instance is and how to leverage routing individual alert instances by using label matchers and notification policies. -### Add a data source - -Grafana includes a [test data source](https://grafana.com/docs/grafana/latest/datasources/testdata/) that creates simulated time series data. - -1. In Grafana navigate to **Connections > Add new connection**. -1. Search for **TestData**. -1. Click **Add new data source**. -1. Click **Save & test**. - - You should see a message confirming that the data source is working. + + ### Create an alert rule @@ -97,29 +231,35 @@ Make it short and descriptive as this will appear in your alert notification. Fo ### Define query and alert condition -In this section, we define queries, expressions (used to manipulate the data), and the condition that must be met for the alert to be triggered. +In this section, we use the default options for Grafana-managed alert rule creation. The default options let us define the query, a expression (used to manipulate the data -- the `WHEN` field in the UI), and the condition that must be met for the alert to be triggered (in default mode is the threshold). + +Grafana includes a [test data source](https://grafana.com/docs/grafana/latest/datasources/testdata/) that creates simulated time series data. This data source is included in the demo environment for this tutorial. If you're working in Grafana Cloud or your own local Grafana instance, you can add the data source through the **Connections** menu. 1. Select **TestData** data source from the drop-down menu. 1. From **Scenario** select **CSV Content**. +1. In the Query editor, switch to **Code** mode by clicking the button on the right. 1. Copy in the following CSV data: -``` -device,views -desktop,1200 -mobile,900 -``` + ``` + device,views + desktop,1200 + mobile,900 + ``` -The above CSV data simulates a data source returning multiple time series, each leading to the creation of an alert instance for that specific time series. Note that the data returned matches the example in the [Alert instance](#alert-instances) section. + The above CSV data simulates a data source returning multiple time series, each leading to the creation of an alert instance for that specific time series. Note that the data returned matches the example in the [Alert instance](#alert-instances) section. + +1. In the **Alert condition** section: + + - Keep `Last` as the value for the reducer function (`WHEN`), and `1000` as the threshold value. This is the value above which the alert rule should trigger. -1. Remove the ‘B’ **Reduce expression** (click the bin icon). The Reduce expression is default, and in this case, is not required since the queried data is already reduced. Note that the Threshold expression is now your **Alert condition**. -1. In the ‘C’ **Threshold expression**: - - Change the **Input** to ‘**A**’ to select the data source. - - Enter `1000` as the threshold value. This is the value above which the alert rule should trigger. 1. Click **Preview** to run the queries. It should return two series.`desktop` in Firing state, and `mobile` in Normal state. The values `1`, and `0` mean that the condition is either `true` or `false`. -{{< figure alt="Screenshot showing a preview of a query in Grafana that returns two alert instances, including the query results and relevant alert details" src="/media/docs/alerting/get-started-expression-instances.png" max-width="1200px" caption="Preview of a query returning two alert instances in Grafana." >}} +{{< figure alt="Screenshot showing a preview of a query in Grafana that returns two alert instances, including the query results and relevant alert details" src="/media/docs/alerting/firing-instances.png" max-width="1200px" caption="Preview of a query returning two alert instances in Grafana." >}} + + + ### Set evaluation behavior @@ -143,14 +283,24 @@ In this section, you can select how you want to route your alert instances. Sinc {{< figure alt="Screenshot showing a routing preview of matched notification policies, detailing how alerts are matched and routed to specific notification channels" src="/media/docs/alerting/get-started-alert-instace-routing-prev.png" max-width="1200px" caption="Routing preview of matched notification policies" >}} + + {{< admonition type="note" >}} Even if both labels match the policies, only the alert instance in Firing state produces an alert notification. {{}} + + + {{< docs/ignore >}} + Even if both labels match the policies, only the alert instance in Firing state produces an alert notification. + {{< /docs/ignore >}} 1. Click **Save rule and exit**. Now that we have set up the alert rule, it’s time to check the alert notification. + + + ## Receive alert notifications Now that the alert rule has been configured, you should receive alert [notifications](http://grafana.com/docs/grafana/next/alerting/fundamentals/alert-rule-evaluation/state-and-health/#notifications) in the contact point whenever the alert triggers and gets resolved. In our example, each alert instance should be routed separately as we configured labels to match notification policies. Once the evaluation interval has concluded (1m), you should receive an alert notification in the Webhook endpoint. @@ -161,6 +311,9 @@ The alert notification details show that the alert instance corresponding to the Feel free to change the CSV data in the alert rule to trigger the routing of the alert instance that matches the label `device=mobile`. + + + ## Summary In this tutorial, you have learned how Grafana Alerting can route individual alert instances using the labels generated by the data-source query and match these labels with notification policies, which in turn routes alert notifications to specific contact points. @@ -168,3 +321,5 @@ In this tutorial, you have learned how Grafana Alerting can route individual ale If you run into any problems, you are welcome to post questions in our [Grafana Community forum](https://community.grafana.com/). Enjoy your monitoring! + + diff --git a/docs/sources/tutorials/alerting-get-started/index.md b/docs/sources/tutorials/alerting-get-started/index.md index 7ebea1418eb..4b832c334e9 100644 --- a/docs/sources/tutorials/alerting-get-started/index.md +++ b/docs/sources/tutorials/alerting-get-started/index.md @@ -11,20 +11,20 @@ labels: - cloud tags: - beginner -title: Get started with Grafana Alerting - Part 1 +title: Get started with Grafana Alerting - Part 1 of 2 weight: 50 killercoda: - title: Get started with Grafana Alerting - Part 1 - description: Get started with Grafana Alerting by creating your first alert in just a few minutes. Learn how to set up an alert, send alert notifications to a public webhook, and generate sample data to observe your alert in action. + title: Get started with Grafana Alerting - Part 1 of 2 + description: Get started with Grafana Alerting by creating your first alert rule, sending notifications to a webhook, and generating data to test it live — Part 1. backend: imageid: ubuntu --- -# Get Started with Grafana Alerting - Part 1 +# Get started with Grafana Alerting - Part 1 of 2 -In this guide, we'll walk you through the process of setting up your first alert in just a few minutes. You'll witness your alert in action with real-time data, as well as sending alert notifications. +In this guide, we walk you through the process of setting up your first alert in just a few minutes. You'll witness your alert in action with real-time data, as well as sending alert notifications. In this tutorial you will: @@ -36,7 +36,7 @@ In this tutorial you will: {{< admonition type="tip" >}} -Before you dive in, remember that you can [explore advanced topics like alert instances and notification routing](http://grafana.com/tutorials/alerting-get-started-pt2/) in the second part of this guide. +Once you have completed Part 1, don’t forget to explore the advanced but essential alerting topics in [Part 2 Alert instances and notification routing](http://www.grafana.com/tutorials/alerting-get-started-pt2/). {{< /admonition >}} @@ -44,7 +44,7 @@ Before you dive in, remember that you can [explore advanced topics like alert in {{< docs/ignore >}} -> Before you dive in, remember that you can [explore advanced topics like alert instances and notification routing](http://grafana.com/tutorials/alerting-get-started-pt2/) in the second part of this guide. +> Once you have completed Part 1, don’t forget to explore the advanced but essential alerting topics in [Part 2 Alert instances and notification routing](http://www.grafana.com/tutorials/alerting-get-started-pt2/). {{< /docs/ignore >}} @@ -52,36 +52,28 @@ Before you dive in, remember that you can [explore advanced topics like alert in -{{< docs/ignore >}} - -## Set up the Grafana stack - -{{< /docs/ignore >}} - ## Before you begin There are different ways you can follow along with this tutorial. -### Grafana Cloud +- **Grafana Cloud** -As a Grafana Cloud user, you don't have to install anything. [Create your free account](http://grafana.com/auth/sign-up/create-user). + - As a Grafana Cloud user, you don't have to install anything. [Create your free account](http://www.grafana.com/auth/sign-up/create-user). -Continue to [Create a contact point](#create-a-contact-point). + Continue to [Create a contact point](#create-a-contact-point). -### Interactive learning environment +- **Interactive learning environment** -Alternatively, you can try out this example in our interactive learning environment: [Get started with Grafana Alerting](https://killercoda.com/grafana-labs/course/grafana/alerting-get-started/). + - Alternatively, you can [try out this example in our interactive learning environment](https://killercoda.com/grafana-labs/course/grafana/alerting-get-started/). It's a fully configured environment with all the dependencies already installed. -It's a fully configured environment with all the dependencies already installed. +- **Grafana OSS** -### Grafana OSS + - If you opt to run a Grafana stack locally, ensure you have the following applications installed: -If you opt to run a Grafana stack locally, ensure you have the following applications installed: + - [Docker Compose](https://docs.docker.com/get-docker/) (included in Docker for Desktop for macOS and Windows) + - [Git](https://git-scm.com/) -- [Docker Compose](https://docs.docker.com/get-docker/) (included in Docker for Desktop for macOS and Windows) -- [Git](https://git-scm.com/) - -#### Set up the Grafana stack (OSS users) +### Set up the Grafana stack (OSS users) @@ -154,7 +146,7 @@ To demonstrate the observation of data using the Grafana stack, download and run Besides being an open-source observability tool, Grafana has its own built-in alerting service. This means that you can receive notifications whenever there is an event of interest in your data, and even see these events graphed in your visualizations. -In this step, we'll set up a new [contact point](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier/). This contact point will use the _webhooks_ integration. In order to make this work, we also need an endpoint for our webhook integration to receive the alert. We will use [Webhook.site](https://webhook.site/) to quickly set up that test endpoint. This way we can make sure that our alert is actually sending a notification somewhere. +In this step, we set up a new contact point. This contact point uses the [webhook integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier/). In order to make this work, we also need an endpoint for our webhook integration to receive the alert. We can use [Webhook.site](https://webhook.site/) to quickly set up that test endpoint. This way we can make sure that our alert is actually sending a notification somewhere. 1. In your browser, **sign in** to your Grafana Cloud account. @@ -168,12 +160,12 @@ Your webhook endpoint is now waiting for the first request. Next, let's configure a contact point in Grafana's Alerting UI to send notifications to our webhook endpoint. 1. Return to Grafana. In Grafana's sidebar, hover over the **Alerting** (bell) icon and then click **Contact points**. -1. Click **+ Add contact point**. +1. Click **+ Create contact point**. 1. In **Name**, write **Webhook**. 1. In **Integration**, choose **Webhook**. 1. In **URL**, paste the endpoint to your webhook endpoint. 1. Click **Test**, and then click **Send test notification** to send a test alert to your webhook endpoint. -1. Navigate back to [Webhook.site](https://webhook.site/). On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent. +1. Navigate back to _Webhook.site_. On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent. {{< figure src="/media/docs/alerting/alerting-webhook-detail.png" max-width="1200px" caption="A POST entry in Webhook.site" >}} @@ -187,38 +179,29 @@ We have created a dummy Webhook endpoint and created a new Alerting contact poin ## Create an alert -Next, we'll establish an [alert rule](http://grafana.com/docs/grafana/next/alerting/fundamentals/alert-rule-evaluation/) within Grafana Alerting to notify us whenever alert rules are triggered and resolved. +Next, we establish an [alert rule](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-grafana-managed-rule/) within Grafana Alerting to notify us whenever alert rules are triggered and resolved. 1. In Grafana, **navigate to Alerting** > **Alert rules**. Click on **New alert rule**. -1. Enter alert rule name for your alert rule. Make it short and descriptive as this will appear in your alert notification. For instance, **database-metrics** +1. Enter alert rule name for your alert rule. Make it short and descriptive as this appears in your alert notification. For instance, **database-metrics** ### Define query and alert condition -In this section, we define queries, expressions (used to manipulate the data), and the condition that must be met for the alert to be triggered. +In this section, we use the default options for Grafana-managed alert rule creation. The default options let us define the query, a expression (used to manipulate the data -- the `WHEN` field in the UI), and the condition that must be met for the alert to be triggered (in default mode is the threshold). -1. Select the **Prometheus** data source from the drop-down menu. -1. In the Query editor, switch to **Code** mode by clicking the button at the right. -1. Enter the following query: +Grafana includes a [test data source](https://grafana.com/docs/grafana/latest/datasources/testdata/) that creates simulated time series data. This data source is included in the demo environment for this tutorial. If you're working in Grafana Cloud or your own local Grafana instance, you can add the data source through the **Connections** menu. - ```promql - vector(1) - ``` +1. Select the **TestData** data source from the drop-down menu. - In Prometheus, `vector(1)` is a special type of PromQL query that generates a constant vector. This is useful in testing and query manipulation, where you might need a constant value for calculations or comparisons. This query will allow you to create an alert rule that will be always firing. +1. In the **Alert condition** section: -1. Remove the ‘B’ **Reduce expression** (click the bin icon). The Reduce expression comes by default, and in this case, it is not needed since the queried data is already reduced. Note that the Threshold expression is now your **Alert condition**. + - Keep `Last` as the value for the reducer function (`WHEN`), and `0` as the threshold value. This is the value above which the alert rule should trigger. -1. In the ‘C’ **Threshold expression**: +1. Click **Preview alert rule condition** to run the query. - - Change the **Input** to **'A'** to select the data source. - - Enter `0` as the threshold value. This is the value above which the alert rule should trigger. + It should return random time series data. The alert rule state should be `Firing`. -1. Click **Preview** to run the queries. - - It should return a single sample with the value 1 at the current timestamp. And, since `1` is above `0`, the alert condition has been met, and the alert rule state is `Firing`. - - {{< figure src="/media/docs/alerting/alerting-always-firing-alert.png" max-width="1200px" caption="A preview of a firing alert" >}} + {{< figure src="/media/docs/alerting/random-walk-firing-alert-rule.png" max-width="1200px" caption="A preview of a firing alert" >}} ### Set evaluation behavior @@ -230,9 +213,9 @@ The [alert rule evaluation](https://grafana.com/docs/grafana/latest/alerting/fun To set up the evaluation: -1. In **Folder**, click **+ New folder** and enter a name. For example: _metric-alerts_. This folder will contain our alerts. -1. In the **Evaluation group**, repeat the above step to create a new evaluation group. We will name it _1m-evaluation_. -1. Choose an **Evaluation interval** (how often the alert will be evaluated). +1. In **Folder**, click **+ New folder** and enter a name. For example: _metric-alerts_. This folder contains our alerts. +1. In the **Evaluation group**, repeat the above step to create a new evaluation group. Name it _1m-evaluation_. +1. Choose an **Evaluation interval** (how often the alert are evaluated). For example, every `1m` (1 minute). 1. Set the pending period to, `0s` (zero seconds), so the alert rule fires the moment the condition is met. @@ -277,13 +260,13 @@ By incrementing the threshold, the condition is no longer met, and after the eva -## Learn more +## Learn more in [Grafana Alerting Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) {{< admonition type="tip" >}} -Advance your skills by exploring [alert instances and notification routing](http://grafana.com/tutorials/alerting-get-started-pt2/) in Part 2 of your learning journey. +In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /admonition >}} @@ -291,7 +274,7 @@ Advance your skills by exploring [alert instances and notification routing](http {{< docs/ignore >}} -Advance your skills by exploring [alert instances and notification routing](http://grafana.com/tutorials/alerting-get-started-pt2/) in Part 2 of your learning journey. +In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /docs/ignore >}} diff --git a/docs/sources/tutorials/create-alerts-with-logs/index.md b/docs/sources/tutorials/create-alerts-with-logs/index.md index cc04b4731e7..39de7c380c2 100644 --- a/docs/sources/tutorials/create-alerts-with-logs/index.md +++ b/docs/sources/tutorials/create-alerts-with-logs/index.md @@ -1,7 +1,5 @@ --- Feedback Link: https://github.com/grafana/tutorials/issues/new -authors: - - melori_arellano categories: - alerting description: Create alerts with Logs @@ -15,10 +13,10 @@ labels: - alerting tags: - advanced -title: How to create alerts with log data +title: How to create alert rules with log data weight: 70 killercoda: - title: How to create alerts with log data + title: How to create alert rules with log data description: Learn how to use Loki with Grafana Alerting to keep track of what’s happening in your environment with real log data. preprocessing: substitutions: @@ -43,14 +41,14 @@ In this tutorial, you'll: {{< admonition type="tip" >}} -Check out our [advanced alerting tutorial](https://grafana.com/tutorials/alerting-get-started-pt2/) to explore advanced topics such as alert instances and notification routing. +In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /admonition >}} {{< docs/ignore >}} -> Check out our [advanced alerting tutorial](https://grafana.com/tutorials/alerting-get-started-pt2/) to explore advanced topics such as alert instances and notification routing. +> In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /docs/ignore >}} @@ -62,31 +60,29 @@ Check out our [advanced alerting tutorial](https://grafana.com/tutorials/alertin -### Grafana Cloud users +There are different ways you can follow along with this tutorial. -As a Grafana Cloud user, you don't have to install anything. +- **Grafana OSS** -Continue to [Generate sample logs](#generate-sample-logs). + To run a Grafana stack locally, ensure you have the following applications installed: - + - [Docker Compose](https://docs.docker.com/get-docker/) (included in Docker for Desktop for macOS and Windows) + - [Git](https://git-scm.com/) -### Grafana OSS users +- **Interactive learning environment** - + - Alternatively, you can [try out this example in our interactive learning environment](https://killercoda.com/grafana-labs/course/grafana/alerting-loki-logs). It's a fully configured environment with all the dependencies already installed. -In order to run a Grafana stack locally, ensure you have the following applications installed. - -- [Docker Compose](https://docs.docker.com/get-docker/) (included in Docker for Desktop for macOS and Windows) -- [Git](https://git-scm.com/) +## Set up the Grafana stack -To demonstrate the observation of data using the Grafana stack, download the files to your local machine. +To demonstrate the observation of data using the Grafana stack, download and run the following files. 1. Download and save a Docker compose file to run Grafana, Loki and Promtail. ```bash - wget https://raw.githubusercontent.com/grafana/loki/v2.8.0/production/docker-compose.yaml -O docker-compose.yaml + wget https://raw.githubusercontent.com/grafana/loki/refs/heads/main/production/docker-compose.yaml -O docker-compose.yaml ``` 2. Run the Grafana stack. @@ -113,26 +109,14 @@ If you already have Grafana, Loki, or Prometheus running on your system, you mig {{< /docs/ignore >}} - - -{{< admonition type="tip" >}} -Alternatively, you can try out this example in our interactive learning environment: [Get started with Grafana Alerting](https://killercoda.com/grafana-labs/course/grafana/alerting-loki-logs). - -It's a fully configured environment with all the dependencies already installed. - -![Interactive](/media/docs/grafana/full-stack-ile.png) - -Provide feedback, report bugs, and raise issues in the [Grafana Killercoda repository](https://github.com/grafana/killercoda). -{{< /admonition >}} - - - ## Generate sample logs +To demonstrate how to create alert rules based on logs, you’ll use a script that generates realistic log entries to simulate typical monitoring data in Grafana. Running this script outputs logs continuously, each containing a timestamp, HTTP method (either GET or POST), status code (200 for success or 500 for failures), and request duration in milliseconds. + 1. Download and save a Python file that generates logs. ```bash @@ -162,13 +146,13 @@ If you don't see the sample logs in Explore: Besides being an open-source observability tool, Grafana has its own built-in alerting service. This means that you can receive notifications whenever there is an event of interest in your data, and even see these events graphed in your visualizations. -In this step, we'll set up a new [contact point](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier/). This contact point will use the _webhooks_ integration. In order to make this work, we also need an endpoint for our webhook integration to receive the alert. We will use [Webhook.site](https://webhook.site/) to quickly set up that test endpoint. This way we can make sure that our alert is actually sending a notification somewhere. +In this step, we set up a new contact point. This contact point uses the [webhook integration](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier/). This contact point uses the _webhooks_ integration. In order to make this work, we also need an endpoint for our webhook integration to receive the alert. We can use [Webhook.site](https://webhook.site/) to quickly set up that test endpoint. This way we can make sure that our alert is actually sending a notification somewhere. 1. In your browser, **sign in** to your Grafana Cloud account. - OSS users: To log in, navigate to [http://localhost:3000](http://localhost:3000), where Grafana is running. + OSS users: To log in, navigate to [http://localhost:3000](http://localhost:3000), where Grafana should be running. 1. In another tab, go to [Webhook.site](https://webhook.site/). 1. Copy Your unique URL. @@ -176,7 +160,7 @@ In this step, we'll set up a new [contact point](https://grafana.com/docs/grafan {{< docs/ignore >}} -1. Navigate to [http://localhost:3000](http://localhost:3000), where Grafana is running. +1. Navigate to [http://localhost:3000](http://localhost:3000), where Grafana should be running. 1. In another tab, go to [Webhook.site](https://webhook.site/). 1. Copy Your unique URL. {{< /docs/ignore >}} @@ -186,12 +170,12 @@ Your webhook endpoint is now waiting for the first request. Next, let's configure a contact point in Grafana's Alerting UI to send notifications to our webhook endpoint. 1. Return to Grafana. In Grafana's sidebar, hover over the **Alerting** (bell) icon and then click **Contact points**. -1. Click **+ Add contact point**. +1. Click **+ Create contact point**. 1. In **Name**, write **Webhook**. 1. In **Integration**, choose **Webhook**. 1. In **URL**, paste the endpoint to your webhook endpoint. 1. Click **Test**, and then click **Send test notification** to send a test alert to your webhook endpoint. -1. Navigate back to [Webhook.site](https://webhook.site/). On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent. +1. Navigate back to _Webhook.site_. On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent. {{< figure src="/media/docs/alerting/alerting-webhook-detail.png" max-width="1200px" caption="A POST entry in Webhook.site" >}} @@ -209,56 +193,48 @@ Next, we'll establish an [alert rule](http://grafana.com/docs/grafana/next/alert 1. In Grafana, **navigate to Alerting** > **Alert rules**. 1. Click on **New alert rule**. -1. Enter alert rule name for your alert rule. Make it short and descriptive as this will appear in your alert notification. For instance, **web-requests-logs** +1. Enter alert rule name for your alert rule. Make it short and descriptive as this appears in your alert notification. For instance, **web-requests-logs** ### Define query and alert condition -In this section, we define queries, expressions (used to manipulate the data), and the condition that must be met for the alert to be triggered. +In this section, we use the default options for Grafana-managed alert rule creation. The default options let us define the query, a expression (used to manipulate the data -- the `WHEN` field in the UI), and the condition that must be met for the alert to be triggered (in default mode is the threshold). 1. Select the **Loki** datasource from the drop-down. -2. In the Query editor, switch to Code mode by clicking the button on the right. -3. Paste the query below. +1. In the Query editor, switch to **Code** mode by clicking the button on the right. +1. Paste the query below. ``` sum by (message)(count_over_time({filename="/var/log/web_requests.log"} != "status=200" | pattern "<_> duration<_>" [10m])) ``` -This query will count the number of log lines with a status code that is not 200 (OK), then sum the result set by message type using an **instant query** and the time interval indicated in brackets. It uses the LogQL pattern parser to add a new label called `message` that contains the level, method, url, and status from the log line. + This query counts the number of log lines with a status code that is not 200 (OK), then sum the result set by message type using an **instant query** and the time interval indicated in brackets. It uses the LogQL pattern parser to add a new label called `message` that contains the level, method, url, and status from the log line. -You can use the **explain query** toggle button for a full explanation of the query syntax. The optional log-generating script creates a sample log line similar to the one below: + You can use the **explain query** toggle button for a full explanation of the query syntax. The optional log-generating script creates a sample log line similar to the one below: -``` -2023-04-22T02:49:32.562825+00:00 level=info method=GET url=test.com status=200 duration=171ms -``` + ``` + 2023-04-22T02:49:32.562825+00:00 level=info method=GET url=test.com status=200 duration=171ms + ``` - + -{{% admonition type="note" %}} + {{% admonition type="note" %}} + If you're using your own logs, modify the LogQL query to match your own log message. Refer to the Loki docs to understand the [pattern parser](https://grafana.com/docs/loki/latest/logql/log_queries/#pattern). + {{% / admonition %}} + -If you're using your own logs, modify the LogQL query to match your own log message. Refer to the Loki docs to understand the [pattern parser](https://grafana.com/docs/loki/latest/logql/log_queries/#pattern). + {{< docs/ignore >}} + If you're using your own logs, modify the LogQL query to match your own log message. Refer to the Loki docs to understand the [pattern parser](https://grafana.com/docs/loki/latest/logql/log_queries/#pattern). + {{< /docs/ignore >}} -{{% / admonition %}} +1. In the **Alert condition** section: - + - Keep `Last` as the value for the reducer function (`WHEN`), and `0` as the threshold value. This is the value above which the alert rule should trigger. -{{< docs/ignore >}} - -If you're using your own logs, modify the LogQL query to match your own log message. Refer to the Loki docs to understand the [pattern parser](https://grafana.com/docs/loki/latest/logql/log_queries/#pattern). - -{{< /docs/ignore >}} - -4. Remove the ‘B’ **Reduce expression** (click the bin icon). The Reduce expression comes by default, and in this case, it is not needed since the queried data is already reduced. Note that the Threshold expression is now your **Alert condition**. - -5. In the ‘C’ **Threshold expression**: - - - Change the **Input** to **'A'** to select the data source. - - Enter `0` as the threshold value. This is the value above which the alert rule should trigger. - -6. Click **Preview** to run the queries. +1. Click **Preview alert rule condition** to run the query. It should return alert instances from log lines with a status code that is not 200 (OK), and that has met the alert condition. The condition for the alert rule to fire is any occurrence that goes over the threshold of `0`. Since the Loki query has returned more than zero alert instances, the alert rule is `Firing`. - {{< figure src="/media/docs/alerting/expression-loki-alert.png" max-width="1200px" caption="Preview of a firing alert instances" >}} + {{< figure src="/media/docs/alerting/firing-loki-alert-rule.png" max-width="1200px" caption="Preview of a firing alert instances" >}} ### Set evaluation behavior @@ -269,9 +245,9 @@ An [evaluation group](https://grafana.com/docs/grafana/latest/alerting/fundament To set up the evaluation: -1. In **Folder**, click **+ New folder** and enter a name. For example: _web-server-alerts_. This folder will contain our alerts. -1. In the **Evaluation group**, repeat the above step to create a new evaluation group. We will name it _1m-evaluation_. -1. Choose an **Evaluation interval** (how often the alert will be evaluated). +1. In **Folder**, click **+ New folder** and enter a name. For example: _web-server-alerts_. This folder contains our alerts. +1. In the **Evaluation group**, repeat the above step to create a new evaluation group. Name it _1m-evaluation_. +1. Choose an **Evaluation interval** (how often the alert are evaluated). For example, every `1m` (1 minute). 1. Set the pending period to, `0s` (zero seconds), so the alert rule fires the moment the condition is met. @@ -288,7 +264,7 @@ Choose the contact point where you want to receive your alert notifications. ## Trigger the alert rule -Since the Python script will continue to generate log data that matches the alert rule condition, once the evaluation interval has concluded, you should receive an alert notification in the Webhook endpoint. +Since the Python script continues to generate log data that matches the alert rule condition, once the evaluation interval has concluded, you should receive an alert notification in the Webhook endpoint. {{< figure src="/media/docs/alerting/alerting-webhook-firing-alert.png" max-width="1200px" caption="Firing alert notification details" >}} @@ -299,14 +275,14 @@ Since the Python script will continue to generate log data that matches the aler {{< admonition type="tip" >}} -Check out our [advanced alerting tutorial](https://grafana.com/tutorials/alerting-get-started-pt2/) to explore advanced topics such as alert instances and notification routing. +In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /admonition >}} {{< docs/ignore >}} -> Check out our [advanced alerting tutorial](https://grafana.com/tutorials/alerting-get-started-pt2/) to explore advanced topics such as alert instances and notification routing. +> In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /docs/ignore >}} diff --git a/docs/sources/tutorials/grafana-fundamentals/index.md b/docs/sources/tutorials/grafana-fundamentals/index.md index 5262044de5c..20f9616fea8 100644 --- a/docs/sources/tutorials/grafana-fundamentals/index.md +++ b/docs/sources/tutorials/grafana-fundamentals/index.md @@ -153,19 +153,19 @@ Grafana is an open source platform for monitoring and observability that lets yo {{< admonition type="note" >}} -This demo does not require a login page or credentials. However, if you choose to install Grafana locally, you will need to log in and provide credentials. In that case, the default username and password is `admin`. +This demo does not require a login page or credentials. However, if you choose to install Grafana locally, you need to log in and provide credentials. In that case, the default username and password is `admin`. {{< /admonition >}} {{< docs/ignore >}} -> This demo does not require a login page or credentials. However, if you choose to install Grafana locally, you will need to log in and provide credentials. In that case, the default username and password is `admin`. +> This demo does not require a login page or credentials. However, if you choose to install Grafana locally, you need to log in and provide credentials. In that case, the default username and password is `admin`. > {{< /docs/ignore >}} The first thing you see is the Home dashboard, which helps you get started. -In the top left corner, you can see the menu icon. Clicking it will open the _sidebar_, the main menu for navigating Grafana. +In the top left corner, you can see the menu icon. Clicking it opens the _sidebar_, the main menu for navigating Grafana. @@ -177,7 +177,7 @@ Grafana Explore is a workflow for troubleshooting and data exploration. In this > Ad-hoc queries are queries that are made interactively, with the purpose of exploring data. An ad-hoc query is commonly followed by another, more specific query. -1. Click the menu icon and, in the sidebar, click **Explore**. A dropdown menu for the list of available data sources is on the upper-left side. The Prometheus data source will already be selected. If not, choose Prometheus. +1. Click the menu icon and, in the sidebar, click **Explore**. A dropdown menu for the list of available data sources is on the upper-left side. The Prometheus data source should already be selected. If not, choose Prometheus. 1. Confirm that you're in code mode by checking the **Builder/Code** toggle at the top right corner of the query panel. 1. In the query editor, where it says _Enter a PromQL query…_, enter `tns_request_duration_seconds_count` and then press Shift + Enter. A graph appears. @@ -259,7 +259,7 @@ Let's generate an error, and analyze it with Explore. 1. Click the log line that says `level=error msg="empty url"` to see more information about the error. - > **Note:** If you're in Live mode, clicking logs will not show more information about the error. Instead, stop and exit the live stream, then click the log line there. + > **Note:** If you're in Live mode, clicking logs does not show more information about the error. Instead, stop and exit the live stream, then click the log line there. Logs are helpful for understanding what went wrong. Later in this tutorial, you'll see how you can correlate logs with metrics from Prometheus to better understand the context of the error. @@ -302,7 +302,7 @@ Every panel consists of a _query_ and a _visualization_. The query defines _what When things go bad, it often helps if you understand the context in which the failure occurred. Time of last deploy, system changes, or database migration can offer insight into what might have caused an outage. Annotations allow you to represent such events directly on your graphs. -In the next part of the tutorial, we will simulate some common use cases that someone would add annotations for. +In the next part of the tutorial, we simulate some common use cases that someone would add annotations for. 1. To manually add an annotation, click anywhere in your graph, then click **Add annotation**. Note: you might need to save the dashboard first. @@ -346,7 +346,7 @@ The log lines returned by your query are now displayed as annotations in the gra Being able to combine data from multiple data sources in one graph allows you to correlate information from both Prometheus and Loki. -Annotations also work very well alongside alert rules. In the next and final section, we will set up an alert rules for our app `grafana.news` and then we will trigger it. This will provide a quick intro to our new Alerting platform. +Annotations also work very well alongside alert rules. In the next and final section, we set up an alert rules for our app `grafana.news` and then we trigger it. This provides a quick intro to our new Alerting platform. @@ -356,7 +356,7 @@ Annotations also work very well alongside alert rules. In the next and final sec Alert rules allow you to identify problems in your system moments after they occur. By quickly identifying unintended changes in your system, you can minimize disruptions to your services. -Grafana's new alerting platform debuted with Grafana 8. A year later, with Grafana 9, it became the default alerting method. In this step we will create a Grafana-managed alert rule. Then we will trigger our new alert rule and send a test message to a dummy endpoint. +Grafana's new alerting platform debuted with Grafana 8. A year later, with Grafana 9, it became the default alerting method. In this step we create a Grafana-managed alert rule. Then we trigger our new alert rule and send a test message to a dummy endpoint. The most basic alert rule consists of two parts: @@ -384,7 +384,7 @@ The most basic alert rule consists of two parts: 1. An _Alert rule_ - An Alert rule defines one or more _conditions_ that Grafana regularly evaluates. When these evaluations meet the rule's criteria, the alert rule is triggered. -To begin, let's set up a webhook contact point. Once we have a usable endpoint, we'll write an alert rule and trigger a notification. +To begin, let's set up a webhook contact point. Once we have a usable endpoint, we write an alert rule and trigger a notification. @@ -392,24 +392,23 @@ To begin, let's set up a webhook contact point. Once we have a usable endpoint, ### Create a contact point for Grafana-managed alert rules -In this step, we'll set up a new contact point. This contact point will use the _webhooks_ channel. In order to make this work, we also need an endpoint for our webhook channel to receive the alert notification. We will use [requestbin.com](https://requestbin.com) to quickly set up that test endpoint. This way we can make sure that our alert manager is actually sending a notification somewhere. +In this step, we set up a new contact point. This contact point uses the _webhooks_ channel. In order to make this work, we also need an endpoint for our webhook channel to receive the alert notification. We can use [Webhook.site](https://webhook.site/) to quickly set up that test endpoint. This way we can make sure that our alert manager is actually sending a notification somewhere. -1. Browse to [requestbin.com](https://requestbin.com). -1. Under the **Create Request Bin** button, click the link to create a **public bin** instead. -1. From Request Bin, copy the endpoint URL. +1. Browse to [Webhook.site](https://webhook.site/). +1. Copy Your unique URL. -Your Request Bin is now waiting for the first request. +Your webhook endpoint is now waiting for the first request. -Next, let's configure a Contact Point in Grafana's Alerting UI to send notifications to our Request Bin. +Next, let's configure a Contact Point in Grafana's Alerting UI to send notifications to our webhook endpoint. 1. Return to Grafana. In Grafana's sidebar, hover over the **Alerting** (bell) icon and then click **Manage Contact points**. 1. Click **+ Add contact point**. -1. In **Name**, write **RequestBin**. +1. In **Name**, write **Webhook**. 1. In **Integration**, choose **Webhook**. -1. In **URL**, paste the endpoint to your request bin. +1. In **URL**, paste the endpoint to your webhook endpoint. -1. Click **Test**, and then click **Send test notification** to send a test alert notification to your request bin. -1. Navigate back to the Request Bin you created earlier. On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent. +1. Click **Test**, and then click **Send test notification** to send a test alert notification to your webhook endpoint. +1. Navigate back to the webhook endpoint you created earlier. On the left side, there's now a `POST /` entry. Click it to see what information Grafana sent. 1. Return to Grafana and click **Save contact point**. We have now created a dummy webhook endpoint and created a new Alerting Contact Point in Grafana. Now we can create an alert rule and link it to this new channel. @@ -423,23 +422,26 @@ We have now created a dummy webhook endpoint and created a new Alerting Contact Now that Grafana knows how to notify us, it's time to set up an alert rule: 1. In Grafana's sidebar, hover over the **Alerting** (bell) icon and then click **Alert rules**. + + In this tutorial, we use the advanced options for Grafana-managed alert rule creation. The advanced options let us define queries, expressions (used to manipulate the data), and the condition that must be met for the alert to be triggered (the default condition is the threshold). + 1. Click **+ New alert rule**. 1. For **Section 1**, name the rule `fundamentals-test`. -1. For **Section 2**, Find the **query A** box. Choose your Prometheus data source. Note that the rule type should automatically switch to Grafana-managed alert rule. -1. Switch to code mode by checking the Builder/Code toggle. +1. For **Section 2**, toggle the **Advanced options** button. +1. Find the **query A** box, and choose your Prometheus data source. 1. Enter the same Prometheus query that we used in our earlier panel: ``` sum(rate(tns_request_duration_seconds_count[5m])) by(route) ``` +1. Keep expressions **B** and **C** as they are. These expressions (Reduce and Threshold, respectively) are included by default when creating a new rule. Enter `0.2` as threshold value. You can read more about queries and conditions [here](/docs/grafana/latest/alerting/fundamentals/alert-rules/queries-conditions/#expression-queries). 1. Scroll down to bottom of section #2 and click the **Preview** button. You should see some data returned. -1. Keep expressions “B” and "C" as they are. These expressions (Reduce and Threshold, respectively) are included by default when creating a new rule. Expression "B", selects the last value of our query “A”, while the Threshold expression "C" will check if the last value from expression "B" is above a specific value. In addition, the Threshold expression is the alert rule condition by default. Enter `0.2` as threshold value. [You can read more about queries and conditions here](/docs/grafana/latest/alerting/fundamentals/alert-rules/queries-conditions/#expression-queries). -1. In **Section 3**, in Folder, create a new folder, by clicking `New folder` and typing a name for the folder. This folder will contain our alert rules. For example: `fundamentals`. Then, click `create`. -1. In the Evaluation group, repeat the above step to create a new one. We will name it `fundamentals` too. -1. Choose an Evaluation interval (how often the alert rule will be evaluated). For example, every `10s` (10 seconds). +1. In **Section 3**, in Folder, create a new folder, by clicking `New folder` and typing a name for the folder. This folder contains our alert rules. For example: `fundamentals`. Then, click `create`. +1. In the Evaluation group, repeat the above step to create a new one. Name it `fundamentals` too. +1. Choose an Evaluation interval (how often the alert rule are evaluated). For example, every `10s` (10 seconds). 1. Set the pending period. This is the time that a condition has to be met until the alert instance enters in Firing state and a notification is sent. Enter `0s`. For the purposes of this tutorial, the evaluation interval is intentionally short. This makes it easier to test. This setting makes Grafana wait until an alert instance has fired for a given time before Grafana sends the notification. -1. In **Section 4**, choose **RequestBin** as the **Contact point**. +1. In **Section 4**, choose **Webhook** as the **Contact point**. 1. Click **Save rule and exit** at the top of the page. ### Trigger a Grafana-managed alert rule @@ -449,7 +451,7 @@ We have now configured an alert rule and a contact point. Now let's see if we ca 1. Browse to [localhost:8081](http://localhost:8081). 1. Add a new title and URL, repeatedly click the vote button, or refresh the page to generate a traffic spike. -Once the query `sum(rate(tns_request_duration_seconds_count[5m])) by(route)` returns a value greater than `0.2` Grafana will trigger our alert rule. Browse to the Request Bin we created earlier and find the sent Grafana alert notification with details and metadata. +Once the query `sum(rate(tns_request_duration_seconds_count[5m])) by(route)` returns a value greater than `0.2` Grafana triggers our alert rule. Browse to the webhook endpoint we created earlier and find the sent Grafana alert notification with details and metadata. @@ -492,7 +494,7 @@ Let's see how we can configure this. {{< admonition type="tip" >}} -Check out our [advanced alerting tutorial](http://grafana.com/tutorials/alerting-get-started-pt2/) for more insights and tips. +In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /admonition >}} @@ -500,7 +502,7 @@ Check out our [advanced alerting tutorial](http://grafana.com/tutorials/alerting {{< docs/ignore >}} -> Check out our [advanced alerting tutorial](http://grafana.com/tutorials/alerting-get-started-pt2/) for more insights and tips. +> In [Get started with Grafana Alerting - Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/) you can advance your skills by exploring alert instances and notification routing. {{< /docs/ignore >}} diff --git a/docs/sources/upgrade-guide/when-to-upgrade/index.md b/docs/sources/upgrade-guide/when-to-upgrade/index.md index a60517fac9c..e3ee3e3bd12 100644 --- a/docs/sources/upgrade-guide/when-to-upgrade/index.md +++ b/docs/sources/upgrade-guide/when-to-upgrade/index.md @@ -42,24 +42,57 @@ We provide release documentation in multiple places to address different needs: ## When to expect releases -Currently, Grafana is on a monthly release cycle. Here’s a look at scheduled releases for 2024: +Currently, Grafana is on a monthly release cycle. Here’s a look at scheduled releases for 2024-early 2025: | **Anticipated release date** | **Grafana versions** | **Release type** | | ---------------------------- | -------------------- | ------------------ | | May 14, 2024 | 11 | Major and patching | | June 25, 2024 | 11.1 | Minor and patching | -| July 23, 2024 | 11.1.x | Patching | +| July 23, 2024 | Supported versions | Patching | | Aug. 27, 2024 | 11.2 | Minor and patching | -| Sept. 24, 2024 | 11.2.x | Patching | +| Sept. 24, 2024 | Supported versions | Patching | | Oct. 22, 2024 | 11.3 | Minor and patching | -| November/December 2024 | To be determined | To be determined | +| Nov. 19, 2024 | Supported versions | Patching | +| Dec. 5, 2024 | 11.4 | Minor and patching | +| Jan. 28, 2025 | 11.5 | Minor and patching | ### A few important notes - The schedule above outlines how we plan release dates. However, unforeseen events and circumstances may cause dates to change. - High severity security and feature degradation incidents will result in ad-hoc releases that are not scheduled ahead of time. - Patching releases are for the current (last released) minor version of Grafana. Additional older versions of Grafana may be included if there is a critical bug or security vulnerability that needs to be patched. -- A Grafana release freeze occurs for a week in November and again during the end of December. This does not apply to changes that may be required during the course of an operational or security incident. +- Each year Grafana implements two release freezes to accommodate for the holiday season. This year our release freezes will be implemented as outlined below: + +**Freeze dates:** + +- November 25, 2024 - December 2, 2024 +- December 19, 2024 - January 2, 2025 + +**Impacted products:** + +- Grafana in Grafana Cloud’s Grafana +- Grafana OSS +- Grafana Enterprise +- Grafana query service +- Multi-tenant data source services + +During these times, no scheduled releases will be executed. However, this does not apply to changes that may be required during the course of an operational or security incident. + +## Grafana security releases: improved version naming convention + +We've enhanced our naming convention for security release versions to make it easier to clearly identify our security releases from our standard patching releases. + +In the past, critical vulnerabilities triggered unscheduled releases that incremented the patch version (e.g., 10.3.0 to 10.3.1). However, we found that the naming convention for these releases didn't clearly communicate the nature of the update. For example, if there was a version change from 11.3.0 to 11.3.1, there was no indication whether it was a security fix, a bug fix, or a minor feature update. This lack of clarity led to confusion about the urgency and nature of the update.
          + +{{< admonition type="note" >}} +Docker does not allow the plus sign (`+`) in image tag names. A plus sign (`+`) will be a rendered as a dash (`-`) in the docker tag. +{{< /admonition >}} + +Our new approach directly addresses this issue. Going forward, security releases will be appended with "+security" to indicate that the release is the indicated version PLUS the security fix. + +**For example**: A release named "11.2.3+security-01" would consist of what was released in 11.2.3 PLUS the indicated security fix. Once released, the security fix will also then be automatically included in all future releases of the impacted version. + +This naming convention should make it easier to identify security updates and the Grafana version they're based on, allowing for a better understanding of the importance and urgency of each release. ## What to know about version support @@ -72,13 +105,14 @@ For self-managed Grafana (both Enterprise and OSS), the support for versions is Here is an overview of projected version support through 2024: -| **Version** | **Release date** | **Support end of life (EOL)** | -| ------------------------- | ----------------------- | ----------------------------- | -| 10.3 | January 2024 | October 2024 | -| 10.4 (Last minor of 10.0) | March 2024 | June 2025 (extended support) | -| 11.0 | May 2024 | February 2025 | -| 11.1 | June 2024 | March 2025 | -| 11.2 | August 2024 (tentative) | May 2025 (tentative) | +| **Version** | **Release date** | **Support end of life (EOL)** | +| ------------------------- | ---------------- | -------------------------------------- | +| 10.3 | January 2024 | NO LONGER SUPPORTED as of October 2024 | +| 10.4 (Last minor of 10.0) | March 2024 | June 2025 (extended support) | +| 11.0 | May 2024 | February 2025 | +| 11.1 | June 2024 | March 2025 | +| 11.2 | August 2024 | May 2025 | +| 11.3 | October 2024 | July 2025 | {{< admonition type="note" >}} Grafana 9.5.x was the last supported minor for the 9.0 major release and is no longer supported as of July 2024. diff --git a/docs/sources/whatsnew/whats-new-in-v11-2.md b/docs/sources/whatsnew/whats-new-in-v11-2.md index 1fe71c25854..921739746b8 100644 --- a/docs/sources/whatsnew/whats-new-in-v11-2.md +++ b/docs/sources/whatsnew/whats-new-in-v11-2.md @@ -277,3 +277,33 @@ _Available in public preview in all editions of Grafana_ [The SSO settings API](https://grafana.com/docs/grafana//developers/http_api/sso-settings/) has been updated to include support for LDAP settings. This feature is experimental behind the feature flag `ssoSettingsLDAP`. You will soon be able to configure LDAP from the UI and Terraform. + +### Reduce number of required fields from the SAML form + + + +_Generally available in Grafana Enterprise and Grafana Cloud Pro and Advanced_ + +The private key and certificate fields are no longer mandatory in the SAML form. To configure SAML without providing a private key and a certificate you have to opt out from using signed requests. + +{{< figure src="/media/docs/grafana/screenshot-grafana-11-2-saml-sign-requests.png" alt="Sign requests in SAML config form" >}} + +### Generate SAML certificate and private key + + + +_Generally available in Grafana Enterprise and Grafana Cloud Pro_ + +You can generate a new certificate and private key for SAML directly from the UI form. Click on the **Generate key and certificate** button from the **Sign requests** tab in the SAML form and then fill in the information you want to be embedded in your generated certificate. + +{{< video-embed src="/media/docs/grafana/screen-recording-11-2-generate-saml-certificate.mp4" >}} + +### OpenID Connect Discovery URL for Generic OAuth + + + +_Generally available in all editions of Grafana_ + +The OpenID Connect Discovery URL is available in the Generic OAuth form. The info extracted from this URL will be used to populate the Auth URL, Token URL and API URL fields. + +{{< video-embed src="/media/docs/grafana/screen-recording-11-2-openid-discovery-url.mp4" >}} diff --git a/docs/sources/whatsnew/whats-new-in-v11-3.md b/docs/sources/whatsnew/whats-new-in-v11-3.md index bc9cce95b15..aafe83f2695 100644 --- a/docs/sources/whatsnew/whats-new-in-v11-3.md +++ b/docs/sources/whatsnew/whats-new-in-v11-3.md @@ -22,7 +22,7 @@ weight: -45 Welcome to Grafana 11.3! Scenes-powered dashboards are now generally available and the Explore Logs plugin is now installed by default. The dashboard experience has also improved in other ways including the ability to trigger API calls from any canvas element with the new **Actions** option and an update to transformations so you can apply calculations to dynamic fields. We've also simplified the alert setup experience, added customizable announcement banners that admins can send to all users, and improved some default permissions. - +{{< youtube id="a0rdRbYPCls" >}} For even more detail about all the changes in this release, refer to the [changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md). For the specific steps we recommend when you upgrade to v11.3, check out our [Upgrade Guide](https://grafana.com/docs/grafana//upgrade-guide/upgrade-v11.3/). @@ -172,6 +172,22 @@ While **Explore Logs** is [GA in cloud](https://grafana.com/blog/2024/09/24/quer This is configured by the [`preinstall` configuration parameter](https://github.com/grafana/grafana/blob/9ece88d5852dceb90f83271e66902eece24f908f/conf/defaults.ini#L1748) in your Grafana configuration. For more information about Explore logs, refer to [the documentation](https://grafana.com/docs/grafana//explore/simplified-exploration/logs/). +## Correlations + +### Add correlations to external URLs in Explore + + + +_Generally available in all editions of Grafana_ + +Correlations is a feature that allows Grafana users to set up links between their data sources. Previously, the link generated would only be from one query to another—meaning results from a query could only generate links to open a second Explore pane with other query results. + +With this feature, users can now link to third party web-based software based on their search results. The format follows the standard Grafana format for using variables. + +{{< youtube id="KuVlzjaVc0Y" >}} + +[Documentation](https://grafana.com/docs/grafana//administration/correlations/) + ## Alerting ### Simplified query section for alert rule creation diff --git a/e2e/dashboards-suite/dashboard-templating.spec.ts b/e2e/dashboards-suite/dashboard-templating.spec.ts index 7ff5f905262..8ce63e4a975 100644 --- a/e2e/dashboards-suite/dashboard-templating.spec.ts +++ b/e2e/dashboards-suite/dashboard-templating.spec.ts @@ -9,6 +9,9 @@ describe('Dashboard templating', () => { // Open dashboard global variables and interpolation e2e.flows.openDashboard({ uid: 'HYaGDGIMk' }); + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const example = `Example: from=now-6h&to=now&timezone=${encodeURIComponent(timeZone)}`; + const items: string[] = []; const expectedItems: string[] = [ '__dashboard = Templating - Global variables and interpolation', @@ -36,7 +39,7 @@ describe('Dashboard templating', () => { `Server:text = All`, `Server:queryparam = var-Server=$__all`, `1 < 2`, - `Example: from=now-6h&to=now`, + example, ]; cy.get('.markdown-html li') @@ -51,10 +54,10 @@ describe('Dashboard templating', () => { }); // Check link interpolation is working correctly - cy.contains('a', 'Example: from=now-6h&to=now').should( + cy.contains('a', example).should( 'have.attr', 'href', - 'https://example.com/?from=now-6h&to=now' + `https://example.com/?from=now-6h&to=now&timezone=${encodeURIComponent(timeZone)}` ); }); }); diff --git a/e2e/old-arch/various-suite/loki-editor.spec.ts b/e2e/old-arch/various-suite/loki-editor.spec.ts deleted file mode 100644 index 42bf063c137..00000000000 --- a/e2e/old-arch/various-suite/loki-editor.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { e2e } from '../utils'; -import { waitForMonacoToLoad } from '../utils/support/monaco'; - -const dataSourceName = 'LokiEditor'; -const addDataSource = () => { - e2e.flows.addDataSource({ - type: 'Loki', - expectedAlertMessage: 'Unable to connect with Loki. Please check the server logs for more details.', - name: dataSourceName, - form: () => { - cy.get('#connection-url').type('http://loki-url:3100'); - }, - }); -}; - -describe('Loki Query Editor', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - afterEach(() => { - e2e.flows.revertAllChanges(); - }); - - it('Autocomplete features should work as expected.', () => { - addDataSource(); - - cy.intercept(/labels?/, (req) => { - req.reply({ status: 'success', data: ['instance', 'job', 'source'] }); - }); - - cy.intercept(/series?/, (req) => { - req.reply({ status: 'success', data: [{ instance: 'instance1' }] }); - }); - - // Go to Explore and choose Loki data source - e2e.pages.Explore.visit(); - e2e.components.DataSourcePicker.container().should('be.visible').click(); - cy.contains(dataSourceName).scrollIntoView().should('be.visible').click(); - - e2e.components.RadioButton.container().filter(':contains("Code")').click(); - - waitForMonacoToLoad(); - - // adds closing braces around empty value - e2e.components.QueryField.container().type('time('); - cy.get('.monaco-editor textarea:first').should(($el) => { - expect($el.val()).to.eq('time()'); - }); - - // removes closing brace when opening brace is removed - e2e.components.QueryField.container().type('{selectall}{backspace}avg_over_time({backspace}'); - cy.get('.monaco-editor textarea:first').should(($el) => { - expect($el.val()).to.eq('avg_over_time'); - }); - - // keeps closing brace when opening brace is removed and inner values exist - e2e.components.QueryField.container().type( - '{selectall}{backspace}time(test{leftArrow}{leftArrow}{leftArrow}{leftArrow}{backspace}' - ); - cy.get('.monaco-editor textarea:first').should(($el) => { - expect($el.val()).to.eq('timetest)'); - }); - - // overrides an automatically inserted brace - e2e.components.QueryField.container().type('{selectall}{backspace}time()'); - cy.get('.monaco-editor textarea:first').should(($el) => { - expect($el.val()).to.eq('time()'); - }); - - // does not override manually inserted braces - e2e.components.QueryField.container().type('{selectall}{backspace}))'); - cy.get('.monaco-editor textarea:first').should(($el) => { - expect($el.val()).to.eq('))'); - }); - - /** Runner plugin */ - - // Should execute the query when enter with shift is pressed - e2e.components.QueryField.container().type('{selectall}{backspace}{shift+enter}'); - cy.get('[data-testid="explore-no-data"]').should('be.visible'); - - /** Suggestions plugin */ - e2e.components.QueryField.container().type('{selectall}av'); - cy.contains('avg').should('be.visible'); - cy.contains('avg_over_time').should('be.visible'); - }); -}); diff --git a/e2e/old-arch/various-suite/loki-query-builder.spec.ts b/e2e/old-arch/various-suite/loki-query-builder.spec.ts deleted file mode 100644 index 45b527fca5b..00000000000 --- a/e2e/old-arch/various-suite/loki-query-builder.spec.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { e2e } from '../utils'; - -const MISSING_LABEL_FILTER_ERROR_MESSAGE = 'Select at least 1 label filter (label and value)'; -const dataSourceName = 'LokiBuilder'; -const addDataSource = () => { - e2e.flows.addDataSource({ - type: 'Loki', - expectedAlertMessage: 'Unable to connect with Loki. Please check the server logs for more details.', - name: dataSourceName, - form: () => { - cy.get('#connection-url').type('http://loki-url:3100'); - }, - }); -}; - -const finalQuery = 'rate({instance=~"instance1|instance2"} | logfmt | __error__=`` [$__auto]'; - -describe('Loki query builder', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - - cy.request({ - url: `${Cypress.env('BASE_URL')}/api/datasources/name/${dataSourceName}`, - failOnStatusCode: false, - }).then((response) => { - if (response.isOkStatusCode) { - return; - } - addDataSource(); - }); - }); - - it('should be able to use all modes', () => { - cy.intercept(/labels\?/, (req) => { - req.reply({ status: 'success', data: ['instance', 'job', 'source'] }); - }).as('labelsRequest'); - - cy.intercept(/series?/, (req) => { - req.reply({ status: 'success', data: [{ instance: 'instance1' }] }); - }); - - cy.intercept(/values/, (req) => { - req.reply({ status: 'success', data: ['instance1', 'instance2'] }); - }).as('valuesRequest'); - - cy.intercept(/index\/stats/, (req) => { - req.reply({ streams: 2, chunks: 2660, bytes: 2721792, entries: 14408 }); - }); - - // Go to Explore and choose Loki data source - e2e.pages.Explore.visit(); - e2e.components.DataSourcePicker.container().should('be.visible').click(); - cy.contains(dataSourceName).scrollIntoView().should('be.visible').click(); - - // Start in builder mode, click and choose query pattern - e2e.components.QueryBuilder.queryPatterns().click(); - cy.contains('Log query starters').click(); - cy.contains('Use this query').click(); - cy.contains('No pipeline errors').should('be.visible'); - cy.contains('Logfmt').should('be.visible'); - cy.contains('{} | logfmt | __error__=``').should('be.visible'); - - // Add operation - cy.contains('Operations').should('be.visible').click(); - cy.contains('Range functions').should('be.visible').click(); - cy.contains('Rate').should('be.visible').click(); - cy.contains('rate({} | logfmt | __error__=`` [$__auto]').should('be.visible'); - - // Check for expected error - cy.contains(MISSING_LABEL_FILTER_ERROR_MESSAGE).should('be.visible'); - - // Add labels to remove error - e2e.components.QueryBuilder.labelSelect().should('be.visible').click(); - // wait until labels are loaded and set on the component before starting to type - e2e.components.QueryBuilder.inputSelect().type('i'); - cy.wait('@labelsRequest'); - e2e.components.QueryBuilder.inputSelect().type('nstance{enter}'); - e2e.components.QueryBuilder.matchOperatorSelect() - .should('be.visible') - .click({ force: true }) - .children('div') - .children('input') - .type('=~{enter}', { force: true }); - e2e.components.QueryBuilder.valueSelect().should('be.visible').click(); - e2e.components.QueryBuilder.valueSelect().children('div').children('input').type('instance1{enter}'); - cy.wait('@valuesRequest'); - e2e.components.QueryBuilder.valueSelect().children('div').children('input').type('instance2{enter}'); - - cy.contains(MISSING_LABEL_FILTER_ERROR_MESSAGE).should('not.exist'); - cy.contains(finalQuery).should('be.visible'); - - // Change to code editor - e2e.components.RadioButton.container().filter(':contains("Code")').click(); - - // We need to test this manually because the final query is split into separate DOM elements using cy.contains(finalQuery).should('be.visible'); does not detect the query. - cy.contains('rate').should('be.visible'); - cy.contains('instance1|instance2').should('be.visible'); - cy.contains('logfmt').should('be.visible'); - cy.contains('__error__').should('be.visible'); - cy.contains('$__auto').should('be.visible'); - - // Checks the explain mode toggle - cy.contains('label', 'Explain').click(); - cy.contains('Fetch all log lines matching label filters.').should('be.visible'); - }); -}); diff --git a/e2e/old-arch/various-suite/loki-table-explore-to-dash.spec.ts b/e2e/old-arch/various-suite/loki-table-explore-to-dash.spec.ts deleted file mode 100644 index c3834e7e7b9..00000000000 --- a/e2e/old-arch/various-suite/loki-table-explore-to-dash.spec.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { e2e } from '../utils'; - -const dataSourceName = 'LokiEditor' + Date.now(); -const addDataSource = () => { - e2e.flows.addDataSource({ - type: 'Loki', - expectedAlertMessage: 'Unable to connect with Loki. Please check the server logs for more details.', - name: dataSourceName, - form: () => { - cy.get('#connection-url').type('http://loki-url:3100'); - }, - }); -}; - -const lokiQueryResult = { - status: 'success', - results: { - A: { - status: 200, - frames: [ - { - schema: { - refId: 'A', - meta: { - typeVersion: [0, 0], - custom: { - frameType: 'LabeledTimeValues', - }, - stats: [ - { - displayName: 'Summary: bytes processed per second', - unit: 'Bps', - value: 223921, - }, - { - displayName: 'Summary: total bytes processed', - unit: 'decbytes', - value: 4156, - }, - { - displayName: 'Summary: exec time', - unit: 's', - value: 0.01856, - }, - ], - executedQueryString: 'Expr: {targetLabelName="targetLabelValue"}', - }, - fields: [ - { - name: 'labels', - type: 'other', - typeInfo: { - frame: 'json.RawMessage', - }, - }, - { - name: 'Time', - type: 'time', - typeInfo: { - frame: 'time.Time', - }, - }, - { - name: 'Line', - type: 'string', - typeInfo: { - frame: 'string', - }, - }, - { - name: 'tsNs', - type: 'string', - typeInfo: { - frame: 'string', - }, - }, - { - name: 'id', - type: 'string', - typeInfo: { - frame: 'string', - }, - }, - ], - }, - data: { - values: [ - [ - { - targetLabelName: 'targetLabelValue', - instance: 'server\\1', - job: '"grafana/data"', - nonIndexed: 'value', - place: 'moon', - re: 'one.two$three^four', - source: 'data', - }, - ], - [1700077283237], - [ - '{"_entry":"log text with ANSI \\u001b[31mpart of the text\\u001b[0m [149702545]","counter":"22292","float":"NaN","wave":-0.5877852522916832,"label":"val3","level":"info"}', - ], - ['1700077283237000000'], - ['1700077283237000000_9b025d35'], - ], - }, - }, - ], - }, - }, -}; - -describe('Loki Query Editor', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - afterEach(() => { - e2e.flows.revertAllChanges(); - }); - - beforeEach(() => { - cy.window().then((win) => { - cy.setLocalStorage('grafana.featureToggles', 'logsExploreTableVisualisation=1'); - cy.setLocalStorage('grafana.featureToggles', 'dashboardScene=false'); - }); - }); - it('Should be able to add explore table to dashboard', () => { - addDataSource(); - - cy.intercept(/labels?/, (req) => { - req.reply({ status: 'success', data: ['instance', 'job', 'source'] }); - }); - - cy.intercept(/series?/, (req) => { - req.reply({ status: 'success', data: [{ instance: 'instance1' }] }); - }); - - cy.intercept(/\/api\/ds\/query\?ds_type=loki?/, (req) => { - req.reply(lokiQueryResult); - }); - - // Go to Explore and choose Loki data source - e2e.pages.Explore.visit(); - e2e.components.DataSourcePicker.container().should('be.visible').click(); - cy.contains(dataSourceName).scrollIntoView().should('be.visible').click(); - - cy.contains('Code').click({ force: true }); - - // Wait for lazy loading - // const monacoLoadingText = 'Loading...'; - - // e2e.components.QueryField.container().should('be.visible').should('have.text', monacoLoadingText); - e2e.components.QueryField.container() - .find('.view-overlays[role="presentation"]') - .get('.cdr') - .then(($el) => { - const win = $el[0].ownerDocument.defaultView; - const after = win.getComputedStyle($el[0], '::after'); - const content = after.getPropertyValue('content'); - expect(content).to.eq('"Enter a Loki query (run with Shift+Enter)"'); - }); - - // Write a simple query - e2e.components.QueryField.container().type('query').type('{instance="instance1"'); - cy.get('.monaco-editor textarea:first').should(($el) => { - expect($el.val()).to.eq('query{instance="instance1"}'); - }); - - // Submit the query - e2e.components.QueryField.container().type('{shift+enter}'); - // Assert the no-data message is not visible - cy.get('[data-testid="explore-no-data"]').should('not.exist'); - - // Click on the table toggle - cy.contains('Table').click({ force: true }); - - // One row with two cells - cy.get('[role="cell"]').should('have.length', 2); - - cy.contains('label', 'targetLabelName').should('be.visible'); - cy.contains('label', 'targetLabelName').click(); - cy.contains('label', 'targetLabelName').within(() => { - cy.get('input[type="checkbox"]').check({ force: true }); - }); - - cy.contains('label', 'targetLabelName').within(() => { - cy.get('input[type="checkbox"]').should('be.checked'); - }); - - const exploreCells = cy.get('[role="cell"]'); - - // Now we should have a row with 3 columns - exploreCells.should('have.length', 3); - // And a value of "targetLabelValue" - exploreCells.should('contain', 'targetLabelValue'); - - const addToButton = cy.get('[aria-label="Add"]'); - addToButton.should('be.visible'); - addToButton.click(); - - const addToDashboardButton = cy.get('[aria-label="Add to dashboard"]'); - - // Now let's add this to a dashboard - addToDashboardButton.should('be.visible'); - addToDashboardButton.click(); - - const addPanelToDashboardButton = cy.contains('Add panel to dashboard'); - addPanelToDashboardButton.should('be.visible'); - - const openDashboardButton = cy.contains('Open dashboard'); - openDashboardButton.should('be.visible'); - openDashboardButton.click(); - - const panel = cy.get('[data-panelid="1"]'); - panel.should('be.visible'); - - const cells = panel.find('[role="table"] [role="cell"]'); - // Should have 3 columns - cells.should('have.length', 3); - // Cells contain strings found in log line - cells.contains('"wave":-0.5877852522916832'); - - // column has correct value of "targetLabelValue", need to requery the DOM because of the .contains call above - cy.get('[data-panelid="1"]').find('[role="table"] [role="cell"]').contains('targetLabelValue'); - }); -}); diff --git a/e2e/old-arch/various-suite/trace-view-scrolling.spec.ts b/e2e/old-arch/various-suite/trace-view-scrolling.spec.ts index be24aa79e20..a713ddd2230 100644 --- a/e2e/old-arch/various-suite/trace-view-scrolling.spec.ts +++ b/e2e/old-arch/various-suite/trace-view-scrolling.spec.ts @@ -32,7 +32,7 @@ describe('Trace view', () => { e2e.components.TraceViewer.spanBar() .its('length') .then((oldLength) => { - e2e.pages.Explore.General.scrollView().children('.scrollbar-view').scrollTo('center'); + e2e.pages.Explore.General.scrollView().children().first().scrollTo('center'); // After scrolling we should load more spans e2e.components.TraceViewer.spanBar().should(($span) => { diff --git a/e2e/plugin-e2e/mysql/mocks/mysql.mocks.ts b/e2e/plugin-e2e/mysql/mocks/mysql.mocks.ts index eb213f0aafc..a72c83903f3 100644 --- a/e2e/plugin-e2e/mysql/mocks/mysql.mocks.ts +++ b/e2e/plugin-e2e/mysql/mocks/mysql.mocks.ts @@ -21,14 +21,14 @@ export const tablesResponse = { }, }; -export const fieldsResponse = { +export const fieldsResponse = (refId: string) => ({ results: { - fields: { + [refId]: { status: 200, frames: [ { schema: { - refId: 'fields', + refId, meta: { executedQueryString: "SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'DataMaker' AND table_name = 'RandomIntsWithTimes' ORDER BY column_name", @@ -48,7 +48,7 @@ export const fieldsResponse = { ], }, }, -}; +}); export const datasetResponse = { results: { diff --git a/e2e/plugin-e2e/mysql/mysql.spec.ts b/e2e/plugin-e2e/mysql/mysql.spec.ts index 8adfd456fbc..8d56d882f27 100644 --- a/e2e/plugin-e2e/mysql/mysql.spec.ts +++ b/e2e/plugin-e2e/mysql/mysql.spec.ts @@ -1,29 +1,10 @@ import { selectors } from '@grafana/e2e-selectors'; import { expect, test } from '@grafana/plugin-e2e'; -import { - tablesResponse, - fieldsResponse, - datasetResponse, - normalTableName, - tableNameWithSpecialCharacter, -} from './mocks/mysql.mocks'; +import { normalTableName, tableNameWithSpecialCharacter } from './mocks/mysql.mocks'; +import { mockDataSourceRequest } from './utils'; -test.beforeEach(async ({ context, selectors, explorePage }) => { - await explorePage.datasource.set('gdev-mysql'); - await context.route(selectors.apis.DataSource.queryPattern, async (route, request) => { - switch (request.postDataJSON().queries[0].refId) { - case 'tables': - return route.fulfill({ json: tablesResponse, status: 200 }); - case 'fields': - return route.fulfill({ json: fieldsResponse, status: 200 }); - case 'datasets': - return route.fulfill({ json: datasetResponse, status: 200 }); - default: - return route.continue(); - } - }); -}); +test.beforeEach(mockDataSourceRequest); test('code editor autocomplete should handle table name escaping/quoting', async ({ explorePage, selectors, page }) => { await page.getByLabel('Code').check(); diff --git a/e2e/plugin-e2e/mysql/utils.ts b/e2e/plugin-e2e/mysql/utils.ts new file mode 100644 index 00000000000..6d585a5d1b9 --- /dev/null +++ b/e2e/plugin-e2e/mysql/utils.ts @@ -0,0 +1,23 @@ +import { PlaywrightTestArgs } from '@playwright/test'; + +import { PluginFixture } from '@grafana/plugin-e2e'; + +import { datasetResponse, fieldsResponse, tablesResponse } from './mocks/mysql.mocks'; + +export async function mockDataSourceRequest({ context, explorePage, selectors }: PlaywrightTestArgs & PluginFixture) { + await explorePage.datasource.set('gdev-mysql'); + await context.route(selectors.apis.DataSource.queryPattern, async (route, request) => { + const refId = request.postDataJSON().queries[0].refId; + if (/fields-.*/g.test(refId)) { + return route.fulfill({ json: fieldsResponse(refId), status: 200 }); + } + switch (refId) { + case 'tables': + return route.fulfill({ json: tablesResponse, status: 200 }); + case 'datasets': + return route.fulfill({ json: datasetResponse, status: 200 }); + default: + return route.continue(); + } + }); +} diff --git a/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts b/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts new file mode 100644 index 00000000000..706364df9d8 --- /dev/null +++ b/e2e/plugin-e2e/mysql/visual-query-builder.spec.ts @@ -0,0 +1,47 @@ +import { selectors } from '@grafana/e2e-selectors'; +import { test, expect } from '@grafana/plugin-e2e'; + +import { normalTableName } from './mocks/mysql.mocks'; +import { mockDataSourceRequest } from './utils'; + +test.beforeEach(mockDataSourceRequest); + +test.use({ featureToggles: { sqlQuerybuilderFunctionParameters: true } }); + +test('visual query builder should handle macros', async ({ explorePage, page }) => { + await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.headerTableSelector).click(); + await page.getByText(normalTableName, { exact: true }).click(); + + // Open Data operations + await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectAggregation).click(); + const select = page.getByLabel('Select options menu'); + await select.locator(page.getByText('$__timeGroupAlias')).click(); + + // Open column selector + await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectFunctionParameter('Column')).click(); + await select.locator(page.getByText('createdAt')).click(); + + // Open Interval selector + await explorePage + .getByGrafanaSelector(selectors.components.SQLQueryEditor.selectFunctionParameter('Interval')) + .click(); + await select.locator(page.getByText('$__interval')).click(); + + await page.getByRole('button', { name: 'Add column' }).click(); + + await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectAggregation).nth(1).click(); + await select.locator(page.getByText('AVG')).click(); + + await explorePage + .getByGrafanaSelector(selectors.components.SQLQueryEditor.selectFunctionParameter('Column')) + .nth(1) + .click(); + await select.locator(page.getByText('bigint')).click(); + + // Validate the query + await expect( + explorePage.getByGrafanaSelector(selectors.components.CodeEditor.container).getByRole('textbox') + ).toHaveValue( + `SELECT\n $__timeGroupAlias(createdAt, $__interval),\n AVG(\`bigint\`)\nFROM\n DataMaker.normalTable\nLIMIT\n 50` + ); +}); diff --git a/e2e/plugin-e2e/plugin-e2e-api-tests/README.md b/e2e/plugin-e2e/plugin-e2e-api-tests/README.md index 477a6c384dc..077f7d78d74 100644 --- a/e2e/plugin-e2e/plugin-e2e-api-tests/README.md +++ b/e2e/plugin-e2e/plugin-e2e-api-tests/README.md @@ -1,8 +1,3 @@ # @grafana/plugin-e2e API tests -The purpose of the E2E tests in this directory is not to test the plugins per se - it's to verify that the fixtures, models and expect matchers provided by the [`@grafana/plugin-e2e`](https://github.com/grafana/plugin-tools/tree/main/packages/plugin-e2e) package are compatible with the latest version of Grafana. If you find that any of these tests are failing, it's probably due to one of the following reasons: - -- you have changed a value of a selector defined in @grafana/e2e-selector -- you have made structural changes to the UI - -For information on how to address this, follow the instructions in the [contributing guidelines](https://github.com/grafana/plugin-tools/blob/main/packages/plugin-e2e/CONTRIBUTING.md#how-to-fix-broken-test-scenarios-after-changes-in-grafana) for the @grafana/plugin-e2e package in the plugin-tools repository. +The purpose of the E2E tests in this directory is not to test the plugins per se - it's to verify that the fixtures, models and expect matchers provided by the [`@grafana/plugin-e2e`](https://github.com/grafana/plugin-tools/tree/main/packages/plugin-e2e) package are compatible with the latest version of Grafana. If you find that any of these tests are failing, it's likely because you have made changes in the Grafana UI that breaks the end-to-end testing APIs in plugin-e2e. For information on how to address this, follow the instructions in the [contributing guidelines](https://github.com/grafana/plugin-tools/blob/main/packages/plugin-e2e/CONTRIBUTING.md#how-to-fix-broken-test-scenarios-after-changes-in-grafana) for the @grafana/plugin-e2e package in the plugin-tools repository. diff --git a/e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/dashboard.ts b/e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/dashboard.spec.ts similarity index 100% rename from e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/dashboard.ts rename to e2e/plugin-e2e/plugin-e2e-api-tests/as-admin-user/dashboard.spec.ts diff --git a/e2e/test-plugins/grafana-extensionstest-app/package.json b/e2e/test-plugins/grafana-extensionstest-app/package.json index fc38d4f3e24..41b2b728345 100644 --- a/e2e/test-plugins/grafana-extensionstest-app/package.json +++ b/e2e/test-plugins/grafana-extensionstest-app/package.json @@ -11,7 +11,6 @@ "author": "Grafana Labs", "license": "Apache-2.0", "devDependencies": { - "@grafana/eslint-config": "7.0.0", "@grafana/plugin-configs": "11.4.0-pre", "@types/lodash": "4.17.7", "@types/node": "20.14.14", diff --git a/e2e/test-plugins/grafana-test-datasource/CHANGELOG.md b/e2e/test-plugins/grafana-test-datasource/CHANGELOG.md new file mode 100644 index 00000000000..825c32f0d03 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/e2e/test-plugins/grafana-test-datasource/README.md b/e2e/test-plugins/grafana-test-datasource/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/e2e/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx b/e2e/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx new file mode 100644 index 00000000000..2c46992a5d9 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx @@ -0,0 +1,96 @@ +import { ChangeEvent } from 'react'; +import { Checkbox, InlineField, InlineSwitch, Input, SecretInput, Select } from '@grafana/ui'; +import { DataSourcePluginOptionsEditorProps, SelectableValue, toOption } from '@grafana/data'; +import { MyDataSourceOptions, MySecureJsonData } from '../types'; + +interface Props extends DataSourcePluginOptionsEditorProps {} + +export function ConfigEditor(props: Props) { + const { onOptionsChange, options } = props; + const { jsonData, secureJsonFields, secureJsonData } = options; + + const onJsonDataChange = (key: string, value: string | number | boolean) => { + onOptionsChange({ + ...options, + jsonData: { + ...jsonData, + [key]: value, + }, + }); + }; + + // Secure field (only sent to the backend) + const onSecureJsonDataChange = (key: string, value: string | number) => { + onOptionsChange({ + ...options, + secureJsonData: { + [key]: value, + }, + }); + }; + + const onResetAPIKey = () => { + onOptionsChange({ + ...options, + secureJsonFields: { + ...options.secureJsonFields, + apiKey: false, + }, + secureJsonData: { + ...options.secureJsonData, + apiKey: '', + }, + }); + }; + + return ( + <> + + ) => onJsonDataChange('path', e.target.value)} + value={jsonData.path} + placeholder="Enter the path, e.g. /api/v1" + width={40} + /> + + + ) => onSecureJsonDataChange('path', e.target.value)} + /> + + + ) => onJsonDataChange('switchEnabled', e.target.checked)} + /> + + + ) => onJsonDataChange('checkboxEnabled', e.target.checked)} + /> + + + + + + + + + ); +} diff --git a/e2e/test-plugins/grafana-test-datasource/datasource.ts b/e2e/test-plugins/grafana-test-datasource/datasource.ts new file mode 100644 index 00000000000..d4e61516512 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/datasource.ts @@ -0,0 +1,93 @@ +import { getBackendSrv, isFetchError } from '@grafana/runtime'; +import { + CoreApp, + DataQueryRequest, + DataQueryResponse, + DataSourceApi, + DataSourceInstanceSettings, + createDataFrame, + FieldType, +} from '@grafana/data'; + +import { MyQuery, MyDataSourceOptions, DEFAULT_QUERY, DataSourceResponse } from './types'; +import { lastValueFrom } from 'rxjs'; + +export class DataSource extends DataSourceApi { + baseUrl: string; + + constructor(instanceSettings: DataSourceInstanceSettings) { + super(instanceSettings); + this.baseUrl = instanceSettings.url!; + } + + getDefaultQuery(_: CoreApp): Partial { + return DEFAULT_QUERY; + } + + filterQuery(query: MyQuery): boolean { + // if no query has been provided, prevent the query from being executed + return !!query.queryText; + } + + async query(options: DataQueryRequest): Promise { + const { range } = options; + const from = range!.from.valueOf(); + const to = range!.to.valueOf(); + + // Return a constant for each query. + const data = options.targets.map((target) => { + return createDataFrame({ + refId: target.refId, + fields: [ + { name: 'Time', values: [from, to], type: FieldType.time }, + { name: 'Value', values: [target.constant, target.constant], type: FieldType.number }, + ], + }); + }); + + return { data }; + } + + async request(url: string, params?: string) { + const response = getBackendSrv().fetch({ + url: `${this.baseUrl}${url}${params?.length ? `?${params}` : ''}`, + }); + return lastValueFrom(response); + } + + /** + * Checks whether we can connect to the API. + */ + async testDatasource() { + const defaultErrorMessage = 'Cannot connect to API'; + + try { + const response = await this.request('/health'); + if (response.status === 200) { + return { + status: 'success', + message: 'Success', + }; + } else { + return { + status: 'error', + message: response.statusText ? response.statusText : defaultErrorMessage, + }; + } + } catch (err) { + let message = ''; + if (typeof err === 'string') { + message = err; + } else if (isFetchError(err)) { + message = 'Fetch error: ' + (err.statusText ? err.statusText : defaultErrorMessage); + if (err.data && err.data.error && err.data.error.code) { + message += ': ' + err.data.error.code + '. ' + err.data.error.message; + } + } + return { + status: 'error', + message, + }; + } + } +} diff --git a/e2e/test-plugins/grafana-test-datasource/img/logo.svg b/e2e/test-plugins/grafana-test-datasource/img/logo.svg new file mode 100644 index 00000000000..3d284dea3af --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/e2e/test-plugins/grafana-test-datasource/module.ts b/e2e/test-plugins/grafana-test-datasource/module.ts new file mode 100644 index 00000000000..b8231ebbddb --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/module.ts @@ -0,0 +1,9 @@ +import { DataSourcePlugin } from '@grafana/data'; +import { DataSource } from './datasource'; +import { ConfigEditor } from './components/ConfigEditor'; +import { QueryEditor } from './components/QueryEditor'; +import { MyQuery, MyDataSourceOptions } from './types'; + +export const plugin = new DataSourcePlugin(DataSource) + .setConfigEditor(ConfigEditor) + .setQueryEditor(QueryEditor); diff --git a/e2e/test-plugins/grafana-test-datasource/package.json b/e2e/test-plugins/grafana-test-datasource/package.json new file mode 100644 index 00000000000..ea3c0c71052 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/package.json @@ -0,0 +1,47 @@ +{ + "name": "@test-plugins/grafana-e2etest-datasource", + "version": "11.4.0-pre", + "private": true, + "scripts": { + "build": "webpack -c ./webpack.config.ts --env production", + "dev": "webpack -w -c ./webpack.config.ts --env development", + "typecheck": "tsc --noEmit", + "lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx ." + }, + "author": "Grafana", + "license": "Apache-2.0", + "devDependencies": { + "@grafana/plugin-configs": "11.4.0-pre", + "@types/lodash": "4.17.7", + "@types/node": "20.14.14", + "@types/prismjs": "1.26.4", + "@types/react": "18.3.3", + "@types/react-dom": "18.2.25", + "@types/semver": "7.5.8", + "@types/uuid": "9.0.8", + "glob": "10.4.1", + "ts-node": "10.9.2", + "typescript": "5.5.4", + "webpack": "5.95.0", + "webpack-merge": "5.10.0" + }, + "engines": { + "node": ">=20" + }, + "dependencies": { + "@emotion/css": "11.11.2", + "@grafana/data": "workspace:*", + "@grafana/runtime": "workspace:*", + "@grafana/schema": "workspace:*", + "@grafana/ui": "workspace:*", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "^6.22.0", + "rxjs": "7.8.1", + "tslib": "2.6.3" + }, + "peerDependencies": { + "@grafana/runtime": "*" + }, + "packageManager": "yarn@4.4.0" +} diff --git a/e2e/test-plugins/grafana-test-datasource/plugin.json b/e2e/test-plugins/grafana-test-datasource/plugin.json new file mode 100644 index 00000000000..8ca25220fbd --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/plugin.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json", + "type": "datasource", + "name": "Test", + "id": "grafana-e2etest-datasource", + "metrics": true, + "info": { + "description": "", + "author": { + "name": "Grafana" + }, + "keywords": ["datasource"], + "logos": { + "small": "img/logo.svg", + "large": "img/logo.svg" + }, + "links": [], + "screenshots": [], + "version": "%VERSION%", + "updated": "%TODAY%" + }, + "dependencies": { + "grafanaDependency": ">=10.4.0", + "plugins": [] + } +} diff --git a/e2e/test-plugins/grafana-test-datasource/tests/configEditor.spec.ts b/e2e/test-plugins/grafana-test-datasource/tests/configEditor.spec.ts new file mode 100644 index 00000000000..b72ff0580b5 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/tests/configEditor.spec.ts @@ -0,0 +1,39 @@ +import { test, expect, DataSourceConfigPage } from '@grafana/plugin-e2e'; + +// The following tests verify that label and input field association is working correctly. +// If these tests break, e2e tests in external plugins will break too. + +test.describe('config editor ', () => { + let configPage: DataSourceConfigPage; + test.beforeEach(async ({ createDataSourceConfigPage }) => { + configPage = await createDataSourceConfigPage({ type: 'grafana-e2etest-datasource' }); + }); + + test('text input field', async ({ page }) => { + const field = page.getByRole('textbox', { name: 'API key' }); + await expect(field).toBeEmpty(); + await field.fill('test text'); + await expect(field).toHaveValue('test text'); + }); + + test('switch field', async ({ page }) => { + const field = page.getByLabel('Switch Enabled'); + await expect(field).not.toBeChecked(); + await field.check(); + await expect(field).toBeChecked(); + }); + + test('checkbox field', async ({ page }) => { + const field = page.getByRole('checkbox', { name: 'Checkbox Enabled' }); + await expect(field).not.toBeChecked(); + await field.check({ force: true }); + await expect(field).toBeChecked(); + }); + + test('select field', async ({ page, selectors }) => { + const field = page.getByRole('combobox', { name: 'Auth type' }); + await field.click(); + const option = selectors.components.Select.option; + await expect(configPage.getByGrafanaSelector(option)).toHaveText(['keys', 'credentials']); + }); +}); diff --git a/e2e/test-plugins/grafana-test-datasource/tsconfig.json b/e2e/test-plugins/grafana-test-datasource/tsconfig.json new file mode 100644 index 00000000000..40352099203 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "types": ["node", "jest", "@testing-library/jest-dom"] + }, + "extends": "@grafana/plugin-configs/tsconfig.json", + "include": ["."] +} diff --git a/e2e/test-plugins/grafana-test-datasource/types.ts b/e2e/test-plugins/grafana-test-datasource/types.ts new file mode 100644 index 00000000000..b5c8d73b031 --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/types.ts @@ -0,0 +1,37 @@ +import { DataSourceJsonData } from '@grafana/data'; +import { DataQuery } from '@grafana/schema'; + +export interface MyQuery extends DataQuery { + queryText?: string; + constant: number; +} + +export const DEFAULT_QUERY: Partial = { + constant: 6.5, +}; + +export interface DataPoint { + Time: number; + Value: number; +} + +export interface DataSourceResponse { + datapoints: DataPoint[]; +} + +/** + * These are options configured for each DataSource instance + */ +export interface MyDataSourceOptions extends DataSourceJsonData { + switchEnabled: boolean; + checkboxEnabled: boolean; + authType: string; + path?: string; +} + +/** + * Value that is used in the backend, but never sent over HTTP to the frontend + */ +export interface MySecureJsonData { + apiKey?: string; +} diff --git a/e2e/test-plugins/grafana-test-datasource/webpack.config.ts b/e2e/test-plugins/grafana-test-datasource/webpack.config.ts new file mode 100644 index 00000000000..3303ed94f3c --- /dev/null +++ b/e2e/test-plugins/grafana-test-datasource/webpack.config.ts @@ -0,0 +1,44 @@ +import CopyWebpackPlugin from 'copy-webpack-plugin'; +import grafanaConfig from '@grafana/plugin-configs/webpack.config'; +import { mergeWithCustomize, unique } from 'webpack-merge'; +import { Configuration } from 'webpack'; + +function skipFiles(f: string): boolean { + if (f.includes('/dist/')) { + // avoid copying files already in dist + return false; + } + if (f.includes('/node_modules/')) { + // avoid copying tsconfig.json + return false; + } + if (f.includes('/package.json')) { + // avoid copying package.json + return false; + } + return true; +} + +const config = async (env: Record): Promise => { + const baseConfig = await grafanaConfig(env); + const customConfig = { + plugins: [ + new CopyWebpackPlugin({ + patterns: [ + // To `compiler.options.output` + { from: 'README.md', to: '.', force: true }, + { from: 'plugin.json', to: '.' }, + { from: 'CHANGELOG.md', to: '.', force: true }, + { from: '**/*.json', to: '.', filter: skipFiles }, + { from: '**/*.svg', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional + ], + }), + ], + }; + + return mergeWithCustomize({ + customizeArray: unique('plugins', ['CopyPlugin'], (plugin) => plugin.constructor && plugin.constructor.name), + })(baseConfig, customConfig); +}; + +export default config; diff --git a/e2e/various-suite/loki-table-explore-to-dash.spec.ts b/e2e/various-suite/loki-table-explore-to-dash.spec.ts index 088745c7fca..1477f6f0165 100644 --- a/e2e/various-suite/loki-table-explore-to-dash.spec.ts +++ b/e2e/various-suite/loki-table-explore-to-dash.spec.ts @@ -110,8 +110,7 @@ const lokiQueryResult = { }, }; -// Skipping due to race conditions with same old arch test e2e/various-suite/loki-table-explore-to-dash.spec.ts -describe.skip('Loki Query Editor', () => { +describe('Loki Query Editor', () => { beforeEach(() => { e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); }); @@ -121,9 +120,7 @@ describe.skip('Loki Query Editor', () => { }); beforeEach(() => { - cy.window().then((win) => { - win.localStorage.setItem('grafana.featureToggles', 'logsExploreTableVisualisation=1'); - }); + cy.setLocalStorage('grafana.featureToggles', 'logsExploreTableVisualisation=1'); }); it('Should be able to add explore table to dashboard', () => { addDataSource(); @@ -212,7 +209,7 @@ describe.skip('Loki Query Editor', () => { openDashboardButton.should('be.visible'); openDashboardButton.click(); - const panel = cy.get('[data-panelid="1"]'); + const panel = cy.get('[data-viz-panel-key="panel-1"]'); panel.should('be.visible'); const cells = panel.find('[role="table"] [role="cell"]'); @@ -222,6 +219,6 @@ describe.skip('Loki Query Editor', () => { cells.contains('"wave":-0.5877852522916832'); // column has correct value of "targetLabelValue", need to requery the DOM because of the .contains call above - cy.get('[data-panelid="1"]').find('[role="table"] [role="cell"]').contains('targetLabelValue'); + cy.get('[data-viz-panel-key="panel-1"]').find('[role="table"] [role="cell"]').contains('targetLabelValue'); }); }); diff --git a/e2e/various-suite/trace-view-scrolling.spec.ts b/e2e/various-suite/trace-view-scrolling.spec.ts index be24aa79e20..a713ddd2230 100644 --- a/e2e/various-suite/trace-view-scrolling.spec.ts +++ b/e2e/various-suite/trace-view-scrolling.spec.ts @@ -32,7 +32,7 @@ describe('Trace view', () => { e2e.components.TraceViewer.spanBar() .its('length') .then((oldLength) => { - e2e.pages.Explore.General.scrollView().children('.scrollbar-view').scrollTo('center'); + e2e.pages.Explore.General.scrollView().children().first().scrollTo('center'); // After scrolling we should load more spans e2e.components.TraceViewer.spanBar().should(($span) => { diff --git a/e2e/various-suite/verify-i18n.spec.ts b/e2e/various-suite/verify-i18n.spec.ts new file mode 100644 index 00000000000..3de37d5d863 --- /dev/null +++ b/e2e/various-suite/verify-i18n.spec.ts @@ -0,0 +1,65 @@ +import { e2e } from '../utils'; +import { fromBaseUrl } from '../utils/support/url'; + +describe('Verify i18n', () => { + const I18N_USER = 'i18n-test'; + const I18N_PASSWORD = 'i18n-test'; + + // create a new user to isolate the language changes from other tests + before(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + cy.request({ + method: 'POST', + url: fromBaseUrl('/api/admin/users'), + body: { + email: I18N_USER, + login: I18N_USER, + name: I18N_USER, + password: I18N_PASSWORD, + }, + }).then((response) => { + cy.wrap(response.body.uid).as('uid'); + }); + }); + + // remove the user created in the before hook + after(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + cy.get('@uid').then((uid) => { + cy.request({ + method: 'DELETE', + url: fromBaseUrl(`/api/admin/users/${uid}`), + }); + }); + }); + + beforeEach(() => { + e2e.flows.login(I18N_USER, I18N_PASSWORD); + }); + + // map between languages in the language picker and the corresponding translation of the 'Language' label + const languageMap: Record = { + Deutsch: 'Sprache', + English: 'Language', + Español: 'Idioma', + Français: 'Langue', + 'Português Brasileiro': 'Idioma', + '中文(简体)': '语言', + }; + + // basic test which loops through the defined languages in the picker + // and verifies that the corresponding label is translated correctly + it('loads all the languages correctly', () => { + cy.visit('/profile'); + const LANGUAGE_SELECTOR = '[id="locale-select"]'; + + cy.wrap(Object.entries(languageMap)).each(([language, label]: [string, string]) => { + cy.get(LANGUAGE_SELECTOR).should('not.be.disabled'); + cy.get(LANGUAGE_SELECTOR).click(); + cy.get(LANGUAGE_SELECTOR).clear().type(language).type('{downArrow}{enter}'); + e2e.components.UserProfile.preferencesSaveButton().click(); + cy.contains('label', label).should('be.visible'); + cy.get(LANGUAGE_SELECTOR).should('have.value', language); + }); + }); +}); diff --git a/emails/templates/passwordless_verify_existing_user.mjml b/emails/templates/passwordless_verify_existing_user.mjml new file mode 100644 index 00000000000..1c583bc03ad --- /dev/null +++ b/emails/templates/passwordless_verify_existing_user.mjml @@ -0,0 +1,51 @@ + + + + + + + + {{ Subject .Subject .TemplateData "Verify your email" }} + + + + + + + + + + +

          Please verify your email

          +
          + + Copy and paste the confirmation code into the login form to verify your email address. This confirmation code + will expire in {{ .Expire }} minutes. + +
          +
          + + + {{ .ConfirmationCode }} + + + + + Alternatively, you can use the button below to verify your email address. + + Verify your email + + You can also copy and paste this link into your browser directly: + + {{ .AppUrl }}login?code={{ .Code }}&confirmationCode={{ .ConfirmationCode }} + + + +
          + + + +
          +
          diff --git a/emails/templates/passwordless_verify_existing_user.txt b/emails/templates/passwordless_verify_existing_user.txt new file mode 100644 index 00000000000..ccc8f1f3d1d --- /dev/null +++ b/emails/templates/passwordless_verify_existing_user.txt @@ -0,0 +1,10 @@ +[[HiddenSubject .Subject "Verify your email"]] + +Hi, + +Copy and paste the email verification code: +[[.ConfirmationCode]] +into the login form to verify your email address. This confirmation code will expire in {{ .Expire }} minutes. +Alternatively, you can use the button below to verify your email address. + +[[.AppUrl]]login/?code=[[.Code]]&confirmationCode=[[.ConfirmationCode]] diff --git a/emails/templates/passwordless_verify_new_user.mjml b/emails/templates/passwordless_verify_new_user.mjml new file mode 100644 index 00000000000..d08457ea7e5 --- /dev/null +++ b/emails/templates/passwordless_verify_new_user.mjml @@ -0,0 +1,53 @@ + + + + + + + + {{ Subject .Subject .TemplateData "Welcome to Grafana, please complete your sign up!" }} + + + + + + + + + + +

          Please complete your signup

          +
          + + Copy and paste the confirmation code into the sign up form to verify your email address. This confirmation + code will expire in {{ .Expire }} minutes. + +
          +
          + + + {{ .ConfirmationCode }} + + + + + Alternatively, you can use the button below to complete your sign up. + + Complete Sign Up + + You can also copy and paste this link into your browser directly: + + {{ .AppUrl }}login?code={{ .Code }}&confirmationCode={{ .ConfirmationCode }}&signup=true + + + +
          + + + +
          +
          diff --git a/emails/templates/passwordless_verify_new_user.txt b/emails/templates/passwordless_verify_new_user.txt new file mode 100644 index 00000000000..938d1dab934 --- /dev/null +++ b/emails/templates/passwordless_verify_new_user.txt @@ -0,0 +1,10 @@ +[[HiddenSubject .Subject "Welcome to Grafana, please complete your signup!"]] + +Hi, + +Copy and paste the email verification code: +[[.ConfirmationCode]] +into the sign up form to verify your email address. This confirmation code will expire in {{ .Expire }} minutes. +Alternatively, you can use the button below to verify your email address. + +[[.AppUrl]]login/?code=[[.Code]]&confirmationCode=[[.ConfirmationCode]] diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000000..450fba7f66d --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,325 @@ +// @ts-check +const emotionPlugin = require('@emotion/eslint-plugin'); +const { fixupPluginRules } = require('@eslint/compat'); +const importPlugin = require('eslint-plugin-import'); +const jestPlugin = require('eslint-plugin-jest'); +const jestDomPlugin = require('eslint-plugin-jest-dom'); +const jsxA11yPlugin = require('eslint-plugin-jsx-a11y'); +const lodashPlugin = require('eslint-plugin-lodash'); +const barrelPlugin = require('eslint-plugin-no-barrel-files'); +const reactPlugin = require('eslint-plugin-react'); +const testingLibraryPlugin = require('eslint-plugin-testing-library'); +const unicornPlugin = require('eslint-plugin-unicorn'); + +const grafanaConfig = require('@grafana/eslint-config/flat'); +const grafanaPlugin = require('@grafana/eslint-plugin'); + +const bettererConfig = require('./.betterer.eslint.config'); +const getEnvConfig = require('./scripts/webpack/env-util'); + +const envConfig = getEnvConfig(); +const enableBettererRules = envConfig.frontend_dev_betterer_eslint_rules; + +/** + * @type {Array} + */ +module.exports = [ + { + name: 'grafana/ignores', + ignores: [ + '.github', + '.yarn', + '**/.*', // dotfiles aren't ignored by default in FlatConfig + '**/*.gen.ts', + '**/build/', + '**/compiled/', + '**/dist/', + 'data/', + 'deployment_tools_config.json', + 'devenv', + 'e2e/test-plugins', + 'e2e/tmp', + 'packages/grafana-ui/src/components/Icon/iconBundle.ts', + 'pkg', + 'playwright-report', + 'public/lib/monaco/', // this path is no longer required but local dev environments may still have it + 'public/locales/_build', + 'public/locales/**/*.js', + 'public/vendor/', + 'scripts/grafana-server/tmp', + ], + }, + // Conditionally run the betterer rules if enabled in dev's config + ...(enableBettererRules ? bettererConfig : []), + grafanaConfig, + { + name: 'react/jsx-runtime', + // @ts-ignore - not sure why but flat config is typed as a maybe? + ...reactPlugin.configs.flat['jsx-runtime'], + }, + { + name: 'grafana/defaults', + linterOptions: { + // This reports unused disable directives that we can clean up but + // it also conflicts with the betterer eslint rules so disabled + reportUnusedDisableDirectives: false, + }, + files: ['**/*.{ts,tsx,js}'], + plugins: { + '@emotion': emotionPlugin, + lodash: lodashPlugin, + jest: jestPlugin, + import: importPlugin, + 'jsx-a11y': jsxA11yPlugin, + 'no-barrel-files': barrelPlugin, + '@grafana': grafanaPlugin, + }, + + settings: { + 'import/internal-regex': '^(app/)|(@grafana)', + 'import/external-module-folders': ['node_modules', '.yarn'], + }, + + rules: { + '@grafana/no-border-radius-literal': 'error', + '@grafana/no-unreduced-motion': 'error', + 'react/prop-types': 'off', + // need to ignore emotion's `css` prop, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md#rule-options + 'react/no-unknown-property': ['error', { ignore: ['css'] }], + '@emotion/jsx-import': 'error', + '@emotion/syntax-preference': [2, 'object'], + 'lodash/import-scope': [2, 'member'], + 'jest/no-focused-tests': 'error', + 'import/order': [ + 'error', + { + groups: [['builtin', 'external'], 'internal', 'parent', 'sibling', 'index'], + 'newlines-between': 'always', + alphabetize: { order: 'asc' }, + }, + ], + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: 'react-redux', + importNames: ['useDispatch', 'useSelector'], + message: 'Please import from app/types instead.', + }, + { + name: 'react-i18next', + importNames: ['Trans', 't'], + message: 'Please import from app/core/internationalization instead', + }, + ], + }, + ], + + // Use typescript's no-redeclare for compatibility with overrides + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': ['error'], + }, + }, + { + name: 'grafana/uplot-overrides', + files: ['packages/grafana-ui/src/components/uPlot/**/*.{ts,tsx}'], + rules: { + 'react-hooks/rules-of-hooks': 'off', + 'react-hooks/exhaustive-deps': 'off', + }, + }, + { + name: 'grafana/theme-demo-overrides', + files: ['packages/grafana-ui/src/components/ThemeDemos/**/*.{ts,tsx}'], + rules: { + '@emotion/jsx-import': 'off', + 'react/jsx-uses-react': 'off', + 'react/react-in-jsx-scope': 'off', + }, + }, + { + name: 'grafana/public-dashboards-overrides', + files: ['public/dashboards/scripted*.js'], + rules: { + 'no-redeclare': 'error', + '@typescript-eslint/no-redeclare': 'off', + }, + }, + { + name: 'grafana/jsx-a11y-overrides', + files: ['**/*.tsx'], + ignores: ['**/*.{spec,test}.tsx'], + rules: { + // rules marked "off" are those left in the recommended preset we need to fix + // we should remove the corresponding line and fix them one by one + // any marked "error" contain specific overrides we'll need to keep + 'jsx-a11y/no-autofocus': [ + 'error', + { + ignoreNonDOM: true, + }, + ], + 'jsx-a11y/label-has-associated-control': [ + 'error', + { + controlComponents: ['NumberInput'], + depth: 2, + }, + ], + }, + }, + { + name: 'grafana/data-overrides', + files: ['packages/grafana-data/**/*.{ts,tsx}'], + ignores: ['packages/grafana-data/src/**/*.{spec,test}.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: ['@grafana/runtime', '@grafana/ui', '@grafana/data'], + }, + ], + }, + }, + { + name: 'grafana/ui-overrides', + files: ['packages/grafana-ui/**/*.{ts,tsx}'], + ignores: ['packages/grafana-ui/**/*.{test,story}.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: ['@grafana/runtime', '@grafana/data/*', '@grafana/ui', '@grafana/e2e-selectors/*'], + paths: [ + { + name: 'react-i18next', + importNames: ['Trans', 't'], + message: 'Please import from grafana-ui/src/utils/i18n instead', + }, + ], + }, + ], + }, + }, + { + name: 'grafana/schema-overrides', + files: ['packages/grafana-schema/**/*.{ts,tsx}'], + ignores: ['packages/grafana-schema/**/*.test.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: ['@grafana/*'], + }, + ], + }, + }, + { + name: 'grafana/runtime-overrides', + files: ['packages/grafana-runtime/**/*.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: ['@grafana/runtime', '@grafana/data/*', '@grafana/ui/*', '@grafana/e2e/*'], + }, + ], + }, + }, + { + name: 'grafana/flamegraph-overrides', + files: ['packages/grafana-flamegraph/**/*.{ts,tsx}'], + ignores: ['packages/grafana-flamegraph/**/*.{test,story}.{ts,tsx}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: ['@grafana/runtime', '@grafana/e2e', '@grafana/e2e-selectors/*'], + }, + ], + }, + }, + { + name: 'grafana/alerting-overrides', + plugins: { + unicorn: unicornPlugin, + }, + files: ['public/app/features/alerting/**/*.{ts,tsx,js,jsx}'], + rules: { + 'dot-notation': 'error', + 'prefer-const': 'error', + 'react/no-unused-prop-types': 'error', + 'unicorn/no-unused-properties': 'error', + }, + }, + { + name: 'grafana/alerting-test-overrides', + plugins: { + 'testing-library': fixupPluginRules({ rules: testingLibraryPlugin.rules }), + 'jest-dom': jestDomPlugin, + }, + files: [ + 'public/app/features/alerting/**/__tests__/**/*.[jt]s?(x)', + 'public/app/features/alerting/**/?(*.)+(spec|test).[jt]s?(x)', + ], + rules: { + ...testingLibraryPlugin.configs['flat/react'].rules, + ...jestDomPlugin.configs['flat/recommended'].rules, + 'testing-library/prefer-user-event': 'error', + 'jest/expect-expect': ['error', { assertFunctionNames: ['expect*', 'reducerTester'] }], + }, + }, + { + name: 'grafana/explore-traceview-overrides', + files: ['public/app/features/explore/TraceView/components/demo/**/*.{ts,tsx,js,jsx}'], + rules: { + 'import/no-extraneous-dependencies': 'off', + }, + }, + { + name: 'grafana/decoupled-plugins-overrides', + files: [ + 'public/app/plugins/datasource/azuremonitor/**/*.{ts,tsx}', + 'public/app/plugins/datasource/cloud-monitoring/**/*.{ts,tsx}', + 'public/app/plugins/datasource/cloudwatch/**/*.{ts,tsx}', + 'public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}', + 'public/app/plugins/datasource/elasticsearch/**/*.{ts,tsx}', + 'public/app/plugins/datasource/grafana-postgresql-datasource/**/*.{ts,tsx}', + 'public/app/plugins/datasource/grafana-pyroscope-datasource/**/*.{ts,tsx}', + 'public/app/plugins/datasource/grafana-testdata-datasource/**/*.{ts,tsx}', + 'public/app/plugins/datasource/jaeger/**/*.{ts,tsx}', + 'public/app/plugins/datasource/loki/**/*.{ts,tsx}', + 'public/app/plugins/datasource/loki/**/*.{ts,tsx}', + 'public/app/plugins/datasource/mysql/**/*.{ts,tsx}', + 'public/app/plugins/datasource/parca/**/*.{ts,tsx}', + 'public/app/plugins/datasource/tempo/**/*.{ts,tsx}', + 'public/app/plugins/datasource/zipkin/**/*.{ts,tsx}', + ], + plugins: { + import: importPlugin, + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.ts', '.tsx'], + }, + }, + }, + rules: { + 'import/no-restricted-paths': [ + 'error', + { + zones: [ + { + target: './public/app/plugins', + from: './public', + except: ['./app/plugins'], + message: 'Core plugins are not allowed to depend on Grafana core packages', + }, + ], + }, + ], + }, + }, +]; diff --git a/go.mod b/go.mod index 6138c6c3f49..d33df08e1bb 100644 --- a/go.mod +++ b/go.mod @@ -11,21 +11,22 @@ replace cuelang.org/go => github.com/grafana/cue v0.0.0-20230926092038-971951014 replace github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.52.0 require ( - buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.10.0-20240523185345-933eab74d046.1 // @grafana/observability-traces-and-profiling - buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.1-20240523185345-933eab74d046.1 // @grafana/observability-traces-and-profiling + buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1 // @grafana/observability-traces-and-profiling + buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2 // @grafana/observability-traces-and-profiling cloud.google.com/go/kms v1.18.5 // @grafana/grafana-backend-group cloud.google.com/go/storage v1.43.0 // @grafana/grafana-backend-group - cuelang.org/go v0.6.0-0.dev // @grafana/grafana-as-code + connectrpc.com/connect v1.17.0 // @grafana/observability-traces-and-profiling + cuelang.org/go v0.8.2 // @grafana/grafana-as-code filippo.io/age v1.1.1 // @grafana/identity-access-team github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // @grafana/partner-datasources github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // @grafana/grafana-backend-group github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // @grafana/grafana-backend-group github.com/Azure/azure-storage-blob-go v0.15.0 // @grafana/grafana-backend-group github.com/Azure/go-autorest/autorest v0.11.29 // @grafana/grafana-backend-group - github.com/Azure/go-autorest/autorest/adal v0.9.23 // @grafana/grafana-backend-group + github.com/Azure/go-autorest/autorest/adal v0.9.24 // @grafana/grafana-backend-group github.com/BurntSushi/toml v1.4.0 // @grafana/identity-access-team github.com/Masterminds/semver v1.5.0 // @grafana/grafana-backend-group - github.com/Masterminds/semver/v3 v3.2.0 // @grafana/grafana-release-guild + github.com/Masterminds/semver/v3 v3.2.0 // @grafana/grafana-developer-enablement-squad github.com/Masterminds/sprig/v3 v3.2.3 // @grafana/grafana-backend-group github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // @grafana/plugins-platform-backend github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // @grafana/grafana-backend-group @@ -36,11 +37,11 @@ require ( github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend - github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-release-guild + github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad + github.com/blevesearch/bleve/v2 v2.4.3 // @grafana/grafana-search-and-storage github.com/blugelabs/bluge v0.1.9 // @grafana/grafana-backend-group github.com/blugelabs/bluge_segment_api v0.2.0 // @grafana/grafana-backend-group github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // @grafana/grafana-backend-group - github.com/bufbuild/connect-go v1.10.0 // @grafana/observability-traces-and-profiling github.com/bwmarrin/snowflake v0.3.0 // @grafan/grafana-app-platform-squad github.com/centrifugal/centrifuge v0.33.3 // @grafana/grafana-app-platform-squad github.com/crewjam/saml v0.4.13 // @grafana/identity-access-team @@ -61,7 +62,7 @@ require ( github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group github.com/gogo/protobuf v1.3.2 // @grafana/alerting-backend - github.com/golang-jwt/jwt/v4 v4.5.0 // @grafana/grafana-backend-group + github.com/golang-jwt/jwt/v4 v4.5.1 // @grafana/grafana-backend-group github.com/golang-migrate/migrate/v4 v4.7.0 // @grafana/grafana-backend-group github.com/golang/mock v1.6.0 // @grafana/alerting-backend github.com/golang/protobuf v1.5.4 // @grafana/grafana-backend-group @@ -71,23 +72,24 @@ require ( github.com/google/wire v0.6.0 // @grafana/grafana-backend-group github.com/googleapis/gax-go/v2 v2.13.0 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group - github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20241010165806-807ddf183724 // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240 // @grafana/identity-access-team - github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e // @grafana/identity-access-team - github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad + github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad + github.com/grafana/alerting v0.0.0-20241021123319-be61d61f71e7 // @grafana/alerting-backend + github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a // @grafana/identity-access-team + github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d // @grafana/identity-access-team + github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // @grafana/dataviz-squad github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics - github.com/grafana/dskit v0.0.0-20240801171758-736c44c85382 // @grafana/grafana-backend-group + github.com/grafana/dskit v0.0.0-20240905221822-931a021fb06b // @grafana/grafana-backend-group github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 // @grafana/sharing-squad github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e // @grafana/grafana-operator-experience-squad + github.com/grafana/grafana-app-sdk v0.23.1 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-aws-sdk v0.31.3 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.3.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.1.0 // @grafana/partner-datasources github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group - github.com/grafana/grafana-plugin-sdk-go v0.255.0 // @grafana/plugins-platform-backend + github.com/grafana/grafana-plugin-sdk-go v0.259.4 // @grafana/plugins-platform-backend github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad @@ -95,19 +97,20 @@ require ( // For local development grafana/grafana will always use the local files // Check go.work file for details github.com/grafana/grafana/pkg/promlib v0.0.6 // @grafana/observability-metrics + github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 // @grafana/grafana-app-platform-squad github.com/grafana/otel-profiling-go v0.5.1 // @grafana/grafana-backend-group github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // @grafana/observability-traces-and-profiling - github.com/grafana/pyroscope/api v0.3.0 // @grafana/observability-traces-and-profiling + github.com/grafana/pyroscope/api v1.0.0 // @grafana/observability-traces-and-profiling github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 // @grafana/observability-traces-and-profiling github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // @grafana/plugins-platform-backend github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // @grafana/grafana-backend-group github.com/hashicorp/go-hclog v1.6.3 // @grafana/plugins-platform-backend github.com/hashicorp/go-multierror v1.1.1 // @grafana/alerting-squad - github.com/hashicorp/go-plugin v1.6.1 // @grafana/plugins-platform-backend + github.com/hashicorp/go-plugin v1.6.2 // @grafana/plugins-platform-backend github.com/hashicorp/go-version v1.7.0 // @grafana/grafana-backend-group github.com/hashicorp/golang-lru/v2 v2.0.7 // @grafana/alerting-backend github.com/hashicorp/hcl/v2 v2.17.0 // @grafana/alerting-backend - github.com/huandu/xstrings v1.3.3 // @grafana/partner-datasources + github.com/huandu/xstrings v1.5.0 // @grafana/partner-datasources github.com/influxdata/influxdb-client-go/v2 v2.13.0 // @grafana/observability-metrics github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // @grafana/grafana-app-platform-squad github.com/jmespath/go-jmespath v0.4.0 // indirect; @grafana/grafana-backend-group @@ -117,7 +120,7 @@ require ( github.com/linkedin/goavro/v2 v2.10.0 // @grafana/grafana-backend-group github.com/m3db/prometheus_remote_client_golang v0.4.4 // @grafana/grafana-backend-group github.com/madflojo/testcerts v1.1.1 // @grafana/alerting-backend - github.com/magefile/mage v1.15.0 // @grafana/grafana-release-guild + github.com/magefile/mage v1.15.0 // @grafana/grafana-developer-enablement-squad github.com/matryer/is v1.4.0 // @grafana/grafana-as-code github.com/mattn/go-isatty v0.0.20 // @grafana/grafana-backend-group github.com/mattn/go-sqlite3 v1.14.22 // @grafana/grafana-backend-group @@ -135,9 +138,9 @@ require ( github.com/openfga/openfga v1.6.2 // @grafana/identity-access-team github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend - github.com/prometheus/client_golang v1.20.4 // @grafana/alerting-backend + github.com/prometheus/client_golang v1.20.5 // @grafana/alerting-backend github.com/prometheus/client_model v0.6.1 // @grafana/grafana-backend-group - github.com/prometheus/common v0.60.0 // @grafana/alerting-backend + github.com/prometheus/common v0.60.1 // @grafana/alerting-backend github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3 // @grafana/alerting-backend github.com/redis/go-redis/v9 v9.1.0 // @grafana/alerting-backend github.com/robfig/cron/v3 v3.0.1 // @grafana/grafana-backend-group @@ -148,39 +151,39 @@ require ( github.com/stretchr/testify v1.9.0 // @grafana/grafana-backend-group github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // @grafana/grafana-backend-group github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f // @grafana/grafana-backend-group - github.com/urfave/cli v1.22.15 // indirect; @grafana/grafana-backend-group + github.com/urfave/cli v1.22.16 // indirect; @grafana/grafana-backend-group github.com/urfave/cli/v2 v2.27.1 // @grafana/grafana-backend-group github.com/wk8/go-ordered-map v1.0.0 // @grafana/grafana-backend-group github.com/xlab/treeprint v1.2.0 // @grafana/observability-traces-and-profiling github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // @grafana/grafana-operator-experience-squad github.com/yudai/gojsondiff v1.0.0 // @grafana/grafana-backend-group go.opentelemetry.io/collector/pdata v1.12.0 // @grafana/grafana-backend-group - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // @grafana/plugins-platform-backend - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0 // @grafana/grafana-operator-experience-squad - go.opentelemetry.io/contrib/propagators/jaeger v1.29.0 // @grafana/grafana-backend-group - go.opentelemetry.io/contrib/samplers/jaegerremote v0.25.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel v1.31.0 // @grafana/grafana-backend-group + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // @grafana/plugins-platform-backend + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0 // @grafana/grafana-operator-experience-squad + go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 // @grafana/grafana-backend-group + go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel v1.32.0 // @grafana/grafana-backend-group go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/sdk v1.31.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/trace v1.31.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/sdk v1.32.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/trace v1.32.0 // @grafana/grafana-backend-group go.uber.org/atomic v1.11.0 // @grafana/alerting-backend go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage gocloud.dev v0.39.0 // @grafana/grafana-app-platform-squad golang.org/x/crypto v0.28.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // @grafana/alerting-backend - golang.org/x/mod v0.20.0 // indirect; @grafana/grafana-backend-group + golang.org/x/mod v0.21.0 // indirect; @grafana/grafana-backend-group golang.org/x/net v0.30.0 // @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.23.0 // @grafana/identity-access-team - golang.org/x/sync v0.8.0 // @grafana/alerting-backend - golang.org/x/text v0.19.0 // @grafana/grafana-backend-group + golang.org/x/sync v0.9.0 // @grafana/alerting-backend + golang.org/x/text v0.20.0 // @grafana/grafana-backend-group golang.org/x/time v0.6.0 // @grafana/grafana-backend-group - golang.org/x/tools v0.24.0 // @grafana/grafana-as-code + golang.org/x/tools v0.26.0 // @grafana/grafana-as-code gonum.org/v1/gonum v0.15.1 // @grafana/observability-metrics - google.golang.org/api v0.191.0 // @grafana/grafana-backend-group + google.golang.org/api v0.193.0 // @grafana/grafana-backend-group google.golang.org/grpc v1.67.1 // @grafana/plugins-platform-backend - google.golang.org/protobuf v1.35.1 // @grafana/plugins-platform-backend + google.golang.org/protobuf v1.35.2 // @grafana/plugins-platform-backend gopkg.in/ini.v1 v1.67.0 // @grafana/alerting-backend gopkg.in/mail.v2 v2.3.1 // @grafana/grafana-backend-group gopkg.in/yaml.v3 v3.0.1 // @grafana/alerting-backend @@ -200,11 +203,11 @@ require ( ) require ( - cloud.google.com/go v0.115.0 // indirect - cloud.google.com/go/auth v0.8.1 // indirect + cloud.google.com/go v0.115.1 // indirect + cloud.google.com/go/auth v0.9.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.0 // indirect - cloud.google.com/go/iam v1.1.13 // indirect + cloud.google.com/go/iam v1.2.0 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect @@ -231,7 +234,7 @@ require ( github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f // indirect + github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -254,7 +257,7 @@ require ( github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect @@ -264,7 +267,7 @@ require ( github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/emicklei/proto v1.10.0 // indirect + github.com/emicklei/proto v1.13.2 // indirect github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -300,10 +303,10 @@ require ( github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect; @grafana/plugins-platform-backend - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // @grafana/identity-access-team + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // @grafana/identity-access-team github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-msgpack v1.1.5 // indirect github.com/hashicorp/go-sockaddr v1.0.6 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect @@ -343,13 +346,12 @@ require ( github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattetti/filebuffer v1.0.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-ieproxy v0.0.11 // indirect + github.com/mattn/go-ieproxy v0.0.12 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/miekg/dns v1.1.61 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mithrandie/csvq v1.18.1 // indirect @@ -381,7 +383,7 @@ require ( github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.11.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect + github.com/protocolbuffers/txtpbfmt v0.0.0-20240116145035-ef3ab179eed6 // indirect github.com/redis/rueidis v1.0.45 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -422,18 +424,18 @@ require ( go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.mongodb.org/mongo-driver v1.16.1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect - go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // @grafana/identity-access-team - golang.org/x/sys v0.26.0 // indirect + golang.org/x/sys v0.27.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect - google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect; @grafana/grafana-backend-group - google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto v0.0.0-20240820151423-278611b39280 // indirect; @grafana/grafana-backend-group + google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -466,28 +468,62 @@ require ( ) require ( - github.com/getkin/kin-openapi v0.127.0 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/apps/playlist v0.0.0-20240917082838-e2bce38a7990 // @grafana/grafana-app-platform-squad + github.com/getkin/kin-openapi v0.128.0 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/apps/playlist v0.0.0-20241105090059-facca37f4d1f // @grafana/grafana-app-platform-squad ) require github.com/jmespath-community/go-jmespath v1.1.1 // @grafana/identity-access-team +require github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs + +require github.com/openzipkin/zipkin-go v0.4.3 // @grafana/oss-big-tent + require ( - cloud.google.com/go/longrunning v0.5.12 // indirect + cloud.google.com/go/longrunning v0.6.0 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect + github.com/blevesearch/bleve_index_api v1.1.12 // indirect + github.com/blevesearch/geo v0.1.20 // indirect + github.com/blevesearch/go-faiss v1.0.23 // indirect + github.com/blevesearch/gtreap v0.1.1 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect + github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect + github.com/blevesearch/zapx/v11 v11.3.10 // indirect + github.com/blevesearch/zapx/v12 v12.3.10 // indirect + github.com/blevesearch/zapx/v13 v13.3.10 // indirect + github.com/blevesearch/zapx/v14 v14.3.10 // indirect + github.com/blevesearch/zapx/v15 v15.3.16 // indirect + github.com/blevesearch/zapx/v16 v16.1.8 // indirect + github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 // indirect + github.com/cespare/xxhash v1.1.0 // indirect github.com/dolthub/maphash v0.1.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/gammazero/deque v0.2.1 // indirect - github.com/grafana/grafana-app-sdk v0.19.0 // indirect - github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 // indirect + github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect + github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect + github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/sqlds/v4 v4.1.0 // indirect + github.com/hashicorp/consul/api v1.29.4 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/serf v0.10.1 // indirect github.com/maypok86/otter v1.2.2 // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect + github.com/pires/go-proxyproto v0.7.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect + github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect + github.com/sony/gobreaker v0.5.0 // indirect + go.etcd.io/bbolt v1.3.10 // indirect + go.opentelemetry.io/collector/featuregate v1.9.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect + go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + k8s.io/apiextensions-apiserver v0.31.1 // indirect ) // Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream -replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240523142256-cc370b98af7c +replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 // Use our fork of the upstream alertmanagers. // This is required in order to get notification delivery errors from the receivers API. diff --git a/go.sum b/go.sum index 5152e1b75ca..f88c152a81a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.10.0-20240523185345-933eab74d046.1 h1:q6MBjQ5fj3ygW/ySxOck7iwLhPQWbzOKJRZYzXRBmBk= -buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.10.0-20240523185345-933eab74d046.1/go.mod h1:DEC5vsD4oLn7c6QeBVfUS7WpW5iwmW5lXfhpsjVxVt0= -buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.1-20240523185345-933eab74d046.1 h1:Osqg+/+sFJKr5iyna6/AvyxXPY0jqaIGr3ltzzSDLRk= -buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.1-20240523185345-933eab74d046.1/go.mod h1:lbDqoSeErWK6pETEKo/LO+JmU2GbZqVE8ILESypLuZU= +buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1 h1:Ur1tEsOxct4cuBumorzL/Zl9rRedJKdkPqufih3WsqQ= +buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1/go.mod h1:gC0oJPXUcGXzgiyUzMPBIgIfHbdoovWQD3/njIe5EVA= +buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2 h1:oRhx9wmKP52zEp1/PWWjaNxWtxWFrKXYSF1UJygjbzQ= +buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2/go.mod h1:w3CrNzdvwGJ4FwUlhshojc2FDXDN+3ou5nlcLTu7dHs= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -50,8 +50,8 @@ cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYN cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= -cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -189,8 +189,8 @@ cloud.google.com/go/assuredworkloads v1.11.3/go.mod h1:vEjfTKYyRUaIeA0bsGJceFV2J cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= cloud.google.com/go/auth v0.2.0/go.mod h1:+yb+oy3/P0geX6DLKlqiGHARGR6EX2GRtYCzWOCQSbU= -cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo= -cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= +cloud.google.com/go/auth v0.9.0 h1:cYhKl1JUhynmxjXfrk4qdPc6Amw7i+GC9VLflgT0p5M= +cloud.google.com/go/auth v0.9.0/go.mod h1:2HsApZBr9zGZhC9QAXsYVYaWk8kNUt37uny+XVKi7wM= cloud.google.com/go/auth/oauth2adapt v0.2.0/go.mod h1:AfqujpDAlTfLfeCIl/HJZZlIxD8+nJoZ5e0x1IxGq5k= cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= @@ -700,8 +700,8 @@ cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQy cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= -cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= +cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8= +cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -781,8 +781,8 @@ cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUz cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y= cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= -cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= -cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= +cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI= +cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= @@ -1334,6 +1334,8 @@ cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCw cloud.google.com/go/workflows v1.12.2/go.mod h1:+OmBIgNqYJPVggnMo9nqmizW0qEXHhmnAzK/CnBqsHc= cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= +connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= +connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= @@ -1370,8 +1372,9 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0 h1:ui3YNbxfW7J3tTFIZMH6LIGRjCngp+J+nIFlnizfNTE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0/go.mod h1:gZmgV+qBqygoznvqo2J9oKZAFziqhLZ2xE/WVUmzkHA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0/go.mod h1:QyiQdW4f4/BIfB8ZutZ2s+28RAgfa/pT+zS++ZHyM1I= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= @@ -1393,8 +1396,8 @@ github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/ github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= -github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4= +github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -1416,7 +1419,6 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -1446,12 +1448,14 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= @@ -1466,6 +1470,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0= github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0= +github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4= +github.com/Workiva/go-datastructures v1.1.5/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -1586,8 +1592,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzx github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f h1:y06x6vGnFYfXUoVMbrcP1Uzpj4JG01eB5vRps9G8agM= github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s= +github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0= +github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= @@ -1609,19 +1616,45 @@ github.com/bits-and-blooms/bitset v1.12.0 h1:U/q1fAF7xXRhFCrhROzIfffYnu+dlS38vCZ github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/blevesearch/bleve/v2 v2.4.3 h1:XDYj+1prgX84L2Cf+V3ojrOPqXxy0qxyd2uLMmeuD+4= +github.com/blevesearch/bleve/v2 v2.4.3/go.mod h1:hEPDPrbYw3vyrm5VOa36GyS4bHWuIf4Fflp7460QQXY= +github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY= +github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8= +github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM= +github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w= +github.com/blevesearch/go-faiss v1.0.23 h1:Wmc5AFwDLKGl2L6mjLX1Da3vCL0EKa2uHHSorcIS1Uc= +github.com/blevesearch/go-faiss v1.0.23/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= +github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= +github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA= github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= +github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY= +github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0= github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= +github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= +github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= github.com/blevesearch/vellum v1.0.5/go.mod h1:atE0EH3fvk43zzS7t1YNdNC7DbmcC3uz+eMD5xZ2OyQ= github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI= github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k= +github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk= +github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ= +github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s= +github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs= +github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8= +github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk= +github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU= +github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns= +github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE= +github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg= +github.com/blevesearch/zapx/v16 v16.1.8 h1:Bxzpw6YQpFs7UjoCV1+RvDw6fmAT2GZxldwX8b3wVBM= +github.com/blevesearch/zapx/v16 v16.1.8/go.mod h1:JqQlOqlRVaYDkpLIl3JnKql8u4zKTNlVEa3nLsi0Gn8= github.com/blugelabs/bluge v0.1.9 h1:bPgXlcsWugrXNjzeoLdOnvfJpHsyODKpYaAndayl/SM= github.com/blugelabs/bluge v0.1.9/go.mod h1:5d7LktUkQgvbh5Bmi6tPWtvo4+6uRTm6gAwP+5z6FqQ= github.com/blugelabs/bluge_segment_api v0.2.0 h1:cCX1Y2y8v0LZ7+EEJ6gH7dW6TtVTW4RhG0vp3R+N2Lo= @@ -1639,8 +1672,6 @@ github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0= github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47mjdTbg= -github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -1648,6 +1679,8 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4= +github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds= github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -1664,6 +1697,7 @@ github.com/centrifugal/centrifuge v0.33.3 h1:uyqBc27oM+qnC3NX5imvZxuk9+u2ze6QGWH github.com/centrifugal/centrifuge v0.33.3/go.mod h1:GaOF4CiREY5x6lW7zYUz46Qrc6aUZ6J/AMV/kROc2+U= github.com/centrifugal/protocol v0.13.4 h1:I0YxXtFNfn/ndDIZp5RkkqQcSSNH7DNPUbXKYtJXDzs= github.com/centrifugal/protocol v0.13.4/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -1738,8 +1772,9 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -1778,10 +1813,12 @@ github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fp github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= -github.com/digitalocean/godo v1.113.0 h1:CLtCxlP4wDAjKIQ+Hshht/UNbgAp8/J/XBH1ZtDCF9Y= github.com/digitalocean/godo v1.113.0/go.mod h1:Z2mTP848Vi3IXXl5YbPekUgr4j4tOePomA+OE1Ag98w= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4= +github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlmiddlecote/sqlstats v1.0.2 h1:gSU11YN23D/iY50A2zVYwgXgy072khatTsIW6UPjUtI= github.com/dlmiddlecote/sqlstats v1.0.2/go.mod h1:0CWaIh/Th+z2aI6Q9Jpfg/o21zmGxWhbByHgQSCUQvY= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= @@ -1813,8 +1850,8 @@ github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= -github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b h1:ZHiD4/yE4idlbqvAO6iYCOYRzOMRpxkW+FKasRA3tsQ= -github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M= +github.com/efficientgo/core v1.0.0-rc.3 h1:X6CdgycYWDcbYiJr1H1+lQGzx13o7bq3EUkbB9DsSPc= +github.com/efficientgo/core v1.0.0-rc.3/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps= github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0= github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo= github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q= @@ -1822,8 +1859,8 @@ github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac/go.mod h1:gNh8 github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw= -github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY= +github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= @@ -1854,6 +1891,7 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZ github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= @@ -1894,8 +1932,8 @@ github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0 github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y= github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34= -github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= -github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= @@ -1907,6 +1945,8 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -2046,8 +2086,9 @@ github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= @@ -2058,6 +2099,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo= +github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= @@ -2172,8 +2215,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20240416155748-26353dc0451f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da h1:xRmpO92tb8y+Z85iUOMOicpCfaYcv7o3Cg3wKrIpg8g= +github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= @@ -2224,8 +2267,9 @@ github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDP github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v1.11.0 h1:ls0O747DIq1D8SUHc7r2vI8BFbMLeLFuENaAIfEx7OM= github.com/gophercloud/gophercloud v1.11.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.13.0 h1:8iY9d1DAbzMW6Vok1AxbbK5ZaUjzMp0tdyt4fX9IeJ0= +github.com/gophercloud/gophercloud v1.13.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -2241,16 +2285,17 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/alerting v0.0.0-20241010165806-807ddf183724 h1:u+ZM5TLkdeEoSWXgYWxc4XRfPHhXpR63MyHXJxbBLrc= -github.com/grafana/alerting v0.0.0-20241010165806-807ddf183724/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= -github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240 h1:bBn6sCbBjxjYlvs5JAIGHQSOs8xbDEBWbezxarA/DDo= -github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240/go.mod h1:RKqhn8E5PY2k5Xo6X8FHFgP45/qt9qqfAY7YYJ2mtB8= -github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e h1:I0sSXcqdt/ttiOJ/BVhpfa2q/xAyWSweQwaypGmvLss= -github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= -github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= -github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= +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/alerting v0.0.0-20241021123319-be61d61f71e7 h1:lsM/QscEX+ZDIJm48ynQscH+msETyGYV6ug8L4f2DtM= +github.com/grafana/alerting v0.0.0-20241021123319-be61d61f71e7/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= +github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a h1:X3mroOOXdryRY6SEIQp7S6l1oxt964ywSmcknszHwn0= +github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a/go.mod h1:XFhcSCEDeOgV43x41we7mRBcizDpsTScb7XOuYipQZg= +github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d h1:7nZfaXdC4Xc2ocMz5/Bx/3EsaEO34KsmA2RRcuogLnc= +github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= +github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= github.com/grafana/cuetsy v0.1.11 h1:I3IwBhF+UaQxRM79HnImtrAn8REGdb5M3+C4QrYHoWk= @@ -2259,16 +2304,16 @@ github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmO github.com/grafana/dataplane/examples v0.0.1/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg= github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s= github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= -github.com/grafana/dskit v0.0.0-20240801171758-736c44c85382 h1:hcVxOw584ov31MyEAuNNGjrj4vPZX626F39zuOtWQs0= -github.com/grafana/dskit v0.0.0-20240801171758-736c44c85382/go.mod h1:lcjGB6SuaZ2o44A9nD6p/tR4QXSPbzViRY520Gy6pTQ= +github.com/grafana/dskit v0.0.0-20240905221822-931a021fb06b h1:x2HCzk29I0o5pRPfqWP/qwhXaPGlcz8pohq5kO1NZoE= +github.com/grafana/dskit v0.0.0-20240905221822-931a021fb06b/go.mod h1:SPLNCARd4xdjCkue0O6hvuoveuS1dGJjDnfxYe405YQ= github.com/grafana/e2e v0.1.1 h1:/b6xcv5BtoBnx8cZnCiey9DbjEc8z7gXHO5edoeRYxc= github.com/grafana/e2e v0.1.1/go.mod h1:RpNLgae5VT+BUHvPE+/zSypmOXKwEu4t+tnEMS1ATaE= github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 h1:jxJJ5z0GxqhWFbQUsys3BHG8jnmniJ2Q74tXAG1NaDo= github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447/go.mod h1:IxsY6mns6Q5sAnWcrptrgUrSglTZJXH/kXr9nbpb/9I= github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e h1:UlEET0InuoFautfaFp8lDrNF7rPHYXuBMrzwWx9XqFY= github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e/go.mod h1:IGRj8oOoxwJbHBYl1+OhS9UjQR0dv6SQOep7HqmtyFU= -github.com/grafana/grafana-app-sdk v0.19.0 h1:RY9HvCFR+4WUy81n53wejZnof9qE6++pd6r24d6+JYs= -github.com/grafana/grafana-app-sdk v0.19.0/go.mod h1:y0BgzYxc+a7CwOqkwUhN9zXd5cgZJjd2zAbgHEd/xzo= +github.com/grafana/grafana-app-sdk v0.23.1 h1:BRpUG0bA0oVxjthkmO2thuJBo3nbjaRSSmZJHw+mA8I= +github.com/grafana/grafana-app-sdk v0.23.1/go.mod h1:KzgPnTJfMeckGmMctv6CJb8Jr/o/5rwARDyjXoeR0Fc= github.com/grafana/grafana-aws-sdk v0.31.3 h1:QlgIwyyozYYQf/dL279Baicyax+SuE99Set5chMnq1s= github.com/grafana/grafana-aws-sdk v0.31.3/go.mod h1:5nt5Gmp6+GyM+Jr7xsXKJtbizxbYXXLmEac6kw5paQI= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2 h1:fV6IgVtViXcYZ4VqTAMuVBTLuGAnI27HhQkaLttzbPE= @@ -2280,10 +2325,10 @@ github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkr github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs= github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ= github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk= -github.com/grafana/grafana-plugin-sdk-go v0.255.0 h1:e9bmDTdpR6Ho9ru+UhyVoWguVSSGFgInRjOsl4AftBQ= -github.com/grafana/grafana-plugin-sdk-go v0.255.0/go.mod h1:sE25SkFQSj8DwX4qkF7w++6KL6mzOXo1ycDlk9GgFGw= -github.com/grafana/grafana/apps/playlist v0.0.0-20240917082838-e2bce38a7990 h1:uQMZE/z+Y+o/U0z/g8ckAHss7U7LswedilByA2535DU= -github.com/grafana/grafana/apps/playlist v0.0.0-20240917082838-e2bce38a7990/go.mod h1:3Vi0xv/4OBkBw4R9GAERkSrBnx06qrjpmNBRisucuSM= +github.com/grafana/grafana-plugin-sdk-go v0.259.4 h1:Qt/Ic+7K7n7z4ncZs29q2OhOOPKWjE5+ms2ckeYlTo4= +github.com/grafana/grafana-plugin-sdk-go v0.259.4/go.mod h1:BS2B+lEHC4KtOViiS4C5x9SsR1ihPHAzesGOpaPYeCs= +github.com/grafana/grafana/apps/playlist v0.0.0-20241105090059-facca37f4d1f h1:zZN/Jy7PjoqtrMiBRV5O3x4xAArcSbUznuyAPACrKXI= +github.com/grafana/grafana/apps/playlist v0.0.0-20241105090059-facca37f4d1f/go.mod h1:e97Zxn1WX4Wn9TXEvwTjMNwU6yrjX/K7uVNSCZyEwxY= github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 h1:2H9x4q53pkfUGtSNYD1qSBpNnxrFgylof/TYADb5xMI= github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2/go.mod h1:gBLBniiSUQvyt4LRrpIeysj8Many0DV+hdUKifRE0Ec= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da h1:2E3c/I3ayAy4Z1GwIPqXNZcpUccRapE1aBXA1ho4g7o= @@ -2300,19 +2345,25 @@ github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71 github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d/go.mod h1:KL0LyEIlmuRi/zzuCopFZSmSJzBVu2hMGHIK74i4iE8= github.com/grafana/grafana/pkg/util/xorm v0.0.1 h1:72QZjxWIWpSeOF8ob4aMV058kfgZyeetkAB8dmeti2o= github.com/grafana/grafana/pkg/util/xorm v0.0.1/go.mod h1:eNfbB9f2jM8o9RfwqwjY8SYm5tvowJ8Ly+iE4P9rXII= +github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 h1:NznuPwItog+rwdVg8hAuGKP29ndRSzJAwhxKldkP8oQ= +github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32/go.mod h1:796sq+UcONnSlzA3RtlBZ+b/hrerkZXiEmO8oMjyRwY= +github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 h1:ZYk42718kSXOiIKdjZKljWLgBpzL5z1yutKABksQCMg= +github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0= +github.com/grafana/loki/v3 v3.2.1 h1:VB7u+KHfvL5aHAxgoVBvz5wVhsdGuqKC7uuOFOOe7jw= +github.com/grafana/loki/v3 v3.2.1/go.mod h1:WvdLl6wOS+yahaeQY+xhD2m2XzkHDfKr5FZaX7D/X2Y= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3 h1:6D2gGAwyQBElSrp3E+9lSr7k8gLuP3Aiy20rweLWeBw= github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3/go.mod h1:YeND+6FDA7OuFgDzYODN8kfPhXLCehcpxe4T9mdnpCY= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= -github.com/grafana/pyroscope/api v0.3.0 h1:WcVKNZ8JlriJnD28wTkZray0wGo8dGkizSJXnbG7Gd8= -github.com/grafana/pyroscope/api v0.3.0/go.mod h1:JggA80ToAAUACYGfwL49XoFk5aN5ecHp4pNIZhlk9Uc= +github.com/grafana/pyroscope/api v1.0.0 h1:RWK3kpv8EAnB7JpOqnf//xwE84DdKF03N/iFxpFAoHY= +github.com/grafana/pyroscope/api v1.0.0/go.mod h1:CUrgOgSZDnx4M1mlRoxhrVKkTuKIse9p4FtuPbrGA04= github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/grafana/saml v0.4.15-0.20240523142256-cc370b98af7c h1:SWmG1QLZ36Ay0htq4Wt3dzlNIhWvQ3GUf7mk19dR8nI= -github.com/grafana/saml v0.4.15-0.20240523142256-cc370b98af7c/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw= +github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 h1:SDGrP81Vcd102L3UJEryRd1eestRw73wt+b8vnVEFe0= +github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw= github.com/grafana/sqlds/v4 v4.1.0 h1:dPvqaxmLJYqj5/EgP08Xcyy6RVaDdO8hWHpP4c1FQX4= github.com/grafana/sqlds/v4 v4.1.0/go.mod h1:3Z3r99mgA7+3mzQhABsNZnNtjLx7a0bGOgLVdAsFt2M= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 h1:XMreZ1SPjLpd9zhql5FXKFYwAcgBzS2E2MOPx4n+FyY= @@ -2336,18 +2387,23 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE= -github.com/hashicorp/consul/api v1.29.2 h1:aYyRn8EdE2mSfG14S1+L9Qkjtz8RzmaWh6AcNGRNwPw= -github.com/hashicorp/consul/api v1.29.2/go.mod h1:0YObcaLNDSbtlgzIRtmRXI1ZkeuK0trCBxwZQ4MYnIk= +github.com/hashicorp/consul/api v1.29.4 h1:P6slzxDLBOxUSj3fWo2o65VuKtbtOXFi7TSSgtXutuE= +github.com/hashicorp/consul/api v1.29.4/go.mod h1:HUlfw+l2Zy68ceJavv2zAyArl2fqhGWnMycyt56sBgg= +github.com/hashicorp/consul/proto-public v0.6.2 h1:+DA/3g/IiKlJZb88NBn0ZgXrxJp2NlvCZdEyl+qxvL0= +github.com/hashicorp/consul/proto-public v0.6.2/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A= +github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= +github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -2369,15 +2425,16 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= +github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.2.2/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= -github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= -github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= +github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= +github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= @@ -2441,8 +2498,9 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= @@ -2637,8 +2695,8 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-ieproxy v0.0.11 h1:MQ/5BuGSgDAHZOJe6YY80IF2UVCfGkwfo6AeD7HtHYo= -github.com/mattn/go-ieproxy v0.0.11/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0= +github.com/mattn/go-ieproxy v0.0.12 h1:OZkUFJC3ESNZPQ+6LzC3VJIFSnreeFLQyqvBWtvfL2M= +github.com/mattn/go-ieproxy v0.0.12/go.mod h1:Vn+N61199DAnVeTgaF8eoB9PvLO8P3OBnG95ENh7B7c= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -2686,8 +2744,8 @@ github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLT github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJnn2g= -github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= +github.com/minio/minio-go/v7 v7.0.75 h1:0uLrB6u6teY2Jt+cJUVi9cTvDRuBKWSRzSAcznRkwlE= +github.com/minio/minio-go/v7 v7.0.75/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= @@ -2699,7 +2757,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -2860,6 +2917,8 @@ github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20240926131254-992b301a003f h github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20240926131254-992b301a003f/go.mod h1:ll/hN6kS4EE6B/7J/PbZqac9Nuv7ZHpI+Jfh36JLrbs= github.com/openfga/openfga v1.6.2 h1:tHBAgiCPomCZb3IH0CFqOpTDVDA/Xb8kG87oX4JCbXc= github.com/openfga/openfga v1.6.2/go.mod h1:jzbEpheazf6MFjtanQt1rpxexSRzfa9057F7JlkMv2I= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= @@ -2873,6 +2932,8 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= @@ -2906,6 +2967,8 @@ github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= +github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -2944,8 +3007,8 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -2977,8 +3040,8 @@ github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= -github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= @@ -3000,8 +3063,10 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.52.0 h1:f7kHJgr7+zShpWdTCeKqbCWR7nKTScgLYQwRux9h1V0= github.com/prometheus/prometheus v0.52.0/go.mod h1:3z74cVsmVH0iXOR5QBjB7Pa6A0KJeEAK5A6UsmAFb1g= -github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4= -github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= +github.com/protocolbuffers/txtpbfmt v0.0.0-20240116145035-ef3ab179eed6 h1:MAzmm+JtFxQwTPb1cVMLkemw2OxLy5AB/d/rxtAwGQQ= +github.com/protocolbuffers/txtpbfmt v0.0.0-20240116145035-ef3ab179eed6/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= +github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= +github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= @@ -3056,6 +3121,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/encoding v0.4.0 h1:MEBYvRqiUB2nfR2criEXWqwdY6HJOUrCn5hboVOVmy8= github.com/segmentio/encoding v0.4.0/go.mod h1:/d03Cd8PoaDeceuhUUUQWjU0KhWjrmYrWPgtJHYZSnI= +github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= +github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= @@ -3091,9 +3158,13 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= @@ -3161,8 +3232,8 @@ github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSW github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= -github.com/thanos-io/objstore v0.0.0-20220809103346-8ef1f215e2bf h1:onQsPyHlq2yIWU+Nfl6yStuqnZuVQQN8FZ8sBb2wqtw= -github.com/thanos-io/objstore v0.0.0-20220809103346-8ef1f215e2bf/go.mod h1:v0NhuxxxUFUPatQcVNSCUkBEVezXzl7LSdaBOZygq98= +github.com/thanos-io/objstore v0.0.0-20240818203309-0363dadfdfb1 h1:z0v9BB/p7s4J6R//+0a5M3wCld8KzNjrGRLIwXfrAZk= +github.com/thanos-io/objstore v0.0.0-20240818203309-0363dadfdfb1/go.mod h1:3ukSkG4rIRUGkKM4oIz+BSuUx2e3RlQVVv3Cc3W+Tv4= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= @@ -3186,8 +3257,8 @@ github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a h1:vcrhXnj9g9PIE+cmZga github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM= -github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= +github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= +github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -3242,8 +3313,8 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -3285,6 +3356,8 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/collector/featuregate v1.5.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= +go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= +go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= go.opentelemetry.io/collector/pdata v1.5.0/go.mod h1:TYj8aKRWZyT/KuKQXKyqSEvK/GV+slFaDMEI+Ke64Yw= go.opentelemetry.io/collector/pdata v1.12.0 h1:Xx5VK1p4VO0md8MWm2icwC1MnJ7f8EimKItMWw46BmA= go.opentelemetry.io/collector/pdata v1.12.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI= @@ -3292,20 +3365,22 @@ go.opentelemetry.io/collector/semconv v0.98.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizk go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0 h1:sqmsIQ75l6lfZjjpnXXT9DFVtYEDg6CH0/Cn4/3A1Wg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0/go.mod h1:rsg1EO8LXSs2po50PB5CeY/MSVlhghuKBgXlKnqm6ks= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0 h1:7F3XCD6WYzDkwbi8I8N+oYJWquPVScnRosKGgqjsR8c= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0/go.mod h1:Dk3C0BfIlZDZ5c6eVS7TYiH2vssuyUU3vUsgbrR+5V4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= -go.opentelemetry.io/contrib/propagators/jaeger v1.29.0 h1:+YPiqF5rR6PqHBlmEFLPumbSP0gY0WmCGFayXRcCLvs= -go.opentelemetry.io/contrib/propagators/jaeger v1.29.0/go.mod h1:6PD7q7qquWSp3Z4HeM3e/2ipRubaY1rXZO8NIHVDZjs= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.25.0 h1:nwxen/H3auImb5D4Neo8TDhDseskdRYmlHvi7j1sExc= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.25.0/go.mod h1:KjcLnhrgBMLiPPAqyoGhuNmF6jm38LCPV6hdbBeFOTU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= +go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 h1:K/fOyTMD6GELKTIJBaJ9k3ppF2Njt8MeUGBOwfaWXXA= +go.opentelemetry.io/contrib/propagators/jaeger v1.32.0/go.mod h1:ISE6hda//MTWvtngG7p4et3OCngsrTVfl7c6DjN17f8= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 h1:/SKXyZLAnuj981HVc8G5ZylYK3qD2W6AYR6cJx5kIHw= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0/go.mod h1:cOEzME0M2OKeHB45lJiOKfvUCdg/r75mf7YS5w0tbmE= go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= @@ -3314,17 +3389,19 @@ go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFu go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0/go.mod h1:8GlBGcDk8KKi7n+2S4BT/CPZQYH3erLu0/k64r1MYgo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= @@ -3333,14 +3410,14 @@ go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= @@ -3349,8 +3426,8 @@ go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5Ukgg go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -3387,6 +3464,8 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= gocloud.dev v0.39.0 h1:EYABYGhAalPUaMrbSKOr5lejxoxvXj99nE8XFtsDgds= gocloud.dev v0.39.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -3516,8 +3595,8 @@ golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3541,6 +3620,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -3685,8 +3765,8 @@ golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3826,8 +3906,8 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3874,8 +3954,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3899,6 +3979,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -3981,8 +4062,8 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3994,6 +4075,8 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= @@ -4083,8 +4166,8 @@ google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYl google.golang.org/api v0.164.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= google.golang.org/api v0.174.0/go.mod h1:aC7tB6j0HR1Nl0ni5ghpx6iLasmAX78Zkh/wgxAAjLg= -google.golang.org/api v0.191.0 h1:cJcF09Z+4HAB2t5qTQM1ZtfL/PemsLFkcFG67qq2afk= -google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E= +google.golang.org/api v0.193.0 h1:eOGDoJFsLU+HpCBaDJex2fWiYujAw9KbXgpOAMePoUs= +google.golang.org/api v0.193.0/go.mod h1:Po3YMV1XZx+mTku3cfJrlIYR03wiGrCOsdpC67hjZvw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -4267,8 +4350,8 @@ google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqt google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 h1:CT2Thj5AuPV9phrYMtzX11k+XkzMGfRAet42PmoTATM= -google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988/go.mod h1:7uvplUBj4RjHAxIZ//98LzOvrQ04JBkaixRmCMI29hc= +google.golang.org/genproto v0.0.0-20240820151423-278611b39280 h1:oKt8r1ZvaPqBe3oeGTdyx1iNjuBS+VJcc9QdU1CD3d8= +google.golang.org/genproto v0.0.0-20240820151423-278611b39280/go.mod h1:wxEc5TmU9JSLs1rSqG4z1YzeSNigp/9yIojIPuZVvKQ= google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= @@ -4299,8 +4382,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go. google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w= -google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= -google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= @@ -4345,8 +4428,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240415141817-7cd4c1c1f9ec/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -4436,9 +4519,8 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= @@ -4502,6 +4584,8 @@ honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= diff --git a/go.work b/go.work index e75f9e25b4e..0b92b51884d 100644 --- a/go.work +++ b/go.work @@ -6,6 +6,7 @@ go 1.23.1 use ( . // skip:golangci-lint ./apps/playlist + ./kindsv2 ./pkg/aggregator ./pkg/apimachinery ./pkg/apiserver @@ -25,3 +26,7 @@ replace xorm.io/xorm => ./pkg/util/xorm replace github.com/getkin/kin-openapi => github.com/getkin/kin-openapi v0.125.0 replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3 + +replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 + +replace github.com/goccy/go-json => github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c diff --git a/go.work.sum b/go.work.sum index d0f7f3ddcd4..86530b93fd3 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,253 +1,473 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= +cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.16.0 h1:yloc84fytn4zmJX2GU3TkXGsaieaV7dQ057Qs4sIG2Y= cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= cloud.google.com/go/accessapproval v1.7.11 h1:MgtE8CI+YJWPGGHnxQ9z1VQqV87h+vSGy2MeM/m0ggQ= cloud.google.com/go/accessapproval v1.7.11/go.mod h1:KGK3+CLDWm4BvjN0wFtZqdFUGhxlTvTF6PhAwQJGL4M= +cloud.google.com/go/accessapproval v1.7.12 h1:uxJBvDCor5iuCYOVby/0WTsHSMyBh8bpJ6hzqKdcL64= +cloud.google.com/go/accessapproval v1.7.12/go.mod h1:wvyt8Okohbq1i8/aPbCMBNwGQFZaNli5d+1qa/5zgGo= cloud.google.com/go/accesscontextmanager v1.8.11 h1:IQ3KLJmNKPgstN0ZcRw0niU4KfsiOZmzvcGCF+NT618= cloud.google.com/go/accesscontextmanager v1.8.11/go.mod h1:nwPysISS3KR5qXipAU6cW/UbDavDdTBBgPohbkhGSok= +cloud.google.com/go/accesscontextmanager v1.8.12 h1:u3gLVJFZ8couGeVBZmhK0ON5YU3w9YIXOVr+THOik/M= +cloud.google.com/go/accesscontextmanager v1.8.12/go.mod h1:EmaVYmffq+2jA2waP0/XHECDkaOKVztxVsdzl65t8hw= cloud.google.com/go/aiplatform v1.68.0 h1:EPPqgHDJpBZKRvv+OsB3cr0jYz3EL2pZ+802rBPcG8U= cloud.google.com/go/aiplatform v1.68.0/go.mod h1:105MFA3svHjC3Oazl7yjXAmIR89LKhRAeNdnDKJczME= cloud.google.com/go/analytics v0.23.6 h1:BY8ZY7hQwKBi+lNp1IkiMTOK4xe4lxZCeYv3S9ARXtE= cloud.google.com/go/analytics v0.23.6/go.mod h1:cFz5GwWHrWQi8OHKP9ep3Z4pvHgGcG9lPnFQ+8kXsNo= +cloud.google.com/go/analytics v0.24.0 h1:wIMRXRQAanMWaUGSOglVwEmpZqCC4WAs9YacMuGcD88= +cloud.google.com/go/analytics v0.24.0/go.mod h1:NpavJSb6TSO56hGpX1+4JL7js6AkKl27TEqzW9Sn7E4= cloud.google.com/go/apigateway v1.6.11 h1:VtEvpnqqY2T5gZBzo+p7C87yGH3omHUkPIbRQkmGS9I= cloud.google.com/go/apigateway v1.6.11/go.mod h1:4KsrYHn/kSWx8SNUgizvaz+lBZ4uZfU7mUDsGhmkWfM= +cloud.google.com/go/apigateway v1.6.12 h1:yynQNoSDVxbc8iPA2kLC4RWj1Ir64/FhGcXbyhJKC4c= +cloud.google.com/go/apigateway v1.6.12/go.mod h1:2RX6Op78cxqMtENfJW8kKpwtBCFVJGyvBtSR9l6v7aM= cloud.google.com/go/apigeeconnect v1.6.11 h1:CftZgGXFRLJeD2/5ZIdWuAMxW/88UG9tHhRPI/NY75M= cloud.google.com/go/apigeeconnect v1.6.11/go.mod h1:iMQLTeKxtKL+sb0D+pFlS/TO6za2IUOh/cwMEtn/4g0= +cloud.google.com/go/apigeeconnect v1.6.12 h1:Pq2wTmm41l2w5sLuxEGRZmXhYULKg5qIWcvGEOsFhZQ= +cloud.google.com/go/apigeeconnect v1.6.12/go.mod h1:/DSr1IlfzrXeKjS6c3+8P04avr+4U5S7J3F69SNGFkY= cloud.google.com/go/apigeeregistry v0.8.9 h1:3vLwk0tS9L++6ZyV4RDH4UCydfVoqxJbpWvqG6MTtUw= cloud.google.com/go/apigeeregistry v0.8.9/go.mod h1:4XivwtSdfSO16XZdMEQDBCMCWDp3jkCBRhVgamQfLSA= +cloud.google.com/go/apigeeregistry v0.8.10 h1:DYqJWmGXIfPWnvSER1eIrMvCpzpPaWyfwUbZ9n51tR0= +cloud.google.com/go/apigeeregistry v0.8.10/go.mod h1:3uJa4XfNqvhIvKksKEE7UahxZY1/2Uj07cCfT/RJZZM= cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= cloud.google.com/go/appengine v1.8.11 h1:ZLoWWwakgRzRnXX2bsgk2g1sdzti3wq+ebunTJsZNog= cloud.google.com/go/appengine v1.8.11/go.mod h1:xET3coaDUj+OP4TgnZlgQ+rG2R9fG2nblya13czP56Q= +cloud.google.com/go/appengine v1.8.12 h1:R4n+yN8pyOZQmufa5NlvPKLnG4C0YIAgYUmKLHN7Xgg= +cloud.google.com/go/appengine v1.8.12/go.mod h1:31Ib+S1sYnRQmCtfGqEf6EfzsiYy98EuDtLlvmpmx6U= cloud.google.com/go/area120 v0.8.11 h1:UID1dl7lW2zs8OpYVtVZ5WsXU9kUcxC1nd3nnToHW70= cloud.google.com/go/area120 v0.8.11/go.mod h1:VBxJejRAJqeuzXQBbh5iHBYUkIjZk5UzFZLCXmzap2o= +cloud.google.com/go/area120 v0.8.12 h1:MIAp/4z3v6RVmdhBy20xL3q2eFCpMRSk9gQqxwppu44= +cloud.google.com/go/area120 v0.8.12/go.mod h1:W94qTbrwhzGimOeoClrGdm5DAkMGlg/V6Maldra5QM8= cloud.google.com/go/artifactregistry v1.14.13 h1:NNK4vYVA5NGQmbmYidfJhnfmYU6SSSRUM2oopNouJNs= cloud.google.com/go/artifactregistry v1.14.13/go.mod h1:zQ/T4xoAFPtcxshl+Q4TJBgsy7APYR/BLd2z3xEAqRA= +cloud.google.com/go/artifactregistry v1.14.14 h1:RADsnxebM3WPtZrvWx2PYGWWxyqEsbe3rTTDRad6cZM= +cloud.google.com/go/artifactregistry v1.14.14/go.mod h1:lPHksFcKpcZRrhGNx87a6SSygv0hfWi6Cd0gnWIUU4U= cloud.google.com/go/asset v1.19.5 h1:/R2XZS6lR8oj/Y3L+epD2yy7mf44Zp62H4xZ4vzaR/Y= cloud.google.com/go/asset v1.19.5/go.mod h1:sqyLOYaLLfc4ACcn3YxqHno+J7lRt9NJTdO50zCUcY0= +cloud.google.com/go/asset v1.19.6 h1:p8Huo2TW4GyTfqQZuvUsmvYc3bXC1uS51VvpKfPfqqU= +cloud.google.com/go/asset v1.19.6/go.mod h1:UsijVGuWC6uml/+ODlL+mv6e3dZ52fbdOfOkiv4f0cE= cloud.google.com/go/assuredworkloads v1.11.11 h1:pwZp9o8aF5QmX4Z0YNlRe1ZOUzDw0UALmkem3aPobZc= cloud.google.com/go/assuredworkloads v1.11.11/go.mod h1:vaYs6+MHqJvLKYgZBOsuuOhBgNNIguhRU0Kt7JTGcnI= +cloud.google.com/go/assuredworkloads v1.11.12 h1:Oh8gYugn66aqjxlCEJq7WMEo7W8RR12mPK0B2FFfhL4= +cloud.google.com/go/assuredworkloads v1.11.12/go.mod h1:yYnk9icCH5XEkqjJinBNBDv5mSvi1FYhpA9Q+BpTwew= +cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= +cloud.google.com/go/auth v0.6.1/go.mod h1:eFHG7zDzbXHKmjJddFG/rBlcGp6t25SwRUiEQSlO4x4= cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= cloud.google.com/go/auth v0.8.0/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= cloud.google.com/go/automl v1.13.11 h1:FBCLjGS+Did/wtRHqyS055bRs/EJXx3meTvHPcdZgk8= cloud.google.com/go/automl v1.13.11/go.mod h1:oMJdXRDOVC+Eq3PnGhhxSut5Hm9TSyVx1aLEOgerOw8= +cloud.google.com/go/automl v1.13.12 h1:sjA6i5RbSqtgVzlLStZN3++AHKbZaCCvBmt68NeAAS0= +cloud.google.com/go/automl v1.13.12/go.mod h1:Rw8hmEIlKyvdhbFXjLrLvM2qNKZNwf5oraS5DervadE= cloud.google.com/go/baremetalsolution v1.2.10 h1:VvBiXT9QJ4VpNVyfzHhLScY1aymZxpQgOa20yUvgphw= cloud.google.com/go/baremetalsolution v1.2.10/go.mod h1:eO2c2NMRy5ytcNPhG78KPsWGNsX5W/tUsCOWmYihx6I= +cloud.google.com/go/baremetalsolution v1.2.11 h1:rhMIixzQvm9cy8Ks3etB/JQOMw5vU8z71Jqf7VwI1Ts= +cloud.google.com/go/baremetalsolution v1.2.11/go.mod h1:bqthxNtU+n3gwWxoyXVR9VdSqIfVcgmpYtBlXQkeWq8= cloud.google.com/go/batch v1.9.2 h1:o1RAjc0ExGAAm41YB9LbJZyJDgZR4M6SKyITsd/Smr4= cloud.google.com/go/batch v1.9.2/go.mod h1:smqwS4sleDJVAEzBt/TzFfXLktmWjFNugGDWl8coKX4= +cloud.google.com/go/batch v1.9.4 h1:JQWzsdmjnONvqNWeAubtDI6OIFv/v2lCYGzx8lpxTKg= +cloud.google.com/go/batch v1.9.4/go.mod h1:qqfXThFPI9dyDK1PfidiEOM/MrS+jUQualcQJytJCLA= cloud.google.com/go/beyondcorp v1.0.10 h1:K4blSIQZn3YO4F4LmvWrH52pb8Y0L3NOrwkf22+x67M= cloud.google.com/go/beyondcorp v1.0.10/go.mod h1:G09WxvxJASbxbrzaJUMVvNsB1ZiaKxpbtkjiFtpDtbo= +cloud.google.com/go/beyondcorp v1.0.11 h1:oBXJtKP2J7VLqYQf6l5i2Cqm/qjFJPngnGKbzehkxYU= +cloud.google.com/go/beyondcorp v1.0.11/go.mod h1:V0EIXuYoyqKkHfnNCYZrNv6M+WYWJGIr5h019LurF3I= cloud.google.com/go/bigquery v1.62.0 h1:SYEA2f7fKqbSRRBHb7g0iHTtZvtPSPYdXfmqsjpsBwo= cloud.google.com/go/bigquery v1.62.0/go.mod h1:5ee+ZkF1x/ntgCsFQJAQTM3QkAZOecfCmvxhkJsWRSA= cloud.google.com/go/bigtable v1.27.2-0.20240802230159-f371928b558f h1:UR2/6M/bSN8PPQlhaq+57w21VZLcEvq4ujsHd1p/G2s= cloud.google.com/go/bigtable v1.27.2-0.20240802230159-f371928b558f/go.mod h1:avmXcmxVbLJAo9moICRYMgDyTTPoV0MA0lHKnyqV4fQ= +cloud.google.com/go/bigtable v1.29.0 h1:2CnFjKPwjpZMZdTi2RpppvxzD80zKzDYrLYEQw/NnAs= +cloud.google.com/go/bigtable v1.29.0/go.mod h1:5p909nNdWaNUcWs6KGZO8mI5HUovstlmrIi7+eA5PTQ= cloud.google.com/go/billing v1.18.9 h1:sGRWx7PvsfHuZyx151Xr6CrORIgjvCMO4GRabihSdQQ= cloud.google.com/go/billing v1.18.9/go.mod h1:bKTnh8MBfCMUT1fzZ936CPN9rZG7ZEiHB2J3SjIjByc= +cloud.google.com/go/billing v1.18.10 h1:Hguzxohg+0U/qMFmB0hyK/rM1yH1VSARMgZ8fM1K5as= +cloud.google.com/go/billing v1.18.10/go.mod h1:Lt+Qrjqsde38l/h1+9fzu44Pv9t+Suyf/p973mrg+xU= cloud.google.com/go/binaryauthorization v1.8.7 h1:ItT9uR/0/ok2Ru3LCcbSIBUPsKqTA49ZmxCupqQaeFo= cloud.google.com/go/binaryauthorization v1.8.7/go.mod h1:cRj4teQhOme5SbWQa96vTDATQdMftdT5324BznxANtg= +cloud.google.com/go/binaryauthorization v1.8.8 h1:Gxcy5/ydfTg2CviIxnZPVCNcFoDmQRbjzfAKg8bDJAU= +cloud.google.com/go/binaryauthorization v1.8.8/go.mod h1:D7B3gkNPdZ1Zj2IEyfypDTgbwFgTWE2SE6Csz0f46jg= cloud.google.com/go/certificatemanager v1.8.5 h1:ASC9N81NU8JnGzi9kiY2QTqtTgOziwGv48sjt3YG420= cloud.google.com/go/certificatemanager v1.8.5/go.mod h1:r2xINtJ/4xSz85VsqvjY53qdlrdCjyniib9Jp98ZKKM= +cloud.google.com/go/certificatemanager v1.8.6 h1:ekHCSJWJ8NFWvCRLRRWrJh1iPeJAKx0oIq0i9gFyVRM= +cloud.google.com/go/certificatemanager v1.8.6/go.mod h1:ZsK7vU+XFDfSRwOqB4GjAGzawIIA3dWPXaFC9I5Jsts= cloud.google.com/go/channel v1.17.11 h1:AkKyMl2pSoJxBQtjAd6LYOtMgOaCl/kuiKoSg/Gf/H4= cloud.google.com/go/channel v1.17.11/go.mod h1:gjWCDBcTGQce/BSMoe2lAqhlq0dIRiZuktvBKXUawp0= +cloud.google.com/go/channel v1.17.12 h1:JNtAUfwGEOgeBp1bOxSzY/Hi3H1ARySNrj5cNBmOG+M= +cloud.google.com/go/channel v1.17.12/go.mod h1:DoVQacEH1YuNqIZVN8v67cXGxaUyOgjrst+/+pkVqWU= cloud.google.com/go/cloudbuild v1.16.5 h1:RvK5r8JBCLNg9XmfGPy05t3bmhLJV3Xh3sDHGHAATgM= cloud.google.com/go/cloudbuild v1.16.5/go.mod h1:HXLpZ8QeYZgmDIWpbl9Gs22p6o6uScgQ/cV9HF9cIZU= +cloud.google.com/go/cloudbuild v1.16.6 h1:6kV/JpCSKq64KWuZMYWZ8/utSkifWfEtQ+EcPjWPPLs= +cloud.google.com/go/cloudbuild v1.16.6/go.mod h1:Y7+6WFO8pT53rG0Lve6OZoO4+RkVTHGnHG7EB3uNiQw= cloud.google.com/go/clouddms v1.7.10 h1:EA3y9v5TZiAlwgHJh2vPOEelqYiCxXBYZRCNnGK5q+g= cloud.google.com/go/clouddms v1.7.10/go.mod h1:PzHELq0QDyA7VaD9z6mzh2mxeBz4kM6oDe8YxMxd4RA= +cloud.google.com/go/clouddms v1.7.11 h1:pc78L2+BP6ex67gHqr1YjJ3uGo9gKLRCYk2hzzs9P2g= +cloud.google.com/go/clouddms v1.7.11/go.mod h1:rPNK0gJEkF2//rdxhCKhx+IFBlzkObOZhlhvDY1JKCE= cloud.google.com/go/cloudtasks v1.12.12 h1:p91Brp4nJkyRRI/maYdO+FT+e9tU+2xoGr20s2rvalU= cloud.google.com/go/cloudtasks v1.12.12/go.mod h1:8UmM+duMrQpzzRREo0i3x3TrFjsgI/3FQw3664/JblA= +cloud.google.com/go/cloudtasks v1.12.13 h1:SN2NqXSOdmQzQJjoMV0qLIn6GXG5SzRrRVNrNq2Q6zA= +cloud.google.com/go/cloudtasks v1.12.13/go.mod h1:53OmmKqQTocrbeCL13cuaryBQOflyO8s4NxuRHJlXgc= cloud.google.com/go/compute v1.27.4 h1:XM8ulx6crjdl09XBfji7viFgZOEQuIxBwKmjRH9Rtmc= cloud.google.com/go/compute v1.27.4/go.mod h1:7JZS+h21ERAGHOy5qb7+EPyXlQwzshzrx1x6L9JhTqU= +cloud.google.com/go/compute v1.27.5 h1:iii9Z+FhEeZ5cUkGOEqU+GM7MJSyxMgbE7H7j+JndYY= +cloud.google.com/go/compute v1.27.5/go.mod h1:DfwDGujFTdSeiE8b8ZqadF/uxHFBz+ekGsk8Zfi9dTA= cloud.google.com/go/contactcenterinsights v1.13.6 h1:LRcI5RAlLIbjwT312sGt+gyXcaXTr+v7uEQlNyArO9g= cloud.google.com/go/contactcenterinsights v1.13.6/go.mod h1:mL+DbN3pMQGaAbDC4wZhryLciwSwHf5Tfk4Itr72Zyk= +cloud.google.com/go/contactcenterinsights v1.13.7 h1:KAqbmVHi77C5DwQ5ahpI41czo7LO2AWe0QJLDPfaVmY= +cloud.google.com/go/contactcenterinsights v1.13.7/go.mod h1:N5D7yxGknC0pDUC1OKOLShGQwpidKizKu3smt08153U= cloud.google.com/go/container v1.38.0 h1:GP5zLamfvPZeOTifnGBSER/br76D5eJ97xhcXXrh5tM= cloud.google.com/go/container v1.38.0/go.mod h1:U0uPBvkVWOJGY/0qTVuPS7NeafFEUsHSPqT5pB8+fCY= +cloud.google.com/go/container v1.38.1 h1:Pb0GbZIg/KS4A9gbF3J4JHmrgPpBA2y+4v9N04aJkOs= +cloud.google.com/go/container v1.38.1/go.mod h1:2r4Qiz6IG2LhRFfWhPNmrYD7yzdE2B2kghigVWoSw/g= cloud.google.com/go/containeranalysis v0.12.1 h1:Xb8Eu7vVmWR5nAl5WPTGTx/dCr+R+oF7VbuYV47EHHs= cloud.google.com/go/containeranalysis v0.12.1/go.mod h1:+/lcJIQSFt45TC0N9Nq7/dPbl0isk6hnC4EvBBqyXsM= +cloud.google.com/go/containeranalysis v0.12.2 h1:BE8mBsV41BgUFQO3QW9wdpF3s5owx8idOcJ3I2aAoQA= +cloud.google.com/go/containeranalysis v0.12.2/go.mod h1:XF/U1ZJ9kXfl8HWRzuWMtEtzBb8SvJ0zvySrxrQA3N0= cloud.google.com/go/datacatalog v1.21.0 h1:vl0pQT9TZ5rKi9e69FgtXNCR7I8MVRj4+CnbeXhz6UQ= cloud.google.com/go/datacatalog v1.21.0/go.mod h1:DB0QWF9nelpsbB0eR/tA0xbHZZMvpoFD1XFy3Qv/McI= +cloud.google.com/go/datacatalog v1.21.1 h1:l8yPkaMTlIX/437kBKGURvk4dtZIbotHBuSX2nLbJY8= +cloud.google.com/go/datacatalog v1.21.1/go.mod h1:23qsWWm592aQHwZ4or7VDjNhx7DeNklHAPE3GM47d1U= cloud.google.com/go/dataflow v0.9.11 h1:YIhStasKFDESaUdpnsHsp/5bACYL/yvW0OuZ6zPQ6nY= cloud.google.com/go/dataflow v0.9.11/go.mod h1:CCLufd7I4pPfyp54qMgil/volrL2ZKYjXeYLfQmBGJs= +cloud.google.com/go/dataflow v0.9.12 h1:p80cphmUBav+Htfr9DSQf++P0s8FrQT4BKsGmvYXBxM= +cloud.google.com/go/dataflow v0.9.12/go.mod h1:+2+80N2FOdDFWYhZdC2uTlX7GHP5kOH4vPNtfadggqQ= cloud.google.com/go/dataform v0.9.8 h1:oNtTx9PdH7aPnvrKIsPrh+Y6Mw+8Bw5/ZgLWVHAev/c= cloud.google.com/go/dataform v0.9.8/go.mod h1:cGJdyVdunN7tkeXHPNosuMzmryx55mp6cInYBgxN3oA= +cloud.google.com/go/dataform v0.9.9 h1:4aZ5O9aBHXUgQFZfu4sKjgIJIpzunbxZ1y8GHgtQZG8= +cloud.google.com/go/dataform v0.9.9/go.mod h1:QkiXNcrbFGjYtPtTkn700sfBiGIOG4mmpt26Ds8Ixeg= cloud.google.com/go/datafusion v1.7.11 h1:GVcVisjVKmoj1eNnIp3G3qjjo+7koHr0Kf8tF6Cjqe0= cloud.google.com/go/datafusion v1.7.11/go.mod h1:aU9zoBHgYmoPp4dzccgm/Gi4xWDMXodSZlNZ4WNeptw= +cloud.google.com/go/datafusion v1.7.12 h1:O5p8qgJg+7sn55JnaEnRr2yMX37bOZTMp3GBOa6dG0o= +cloud.google.com/go/datafusion v1.7.12/go.mod h1:ZUaEMjNVppM5ZasVt87QE0jN57O0LKY3uFe67EQ0GGI= cloud.google.com/go/datalabeling v0.8.11 h1:7jSuJEAc7upeMmyICzqfU0OyxUV38JSWW+8r5GmoHX0= cloud.google.com/go/datalabeling v0.8.11/go.mod h1:6IGUV3z7hlkAU5ndKVshv/8z+7pxE+k0qXsEjyzO1Xg= +cloud.google.com/go/datalabeling v0.8.12 h1:iC7Lpzya3QKVLUy8M2J4y+JZrjAFtHg8bvAuLhMpkGw= +cloud.google.com/go/datalabeling v0.8.12/go.mod h1:IBbWnl80akCFj7jZ89/dRB/juuXig+QrQoLg24+vidg= cloud.google.com/go/dataplex v1.18.2 h1:bIU1r1YnsX6P1qTnaRnah/STHoLJ3EHUZVCjJl2+1Eo= cloud.google.com/go/dataplex v1.18.2/go.mod h1:NuBpJJMGGQn2xctX+foHEDKRbizwuiHJamKvvSteY3Q= +cloud.google.com/go/dataplex v1.18.3 h1:jYcAzyFS1DcnpwBVfnUD8wrRlltgU5L1EQdPshCEO+o= +cloud.google.com/go/dataplex v1.18.3/go.mod h1:wcfVhUr529uu9aZSy9WIUUdOCrkB8M5Gikfh3YUuGtE= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= cloud.google.com/go/dataproc/v2 v2.5.3 h1:OgTfUARkF8AfkNmoyT0wyLLXNh4LbT3l55s5gUlvFOk= cloud.google.com/go/dataproc/v2 v2.5.3/go.mod h1:RgA5QR7v++3xfP7DlgY3DUmoDSTaaemPe0ayKrQfyeg= +cloud.google.com/go/dataproc/v2 v2.5.4 h1:otFEJPOxzbAniKw4Aa1GVJlxjejR4/qHBlmU+xhY6MI= +cloud.google.com/go/dataproc/v2 v2.5.4/go.mod h1:rpxihxKtWjPl8MDwjGiYgMva8nEWQSyzvl3e0p4ATt4= cloud.google.com/go/dataqna v0.8.11 h1:bEUidOYRS0EQ7qHbZtcnospuks72iTapboszXU9poz8= cloud.google.com/go/dataqna v0.8.11/go.mod h1:74Icl1oFKKZXPd+W7YDtqJLa+VwLV6wZ+UF+sHo2QZQ= +cloud.google.com/go/dataqna v0.8.12 h1:bTiS9ppeA86EnJnXqZZLiQWXglFpyaF2TK/iPkZ/ga0= +cloud.google.com/go/dataqna v0.8.12/go.mod h1:86JdVMqh3521atZY1P7waaa50vzIbErTLY7gsio+umg= cloud.google.com/go/datastore v1.17.1 h1:6Me8ugrAOAxssGhSo8im0YSuy4YvYk4mbGvCadAH5aE= cloud.google.com/go/datastore v1.17.1/go.mod h1:mtzZ2HcVtz90OVrEXXGDc2pO4NM1kiBQy8YV4qGe0ZM= cloud.google.com/go/datastream v1.10.10 h1:klGhjQCLoLIRHMzMFIqM73cPNKliGveqC+Vrms+ce6A= cloud.google.com/go/datastream v1.10.10/go.mod h1:NqchuNjhPlISvWbk426/AU/S+Kgv7srlID9P5XOAbtg= +cloud.google.com/go/datastream v1.10.11 h1:LpJhP4g8+JGO5dKwhVSblnpB1hk/qA2/+rq68ppkVF8= +cloud.google.com/go/datastream v1.10.11/go.mod h1:0d9em/ERaof15lY5JU3pWKF7ZJOHiPKcNJsTCBz6TX8= cloud.google.com/go/deploy v1.21.0 h1:/qnNETfztKemA9JmUBOrnH/rG/XFkHOBHygN1Vy5lkg= cloud.google.com/go/deploy v1.21.0/go.mod h1:PaOfS47VrvmYnxG5vhHg0KU60cKeWcqyLbMBjxS8DW8= +cloud.google.com/go/deploy v1.21.2 h1:sperWx8Nk3AFnonH8XXxCdarZfeXZYR7dgmDFoBkuCc= +cloud.google.com/go/deploy v1.21.2/go.mod h1:BDBWUXXCBGrvYxVmSYXIRdNffioym0ChQWDQS0c/wA8= cloud.google.com/go/dialogflow v1.55.0 h1:H28O0WAm2waHpNAz2n9jbv8FApfXxeKAkfHObdP2MMk= cloud.google.com/go/dialogflow v1.55.0/go.mod h1:0u0hSlJiFpMkMpMNoFrQETwDjaRm8Q8hYKv+jz5JeRA= +cloud.google.com/go/dialogflow v1.56.0 h1:s4lhL0DZExduaN534Rl3K488sKkes7LHnDQtbpwFHNk= +cloud.google.com/go/dialogflow v1.56.0/go.mod h1:P1hIske3kr9pSl11nEP4tFfAu2E4US+7PpboeBhM4ag= cloud.google.com/go/dlp v1.16.0 h1:mYjBqgVjseYXlx1TOOFsxSeZLboqxxKR7TqRGOG9vIU= cloud.google.com/go/dlp v1.16.0/go.mod h1:LtPZxZAenBXKzvWIOB2hdHIXuEcK0wW0En8//u+/nNA= +cloud.google.com/go/dlp v1.17.0 h1:BrKrhNjAi5u8EgIadv1mcjEX4bB7FPPcRvr0Cf9bdN4= +cloud.google.com/go/dlp v1.17.0/go.mod h1:9LuCkaCRZxWZ6HyqkmV3/PW0gKIVKoUVNjf0yMKVqMs= cloud.google.com/go/documentai v1.31.0 h1:YRkFK+0ZgEciz1svDkuL9fjbQLq8xvVa1d3NUlhO6B4= cloud.google.com/go/documentai v1.31.0/go.mod h1:5ajlDvaPyl9tc+K/jZE8WtYIqSXqAD33Z1YAYIjfad4= +cloud.google.com/go/documentai v1.32.0 h1:bXTTAjRmu7oVlvLqV4lLc7VKqR/IUtgexBPyLDWUs/o= +cloud.google.com/go/documentai v1.32.0/go.mod h1:X8skObtXBvR31QF+jERAu4mOCpRiJBaqbMvB3FLnMsA= cloud.google.com/go/domains v0.9.11 h1:8peNiXtaMNIF9Wybci859M/yprFcEve1R2z08pErUBs= cloud.google.com/go/domains v0.9.11/go.mod h1:efo5552kUyxsXEz30+RaoIS2lR7tp3M/rhiYtKXkhkk= +cloud.google.com/go/domains v0.9.12 h1:Ot7K4Vl4Ap/y90VYjyz9qL57e+pAhMYVP0bIM7YnPvI= +cloud.google.com/go/domains v0.9.12/go.mod h1:2YamnZleyO3y5zYV+oASWAUoiHBJ0ZmkEcO6MXs5x3c= cloud.google.com/go/edgecontainer v1.2.5 h1:wTo0ulZDSsDzeoVjICJZjZMzZ1Nn9y//AwAQlXbaTbs= cloud.google.com/go/edgecontainer v1.2.5/go.mod h1:OAb6tElD3F3oBujFAup14PKOs9B/lYobTb6LARmoACY= +cloud.google.com/go/edgecontainer v1.2.6 h1:r/4Yqg3Kji26XbCegwta1c7C92uwO0TUSaTwbzG5s/w= +cloud.google.com/go/edgecontainer v1.2.6/go.mod h1:4jyHt4ytGLL8P0S3m6umOL8bJhTw4tVnDUcPQCGlNMM= cloud.google.com/go/errorreporting v0.3.1 h1:E/gLk+rL7u5JZB9oq72iL1bnhVlLrnfslrgcptjJEUE= cloud.google.com/go/errorreporting v0.3.1/go.mod h1:6xVQXU1UuntfAf+bVkFk6nld41+CPyF2NSPCyXE3Ztk= cloud.google.com/go/essentialcontacts v1.6.12 h1:JaQXS+qCFYs8yectfZHpzw4+NjTvFqTuDMCtfPzMvbw= cloud.google.com/go/essentialcontacts v1.6.12/go.mod h1:UGhWTIYewH8Ma4wDRJp8cMAHUCeAOCKsuwd6GLmmQLc= +cloud.google.com/go/essentialcontacts v1.6.13 h1:zKttHKhAV6LxT/SlahypK+2hBjmIVg9elMn3r0WQ5cY= +cloud.google.com/go/essentialcontacts v1.6.13/go.mod h1:52AB7Qmi6TBzA/lsSZER7oi4jR/pY0TXC0lNaaAyfA4= cloud.google.com/go/eventarc v1.13.10 h1:HVJmOVc+7eVFAqMpJRrq0nY0KlYBEBVZW7Gz7TxTio8= cloud.google.com/go/eventarc v1.13.10/go.mod h1:KlCcOMApmUaqOEZUpZRVH+p0nnnsY1HaJB26U4X5KXE= +cloud.google.com/go/eventarc v1.13.11 h1:bFkxnUwdOKKgXjCoxpwd9FYV/yBs8iUhIuiJV4R1jlE= +cloud.google.com/go/eventarc v1.13.11/go.mod h1:1PJ+icw2mJYgqUsICg7Cr8gzMw38f3THiSzVSNPFrNQ= cloud.google.com/go/filestore v1.8.7 h1:LF9t5MClPyFJMuXdez/AjF1uyO9xHKUFF3GUqA+xFPI= cloud.google.com/go/filestore v1.8.7/go.mod h1:dKfyH0YdPAKdYHqAR/bxZeil85Y5QmrEVQwIYuRjcXI= +cloud.google.com/go/filestore v1.8.8 h1:ZPE2+k1eyesicuehhHRXEG7WDo3mW/0li8oNnmuyxYE= +cloud.google.com/go/filestore v1.8.8/go.mod h1:gNT7bpDZSOFWCnRirQw1IehZtA7blbzkO3Q8VQfkeZ0= cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc= cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg= cloud.google.com/go/functions v1.16.6 h1:tPe3/48RpjcFk96VeB6jOKQpK8nliGJLsgjh6pUOyFQ= cloud.google.com/go/functions v1.16.6/go.mod h1:wOzZakhMueNQaBUJdf0yjsJIe0GBRu+ZTvdSTzqHLs0= +cloud.google.com/go/functions v1.18.0 h1:50K2iLtj7xj6xqrhdNOL+aVaHwKi4h7qgNWP9ieTDag= +cloud.google.com/go/functions v1.18.0/go.mod h1:r8uxxI35hdP2slfTjGJvx04NRy8sP/EXUMZ0NYfBd+w= cloud.google.com/go/gaming v1.10.1 h1:5qZmZEWzMf8GEFgm9NeC3bjFRpt7x4S6U7oLbxaf7N8= cloud.google.com/go/gkebackup v1.5.4 h1:mufh0PNpvqbfLV+TcxzSGESX8jGBcjKgctldv7kwQns= cloud.google.com/go/gkebackup v1.5.4/go.mod h1:V+llvHlRD0bCyrkYaAMJX+CHralceQcaOWjNQs8/Ymw= +cloud.google.com/go/gkebackup v1.5.5 h1:ITiOPDl0Q1vgDHdbMvRk0Yk6uEcP8Kq875m6IMJJu6Y= +cloud.google.com/go/gkebackup v1.5.5/go.mod h1:C/XZ2LoG+V97xGc18oCPniO754E0iHt0OXqKatawBMM= cloud.google.com/go/gkeconnect v0.8.11 h1:4bZAzvqhuv1uP+i4yG9cEMQ6ggdP26nBVjUgroPU6IM= cloud.google.com/go/gkeconnect v0.8.11/go.mod h1:ejHv5ehbceIglu1GsMwlH0nZpTftjxEY6DX7tvaM8gA= +cloud.google.com/go/gkeconnect v0.8.12 h1:0n3xfOvU+SN2LJRWex81U3xCNfQtPpxjSkrI3XBdudM= +cloud.google.com/go/gkeconnect v0.8.12/go.mod h1:+SpnnnUx4Xs/mWBJbqC7Mlu9Vv7riQlHSDS1T1ek2+U= cloud.google.com/go/gkehub v0.14.11 h1:hQkVCcOiW/vPVYsthvKl1nje430/TpdFfgeIuqcYVOA= cloud.google.com/go/gkehub v0.14.11/go.mod h1:CsmDJ4qbBnSPkoBltEubK6qGOjG0xNfeeT5jI5gCnRQ= +cloud.google.com/go/gkehub v0.14.12 h1:twVZpPzRnwJQ9UwU3M/Mwn356qboSRo+UxsTmlMtKiM= +cloud.google.com/go/gkehub v0.14.12/go.mod h1:CNYNBCqjIkE9L70gzbRxZOsc++Wcp2oCLkfuytOFqRM= cloud.google.com/go/gkemulticloud v1.2.4 h1:6zV05tyl37HoEjCGGY+zHFNxnKQCjvVpiqWAUVgGaEs= cloud.google.com/go/gkemulticloud v1.2.4/go.mod h1:PjTtoKLQpIRztrL+eKQw8030/S4c7rx/WvHydDJlpGE= +cloud.google.com/go/gkemulticloud v1.2.5 h1:OREoK+FO+5j6eVXdvGsiXHvoFkUZxxoS8WBWk6zWThA= +cloud.google.com/go/gkemulticloud v1.2.5/go.mod h1:zVRNlO7/jFXmvrkBd+UfhI2T7ZBb+N3b3lt/3K60uS0= cloud.google.com/go/grafeas v0.3.6 h1:7bcA10EBgTsxeAVypJhz2Dv3fhrdlO7Ml8l7ZZA2IkE= cloud.google.com/go/grafeas v0.3.6/go.mod h1:to6ECAPgRO2xeqD8ISXHc70nObJuaKZThreQOjeOH3o= +cloud.google.com/go/grafeas v0.3.9 h1:4rfIkfgxFkhgzGc19aBGob+R6OXJVC7nZ+XrpOFTxYg= +cloud.google.com/go/grafeas v0.3.9/go.mod h1:j8hBcywIqtJ3/3QP9yYB/LqjLWBM9dXumBa+xplvyG0= cloud.google.com/go/gsuiteaddons v1.6.11 h1:zydWX0nVT0Ut/P1X25Sy+4Rqe2PH04IzhwlF1BJd8To= cloud.google.com/go/gsuiteaddons v1.6.11/go.mod h1:U7mk5PLBzDpHhgHv5aJkuvLp9RQzZFpa8hgWAB+xVIk= +cloud.google.com/go/gsuiteaddons v1.6.12 h1:lp9f1VVl7Z4S8uqWTvGvazsMhBFpEbUiqCG2uYJ3LRs= +cloud.google.com/go/gsuiteaddons v1.6.12/go.mod h1:hqTWzMXCgS/BPuyiWHzDBZC4K3+a9lcJWBUR+i+6D7A= +cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= cloud.google.com/go/iam v1.1.12/go.mod h1:9LDX8J7dN5YRyzVHxwQzrQs9opFFqn0Mxs9nAeB+Hhg= cloud.google.com/go/iap v1.9.10 h1:j7jQqqSkZ2nWAOCiyaZfnJ+REycTJ2NP2dUEjLoW4aA= cloud.google.com/go/iap v1.9.10/go.mod h1:pO0FEirrhMOT1H0WVwpD5dD9r3oBhvsunyBQtNXzzc0= +cloud.google.com/go/iap v1.9.11 h1:rqm2jCKE/uA2iRYxcFY7O88sUBhru5+/OTK4dphIOyM= +cloud.google.com/go/iap v1.9.11/go.mod h1:UcvTLqySIc8C3Dw3JPZ7QihzzxVQJ7/KUOL9MjxiPZk= cloud.google.com/go/ids v1.4.11 h1:JhlR1d0XhMsj6YmSmbLbbXV5CGkffnUkPj0HNxJYNtc= cloud.google.com/go/ids v1.4.11/go.mod h1:+ZKqWELpJm8WcRRsSvKZWUdkriu4A3XsLLzToTv3418= +cloud.google.com/go/ids v1.4.12 h1:9X6gbqXgOztpxR9pl9xkBgG4jMex0oZDrpwaspCLL1k= +cloud.google.com/go/ids v1.4.12/go.mod h1:SH2yjlk9fKWrRgob/E0Gd1wM+VFztfTdR+LaJRDMiPw= cloud.google.com/go/iot v1.7.11 h1:UBqSUZA6+7bM+mv6uvhl8tVsyT2Fi50njtBFRbrKSlI= cloud.google.com/go/iot v1.7.11/go.mod h1:0vZJOqFy9kVLbUXwTP95e0dWHakfR4u5IWqsKMGIfHk= +cloud.google.com/go/iot v1.7.12 h1:udkAi3LvaD7KdwJUegnUyOBVnruL0It1nmV5TktYF7o= +cloud.google.com/go/iot v1.7.12/go.mod h1:8ntlg5OWnVodAsbs0KDLY58tKEroy+CYciDX/ONxpl4= cloud.google.com/go/kms v1.18.4/go.mod h1:SG1bgQ3UWW6/KdPo9uuJnzELXY5YTTMJtDYvajiQ22g= cloud.google.com/go/language v1.13.0 h1:6Pl97Ei85A3wBJwjXW2S/1IWeUvhQf/lIPQBItnp0FA= cloud.google.com/go/language v1.13.0/go.mod h1:B9FbD17g1EkilctNGUDAdSrBHiFOlKNErLljO7jplDU= +cloud.google.com/go/language v1.13.1 h1:sgAhzmJ/wZBS/4djQJLpjke52sdDhfKuskoCHSTaCHk= +cloud.google.com/go/language v1.13.1/go.mod h1:PY/DAdVW0p2MWl2Lut31AJddEmQBBXMnPUM8nkl/WfA= cloud.google.com/go/lifesciences v0.9.11 h1:xyPSYICJWZElcELYgWCKs5PltyNX3TzOKaQAZA7d/I0= cloud.google.com/go/lifesciences v0.9.11/go.mod h1:NMxu++FYdv55TxOBEvLIhiAvah8acQwXsz79i9l9/RY= +cloud.google.com/go/lifesciences v0.9.12 h1:DGKhxamDcn1v7ieat3sqCjm6HwJqWnrF9Xbq/IDyz80= +cloud.google.com/go/lifesciences v0.9.12/go.mod h1:si0In2nxVPtZnSoDNlEgSV4BJWxxlkdgKh+LXPYMf4w= cloud.google.com/go/logging v1.11.0 h1:v3ktVzXMV7CwHq1MBF65wcqLMA7i+z3YxbUsoK7mOKs= cloud.google.com/go/logging v1.11.0/go.mod h1:5LDiJC/RxTt+fHc1LAt20R9TKiUTReDg6RuuFOZ67+A= +cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA= +cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4= cloud.google.com/go/managedidentities v1.6.11 h1:YU6NtRRBX5R1f3a8ryqhh1dUb1/pt3rnhSO50b63yZY= cloud.google.com/go/managedidentities v1.6.11/go.mod h1:df+8oZ1D4Eri+NrcpuiR5Hd6MGgiMqn0ZCzNmBYPS0A= +cloud.google.com/go/managedidentities v1.6.12 h1:p+n5vvhcpWgwpHoeeUoe+D9/wzIqW5vsWoi2n7YTafA= +cloud.google.com/go/managedidentities v1.6.12/go.mod h1:7KrCfXlxPw85nhlEYF3o5oLC8RtQakMAIGKNiNN3OAg= cloud.google.com/go/maps v1.11.6 h1:HMI0drvgnT+BtsjBofb1Z80P53n63ybmm7l+1w1og9I= cloud.google.com/go/maps v1.11.6/go.mod h1:MOS/NN0L6b7Kumr8bLux9XTpd8+D54DYxBMUjq+XfXs= +cloud.google.com/go/maps v1.11.7 h1:ifdbRNVte+Oq0aHVR4lvXMBSvJhtIcK+G9GsWRQDQ9o= +cloud.google.com/go/maps v1.11.7/go.mod h1:CEGHM/Q0epp0oWFO7kiEk8oDGUUhjd1sj4Rcd/4iwGU= cloud.google.com/go/mediatranslation v0.8.11 h1:QvO405ocKTmcJqjfqL1zps08yrKk8rE+0E1ZNSWfjbw= cloud.google.com/go/mediatranslation v0.8.11/go.mod h1:3sNEm0fx61eHk7rfzBzrljVV9XKr931xI3OFacQBVFg= +cloud.google.com/go/mediatranslation v0.8.12 h1:26qFQZxj9Rii22kl84HHcJ0zWlaHsVdBJZxAil89ejs= +cloud.google.com/go/mediatranslation v0.8.12/go.mod h1:owrIOMto4hzsoqkZe95ePEiMJv4JF7/tgEgWuHC+t40= cloud.google.com/go/memcache v1.10.11 h1:DGPEJOVL4Qix2GLKQKcgzGpNLD7gAnCFLr9ch9YSIhU= cloud.google.com/go/memcache v1.10.11/go.mod h1:ubJ7Gfz/xQawQY5WO5pht4Q0dhzXBFeEszAeEJnwBHU= +cloud.google.com/go/memcache v1.10.12 h1:rQxkJUKO9YlENBUHb9WuSb6vBlOrU5xh9Riz81haxW0= +cloud.google.com/go/memcache v1.10.12/go.mod h1:OfG2zgIXVTNJy2UKDF4o4irKxBqTx9RMZhGKJ/hLJUI= cloud.google.com/go/metastore v1.13.10 h1:E5eAxzIRoVP0DrV+ZtTLMYkkjSs4fcfsbL7wv1mXV2U= cloud.google.com/go/metastore v1.13.10/go.mod h1:RPhMnBxUmTLT1fN7fNbPqtH5EoGHueDxubmJ1R1yT84= +cloud.google.com/go/metastore v1.13.11 h1:yZR2paCUcLscxQLp35FoApsMOFB7pOGcIJTiX6psnlc= +cloud.google.com/go/metastore v1.13.11/go.mod h1:aeP+V0Xs3SLqu4mrQWRyuSg5+fdyPq+kdu1xclnR8y8= cloud.google.com/go/monitoring v1.20.3/go.mod h1:GPIVIdNznIdGqEjtRKQWTLcUeRnPjZW85szouimiczU= cloud.google.com/go/monitoring v1.20.4 h1:zwcViK7mT9SV0kzKqLOI3spRadvsmvw/R9z1MHNeC0E= cloud.google.com/go/monitoring v1.20.4/go.mod h1:v7F/UcLRw15EX7xq565N7Ae5tnYEE28+Cl717aTXG4c= +cloud.google.com/go/monitoring v1.21.0 h1:EMc0tB+d3lUewT2NzKC/hr8cSR9WsUieVywzIHetGro= +cloud.google.com/go/monitoring v1.21.0/go.mod h1:tuJ+KNDdJbetSsbSGTqnaBvbauS5kr3Q/koy3Up6r+4= cloud.google.com/go/networkconnectivity v1.14.10 h1:2EE8pKiv1AI8fBdZCdiUjNgQ+TaBgwE4GxIze4fDdY0= cloud.google.com/go/networkconnectivity v1.14.10/go.mod h1:f7ZbGl4CV08DDb7lw+NmMXQTKKjMhgCEEwFbEukWuOY= +cloud.google.com/go/networkconnectivity v1.14.11 h1:zkOTU4GJQQVOD2bKTlUPFIdEG1MQCgNrnwagL43uK8s= +cloud.google.com/go/networkconnectivity v1.14.11/go.mod h1:XRA6nT7ygTN09gAtCRsFhbqn3u7/9LIUn6S+5G4fs50= cloud.google.com/go/networkmanagement v1.13.6 h1:6TGn7ZZXyj5rloN0vv5Aw0awYbfbheNRg8BKroT7/2g= cloud.google.com/go/networkmanagement v1.13.6/go.mod h1:WXBijOnX90IFb6sberjnGrVtZbgDNcPDUYOlGXmG8+4= +cloud.google.com/go/networkmanagement v1.13.7 h1:dXfIaWKr5vVBBHagmbILBkb7yV+FEsyZmMuTZmG3frk= +cloud.google.com/go/networkmanagement v1.13.7/go.mod h1:foi1eLe3Ayydrr63O3ViMwG1AGS3/BxRSmXpAqMFhkY= cloud.google.com/go/networksecurity v0.9.11 h1:6wUzyHCwDEOkDbAJjT6jxsAi+vMfe3aj2JWwqSFVXOQ= cloud.google.com/go/networksecurity v0.9.11/go.mod h1:4xbpOqCwplmFgymAjPFM6ZIplVC6+eQ4m7sIiEq9oJA= +cloud.google.com/go/networksecurity v0.9.12 h1:BqYfZZHx2koUMiRqEz3BV9AVOsreleSOELi+FZqrEUw= +cloud.google.com/go/networksecurity v0.9.12/go.mod h1:Id0HGMKFJemLolvsoECda71vU2T9JByGPYct6LgMxrw= cloud.google.com/go/notebooks v1.11.9 h1:c8I0EaLGqStRmvX29L7jb4mOrpigxn1mGyBt65OdS0s= cloud.google.com/go/notebooks v1.11.9/go.mod h1:JmnRX0eLgHRJiyxw8HOgumW9iRajImZxr7r75U16uXw= +cloud.google.com/go/notebooks v1.11.10 h1:7FOp+bFhy1WYKbRpfb190Jhj5Qu4paXv5EErwOD1bFY= +cloud.google.com/go/notebooks v1.11.10/go.mod h1:2d3Lwdm5VTxZzxY94V8TffNBk0FBnORieiVBeN+n9QQ= cloud.google.com/go/optimization v1.6.9 h1:++U21U9LWFdgnnVFaq4kDeOafft6gI/CHzsiJ173c6U= cloud.google.com/go/optimization v1.6.9/go.mod h1:mcvkDy0p4s5k7iSaiKrwwpN0IkteHhGmuW5rP9nXA5M= +cloud.google.com/go/optimization v1.6.10 h1:jsPO26IqwEs/x3KJQCmHvL9/gQK35JppdxNbuyXPh5Q= +cloud.google.com/go/optimization v1.6.10/go.mod h1:qWX4Kv90NeBgPfoRwyMbISe8M7Ql1LAOFPNFuOqIvUI= cloud.google.com/go/orchestration v1.9.6 h1:xfczjtNDabsXTnDySAwD/TMfDSkcxEgH1rxfS6BVQtM= cloud.google.com/go/orchestration v1.9.6/go.mod h1:gQvdIsHESZJigimnbUA8XLbYeFlSg/z+A7ppds5JULg= +cloud.google.com/go/orchestration v1.9.7 h1:6KCe2V5E9gCtN09BEzLVTCv8g16LPYzImDqgwXKbnvY= +cloud.google.com/go/orchestration v1.9.7/go.mod h1:Mgtuci4LszRSzKkQucdWvdhTyG+QB4+3ZpsZ4sqalrQ= cloud.google.com/go/orgpolicy v1.12.7 h1:StymaN9vS7949m15Nwgf5aKd9yaRtzWJ4VqHdbXcOEM= cloud.google.com/go/orgpolicy v1.12.7/go.mod h1:Os3GlUFRPf1UxOHTup5b70BARnhHeQNNVNZzJXPbWYI= +cloud.google.com/go/orgpolicy v1.12.8 h1:lBKGvA07J42nQpU6X61hQDIv/jcnuJtz/BhnTjGIDYg= +cloud.google.com/go/orgpolicy v1.12.8/go.mod h1:WHkLGqHILPnMgJ4UTdag6YgztVIgWS+T5T6tywH3cSM= cloud.google.com/go/osconfig v1.13.2 h1:IbbTg7jtTEn4+iEJwgbCYck5NLMOc2eKrqVpQb7Xx6c= cloud.google.com/go/osconfig v1.13.2/go.mod h1:eupylkWQJCwSIEMkpVR4LqpgKkQi0mD4m1DzNCgpQso= +cloud.google.com/go/osconfig v1.13.3 h1:GoiK/q0OAhaGxZMMapulq+lxZU4BEZn8ado2g3yqfqc= +cloud.google.com/go/osconfig v1.13.3/go.mod h1:gIFyyriC1ANob8SnpwrQ6jjNroRwItoBOYfqiG3LkUU= cloud.google.com/go/oslogin v1.13.7 h1:q9x7tjKtfBpXMpiJKwb5UyhMA3GrwmJHvx56uCEuS8M= cloud.google.com/go/oslogin v1.13.7/go.mod h1:xq027cL0fojpcEcpEQdWayiDn8tIx3WEFYMM6+q7U+E= +cloud.google.com/go/oslogin v1.13.8 h1:ukF32m5YIOUgMQXrJW6RkOLxV/VYPa3f1mDr9OjN8bA= +cloud.google.com/go/oslogin v1.13.8/go.mod h1:rc52yAdMXB5mERVeOXRcDnaswQNFTPRJ93VVHmGwJSk= cloud.google.com/go/phishingprotection v0.8.11 h1:3Kr7TINZ+8pbdWe3JnJf9c84ibz60NRTvwLdVtI3SK8= cloud.google.com/go/phishingprotection v0.8.11/go.mod h1:Mge0cylqVFs+D0EyxlsTOJ1Guf3qDgrztHzxZqkhRQM= +cloud.google.com/go/phishingprotection v0.8.12 h1:64kH4KgKV4vYJeAqN9dHbxPLFE5yEKcfvV3Rrh0TZTg= +cloud.google.com/go/phishingprotection v0.8.12/go.mod h1:tkR+cZBpRdu4i04BP1CqaZr2yL7U1o8t+v/SZ2kOSDU= cloud.google.com/go/policytroubleshooter v1.10.9 h1:EHXkBYgHQtVH8P41G2xxmQbMwQh+o5ggno8l3/9CXaA= cloud.google.com/go/policytroubleshooter v1.10.9/go.mod h1:X8HEPVBWz8E+qwI/QXnhBLahEHdcuPO3M9YvSj0LDek= +cloud.google.com/go/policytroubleshooter v1.10.10 h1:oIc5msMHCZgiYbMJPMuqhj478Eo0oT6HkUnLmYBz/rc= +cloud.google.com/go/policytroubleshooter v1.10.10/go.mod h1:9S7SKOsLydGB2u91WKNjHpLScxxkKATIu3Co0fw8LPQ= cloud.google.com/go/privatecatalog v0.9.11 h1:t8dJpQf22H6COeDvp7TDl7+KuwLT6yVmqAVRIUIUj6U= cloud.google.com/go/privatecatalog v0.9.11/go.mod h1:awEF2a8M6UgoqVJcF/MthkF8SSo6OoWQ7TtPNxUlljY= +cloud.google.com/go/privatecatalog v0.9.12 h1:ZmATFJMFK6gbWoDQpLX9y6nmBMeYUCY8Wa5ijc2yKss= +cloud.google.com/go/privatecatalog v0.9.12/go.mod h1:Sl292f/1xY0igI+CFNGfiXJWiN9BvaLpc8mjnCHNRnA= cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= +cloud.google.com/go/pubsub v1.42.0 h1:PVTbzorLryFL5ue8esTS2BfehUs0ahyNOY9qcd+HMOs= +cloud.google.com/go/pubsub v1.42.0/go.mod h1:KADJ6s4MbTwhXmse/50SebEhE4SmUwHi48z3/dHar1Y= cloud.google.com/go/pubsublite v1.8.2 h1:jLQozsEVr+c6tOU13vDugtnaBSUy/PD5zK6mhm+uF1Y= cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= cloud.google.com/go/recaptchaenterprise/v2 v2.14.2 h1:80Mx0i3uyv5dPNUYsNPFk9GJ+19AmTlnWnXFCTC9NkI= cloud.google.com/go/recaptchaenterprise/v2 v2.14.2/go.mod h1:MwPgdgvBkE46aWuuXeBTCB8hQJ88p+CpXInROZYCTkc= +cloud.google.com/go/recaptchaenterprise/v2 v2.14.3 h1:qnU+urA6SZlRErosscaJ2DLG4264D6V8S4OgC3RU6QY= +cloud.google.com/go/recaptchaenterprise/v2 v2.14.3/go.mod h1:MiSHAXwja4btHPJFNJrDke//V+x83/ckXcdwbzn4+e8= cloud.google.com/go/recommendationengine v0.8.11 h1:STJYdA/e/MAh2ZSdjss5YE/d0t0nt0WotBF9V0pgpPQ= cloud.google.com/go/recommendationengine v0.8.11/go.mod h1:cEkU4tCXAF88a4boMFZym7U7uyxvVwcQtKzS85IbQio= +cloud.google.com/go/recommendationengine v0.8.12 h1:X587hwzHqvmKyTR4EzH/QVYtvaCNqeIKhX9QC2iAw4g= +cloud.google.com/go/recommendationengine v0.8.12/go.mod h1:A3c39mOVC4utWlwk+MpchvkZTM6MSJXm3KUwTQ47VzA= cloud.google.com/go/recommender v1.12.7 h1:asEAoj4a3inPCdH8nbPaZDJWhR/xwfKi4tuSmIlaS2I= cloud.google.com/go/recommender v1.12.7/go.mod h1:lG8DVtczLltWuaCv4IVpNphONZTzaCC9KdxLYeZM5G4= +cloud.google.com/go/recommender v1.12.8 h1:3d+XXbeVU9Mt0TW6La+BhVFyCT5M2F1nazhAYrtahfM= +cloud.google.com/go/recommender v1.12.8/go.mod h1:zoJL8kPJJotOoNU3D2fCXW33vhbyIPe0Sq7ObhYLnGM= cloud.google.com/go/redis v1.16.4 h1:9CO6EcuM9/CpgtcjG6JZV+GFw3oDrRfwLwmvwo/uM1o= cloud.google.com/go/redis v1.16.4/go.mod h1:unCVfLP5eFrVhGLDnb7IaSaWxuZ+7cBgwwBwbdG9m9w= +cloud.google.com/go/redis v1.16.5 h1:yqidSLL+AE6IPNnEkAK6ZlxXp5c3zrsTTCB1TrpO054= +cloud.google.com/go/redis v1.16.5/go.mod h1:cWn6WHSEnmVZh9lJ9AN/UwDTtvlcT+TTRGvNIckUbG0= cloud.google.com/go/resourcemanager v1.9.11 h1:N8CmqszjKNOgJnrQVsg+g8VWIEGgcwsD5rPiay9cMC4= cloud.google.com/go/resourcemanager v1.9.11/go.mod h1:SbNAbjVLoi2rt9G74bEYb3aw1iwvyWPOJMnij4SsmHA= +cloud.google.com/go/resourcemanager v1.9.12 h1:p++iHmmeq9iWTia8WhNmPvBhL7MZsglQpZAYlHCguBs= +cloud.google.com/go/resourcemanager v1.9.12/go.mod h1:unouv9x3+I+6kVeE10LGM3oJ8aQrUZganWnRchitbAM= cloud.google.com/go/resourcesettings v1.7.4 h1:1VwLfvJi8QtGrKPwuisGqr6gcgaCSR6A57wIvN+fqkM= cloud.google.com/go/resourcesettings v1.7.4/go.mod h1:seBdLuyeq+ol2u9G2+74GkSjQaxaBWF+vVb6mVzQFG0= +cloud.google.com/go/resourcesettings v1.7.5 h1:zRvaQBE1O7wuB/UtX7Lw119r/xGcPzaY1POGpoMEhEs= +cloud.google.com/go/resourcesettings v1.7.5/go.mod h1:voqqKzYIrnoAqFKV6xk2qhgTnxzfGCJNOuBnHJEzcNU= cloud.google.com/go/retail v1.17.4 h1:YJgpBwCarAPqzaJS8ycIhyn2sAQT1RhTJRiTVBjtJAI= cloud.google.com/go/retail v1.17.4/go.mod h1:oPkL1FzW7D+v/hX5alYIx52ro2FY/WPAviwR1kZZTMs= +cloud.google.com/go/retail v1.17.5 h1:seOuQ16gMSxxre//JZy7lEdAKWwHQJMLYBmSfifJ/VI= +cloud.google.com/go/retail v1.17.5/go.mod h1:DSWPessLdnuvRH+N2FY+j1twyKtpRDKp4Y88dm7VqBw= cloud.google.com/go/run v1.4.0 h1:ai1rnbX92iPqWg9MrbDbebsxlUSAiOK6N9dEDDQeVA0= cloud.google.com/go/run v1.4.0/go.mod h1:4G9iHLjdOC+CQ0CzA0+6nLeR6NezVPmlj+GULmb0zE4= +cloud.google.com/go/run v1.4.1 h1:grsUhL/Dlp7z5z2A4Nxi1oxEyktBkyB8VuobO6fuILs= +cloud.google.com/go/run v1.4.1/go.mod h1:gaXIpytRDfrJjb3pz9PRG2q2KUaDDDV+Uvmq6QRZH20= cloud.google.com/go/scheduler v1.10.12 h1:8BxDXoHCcsAe2fXsvFrkBbTxgl+5JBrIy1+/HRS0nxY= cloud.google.com/go/scheduler v1.10.12/go.mod h1:6DRtOddMWJ001HJ6MS148rtLSh/S2oqd2hQC3n5n9fQ= +cloud.google.com/go/scheduler v1.10.13 h1:KEM2/3r4NIn6eOIL1tAEN1Qrot8xqKYuW1QKEQ+GyFU= +cloud.google.com/go/scheduler v1.10.13/go.mod h1:lDJItkp2hNrCsHOBtVExCzjXBzK9WI3yKNg713/OU4s= cloud.google.com/go/secretmanager v1.13.5/go.mod h1:/OeZ88l5Z6nBVilV0SXgv6XJ243KP2aIhSWRMrbvDCQ= cloud.google.com/go/secretmanager v1.13.6 h1:0ZEl/LuoB4xQsjVfQt3Gi/dZfOv36n4JmdPrMargzYs= cloud.google.com/go/secretmanager v1.13.6/go.mod h1:x2ySyOrqv3WGFRFn2Xk10iHmNmvmcEVSSqc30eb1bhw= cloud.google.com/go/security v1.17.4 h1:ERhxAa02mnMEIIAXvzje+qJ+yWniP6l5uOX+k9ELCaA= cloud.google.com/go/security v1.17.4/go.mod h1:KMuDJH+sEB3KTODd/tLJ7kZK+u2PQt+Cfu0oAxzIhgo= +cloud.google.com/go/security v1.17.5 h1:FzjVVdJKgfHmDt1gsecHehVewA0wLuruHvA1g4o5k4c= +cloud.google.com/go/security v1.17.5/go.mod h1:MA8w7SbQAQO9CQ9r0R7HR0F7g1AJoqx87SFLpapq3OU= cloud.google.com/go/securitycenter v1.33.1 h1:K+jfFUTum2jl//uWCN+QKkKXRgidxTyGfGTqXPyDvUY= cloud.google.com/go/securitycenter v1.33.1/go.mod h1:jeFisdYUWHr+ig72T4g0dnNCFhRwgwGoQV6GFuEwafw= +cloud.google.com/go/securitycenter v1.34.0 h1:rf1n9DviaHlkjqG/Acy61nsQxSGL4w8hIQoSraNNQBM= +cloud.google.com/go/securitycenter v1.34.0/go.mod h1:7esjYVxn7k0nm02CnLNueFWD40FH0eunhookSEUalSs= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= cloud.google.com/go/servicedirectory v1.11.11 h1:8Ky2lY0CWJJIIlsc+rKTn6C3SqOuVEwT3brDC6TJCjk= cloud.google.com/go/servicedirectory v1.11.11/go.mod h1:pnynaftaj9LmRLIc6t3r7r7rdCZZKKxui/HaF/RqYfs= +cloud.google.com/go/servicedirectory v1.11.12 h1:KPlLc16HUiUp5/dGHF7KlBUEub13Ps76J91xCMBG+5U= +cloud.google.com/go/servicedirectory v1.11.12/go.mod h1:A0mXC1awKEK5alkG7p3hxaHtb5SSPqAdeWx09RTIOGY= cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= cloud.google.com/go/shell v1.7.11 h1:RobTXyL33DQITYQh//KJ9GjS4bsdj4fGmm2rkb/ywzM= cloud.google.com/go/shell v1.7.11/go.mod h1:SywZHWac7onifaT9m9MmegYp3GgCLm+tgk+w2lXK8vg= +cloud.google.com/go/shell v1.7.12 h1:UCDVhq9IkB1RWhUIJxj9Xr/c+BVVxMSAnnJUGjPfk4U= +cloud.google.com/go/shell v1.7.12/go.mod h1:QxxwQMvXqDUTYgMwbO7Y2Z6rojGzA7q64aQTCEj7xfM= cloud.google.com/go/spanner v1.65.0 h1:XK15cs9lFFQo5n4Wh9nfrcPXAxWln6NdodDiQKmoD08= cloud.google.com/go/spanner v1.65.0/go.mod h1:dQGB+w5a67gtyE3qSKPPxzniedrnAmV6tewQeBY7Hxs= +cloud.google.com/go/spanner v1.67.0 h1:h8xfobxh5lQu4qJVMPH+wSiyU+ZM6ZTxRNqGeu9iIVA= +cloud.google.com/go/spanner v1.67.0/go.mod h1:Um+TNmxfcCHqNCKid4rmAMvoe/Iu1vdz6UfxJ9GPxRQ= cloud.google.com/go/speech v1.24.0 h1:3j+WpeBY57C0FDJxg317vpKgOLjL/kNxlcNPGSqXkqE= cloud.google.com/go/speech v1.24.0/go.mod h1:HcVyIh5jRXM5zDMcbFCW+DF2uK/MSGN6Rastt6bj1ic= +cloud.google.com/go/speech v1.24.1 h1:sgJ+sBzamry/ZBhbIZX6Dmv/BnrTAvWLXeJT1bmugnU= +cloud.google.com/go/speech v1.24.1/go.mod h1:th/IKNidPLzrbaEiKLIhTv/oTGADe4r4bzxZvYG62EE= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storagetransfer v1.10.10 h1:GfxaYqX+kwlrSrJAENNmRTCGmSTgvouvS3XhgwKpOT8= cloud.google.com/go/storagetransfer v1.10.10/go.mod h1:8+nX+WgQ2ZJJnK8e+RbK/zCXk8T7HdwyQAJeY7cEcm0= +cloud.google.com/go/storagetransfer v1.10.11 h1:AF5iHhnbiVC4BEKf1b8ujWeUuXCfpRzO9aclF2j2A1w= +cloud.google.com/go/storagetransfer v1.10.11/go.mod h1:AMAR/PTS5yKPp1FHP6rk3eJYGmHF14vQYiHddcIgoOA= cloud.google.com/go/talent v1.6.12 h1:JN721EjG+UTfHVVaMhyxwKCCJPjUc8PiS0RnW/7kWfE= cloud.google.com/go/talent v1.6.12/go.mod h1:nT9kNVuJhZX2QgqKZS6t6eCWZs5XEBYRBv6bIMnPmo4= +cloud.google.com/go/talent v1.6.13 h1:DLrLO6LVl6T7v07Jc3+4IaI+sDFRbgd7MTEbTow5qVE= +cloud.google.com/go/talent v1.6.13/go.mod h1:jqjQzIF7ZPCxFSdsfhgUF0wGB+mbytYzyUqaHLiQcQg= cloud.google.com/go/texttospeech v1.7.11 h1:jzko1ahItjLYEWr6n3lTIoBSinD1JzavEuDzYLWZNko= cloud.google.com/go/texttospeech v1.7.11/go.mod h1:Ua125HU+WT2IkIo5MzQtuNpNEk72soShJQVdorZ1SAE= +cloud.google.com/go/texttospeech v1.7.12 h1:W33C7hjh61V1HVAh4E1RqAx2hSPIebmilYqMuqvP/HQ= +cloud.google.com/go/texttospeech v1.7.12/go.mod h1:B1Xck47Mhy/PJMnvrLkv0gfKGinGP78c0XFZjWB7TdY= cloud.google.com/go/tpu v1.6.11 h1:uMrwnK05cocNt3OOp+mZ16xlvIKaXUt3QUXkUbG4LdM= cloud.google.com/go/tpu v1.6.11/go.mod h1:W0C4xaSj1Ay3VX/H96FRvLt2HDs0CgdRPVI4e7PoCDk= +cloud.google.com/go/tpu v1.6.12 h1:n54G/s4O6t2sbTiTkhj795JW6iCBSzI1ccrXp+GAP6c= +cloud.google.com/go/tpu v1.6.12/go.mod h1:IFJa2vI7gxF6fypOQXYmbuFwKLsde4zVwcv1p9zhOqY= cloud.google.com/go/trace v1.10.11/go.mod h1:fUr5L3wSXerNfT0f1bBg08W4axS2VbHGgYcfH4KuTXU= cloud.google.com/go/trace v1.10.12 h1:GoGZv1iAXEa73HgSGNjRl2vKqp5/f2AeKqErRFXA2kg= cloud.google.com/go/trace v1.10.12/go.mod h1:tYkAIta/gxgbBZ/PIzFxSH5blajgX4D00RpQqCG/GZs= +cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs= cloud.google.com/go/translate v1.10.7 h1:W16MpZ2Z3TWoHbNHmyHz9As276lGVTSwxRcquv454R0= cloud.google.com/go/translate v1.10.7/go.mod h1:mH/+8tvcItuy1cOWqU+/Y3iFHgkVUObNIQYI/kiFFiY= +cloud.google.com/go/translate v1.11.0 h1:ncOb+dtBr65+5Ay9VotOA8j5RlrUJXB+qdq+xqpV4Ls= +cloud.google.com/go/translate v1.11.0/go.mod h1:UFNHzrfcEo/ZCmA5SveVqxh0l57BP27HCvroN5o59FI= cloud.google.com/go/video v1.22.0 h1:+FTZi7NtT4FV2Y1j3zC3zYjaRrlGqKsZpbLweredEWM= cloud.google.com/go/video v1.22.0/go.mod h1:CxPshUNAb1ucnzbtruEHlAal9XY+SPG2cFqC/woJzII= +cloud.google.com/go/video v1.22.1 h1:G2gFE34k6TIC0CHW1OTbF6tKfzA3X+MbjkoLblu0vWU= +cloud.google.com/go/video v1.22.1/go.mod h1:+AYF4e9kqQhra0AfKPoOOIUK0Ho7BquOWQK+Te+Qnns= cloud.google.com/go/videointelligence v1.11.11 h1:zl8xijOEavernn/t6mZZ4fg0pIVc2yquHH73oj0Leo4= cloud.google.com/go/videointelligence v1.11.11/go.mod h1:dab2Ca3AXT6vNJmt3/6ieuquYRckpsActDekLcsd6dU= +cloud.google.com/go/videointelligence v1.11.12 h1:dFCXde/zOkP1yhM8aCuFVC+smix46sP2U56OywCOkAQ= +cloud.google.com/go/videointelligence v1.11.12/go.mod h1:dQlDAFtTwsZi3UI+03NVF4XQoarx0VU5/IKMLyVyC2E= cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= cloud.google.com/go/vision/v2 v2.8.6 h1:HyFEUXQa0SvlF0LASCn/x+juNCH4kIXQrUqi6SIcYvE= cloud.google.com/go/vision/v2 v2.8.6/go.mod h1:G3v0uovxCye3u369JfrHGY43H6u/IQ08x9dw5aVH8yY= +cloud.google.com/go/vision/v2 v2.8.7 h1:4hWiRo2j4zzmCzXG+SkJVYq0dvR6TklFHkxdsmdfMsU= +cloud.google.com/go/vision/v2 v2.8.7/go.mod h1:4ADQGbgAAvEDn/2I6XLeBN6mCUq6D44bfjWaqQc6iYU= cloud.google.com/go/vmmigration v1.7.11 h1:yqwkTPpvSw9dUfnl9/APAVrwO9UW1jJZtgbZpNQ+WdU= cloud.google.com/go/vmmigration v1.7.11/go.mod h1:PmD1fDB0TEHGQR1tDZt9GEXFB9mnKKalLcTVRJKzcQA= +cloud.google.com/go/vmmigration v1.7.12 h1:+X5l9sfrevUi5EeOM7LhzFFzGVT/N7u0TJY7jdIiIQA= +cloud.google.com/go/vmmigration v1.7.12/go.mod h1:Fb6yZsMdgFUo3wdDc7vK75KmBzXkY1Tio/053vuvCXU= cloud.google.com/go/vmwareengine v1.2.0 h1:9Fjn/RoeOMo8UQt1TbXmmw7rJApC26BqnISAI1AERcc= cloud.google.com/go/vmwareengine v1.2.0/go.mod h1:rPjCHu6hG9N8d6PhkoDWFkqL9xpbFY+ueVW+0pNFbZg= +cloud.google.com/go/vmwareengine v1.2.1 h1:UChEIPaM3wxBXKLy7Ooqeey3YNesta7PR5D2h7lI61I= +cloud.google.com/go/vmwareengine v1.2.1/go.mod h1:OE5z8qJdTiPpSeWunFenN/RMF7ymRgI0HvJ/c7Zl5U0= cloud.google.com/go/vpcaccess v1.7.11 h1:1XgRP+Q2X6MvE/xnexpQ7ydgav+IO5UcKUIJEbL65J8= cloud.google.com/go/vpcaccess v1.7.11/go.mod h1:a2cuAiSCI4TVK0Dt6/dRjf22qQvfY+podxst2VvAkcI= +cloud.google.com/go/vpcaccess v1.7.12 h1:n077TcPH7S8RjZGBwHWykVfoVaa41O5VK9xn8KR18+0= +cloud.google.com/go/vpcaccess v1.7.12/go.mod h1:Bt9j9aqlNDj1xW5uMNrHyhpc61JZgttbQRecG9xm1cE= cloud.google.com/go/webrisk v1.9.11 h1:2qwEqnXrToIv2Y4xvsUSxCk7R2Ki+3W2+GNyrytoKTQ= cloud.google.com/go/webrisk v1.9.11/go.mod h1:mK6M8KEO0ZI7VkrjCq3Tjzw4vYq+3c4DzlMUDVaiswE= +cloud.google.com/go/webrisk v1.9.12 h1:2WWCTU5744V0AsZomJxzH18LG7jlL13z1iDzgMWAJOQ= +cloud.google.com/go/webrisk v1.9.12/go.mod h1:YaAgE2xKzIN8yQNUspTTeZbvdcifSJh+wcMyXmp8fgg= cloud.google.com/go/websecurityscanner v1.6.11 h1:r3ePI3YN7ujwX8c9gIkgbVjYVwP4yQA4X2z6P7+HNxI= cloud.google.com/go/websecurityscanner v1.6.11/go.mod h1:vhAZjksELSg58EZfUQ1BMExD+hxqpn0G0DuyCZQjiTg= +cloud.google.com/go/websecurityscanner v1.6.12 h1:0RJvr/1Eybjt6klGMrW4U5DVitv1P+BoLmrRU6Xmzc0= +cloud.google.com/go/websecurityscanner v1.6.12/go.mod h1:9WFCBNpS0EIIhQaqiNC3ezZ48qisGPh3Ekz6T2n9Ioc= cloud.google.com/go/workflows v1.12.10 h1:EGJeZmwgE71jxFOI5s9iKST2Bivif3DSzlqVbiXACXQ= cloud.google.com/go/workflows v1.12.10/go.mod h1:RcKqCiOmKs8wFUEf3EwWZPH5eHc7Oq0kamIyOUCk0IE= +cloud.google.com/go/workflows v1.12.11 h1:9pfL+XeaaNDEPTc02VvX70qW36cBrTHmcH1plZwOI7s= +cloud.google.com/go/workflows v1.12.11/go.mod h1:0cYsbMDyqr/1SbEt1DfN+S+mI2AAnVrT7+Hrh7qaxZ0= contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec h1:CSNP8nIEQt4sZEo2sGUiWSmVJ9c5QdyIQvwzZAsn+8Y= contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= @@ -275,6 +495,10 @@ git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8= git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d h1:j6oB/WPCigdOkxtuPl1VSIiLpy7Mdsu6phQffbF19Ng= github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk= github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas= github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1 h1:o/Ws6bEqMeKZUfj1RRm3mQ51O8JGU5w+Qdg2AhHib6A= @@ -287,8 +511,13 @@ github.com/Azure/go-amqp v1.0.5 h1:po5+ljlcNSU8xtapHTe8gIc8yHxCzC03E8afH2g1ftU= github.com/Azure/go-amqp v1.0.5/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= @@ -300,18 +529,30 @@ github.com/ClickHouse/clickhouse-go/v2 v2.28.3 h1:SkFzPULX6nzgfNZd1YD1XTECivjTMr github.com/ClickHouse/clickhouse-go/v2 v2.28.3/go.mod h1:vzn73hp+3JwxtFU4RjPCQ7r6fP2pMKVwdi8E1/Tkua8= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v3 v3.0.0 h1:1PwO5w5VCtlUUl+KTOBsTGZlhjWkcybsGaAau52tOy8= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/sketches-go v1.4.6 h1:acd5fb+QdUzGrosfNLwrIhqyrbMORpvBy7mE+vHlT3I= +github.com/DataDog/sketches-go v1.4.6/go.mod h1:7Y8GN8Jf66DLyDhc94zuWA3uHEt/7ttt8jHOBWWrSOg= +github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW515g= +github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0 h1:kAtNAWwvTt5+iew6baV0kbOrtjYTXPtWNSyOFlcxkBU= github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRBTGfw0Ndv4YqNxlB8zAPJJDxbASE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= +github.com/IBM/go-sdk-core/v5 v5.17.4 h1:VGb9+mRrnS2HpHZFM5hy4J6ppIWnwNrw0G+tLSgcJLc= +github.com/IBM/go-sdk-core/v5 v5.17.4/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns= +github.com/IBM/ibm-cos-sdk-go v1.11.0 h1:Jp55NLN3OvBwucMGpP5wNybyjncsmTZ9+GPHai/1cE8= +github.com/IBM/ibm-cos-sdk-go v1.11.0/go.mod h1:FnWOym0CvrPM0nHoXvceClOEvGVXecPpmVIO5RFjlFk= github.com/IBM/sarama v1.43.0 h1:YFFDn8mMI2QL0wOrG0J2sFoVIAFl7hS9JQi2YZsXtJc= github.com/IBM/sarama v1.43.0/go.mod h1:zlE6HEbC/SMQ9mhEYaF7nNLYOUyrs0obySKCckWP9BM= github.com/IBM/sarama v1.43.2 h1:HABeEqRUh32z8yzY2hGB/j8mHSzC/HA9zlEjqFNCzSw= github.com/IBM/sarama v1.43.2/go.mod h1:Kyo4WkF24Z+1nz7xeVUFWIuKVV8RS3wM8mkvPKMdXFQ= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= +github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= github.com/KimMachineGun/automemlimit v0.6.0 h1:p/BXkH+K40Hax+PuWWPQ478hPjsp9h1CPDhLlA3Z37E= @@ -321,7 +562,6 @@ github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x9 github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.6 h1:U68crOE3y3MPttCMQGywZOLrTeF5HHJ3/vDBCJn9/bA= github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= @@ -331,18 +571,26 @@ github.com/RoaringBitmap/gocroaring v0.4.0 h1:5nufXUgWpBEUNEJXw7926YAA58ZAQRpWPr github.com/RoaringBitmap/real-roaring-datasets v0.0.0-20190726190000-eb7c87156f76 h1:ZYlhPbqQFU+AHfgtCdHGDTtRW1a8geZyiE8c6Q+Sl1s= github.com/RoaringBitmap/roaring v0.9.4 h1:ckvZSX5gwCRaJYBNe7syNawCU5oruY9gQmjXlp4riwo= github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= +github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= +github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A= +github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSdZ4X2o5g= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= +github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= +github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kong v0.8.0 h1:ryDCzutfIqJPnNn0omnrgHLbAggDQM2VWHikE1xqK7s= github.com/alecthomas/kong v0.8.0/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= @@ -356,6 +604,8 @@ github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= +github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible h1:ROMcuN61gI8SfQ+AEMh4d7GZ3gwTZLIhPjtd05TQCG4= +github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0 h1:uF5Q/hWnDU1XZeT6CsrRSxHLroUSEYYO3kgES+yd+So= @@ -393,14 +643,22 @@ github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 h1:hgSBvRT7JEWx2+vEGI9/Ld5rZtl7 github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4/go.mod h1:v7NIzEFIHBiicOMaMTuEmbnzGnqW0d+6ulNALul6fYE= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/baidubce/bce-sdk-go v0.9.188 h1:8MA7ewe4VpX01uYl7Kic6ZvfIReUFdSKbY46ZqlQM7U= +github.com/baidubce/bce-sdk-go v0.9.188/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC8+vGZA= +github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/blevesearch/bleve_index_api v1.0.6/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms= github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:kDy+zgJFJJoJYBvdfBSiZYBbdsUL0XcjHYWezpQBGPA= github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A= github.com/blevesearch/goleveldb v1.0.1 h1:iAtV2Cu5s0GD1lwUiekkFHe2gTMCCNVj2foPclDLIFI= github.com/blevesearch/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ= github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs= +github.com/blevesearch/scorch_segment_api/v2 v2.1.6/go.mod h1:nQQYlp51XvoSVxcciBjtvuHPIVjlWrN1hX4qwK2cqdc= github.com/blevesearch/snowball v0.6.1 h1:cDYjn/NCH+wwt2UdehaLpr2e4BwLIjN4V/TdLsL+B5A= github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg= github.com/blevesearch/stempel v0.2.0 h1:CYzVPaScODMvgE9o+kf6D4RJ/VRomyi9uHF+PtB+Afc= @@ -423,7 +681,6 @@ github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= @@ -441,9 +698,12 @@ github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= +github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= +github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= @@ -451,22 +711,38 @@ github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9D github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coder/quartz v0.1.0 h1:cLL+0g5l7xTf6ordRnUMMiZtRE8Sq5LxpghS63vEXrQ= +github.com/coder/quartz v0.1.0/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= +github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns= +github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= +github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA= +github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= +github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf h1:GOPo6vn/vTN+3IwZBvXX0y5doJfSC7My0cdzelyOCsQ= +github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/couchbase/ghistogram v0.1.0 h1:b95QcQTCzjTUocDXp/uMgSNQi8oj1tGwnJ4bODWZnps= github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= github.com/couchbase/moss v0.2.0 h1:VCYrMzFwEryyhRSeI+/b3tRBSeTpi/8gn5Kf6dxqn+o= github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= @@ -483,6 +759,8 @@ github.com/cznic/ql v1.2.0 h1:lcKp95ZtdF0XkWhGnVIXGF8dVD2X+ClS08tglKtf+ak= github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE= github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 h1:0rkFMAbn5KBKNpJyHQ6Prb95vIKanmAe62KxsrN+sqA= github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc h1:YKKpTb2BrXN2GYyGaygIdis1vXbE7SSAG9axGWIMClg= +github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= +github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14 h1:YI1gOOdmMk3xodBao7fehcvoZsEeOyy/cfhlpCSPgM4= github.com/dave/brenda v1.1.0 h1:Sl1LlwXnbw7xMhq3y2x11McFu43AjDcwkllxxgZ3EZw= github.com/dave/courtney v0.3.0 h1:8aR1os2ImdIQf3Zj4oro+lD/L4Srb5VwGefqZ/jzz7U= @@ -494,42 +772,72 @@ github.com/dave/rebecca v0.9.1 h1:jxVfdOxRirbXL28vXMvUvJ1in3djwkVKXCq339qhBL0= github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g= github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= +github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 h1:IMfrF5LCzP2Vhw7j4IIH3HxPsCLuZYjDqFAM/C88ulg= +github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= +github.com/dolthub/swiss v0.2.1 h1:gs2osYs5SJkAaH5/ggVJqXQxRXtWshF6uE0lgR/Y3Gw= +github.com/dolthub/swiss v0.2.1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8cUrh0= github.com/drone/drone-runtime v1.1.0 h1:IsKbwiLY6+ViNBzX0F8PERJVZZcEJm9rgxEh3uZP5IE= github.com/drone/drone-runtime v1.1.0/go.mod h1:+osgwGADc/nyl40J0fdsf8Z09bgcBZXvXXnLOY48zYs= github.com/drone/drone-yaml v1.2.3 h1:SWzLmzr8ARhbtw1WsVDENa8WFY2Pi9l0FVMfafVUWz8= github.com/drone/drone-yaml v1.2.3/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10= github.com/drone/funcmap v0.0.0-20211123105308-29742f68a7d1 h1:E8hjIYiEyI+1S2XZSLpMkqT9V8+YMljFNBWrFpuVM3A= github.com/drone/funcmap v0.0.0-20211123105308-29742f68a7d1/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E= +github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f h1:/jEs7lulqVO2u1+XI5rW4oFwIIusxuDOVKD9PAzlW2E= +github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f/go.mod h1:nDRkX7PHq+p39AD5/usv3KZMerxZTYU/9rfLS5IDspU= github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b h1:ZHiD4/yE4idlbqvAO6iYCOYRzOMRpxkW+FKasRA3tsQ= +github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elastic/go-sysinfo v1.11.2 h1:mcm4OSYVMyws6+n2HIVMGkln5HOpo5Ie1ZmbbNn0jg4= github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJa7AfT8HpBFQ= github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= github.com/expr-lang/expr v1.16.2 h1:JvMnzUs3LeVHBvGFcXYmXo+Q6DPDmzrlcSBO6Wy3w4s= github.com/expr-lang/expr v1.16.2/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88= +github.com/felixge/fgprof v0.9.4/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= +github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c h1:yKN46XJHYC/gvgH2UsisJ31+n4K3S7QYZSfU2uAWjuI= +github.com/fluent/fluent-bit-go v0.0.0-20230731091245-a7a013e2473c/go.mod h1:L92h+dgwElEyUuShEwjbiHjseW410WIcNz+Bjutc8YQ= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKjNQOdARott/jP7BnUQ9Ah7KaZCvw= @@ -538,14 +846,24 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getkin/kin-openapi v0.125.0 h1:jyQCyf2qXS1qvs2U00xQzkGCqYPhEhZDmSmVt65fXno= github.com/getkin/kin-openapi v0.125.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= @@ -573,6 +891,8 @@ github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 h1:NxXI5pTAtpEaU49b github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea h1:DfZQkvEbdmOe+JK2TMtBM+0I9GSdzE2y/L1/AmD8xKc= github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea/go.mod h1:Y7Vld91/HRbTBm7JwoI7HejdDB0u+e9AUBO9MB7yuZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= @@ -587,26 +907,40 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo= github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k= github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= github.com/goccmack/gocc v0.0.0-20230228185258-2292f9e40198 h1:FSii2UQeSLngl3jFoR4tUKZLprO7qUlh/TKKticc0BM= github.com/goccmack/gocc v0.0.0-20230228185258-2292f9e40198/go.mod h1:DTh/Y2+NbnOVVoypCCQrovMPDKUGp4yZpSbWg5D0XIM= github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4 h1:vF83LI8tAakwEwvWZtrIEx7pOySacl2TOxx6eXk4ePo= +github.com/gocql/gocql v0.0.0-20200526081602-cd04bd7f22a7 h1:TvUE5vjfoa7fFHMlmGOk0CsauNj1w4yJjR9+/GnWVCw= +github.com/gocql/gocql v0.0.0-20200526081602-cd04bd7f22a7/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 h1:utua3L2IbQJmauC5IXdEA547bcoU5dozgQAfc8Onsg4= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4= github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= @@ -614,10 +948,13 @@ github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MG github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9 h1:OF1IPgv+F4NmqmJ98KTjdN97Vs1JxDPB3vbmYzV2dpk= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= +github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= +github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/googleapis/cloud-bigtable-clients-test v0.0.2 h1:S+sCHWAiAc+urcEnvg5JYJUOdlQEm/SEzQ/c/IdAH5M= github.com/googleapis/cloud-bigtable-clients-test v0.0.2/go.mod h1:mk3CrkrouRgtnhID6UZQDK3DrFFa7cYCAJcEmNsHYrY= +github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 h1:zC34cGQu69FG7qzJ3WiKW244WfhDC3xxYMeNOX2gtUQ= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -627,51 +964,78 @@ github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/alerting v0.0.0-20240830172655-aa466962ea18 h1:3cQ+d+fkNL2EqpARaBVG34KlVz7flDujYfDx3njvdh8= github.com/grafana/alerting v0.0.0-20240830172655-aa466962ea18/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10 h1:oDbLKM34O+JUF9EQFS+9aYhdYoeNfUpXqNjFCLIxwF4= github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= -github.com/grafana/dskit v0.0.0-20240801171758-736c44c85382/go.mod h1:lcjGB6SuaZ2o44A9nD6p/tR4QXSPbzViRY520Gy6pTQ= +github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2 h1:qhugDMdQ4Vp68H0tp/0iN17DM2ehRo1rLEdOFe/gB8I= +github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2/go.mod h1:w/aiO1POVIeXUQyl0VQSZjl5OAGDTL5aX+4v0RA1tcw= +github.com/grafana/cuetsy v0.1.10/go.mod h1:Ix97+CPD8ws9oSSxR3/Lf4ahU1I4Np83kjJmDVnLZvc= +github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= +github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= +github.com/grafana/go-json v0.0.0-20240212174302-e53f8071f0c1 h1:vuinElnCixECM4xxPi3d5LohaorOOTe0QgF4J8hY44g= +github.com/grafana/go-json v0.0.0-20240212174302-e53f8071f0c1/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c h1:yKBKEC347YZpgii1KazRCfxHsTaxMqWZzoivM1OTT50= +github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/grafana/gomemcache v0.0.0-20240229205252-cd6a66d6fb56/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= +github.com/grafana/grafana-app-sdk v0.19.0/go.mod h1:y0BgzYxc+a7CwOqkwUhN9zXd5cgZJjd2zAbgHEd/xzo= github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= -github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 h1:XMreZ1SPjLpd9zhql5FXKFYwAcgBzS2E2MOPx4n+FyY= -github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2/go.mod h1:UKONJhBCxmL+0ri27VMledCVzZIJqnl6Ah24A5vCRzs= +github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 h1:bjh0PVYSVVFxzINqPFYJmAmJNrWPgnVjuSdYJGHmtFU= +github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P2qggOAHTj/GCZfoLBle3OvNSYh1VkRBU= github.com/grafana/thema v0.0.0-20230511182720-3146087fcc26 h1:HX927q4X1n451pnGb8U0wq74i8PCzuxVjzv7TyD10kc= github.com/grafana/thema v0.0.0-20230511182720-3146087fcc26/go.mod h1:Pn9nfzCk7nV0mvNgwusgCjCROZP6nm4GpwTnmEhLT24= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hamba/avro/v2 v2.17.2 h1:6PKpEWzJfNnvBgn7m2/8WYaDOUASxfDU+Jyb4ojDgFY= github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= -github.com/hashicorp/consul/api v1.29.2 h1:aYyRn8EdE2mSfG14S1+L9Qkjtz8RzmaWh6AcNGRNwPw= -github.com/hashicorp/consul/api v1.29.2/go.mod h1:0YObcaLNDSbtlgzIRtmRXI1ZkeuK0trCBxwZQ4MYnIk= -github.com/hashicorp/consul/proto-public v0.6.2 h1:+DA/3g/IiKlJZb88NBn0ZgXrxJp2NlvCZdEyl+qxvL0= -github.com/hashicorp/consul/proto-public v0.6.2/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg= github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8= -github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= -github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack/v2 v2.1.1 h1:xQEY9yB2wnHitoSzk/B9UjXWRQ67QKu5AOm8aFp8N3I= +github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/raft v1.7.0 h1:4u24Qn6lQ6uwziM++UgsyiT64Q8GyRn43CV41qPiz1o= +github.com/hashicorp/raft v1.7.0/go.mod h1:N1sKh6Vn47mrWvEArQgILTyng8GoDRNYlgKyK7PMjs0= +github.com/hashicorp/raft-wal v0.4.1 h1:aU8XZ6x8R9BAIB/83Z1dTDtXvDVmv9YVYeXxd/1QBSA= +github.com/hashicorp/raft-wal v0.4.1/go.mod h1:A6vP5o8hGOs1LHfC1Okh9xPwWDcmb6Vvuz/QyqUXlOE= +github.com/heroku/x v0.0.61 h1:yfoAAtnFWSFZj+UlS+RZL/h8QYEp1R4wHVEg0G+Hwh4= +github.com/heroku/x v0.0.61/go.mod h1:C7xYbpMdond+s6L5VpniDUSVPRwm3kZum1o7XiD5ZHk= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91 h1:KyZDvZ/GGn+r+Y3DKZ7UOQ/TP4xV6HNkrwiVMB1GnNY= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/influxdata/influxdb v1.7.6 h1:8mQ7A/V+3noMGCt/P9pD09ISaiz9XvgCk303UYA3gcs= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= +github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b h1:i44CesU68ZBRvtCjBi3QSosCIKrjmMbYlQMFAwVLds4= +github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= +github.com/influxdata/telegraf v1.16.3 h1:x0qeuSGGMg5y+YqP/5ZHwXZu3bcBrO8AAQOTNlYEb1c= +github.com/influxdata/telegraf v1.16.3/go.mod h1:fX/6k7qpIqzVPWyeIamb0wN5hbwc0ANUaTS80lPYFB8= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/jade v1.1.3 h1:p7J/50I0cjo0wq/VWVCDFd8taPJbuFC+bq23SniRFX0= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/pongo2 v0.0.1 h1:zGP7pW51oi5eQZMIlGA3I+FHY9/HOQWDB+572yin0to= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= @@ -692,23 +1056,36 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8 github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jon-whit/go-grpc-prometheus v1.4.0 h1:/wmpGDJcLXuEjXryWhVYEGt9YBRhtLwFEN7T+Flr8sw= github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg5ax6YQEe1I0f6vtBuao= +github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU= +github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a/go.mod h1:dNKs71rs2VJGBAmttu7fouEsRQlRjxy0p1Sx+T5wbpY= +github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o= github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 h1:Vx8kDVhO2qepK8w44lBtp+RzN3ld743i+LYPzODJSpQ= github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9/go.mod h1:ldkoR3iXABBeqlTibQ3MYaviA1oSlPvim6f55biwBh4= +github.com/kataras/neffos v0.0.14 h1:pdJaTvUG3NQfeMbbVCI8JT2T5goPldyyfUB2PJfh1Bs= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w= github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= @@ -721,9 +1098,14 @@ github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHz github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= github.com/knadh/koanf/v2 v2.1.0 h1:eh4QmHHBuU8BybfIJ8mB8K8gsGCD/AUQTdwGq/GzId8= @@ -737,13 +1119,22 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kshvakov/clickhouse v1.3.5 h1:PDTYk9VYgbjPAWry3AoDREeMgOVUFij6bh6IjlloHL0= +github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/leodido/go-syslog/v4 v4.1.0 h1:Wsl194qyWXr7V6DrGWC3xmxA9Ra6XgWO+toNt2fmCaI= +github.com/leodido/go-syslog/v4 v4.1.0/go.mod h1:eJ8rUfDN5OS6dOkCOBYlg2a+hbAg6pJa99QXXgMrd98= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b h1:11UHH39z1RhZ5dc4y4r/4koJo6IYFgTRMe/LlwRTEw0= +github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM= github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= @@ -763,28 +1154,38 @@ github.com/matryer/moq v0.3.3 h1:pScMH9VyrdT4S93yiLpVyU8rCDqGQr24uOyBxmktG5Q= github.com/matryer/moq v0.3.3/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= github.com/matryer/moq v0.5.0 h1:h2PJUYjZSiyEahzVogDRmrgL9Bsx9xYAl8l+LPfmwL8= github.com/matryer/moq v0.5.0/go.mod h1:39GTnrD0mVWHPvWdYj5ki/lxfhLQEtHcLh+tWoYF/iE= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/goveralls v0.0.2 h1:7eJB6EqsPhRVxvwEXGnqdO2sJI0PTsrWoTMXEk9/OQc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= +github.com/mediocregopher/radix/v3 v3.4.2 h1:galbPBjIwmyREgwGCfQEN4X8lxbJnKBYurgz+VfcStA= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mfridman/xflag v0.0.0-20240825232106-efb77353e578 h1:CRrqlUmLebb/QjzRDWE0E66+YyN/v95+w6WyH9ju8/Y= github.com/mfridman/xflag v0.0.0-20240825232106-efb77353e578/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= -github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/minio-go/v7 v7.0.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJnn2g= -github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mithrandie/readline-csvq v1.3.0 h1:VTJEOGouJ8j27jJCD4kBBbNTxM0OdBvE1aY1tMhlqE8= github.com/mithrandie/readline-csvq v1.3.0/go.mod h1:FKyYqDgf/G4SNov7SMFXRWO6LQLXIOeTog/NB97FZl0= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= @@ -792,6 +1193,8 @@ github.com/mostynb/go-grpc-compression v1.2.2 h1:XaDbnRvt2+1vgr0b/l0qh4mJAfIxE0b github.com/mostynb/go-grpc-compression v1.2.2/go.mod h1:GOCr2KBxXcblCuczg3YdLQlcin1/NfyDA348ckuCH6w= github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= +github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 h1:P48LjvUQpTReR3TQRbxSeSBsMXzfK0uol7eRcr7VBYQ= github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU= @@ -806,9 +1209,14 @@ github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADym github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/ncw/swift v1.0.53 h1:luHjjTNtekIEvHg5KdAFIBaH7bWfNkefwFnpDffSIks= +github.com/ncw/swift v1.0.53/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/ncw/swift/v2 v2.0.2 h1:jx282pcAKFhmoZBSdMcCRFn9VWkoBIRsCpe+yZq7vEk= +github.com/ncw/swift/v2 v2.0.2/go.mod h1:z0A9RVdYPjNjXVo2pDOPxZ4eu3oarO1P91fTItcb+Kg= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.97.0 h1:8GH8y3Cq54Ey6He9tyhcVYLfG4TEs/7pp3s6934zNKA= @@ -882,16 +1290,16 @@ github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceive github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.102.0 h1:Pemo9pZa3VMYdrM/bss3f0qqVyBzPSulOBQL8VQcgN8= github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.102.0/go.mod h1:fvjAM+jOQdiXCmAENKH/eWxBBqTaImbq3lpoBI4X5Ek= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= -github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= -github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= -github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= +github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w= +github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= +github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= +github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o= github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/parquet-go/parquet-go v0.20.2-0.20240416173845-962b3c5827c3 h1:dHzXGq6rltLEdYkZcB20RzdL+P3hu8/+rvEjna++/nc= github.com/parquet-go/parquet-go v0.20.2-0.20240416173845-962b3c5827c3/go.mod h1:wMYanjuaE900FTDTNY00JU+67Oqh9uO0pYWRNoPGctQ= @@ -910,8 +1318,8 @@ github.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ul github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= -github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= @@ -924,20 +1332,24 @@ github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:Om github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.mod h1:LoBCZeRh+5hX+fSULNyFnagYlQG/gBsyA/deNzROkq8= github.com/prometheus/statsd_exporter v0.26.0 h1:SQl3M6suC6NWQYEzOvIv+EF6dAMYEqIuZy+o4H9F5Ig= github.com/prometheus/statsd_exporter v0.26.0/go.mod h1:GXFLADOmBTVDrHc7b04nX8ooq3azG61pnECNqT7O5DM= -github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= -github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= +github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78= +github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo= +github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/relvacode/iso8601 v1.4.0 h1:GsInVSEJfkYuirYFxa80nMLbH2aydgZpIf52gYZXUJs= github.com/relvacode/iso8601 v1.4.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I= +github.com/richardartoul/molecule v1.0.0 h1:+LFA9cT7fn8KF39zy4dhOnwcOwRoqKiBkPqKqya+8+U= +github.com/richardartoul/molecule v1.0.0/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+uGV8BrTv8W/SIwk= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4 h1:BN/Nyn2nWMoqGRA7G7paDNDqTXE30mXGqzzybrfo05w= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= @@ -950,24 +1362,29 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCL github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/schollz/progressbar/v3 v3.14.6 h1:GyjwcWBAf+GFDMLziwerKvpuS7ZF+mNTAXIB2aspiZs= +github.com/schollz/progressbar/v3 v3.14.6/go.mod h1:Nrzpuw3Nl0srLY0VlTvC4V6RL50pcEymjy6qyJAaLa0= github.com/scottlepp/go-duck v0.3.1 h1:UDm7AepGq7urXUAtF/Nxbrth6Te+k42pz/6jdpC3gp8= github.com/scottlepp/go-duck v0.3.1/go.mod h1:cQRP0clf3eX6QUizCDTNMqShtW8Ek7ovnURCGOrJgyU= github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e h1:uO75wNGioszjmIzcY/tvdDYKRLVvzggtAmmJkn9j4GQ= github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M= -github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= -github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= +github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= +github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= +github.com/shirou/gopsutil/v4 v4.24.0-alpha.1 h1:lLPAdP4TpfgJ5byoc3EFwNSKZj8kCnDFHtuWTktWl0s= +github.com/shirou/gopsutil/v4 v4.24.0-alpha.1/go.mod h1:GVpYUxBee6CTWux2/JslZ7fYPwqkQ8YDJSXmGAryYy4= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= github.com/stoewer/parquet-cli v0.0.7 h1:rhdZODIbyMS3twr4OM3am8BPPT5pbfMcHLH93whDM5o= @@ -1001,12 +1418,31 @@ github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d/go. github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/twmb/franz-go v1.17.1 h1:0LwPsbbJeJ9R91DPUHSEd4su82WJWcTY1Zzbgbg4CeQ= +github.com/twmb/franz-go v1.17.1/go.mod h1:NreRdJ2F7dziDY/m6VyspWd6sNxHKXdMZI42UfQ3GXM= +github.com/twmb/franz-go/pkg/kmsg v1.8.0 h1:lAQB9Z3aMrIP9qF9288XcFf/ccaSxEitNA1CDTEIeTA= +github.com/twmb/franz-go/pkg/kmsg v1.8.0/go.mod h1:HzYEb8G3uu5XevZbtU0dVbkphaKTHk0X68N5ka4q6mU= +github.com/twmb/franz-go/plugin/kprom v1.1.0 h1:grGeIJbm4llUBF8jkDjTb/b8rKllWSXjMwIqeCCcNYQ= +github.com/twmb/franz-go/plugin/kprom v1.1.0/go.mod h1:cTDrPMSkyrO99LyGx3AtiwF9W6+THHjZrkDE2+TEBIU= github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/fasthttp v1.6.0 h1:uWF8lgKmeaIewWVPwi4GRq2P6+R46IgYZdxWtM+GtEY= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vburenin/ifacemaker v1.2.1 h1:3Vq8B/bfBgjWTkv+jDg4dVL1KHt3k1K4lO7XRxYA2sk= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= @@ -1034,9 +1470,10 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= -github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= -github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf h1:ckwNHVo4bv2tqNkgx3W3HANh3ta1j6TR5qw08J1A7Tw= github.com/ydb-platform/ydb-go-genproto v0.0.0-20240126124512-dbb0e1720dbf/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= github.com/ydb-platform/ydb-go-genproto v0.0.0-20240528144234-5d5a685e41f7 h1:nL8XwD6fSst7xFUirkaWJmE7kM0CdWRYgu6+YQer1d4= @@ -1060,6 +1497,7 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= go.einride.tech/aip v0.67.1 h1:d/4TW92OxXBngkSOwWS2CH5rez869KpKMaN44mdxkFI= go.einride.tech/aip v0.67.1/go.mod h1:ZGX4/zKw8dcgzdLsrvpOOGxfxI2QSk12SlP7d6c0/XI= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= go.opentelemetry.io/collector v0.97.0 h1:qyOju13byHIKEK/JehmTiGMj4pFLa4kDyrOCtTmjHU0= @@ -1163,14 +1601,10 @@ go.opentelemetry.io/collector/extension/auth v0.97.0/go.mod h1:uElLYtzMPA48mu9ba go.opentelemetry.io/collector/extension/auth v0.102.1 h1:GP6oBmpFJjxuVruPb9X40bdf6PNu9779i8anxa+wW6U= go.opentelemetry.io/collector/extension/auth v0.102.1/go.mod h1:U2JWz8AW1QXX2Ap3ofzo5Dn2fZU/Lglld97Vbh8BZS0= go.opentelemetry.io/collector/featuregate v1.5.0 h1:uK8qnYQKz1TMkK+FDTFsywg/EybW/gbnOUaPNUkRznM= -go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= -go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= go.opentelemetry.io/collector/otelcol v0.95.0 h1:lEwogX+fvO17SUgXT7/+q1DUXos0SwsAiUkhd0944AU= go.opentelemetry.io/collector/otelcol v0.95.0/go.mod h1:ETOYHHPSN4a43LvdUwajNSjwr30euS74rjfsrLCWh2A= go.opentelemetry.io/collector/otelcol v0.102.1 h1:JdRG3ven+c5k703QpZG5bxJi4JJOnWaNP/EJvN+oYnI= go.opentelemetry.io/collector/otelcol v0.102.1/go.mod h1:kHf9KBXOLZXajR1On8XJbBBGcgh2I2+/mVVroPzOLJU= -go.opentelemetry.io/collector/pdata v1.12.0 h1:Xx5VK1p4VO0md8MWm2icwC1MnJ7f8EimKItMWw46BmA= -go.opentelemetry.io/collector/pdata v1.12.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI= go.opentelemetry.io/collector/processor v0.97.0 h1:L3R5R7x56LH2inF3sv0ZOsFfulVo8yuIFhO/OgpkCU0= go.opentelemetry.io/collector/processor v0.97.0/go.mod h1:OsxBAPQ2fDytAn+yWLdEQ1yjYfl/OIak1AfKGfI8ALs= go.opentelemetry.io/collector/processor v0.102.1 h1:79NWs7kTgmgxOIQacuZyDf+mYWuoJZS07SHwZT7sZ4Y= @@ -1198,12 +1632,14 @@ go.opentelemetry.io/contrib/config v0.7.0 h1:b1rK5tGTuhhPirJiMxOcyQfZs76j2VapY6O go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3TcL8dU5rM5/xcOQ= go.opentelemetry.io/contrib/exporters/autoexport v0.53.0 h1:13K+tY7E8GJInkrvRiPAhC0gi/7vKjzDNhtmCf+QXG8= go.opentelemetry.io/contrib/exporters/autoexport v0.53.0/go.mod h1:lyQF6xQ4iDnMg4sccNdFs1zf62xd79YI8vZqKjOTwMs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.53.0/go.mod h1:ImRBLMJv177/pwiLZ7tU7HDGNdBv7rS0HQ99eN/zBl8= go.opentelemetry.io/contrib/propagators/b3 v1.23.0 h1:aaIGWc5JdfRGpCafLRxMJbD65MfTa206AwSKkvGS0Hg= go.opentelemetry.io/contrib/propagators/b3 v1.23.0/go.mod h1:Gyz7V7XghvwTq+mIhLFlTgcc03UDroOg8vezs4NLhwU= go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= go.opentelemetry.io/otel/bridge/opencensus v1.26.0 h1:DZzxj9QjznMVoehskOJnFP2gsTCWtDTFBDvFhPAY7nc= go.opentelemetry.io/otel/bridge/opencensus v1.26.0/go.mod h1:rJiX0KrF5m8Tm1XE8jLczpAv5zUaDcvhKecFG0ZoFG4= go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= @@ -1235,12 +1671,16 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbR go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0= go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZHcAyHw5aU9Y= go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE= go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= @@ -1249,8 +1689,11 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEa go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= @@ -1258,25 +1701,33 @@ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp/shiny v0.0.0-20230801115018-d63ba01acd4b h1:sgkbz1SFTsoQIvzTIw45hccUcGocu00QM3qucBYV8b0= golang.org/x/exp/shiny v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= +golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= +golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1285,44 +1736,81 @@ golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE= gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw= +google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk= +google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M= +google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142/go.mod h1:G11eXq53iI5Q+kyNOmCvnzBaxEA2Q/Ik5Tj7nqBE8j4= +google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6/go.mod h1:10yRODfgim2/T8csjQsMPgZOMvtytXKTDRzH6HRGzRw= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= +google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf h1:T4tsZBlZYXK3j40sQNP5MBO32I+rn6ypV1PpklsiV8k= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:5/MT647Cn/GGhwTpXC7QqcaR5Cnee4v4MKCU1/nwnIQ= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20240814211410-ddb44dafa142 h1:abw8n4dbagiJlEWXb1eDu2nB0NDSf6o4J7YO8Fb/ik0= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20240814211410-ddb44dafa142/go.mod h1:gQizMG9jZ0L2ADJaM+JdZV4yTCON/CQpnHRPoM+54w4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1330,8 +1818,11 @@ gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= +honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= +honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= diff --git a/jest.config.js b/jest.config.js index 6d9f76b9317..5418ede2e38 100644 --- a/jest.config.js +++ b/jest.config.js @@ -20,6 +20,7 @@ const esModules = [ '@msagl', 'lodash-es', 'vscode-languageserver-types', + '@bsull/augurs', ].join('|'); module.exports = { @@ -54,6 +55,7 @@ module.exports = { '^@grafana/schema/dist/esm/(.*)$': '/packages/grafana-schema/src/$1', // prevent systemjs amd extra from breaking tests. 'systemjs/dist/extras/amd': '/public/test/mocks/systemjsAMDExtra.ts', + '@bsull/augurs': '/public/test/mocks/augurs.ts', }, // Log the test results with dynamic Loki tags. Drone CI only reporters: ['default', ['/public/test/log-reporter.js', { enable: process.env.DRONE === 'true' }]], diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 6a24fc92266..9ed6b5cc0c1 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -200,6 +200,8 @@ lineage: schemas: [{ current?: #VariableOption // Whether multiple values can be selected or not from variable value list multi?: bool | *false + // Allow custom values to be entered in the variable + allowCustomValue?: bool | *true // Options that can be selected for a variable. options?: [...#VariableOption] // Options to config when to refresh a variable diff --git a/kindsv2/gen.go b/kindsv2/gen.go new file mode 100644 index 00000000000..ee215e3d491 --- /dev/null +++ b/kindsv2/gen.go @@ -0,0 +1,52 @@ +//go:generate go run gen.go + +package main + +import ( + "context" + "os" + + "cuelang.org/go/cue/cuecontext" + "github.com/grafana/cog" +) + +type codegenTargets struct { + schemaPath string + outputPath string +} + +func main() { + cueCtx := cuecontext.New() + + targets := []codegenTargets{ + { + schemaPath: "../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue", + outputPath: "../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen.ts", + }, + } + + for _, target := range targets { + rawSchema, err := os.ReadFile(target.schemaPath) + if err != nil { + panic(err) + } + + value := cueCtx.CompileBytes(rawSchema) + if value.Err() != nil { + panic(value.Err()) + } + + codegenPipeline := cog.TypesFromSchema(). + CUEValue("dashboard", value). + Typescript() + + tsBytes, err := codegenPipeline.Run(context.Background()) + if err != nil { + panic(err) + } + + if err := os.WriteFile(target.outputPath, tsBytes, 0644); err != nil { + panic(err) + } + } +} diff --git a/kindsv2/go.mod b/kindsv2/go.mod new file mode 100644 index 00000000000..ecd83ce978a --- /dev/null +++ b/kindsv2/go.mod @@ -0,0 +1,46 @@ +module github.com/grafana/grafana/kindsv2 + +go 1.23.1 + +require ( + cuelang.org/go v0.8.2 + github.com/grafana/cog v0.0.0-20241029201114-f7f7db0c1070 +) + +require ( + cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/proto v1.13.2 // indirect + github.com/expr-lang/expr v1.16.9 // indirect + github.com/getkin/kin-openapi v0.128.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.5.0 // indirect + github.com/invopop/yaml v0.3.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/protocolbuffers/txtpbfmt v0.0.0-20240116145035-ef3ab179eed6 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/yalue/merged_fs v1.3.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/tools v0.26.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/kindsv2/go.sum b/kindsv2/go.sum new file mode 100644 index 00000000000..c3848f5abe1 --- /dev/null +++ b/kindsv2/go.sum @@ -0,0 +1,94 @@ +cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY= +cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg= +cuelang.org/go v0.8.2 h1:vWfHI1kQlBvwkna7ktAqXjV5LUEAgU6vyMlJjvZZaDw= +cuelang.org/go v0.8.2/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY= +github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= +github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +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/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= +github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= +github.com/grafana/cog v0.0.0-20241029201114-f7f7db0c1070 h1:ncAkFmIq3UbPiPhQ2CuBzdTuwnDqLsFFNqEIXKaiPGA= +github.com/grafana/cog v0.0.0-20241029201114-f7f7db0c1070/go.mod h1:FqZi9WZ/Uzvs3tvo7l+OViThCpfvu3KDGDqHCB2LNbg= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +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/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/protocolbuffers/txtpbfmt v0.0.0-20240116145035-ef3ab179eed6 h1:MAzmm+JtFxQwTPb1cVMLkemw2OxLy5AB/d/rxtAwGQQ= +github.com/protocolbuffers/txtpbfmt v0.0.0-20240116145035-ef3ab179eed6/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= +github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +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= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/package.json b/package.json index a4a7d83a08d..aab622b0ae3 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "version": "11.4.0-pre", "repository": "github:grafana/grafana", "scripts": { - "build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js", + "build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js --progress", "build:nominify": "yarn run build -- --env noMinify=1", "dev": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js", "e2e": "./e2e/start-and-run-suite", @@ -27,7 +27,7 @@ "test:coverage:changes": "jest --coverage --changedSince=origin/main", "test:accessibility-report": "./scripts/generate-a11y-report.sh", "lint": "yarn run lint:ts && yarn run lint:sass", - "lint:ts": "eslint . --ext .js,.tsx,.ts --cache", + "lint:ts": "eslint . --cache", "lint:sass": "yarn stylelint '{public/sass,packages}/**/*.scss' --cache", "test:ci": "mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%}", "lint:fix": "yarn lint:ts --fix", @@ -36,7 +36,7 @@ "packages:prepare": "lerna version --no-push --no-git-tag-version --force-publish --exact", "packages:pack": "mkdir -p ./npm-artifacts && lerna exec --no-private -- yarn pack --out \"../../npm-artifacts/%s-%v.tgz\"", "packages:typecheck": "nx run-many -t typecheck --projects='tag:scope:package'", - "prettier:check": "prettier --check --list-different=false --log-level=warn \"**/*.{ts,tsx,scss,md,mdx,json}\"", + "prettier:check": "prettier --check --list-different=false --log-level=warn \"**/*.{ts,tsx,scss,md,mdx,json,js}\"", "prettier:checkDocs": "prettier --check --list-different=false --log-level=warn \"docs/**/*.md\" \"*.md\" \"packages/**/*.{ts,tsx,scss,md,mdx,json}\"", "prettier:write": "prettier --list-different \"**/*.{js,ts,tsx,scss,md,mdx,json}\" --write", "start": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js --watch", @@ -46,7 +46,7 @@ "storybook": "yarn workspace @grafana/ui storybook --ci", "storybook:build": "yarn workspace @grafana/ui storybook:build", "themes-generate": "esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node", - "themes:usage": "eslint . --ext .tsx,.ts --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --rule '{ @grafana/theme-token-usage: \"error\" }'", + "themes:usage": "eslint . --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --plugin '@grafana' --rule '{ @grafana/theme-token-usage: \"error\" }'", "typecheck": "tsc --noEmit && yarn run packages:typecheck", "plugins:build-bundled": "find plugins-bundled -name package.json -not -path '*/node_modules/*' -execdir yarn build \\;", "watch": "yarn start -d watch,start core:start --watchTheme", @@ -69,20 +69,21 @@ "releaseNotesUrl": "https://grafana.com/docs/grafana/next/release-notes/" }, "devDependencies": { - "@babel/core": "7.25.8", - "@babel/preset-env": "7.25.8", - "@babel/runtime": "7.25.7", + "@babel/core": "7.26.0", + "@babel/preset-env": "7.26.0", + "@babel/runtime": "7.26.0", "@betterer/betterer": "5.4.0", "@betterer/cli": "5.4.0", "@betterer/eslint": "5.4.0", "@cypress/webpack-preprocessor": "6.0.2", "@emotion/eslint-plugin": "11.12.0", - "@grafana/eslint-config": "7.0.0", + "@eslint/compat": "^1.2.0", + "@grafana/eslint-config": "8.0.0", "@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules", - "@grafana/plugin-e2e": "^1.8.3", + "@grafana/plugin-e2e": "^1.11.0", "@grafana/tsconfig": "^2.0.0", "@manypkg/get-packages": "^2.2.0", - "@playwright/test": "1.48.1", + "@playwright/test": "1.48.2", "@pmmmwh/react-refresh-webpack-plugin": "0.5.15", "@react-types/button": "3.10.0", "@react-types/menu": "3.9.12", @@ -90,10 +91,11 @@ "@react-types/shared": "3.25.0", "@rtk-query/codegen-openapi": "^1.2.0", "@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1", + "@stylistic/eslint-plugin-ts": "^2.9.0", "@swc/core": "1.4.2", - "@swc/helpers": "0.5.13", + "@swc/helpers": "0.5.15", "@testing-library/dom": "10.4.0", - "@testing-library/jest-dom": "6.5.0", + "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.0.1", "@testing-library/user-event": "14.5.2", "@types/angular": "1.8.9", @@ -115,18 +117,18 @@ "@types/gtag.js": "^0.0.20", "@types/history": "4.7.11", "@types/ini": "^4", - "@types/jest": "29.5.13", - "@types/jquery": "3.5.31", + "@types/jest": "29.5.14", + "@types/jquery": "3.5.32", "@types/js-yaml": "^4.0.5", "@types/jsurl": "^1.2.28", - "@types/lodash": "4.17.12", + "@types/lodash": "4.17.13", "@types/logfmt": "^1.2.3", "@types/lucene": "^2", - "@types/node": "20.16.13", + "@types/node": "20.16.15", "@types/node-forge": "^1", "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4", "@types/pluralize": "^0.0.33", - "@types/prismjs": "1.26.4", + "@types/prismjs": "1.26.5", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/react-grid-layout": "1.3.5", @@ -152,8 +154,8 @@ "@types/webpack-assets-manifest": "^5", "@types/webpack-env": "^1.18.4", "@types/yargs": "17.0.33", - "@typescript-eslint/eslint-plugin": "6.21.0", - "@typescript-eslint/parser": "6.21.0", + "@typescript-eslint/eslint-plugin": "8.9.0", + "@typescript-eslint/parser": "8.9.0", "autoprefixer": "10.4.20", "babel-loader": "9.2.1", "blob-polyfill": "9.0.20240710", @@ -162,7 +164,8 @@ "chrome-remote-interface": "0.33.2", "codeowners": "^5.1.1", "copy-webpack-plugin": "12.0.2", - "core-js": "3.38.1", + "core-js": "3.39.0", + "crashme": "0.0.15", "css-loader": "7.1.2", "css-minimizer-webpack-plugin": "6.0.0", "cypress": "13.10.0", @@ -171,25 +174,26 @@ "esbuild": "0.24.0", "esbuild-loader": "4.2.2", "esbuild-plugin-browserslist": "^0.15.0", - "eslint": "8.57.0", + "eslint": "9.14.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "28.8.3", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "28.9.0", "eslint-plugin-jest-dom": "^5.4.0", - "eslint-plugin-jsdoc": "48.11.0", - "eslint-plugin-jsx-a11y": "6.10.1", - "eslint-plugin-lodash": "7.4.0", - "eslint-plugin-no-barrel-files": "^1.1.0", - "eslint-plugin-react": "7.37.1", - "eslint-plugin-react-hooks": "4.6.0", - "eslint-plugin-testing-library": "^6.2.2", - "eslint-scope": "^8.0.0", + "eslint-plugin-jsdoc": "50.5.0", + "eslint-plugin-jsx-a11y": "6.10.2", + "eslint-plugin-lodash": "8.0.0", + "eslint-plugin-no-barrel-files": "^1.1.1", + "eslint-plugin-react": "7.37.2", + "eslint-plugin-react-hooks": "5.0.0", + "eslint-plugin-testing-library": "^6.3.0", + "eslint-plugin-unicorn": "^56.0.0", + "eslint-scope": "^8.1.0", "eslint-webpack-plugin": "4.2.0", "expose-loader": "5.0.0", "fork-ts-checker-webpack-plugin": "9.0.2", "glob": "11.0.0", "html-loader": "5.1.0", - "html-webpack-plugin": "5.6.2", + "html-webpack-plugin": "5.6.3", "http-server": "14.1.1", "i18next-parser": "9.0.2", "ini": "^4.1.3", @@ -203,13 +207,13 @@ "jest-watch-typeahead": "^2.2.2", "knip": "^5.10.0", "lerna": "8.1.8", - "mini-css-extract-plugin": "2.9.1", - "msw": "2.4.12", + "mini-css-extract-plugin": "2.9.2", + "msw": "2.6.5", "mutationobserver-shim": "0.3.7", "ngtemplate-loader": "2.1.0", "node-notifier": "10.0.1", "nx": "19.8.2", - "postcss": "8.4.47", + "postcss": "8.4.49", "postcss-loader": "8.1.1", "postcss-reporter": "7.1.0", "postcss-scss": "4.0.9", @@ -220,9 +224,9 @@ "react-test-renderer": "18.2.0", "redux-mock-store": "1.5.5", "rimraf": "6.0.1", - "rudder-sdk-js": "2.48.21", - "sass": "1.79.5", - "sass-loader": "16.0.2", + "rudder-sdk-js": "2.48.29", + "sass": "1.80.7", + "sass-loader": "16.0.3", "smtp-tester": "^2.1.0", "style-loader": "4.0.0", "stylelint": "16.9.0", @@ -245,17 +249,18 @@ "yargs": "^17.5.1" }, "dependencies": { - "@emotion/css": "11.13.4", - "@emotion/react": "11.13.3", + "@bsull/augurs": "^0.6.0", + "@emotion/css": "11.13.5", + "@emotion/react": "11.13.5", "@fingerprintjs/fingerprintjs": "^3.4.2", - "@floating-ui/react": "0.26.25", - "@formatjs/intl-durationformat": "^0.3.0", + "@floating-ui/react": "0.26.28", + "@formatjs/intl-durationformat": "^0.6.0", "@glideapps/glide-data-grid": "^6.0.0", "@grafana/aws-sdk": "0.5.0", - "@grafana/azure-sdk": "0.0.3", + "@grafana/azure-sdk": "0.0.5", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", - "@grafana/experimental": "2.1.2", + "@grafana/experimental": "2.1.4", "@grafana/faro-core": "^1.3.6", "@grafana/faro-web-sdk": "^1.3.6", "@grafana/faro-web-tracing": "^1.8.2", @@ -267,17 +272,17 @@ "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/saga-icons": "workspace:*", - "@grafana/scenes": "5.20.2", - "@grafana/scenes-react": "5.20.2", + "@grafana/scenes": "5.25.1", + "@grafana/scenes-react": "5.25.1", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", "@hello-pangea/dnd": "17.0.0", "@kusto/monaco-kusto": "^10.0.0", - "@leeoniya/ufuzzy": "1.0.14", + "@leeoniya/ufuzzy": "1.0.16", "@lezer/common": "1.2.3", "@lezer/highlight": "1.2.1", - "@lezer/lr": "1.3.3", + "@lezer/lr": "1.4.2", "@locker/near-membrane-dom": "0.13.6", "@locker/near-membrane-shared": "0.13.6", "@locker/near-membrane-shared-dom": "0.13.6", @@ -292,7 +297,7 @@ "@react-aria/overlays": "3.23.4", "@react-aria/utils": "3.25.3", "@react-awesome-query-builder/ui": "6.6.3", - "@reduxjs/toolkit": "2.2.8", + "@reduxjs/toolkit": "2.3.0", "@testing-library/react-hooks": "^8.0.1", "@visx/event": "3.3.0", "@visx/gradient": "3.3.0", @@ -304,13 +309,13 @@ "angular-bindonce": "0.3.1", "angular-route": "1.8.3", "angular-sanitize": "1.8.3", - "ansicolor": "1.1.100", + "ansicolor": "2.0.3", "baron": "3.0.3", "brace": "0.11.1", "centrifuge": "5.2.2", "classnames": "2.5.1", "combokeys": "^3.0.0", - "comlink": "4.4.1", + "comlink": "4.4.2", "common-tags": "1.8.2", "d3": "7.9.0", "d3-force": "3.0.0", @@ -356,10 +361,10 @@ "prismjs": "1.29.0", "rc-slider": "11.1.7", "rc-time-picker": "3.7.3", - "rc-tree": "5.9.0", - "re-resizable": "6.10.0", + "rc-tree": "5.10.1", + "re-resizable": "6.10.1", "react": "18.2.0", - "react-diff-viewer": "^3.1.1", + "react-diff-viewer-continued": "^3.4.0", "react-dom": "18.2.0", "react-draggable": "4.4.6", "react-dropzone": "^14.2.3", @@ -376,7 +381,7 @@ "react-router": "5.3.3", "react-router-dom": "5.3.3", "react-router-dom-v5-compat": "^6.26.1", - "react-select": "5.8.1", + "react-select": "5.8.3", "react-split-pane": "0.1.92", "react-table": "7.8.0", "react-transition-group": "4.4.5", @@ -422,11 +427,10 @@ "debug@npm:^0.7.4": "2.6.9", "slate-dev-environment@^0.2.2": "patch:slate-dev-environment@npm:0.2.5#.yarn/patches/slate-dev-environment-npm-0.2.5-9aeb7da7b5.patch", "react-split-pane@0.1.92": "patch:react-split-pane@npm:0.1.92#.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch", - "history@4.10.1": "patch:history@npm%3A4.10.1#./.yarn/patches/history-npm-4.10.1-ee217563ae.patch", - "history@^4.9.0": "patch:history@npm%3A4.10.1#./.yarn/patches/history-npm-4.10.1-ee217563ae.patch", "redux": "^5.0.0", "@storybook/blocks@npm:8.1.6": "patch:@storybook/blocks@npm%3A8.1.6#~/.yarn/patches/@storybook-blocks-npm-8.1.6-892f57a6d7.patch", "react-grid-layout": "patch:react-grid-layout@npm%3A1.4.4#~/.yarn/patches/react-grid-layout-npm-1.4.4-4024c5395b.patch", + "@grafana/plugin-e2e/@grafana/e2e-selectors": "workspace:*", "@grafana/scenes/@grafana/e2e-selectors": "workspace:*", "@grafana/scenes-react/@grafana/e2e-selectors": "workspace:*" }, diff --git a/packages/grafana-data/.eslintrc b/packages/grafana-data/.eslintrc deleted file mode 100644 index a9902770dcf..00000000000 --- a/packages/grafana-data/.eslintrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "rules": { - "no-restricted-imports": ["error", { "patterns": ["@grafana/runtime", "@grafana/ui", "@grafana/data"] }], - }, - "overrides": [ - { - "files": ["**/*.test.{ts,tsx}"], - "rules": { - "no-restricted-imports": "off", - }, - }, - ], -} diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 8b296797482..4067d00b9b6 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -47,7 +47,7 @@ "history": "4.10.1", "lodash": "4.17.21", "marked": "12.0.2", - "marked-mangle": "1.1.9", + "marked-mangle": "1.1.10", "moment": "2.30.1", "moment-timezone": "0.5.46", "ol": "7.4.0", @@ -65,8 +65,8 @@ "@rollup/plugin-node-resolve": "15.3.0", "@types/dompurify": "^3.0.0", "@types/history": "4.7.11", - "@types/lodash": "4.17.12", - "@types/node": "20.16.13", + "@types/lodash": "4.17.13", + "@types/node": "20.16.15", "@types/papaparse": "5.3.15", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", diff --git a/packages/grafana-data/src/dataframe/StreamingDataFrame.ts b/packages/grafana-data/src/dataframe/StreamingDataFrame.ts index ac928f73f48..bb410406ed9 100644 --- a/packages/grafana-data/src/dataframe/StreamingDataFrame.ts +++ b/packages/grafana-data/src/dataframe/StreamingDataFrame.ts @@ -550,8 +550,11 @@ export function parseLabelsFromField(str: string): Labels { * @internal // not exported in yet */ export function getLastStreamingDataFramePacket(frame: DataFrame) { - const pi = (frame as StreamingDataFrame).packetInfo; - return pi?.action ? pi : undefined; + if (frame instanceof StreamingDataFrame) { + const pi = frame.packetInfo; + return pi.action; + } + return undefined; } // mutable circular push diff --git a/packages/grafana-data/src/datetime/datemath.ts b/packages/grafana-data/src/datetime/datemath.ts index 8ebf5c99269..e95e4575401 100644 --- a/packages/grafana-data/src/datetime/datemath.ts +++ b/packages/grafana-data/src/datetime/datemath.ts @@ -1,4 +1,4 @@ -import { includes, isDate } from 'lodash'; +import { isDate } from 'lodash'; import { TimeZone } from '../types/time'; @@ -12,7 +12,11 @@ import { ISO_8601, } from './moment_wrapper'; -const units: DurationUnit[] = ['y', 'M', 'w', 'd', 'h', 'm', 's', 'Q']; +const units: string[] = ['y', 'M', 'w', 'd', 'h', 'm', 's', 'Q'] satisfies DurationUnit[]; + +const isDurationUnit = (value: string): value is DurationUnit => { + return units.includes(value); +}; /** * Determine if a string contains a relative date time. @@ -169,11 +173,9 @@ export function parseDateMath( isFiscal = true; } - const unit = unitString as DurationUnit; + const unit = unitString; - if (!includes(units, unit)) { - return undefined; - } else { + if (isDurationUnit(unit)) { if (type === 0) { if (isFiscal) { roundToFiscal(fiscalYearStartMonth, result, unit, roundUp); @@ -189,6 +191,8 @@ export function parseDateMath( } else if (type === 2) { result.subtract(num, unit); } + } else { + return undefined; } } return result; diff --git a/packages/grafana-data/src/datetime/durationutil.ts b/packages/grafana-data/src/datetime/durationutil.ts index c8049a641bb..547303134cd 100644 --- a/packages/grafana-data/src/datetime/durationutil.ts +++ b/packages/grafana-data/src/datetime/durationutil.ts @@ -1,6 +1,6 @@ import { add, Duration, intervalToDuration, Interval, isAfter } from 'date-fns'; -const durationMap: { [key in Required]: string[] } = { +const durationMap: Record = { years: ['y', 'Y', 'years'], months: ['M', 'months'], weeks: ['w', 'W', 'weeks'], @@ -8,7 +8,7 @@ const durationMap: { [key in Required]: string[] } = { hours: ['h', 'H', 'hours'], minutes: ['m', 'minutes'], seconds: ['s', 'S', 'seconds'], -}; +} satisfies { [key in keyof Duration]: string[] }; /** * intervalToAbbreviatedDurationString converts interval to readable duration string @@ -26,7 +26,7 @@ export function intervalToAbbreviatedDurationString(interval: Interval, includeS } const duration = intervalToDuration(interval); - return (Object.entries(duration) as Array<[keyof Duration, number | undefined]>).reduce((str, [unit, value]) => { + return Object.entries(duration).reduce((str, [unit, value]) => { if (value && value !== 0 && !(unit === 'seconds' && !includeSeconds && str)) { const padding = str !== '' ? ' ' : ''; return str + `${padding}${value}${durationMap[unit][0]}`; diff --git a/packages/grafana-data/src/datetime/formatter.ts b/packages/grafana-data/src/datetime/formatter.ts index c78a08d562b..7facb730195 100644 --- a/packages/grafana-data/src/datetime/formatter.ts +++ b/packages/grafana-data/src/datetime/formatter.ts @@ -1,4 +1,4 @@ -/* eslint-disable id-blacklist, no-restricted-imports, @typescript-eslint/ban-types */ +/* eslint-disable id-blacklist, no-restricted-imports */ import moment, { Moment } from 'moment-timezone'; import { TimeZone } from '../types/time'; diff --git a/packages/grafana-data/src/datetime/moment_wrapper.ts b/packages/grafana-data/src/datetime/moment_wrapper.ts index 6b60bb524bd..8a017297b4c 100644 --- a/packages/grafana-data/src/datetime/moment_wrapper.ts +++ b/packages/grafana-data/src/datetime/moment_wrapper.ts @@ -1,7 +1,7 @@ import moment, { Moment, MomentInput, DurationInputArg1, DurationInputArg2 } from 'moment'; import { TimeZone } from '../types/time'; -/* eslint-disable id-blacklist, no-restricted-imports, @typescript-eslint/ban-types */ +/* eslint-disable id-blacklist, no-restricted-imports */ export interface DateTimeBuiltinFormat { __momentBuiltinFormatBrand: any; } diff --git a/packages/grafana-data/src/datetime/parser.ts b/packages/grafana-data/src/datetime/parser.ts index 3c97338373e..5e1bf608401 100644 --- a/packages/grafana-data/src/datetime/parser.ts +++ b/packages/grafana-data/src/datetime/parser.ts @@ -1,4 +1,4 @@ -/* eslint-disable id-blacklist, no-restricted-imports, @typescript-eslint/ban-types */ +/* eslint-disable id-blacklist, no-restricted-imports */ import { lowerCase } from 'lodash'; import moment from 'moment-timezone'; diff --git a/packages/grafana-data/src/datetime/rangeutil.test.ts b/packages/grafana-data/src/datetime/rangeutil.test.ts index 03d3594afb4..684e4a11935 100644 --- a/packages/grafana-data/src/datetime/rangeutil.test.ts +++ b/packages/grafana-data/src/datetime/rangeutil.test.ts @@ -41,15 +41,49 @@ describe('Range Utils', () => { expect(deserializedTimeRange.from.format()).toBe('1996-07-30T16:00:00Z'); }); - it('should leave the raw part intact if it has calulactions', () => { + it('should leave the raw part intact if it has calculations', () => { + const timeRange = { + from: 'now-6h', + to: 'now', + }; + + const deserialized = convertRawToRange(timeRange); + expect(deserialized.from).not.toBe(timeRange.from); + expect(deserialized.raw.from).not.toBe(deserialized.from); + expect(deserialized.raw.from).toBe(timeRange.from); + expect(deserialized.to).not.toBe(timeRange.to); + expect(deserialized.raw.to).not.toBe(deserialized.from); + expect(deserialized.raw.to).toBe(timeRange.to); + }); + + it('should leave the raw part intact if it has calculations for "from"', () => { + const timeRange = { + from: 'now', + to: DEFAULT_DATE_VALUE, + }; + + const deserialized = convertRawToRange(timeRange); + expect(deserialized.from).not.toBe(timeRange.from); + expect(deserialized.raw.from).not.toBe(deserialized.from); + expect(deserialized.raw.from).toBe(timeRange.from); + expect(deserialized.to).not.toBe(timeRange.to); + expect(deserialized.raw.to).toBe(deserialized.to); + expect(deserialized.raw.to).not.toBe(timeRange.to); + }); + + it('should leave the raw part intact if it has calculations for "to"', () => { const timeRange = { from: DEFAULT_DATE_VALUE, to: 'now', }; const deserialized = convertRawToRange(timeRange); - expect(deserialized.raw).toStrictEqual(timeRange); - expect(deserialized.to.toString()).not.toBe(deserialized.raw.to); + expect(deserialized.from).not.toBe(timeRange.from); + expect(deserialized.raw.from).toBe(deserialized.from); + expect(deserialized.raw.from).not.toBe(timeRange.from); + expect(deserialized.to).not.toBe(timeRange.to); + expect(deserialized.raw.to).not.toBe(deserialized.to); + expect(deserialized.raw.to).toBe(timeRange.to); }); }); diff --git a/packages/grafana-data/src/datetime/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts index 334397ebbac..d984321c532 100644 --- a/packages/grafana-data/src/datetime/rangeutil.ts +++ b/packages/grafana-data/src/datetime/rangeutil.ts @@ -207,11 +207,14 @@ export const convertRawToRange = ( const from = dateTimeParse(raw.from, { roundUp: false, timeZone, fiscalYearStartMonth, format }); const to = dateTimeParse(raw.to, { roundUp: true, timeZone, fiscalYearStartMonth, format }); - if (dateMath.isMathString(raw.from) || dateMath.isMathString(raw.to)) { - return { from, to, raw }; - } - - return { from, to, raw: { from, to } }; + return { + from, + to, + raw: { + from: dateMath.isMathString(raw.from) ? raw.from : from, + to: dateMath.isMathString(raw.to) ? raw.to : to, + }, + }; }; export function isRelativeTime(v: DateTime | string) { diff --git a/packages/grafana-data/src/field/__snapshots__/fieldOverrides.test.ts.snap b/packages/grafana-data/src/field/__snapshots__/fieldOverrides.test.ts.snap new file mode 100644 index 00000000000..aea09b13dad --- /dev/null +++ b/packages/grafana-data/src/field/__snapshots__/fieldOverrides.test.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`setFieldConfigDefaults applies the base threshold when one does not exist in the config 1`] = ` +{ + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": -Infinity, + }, + { + "color": "green", + "value": 50, + }, + ], +} +`; diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 0d16b66397b..0fd6e2d834e 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -730,6 +730,33 @@ describe('setFieldConfigDefaults', () => { } `); }); + + it('applies the base threshold when one does not exist in the config', () => { + const defaultConfig: FieldConfig = { + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [{ value: -Infinity, color: 'red' }], + }, + }; + + const config: FieldConfig = { + thresholds: { + mode: ThresholdsMode.Absolute, + steps: [{ value: 50, color: 'green' }], + }, + }; + + const context: FieldOverrideEnv = { + data: [], + field: { type: FieldType.number } as Field, + dataFrameIndex: 0, + fieldConfigRegistry: customFieldRegistry, + }; + + setFieldConfigDefaults(config, defaultConfig, context); + + expect(config.thresholds).toMatchSnapshot(); + }); }); describe('setDynamicConfigValue', () => { diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index 479f31155c6..0924a417231 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -2,7 +2,7 @@ import { isNumber, set, unset, get, cloneDeep } from 'lodash'; import { useMemo, useRef } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; -import { VariableFormatID } from '@grafana/schema'; +import { ThresholdsMode, VariableFormatID } from '@grafana/schema'; import { compareArrayValues, compareDataFrameStructures } from '../dataframe/frameComparisons'; import { guessFieldTypeForField } from '../dataframe/processDataFrame'; @@ -347,6 +347,18 @@ export function setFieldConfigDefaults(config: FieldConfig, defaults: FieldConfi // Combine the data source links and the panel default config links config.links = [...config.links, ...defaults.links]; } + + // if we have a base threshold set by default but not on the config, we need to merge it in + const defaultBaseStep = + defaults?.thresholds?.mode === ThresholdsMode.Absolute && + defaults.thresholds?.steps.find((step) => step.value === -Infinity); + if ( + config.thresholds?.mode === ThresholdsMode.Absolute && + !config.thresholds.steps.some((step) => step.value === -Infinity) && + defaultBaseStep + ) { + config.thresholds.steps = [defaultBaseStep, ...config.thresholds.steps]; + } for (const fieldConfigProperty of context.fieldConfigRegistry.list()) { if (fieldConfigProperty.isCustom && !config.custom) { config.custom = {}; diff --git a/packages/grafana-data/src/panel/PanelPlugin.ts b/packages/grafana-data/src/panel/PanelPlugin.ts index c4b2e0ed037..cb0325ac65c 100644 --- a/packages/grafana-data/src/panel/PanelPlugin.ts +++ b/packages/grafana-data/src/panel/PanelPlugin.ts @@ -94,7 +94,7 @@ export type PanelOptionsSupplier = ( export class PanelPlugin< TOptions = any, - TFieldConfigOptions extends object = any, + TFieldConfigOptions extends object = {}, > extends GrafanaPlugin { private _defaults?: TOptions; private _fieldConfigDefaults: FieldConfigSource = { diff --git a/packages/grafana-data/src/themes/createShape.ts b/packages/grafana-data/src/themes/createShape.ts index 881633523f4..8e6497639a7 100644 --- a/packages/grafana-data/src/themes/createShape.ts +++ b/packages/grafana-data/src/themes/createShape.ts @@ -22,7 +22,7 @@ export function createShape(options: ThemeShapeInput): ThemeShape { const baseBorderRadius = options.borderRadius ?? 2; const radius = { - default: '2px', + default: `${baseBorderRadius}px`, pill: '9999px', circle: '100%', }; diff --git a/packages/grafana-data/src/themes/registry.ts b/packages/grafana-data/src/themes/registry.ts index 85e9e399153..f05ea638735 100644 --- a/packages/grafana-data/src/themes/registry.ts +++ b/packages/grafana-data/src/themes/registry.ts @@ -1,5 +1,6 @@ import { Registry, RegistryItem } from '../utils/Registry'; +import { createColors } from './createColors'; import { createTheme } from './createTheme'; import { GrafanaTheme2 } from './types'; @@ -35,8 +36,7 @@ const themeRegistry = new Registry(() => { { id: 'system', name: 'System preference', build: getSystemPreferenceTheme }, { id: 'dark', name: 'Dark', build: () => createTheme({ colors: { mode: 'dark' } }) }, { id: 'light', name: 'Light', build: () => createTheme({ colors: { mode: 'light' } }) }, - { id: 'blue-night', name: 'Blue night', build: createBlueNight, isExtra: true }, - { id: 'midnight', name: 'Midnight', build: createMidnight, isExtra: true }, + { id: 'debug', name: 'Debug', build: createDebug, isExtra: true }, ]; }); @@ -47,47 +47,77 @@ function getSystemPreferenceTheme() { } /** - * Just a temporary placeholder for a possible new theme + * a very ugly theme that is useful for debugging and checking if the theme is applied correctly + * borders are red, + * backgrounds are blue, + * text is yellow, + * and grafana loves you <3 + * (also corners are rounded, action states (hover, focus, selected) are purple) */ -function createMidnight(): GrafanaTheme2 { - const whiteBase = '204, 204, 220'; +function createDebug(): GrafanaTheme2 { + const baseDarkColors = createColors({ + mode: 'dark', + }); return createTheme({ - name: 'Midnight', + name: 'Debug', colors: { mode: 'dark', background: { - canvas: '#000000', - primary: '#000000', - secondary: '#181818', + canvas: '#000033', + primary: '#000044', + secondary: '#000055', + }, + text: { + primary: '#bbbb00', + secondary: '#888800', + disabled: '#444400', + link: '#dddd00', + maxContrast: '#ffff00', }, border: { - weak: `rgba(${whiteBase}, 0.17)`, - medium: `rgba(${whiteBase}, 0.25)`, - strong: `rgba(${whiteBase}, 0.35)`, - }, - }, - }); -} - -/** - * Just a temporary placeholder for a possible new theme - */ -function createBlueNight(): GrafanaTheme2 { - return createTheme({ - name: 'Blue night', - colors: { - mode: 'dark', - background: { - canvas: '#15161d', - primary: '#15161d', - secondary: '#1d1f2e', - }, - border: { - weak: `#2e304f`, - medium: `#2e304f`, - strong: `#2e304f`, + weak: '#ff000044', + medium: '#ff000088', + strong: '#ff0000ff', }, + primary: { + ...baseDarkColors.primary, + border: '#ff000088', + text: '#cccc00', + contrastText: '#ffff00', + shade: '#9900dd', + }, + secondary: { + ...baseDarkColors.secondary, + border: '#ff000088', + text: '#cccc00', + contrastText: '#ffff00', + shade: '#9900dd', + }, + info: { + ...baseDarkColors.info, + shade: '#9900dd', + }, + warning: { + ...baseDarkColors.warning, + shade: '#9900dd', + }, + success: { + ...baseDarkColors.success, + shade: '#9900dd', + }, + error: { + ...baseDarkColors.error, + shade: '#9900dd', + }, + action: { + hover: '#9900dd', + focus: '#6600aa', + selected: '#440088', + }, + }, + shape: { + borderRadius: 8, }, }); } diff --git a/packages/grafana-data/src/transformations/matchers/refIdMatcher.ts b/packages/grafana-data/src/transformations/matchers/refIdMatcher.ts index 629600bacfa..9ee6962be47 100644 --- a/packages/grafana-data/src/transformations/matchers/refIdMatcher.ts +++ b/packages/grafana-data/src/transformations/matchers/refIdMatcher.ts @@ -1,4 +1,4 @@ -import { stringToJsRegex } from '../../text/string'; +import { stringStartsAsRegEx, stringToJsRegex } from '../../text/string'; import { DataFrame } from '../../types/dataFrame'; import { FrameMatcherInfo } from '../../types/transformations'; @@ -12,9 +12,20 @@ const refIdMatcher: FrameMatcherInfo = { defaultOptions: 'A', get: (pattern: string) => { - const regex = stringToJsRegex(pattern); + let regex: RegExp | null = null; + + if (stringStartsAsRegEx(pattern)) { + try { + regex = stringToJsRegex(pattern); + } catch (error) { + if (error instanceof Error) { + console.warn(error.message); + } + } + } + return (frame: DataFrame) => { - return regex.test(frame.refId || ''); + return regex?.test(frame.refId || '') ?? frame.refId === pattern; }; }, diff --git a/packages/grafana-data/src/transformations/standardTransformersRegistry.ts b/packages/grafana-data/src/transformations/standardTransformersRegistry.ts index 87970a33994..4e5a11d318a 100644 --- a/packages/grafana-data/src/transformations/standardTransformersRegistry.ts +++ b/packages/grafana-data/src/transformations/standardTransformersRegistry.ts @@ -16,7 +16,7 @@ export interface TransformerUIProps { onChange: (options: T) => void; } -export interface TransformerRegistryItem extends RegistryItem { +export interface TransformerRegistryItem extends RegistryItem { /** * Object describing transformer configuration */ @@ -50,4 +50,4 @@ export enum TransformerCategory { * Registry of transformation options that can be driven by * stored configuration files. */ -export const standardTransformersRegistry = new Registry>(); +export const standardTransformersRegistry = new Registry(); diff --git a/packages/grafana-data/src/transformations/transformDataFrame.ts b/packages/grafana-data/src/transformations/transformDataFrame.ts index 810177aa1c2..477801b650e 100644 --- a/packages/grafana-data/src/transformations/transformDataFrame.ts +++ b/packages/grafana-data/src/transformations/transformDataFrame.ts @@ -43,11 +43,7 @@ function filterInput(data: DataFrame[], matcher?: FrameMatcher) { } const postProcessTransform = - ( - before: DataFrame[], - info: TransformerRegistryItem, - matcher?: FrameMatcher - ): MonoTypeOperatorFunction => + (before: DataFrame[], info: TransformerRegistryItem, matcher?: FrameMatcher): MonoTypeOperatorFunction => (source) => source.pipe( map((after) => { diff --git a/packages/grafana-data/src/transformations/transformers/calculateField.test.ts b/packages/grafana-data/src/transformations/transformers/calculateField.test.ts index 3091be651bd..59872c2019b 100644 --- a/packages/grafana-data/src/transformations/transformers/calculateField.test.ts +++ b/packages/grafana-data/src/transformations/transformers/calculateField.test.ts @@ -41,6 +41,15 @@ describe('calculateField transformer w/ timeseries', () => { mockTransformationsRegistry([calculateFieldTransformer]); }); + beforeEach(() => { + seriesA.fields.forEach((f) => { + delete f.state; + }); + seriesBC.fields.forEach((f) => { + delete f.state; + }); + }); + it('will filter and alias', async () => { const cfg = { id: DataTransformerID.calculateField, @@ -198,6 +207,56 @@ describe('calculateField transformer w/ timeseries', () => { }); }); + it('multiple queries + field + static number', async () => { + const cfg = { + id: DataTransformerID.calculateField, + options: { + mode: CalculateFieldMode.BinaryOperation, + binary: { + left: 'B', + operator: BinaryOperationID.Add, + right: '2', + }, + replaceFields: true, + }, + }; + + await expect(transformDataFrame([cfg], [seriesA, seriesBC])).toEmitValuesWith((received) => { + const data = received[0]; + expect(data).toMatchInlineSnapshot(` + [ + { + "fields": [ + { + "config": {}, + "name": "TheTime", + "state": { + "displayName": "TheTime", + "multipleFrames": true, + }, + "type": "time", + "values": [ + 1000, + 2000, + ], + }, + { + "config": {}, + "name": "B + 2", + "type": "number", + "values": [ + 4, + 202, + ], + }, + ], + "length": 2, + }, + ] + `); + }); + }); + it('all numbers + static number', async () => { const cfg = { id: DataTransformerID.calculateField, @@ -231,6 +290,85 @@ describe('calculateField transformer w/ timeseries', () => { }); }); + it('all numbers + static number (multi-frame, avoids join)', async () => { + const cfg = { + id: DataTransformerID.calculateField, + options: { + mode: CalculateFieldMode.BinaryOperation, + binary: { + left: { matcher: { id: FieldMatcherID.byType, options: FieldType.number } }, + operator: BinaryOperationID.Add, + right: '2', + }, + replaceFields: true, + }, + }; + + await expect(transformDataFrame([cfg], [seriesA, seriesBC])).toEmitValuesWith((received) => { + const data = received[0]; + + expect(data).toMatchInlineSnapshot(` + [ + { + "fields": [ + { + "config": {}, + "name": "TheTime", + "type": "time", + "values": [ + 1000, + 2000, + ], + }, + { + "config": {}, + "name": "A + 2", + "type": "number", + "values": [ + 3, + 102, + ], + }, + ], + "length": 2, + }, + { + "fields": [ + { + "config": {}, + "name": "TheTime", + "type": "time", + "values": [ + 1000, + 2000, + ], + }, + { + "config": {}, + "name": "B + 2", + "type": "number", + "values": [ + 4, + 202, + ], + }, + { + "config": {}, + "name": "C + 2", + "type": "number", + "values": [ + 5, + 302, + ], + }, + ], + "length": 2, + }, + ] + `); + }); + }); + it('all numbers + field number', async () => { const cfg = { id: DataTransformerID.calculateField, @@ -590,6 +728,35 @@ describe('calculateField transformer w/ timeseries', () => { }); }); + it('calculates fixed, trailing moving average with missing values', async () => { + const cfg = { + id: DataTransformerID.calculateField, + options: { + mode: CalculateFieldMode.WindowFunctions, + window: { + windowAlignment: WindowAlignment.Trailing, + field: 'x', + windowSize: 2, + windowSizeMode: WindowSizeMode.Fixed, + reducer: ReducerID.mean, + }, + }, + }; + + const series = toDataFrame({ + fields: [{ name: 'x', type: FieldType.number, values: [1, undefined, 3, 4, 5] }], + }); + + await expect(transformDataFrame([cfg], [series])).toEmitValuesWith((received) => { + const data = received[0][0]; + + //console.log(data.fields); + + expect(data.fields.length).toEqual(2); + expect(data.fields[1].values).toEqual([1, 1, 3, 3.5, 4.5]); + }); + }); + it('throws error when calculating moving average if window size < 1', async () => { const cfg = { id: DataTransformerID.calculateField, diff --git a/packages/grafana-data/src/transformations/transformers/calculateField.ts b/packages/grafana-data/src/transformations/transformers/calculateField.ts index 72a136c3980..80f41cc985e 100644 --- a/packages/grafana-data/src/transformations/transformers/calculateField.ts +++ b/packages/grafana-data/src/transformations/transformers/calculateField.ts @@ -128,10 +128,16 @@ export const calculateFieldTransformer: DataTransformerInfo (outerSource) => { - const operator = - options && options.timeSeries !== false - ? ensureColumnsTransformer.operator(null, ctx) - : noopTransformer.operator({}, ctx); + const mode = options.mode ?? CalculateFieldMode.ReduceRow; + + const asTimeSeries = options.timeSeries !== false; + const isBinaryFixed = mode === CalculateFieldMode.BinaryOperation && options.binary?.right.fixed != null; + + const needsSingleFrame = asTimeSeries && !isBinaryFixed; + + const operator = needsSingleFrame + ? ensureColumnsTransformer.operator(null, ctx) + : noopTransformer.operator({}, ctx); if (options.alias != null) { options.alias = ctx.interpolate(options.alias); @@ -140,7 +146,6 @@ export const calculateFieldTransformer: DataTransformerInfo { - const mode = options.mode ?? CalculateFieldMode.ReduceRow; let creator: ValuesCreator | undefined = undefined; switch (mode) { @@ -172,9 +177,10 @@ export const calculateFieldTransformer: DataTransformerInfo { + const outFrames = data.map((frame) => { const { timeField } = getTimeField(frame); const newFields: Field[] = []; + let didAddNewFields = false; if (timeField && options.timeSeries !== false) { newFields.push(timeField); } @@ -206,10 +212,18 @@ export const calculateFieldTransformer: DataTransformerInfo frame != null); } else { creator = getBinaryCreator(defaults(binaryOptions, defaultBinaryOptions), data, ctx); } @@ -242,10 +256,14 @@ export const calculateFieldTransformer: DataTransformerInfo { + const outFrames = data.map((frame) => { // delegate field creation to the specific function const values = creator!(frame); if (!values) { + // if nothing was done to frame, omit it when replacing fields + if (options.replaceFields) { + return undefined; + } return frame; } @@ -273,6 +291,7 @@ export const calculateFieldTransformer: DataTransformerInfo frame != null); }) ); }, @@ -338,8 +357,11 @@ function getTrailingWindowValues(frame: DataFrame, reducer: ReducerID, selectedF sum += currentValue; if (i > window - 1) { - sum -= selectedField.values[i - window]; - count--; + const value = selectedField.values[i - window]; + if (value != null) { + sum -= value; + count--; + } } } vals.push(count === 0 ? 0 : sum / count); diff --git a/packages/grafana-data/src/transformations/transformers/filterByRefId.test.ts b/packages/grafana-data/src/transformations/transformers/filterByRefId.test.ts index f3c88698a6c..74a6477529b 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByRefId.test.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByRefId.test.ts @@ -42,7 +42,7 @@ describe('filterByRefId transformer', () => { const cfg = { id: DataTransformerID.filterByRefId, options: { - include: 'A|B', + include: '/A|B/', }, }; diff --git a/packages/grafana-data/src/transformations/transformers/reduce.ts b/packages/grafana-data/src/transformations/transformers/reduce.ts index ad17991d3bd..8a28a35fa11 100644 --- a/packages/grafana-data/src/transformations/transformers/reduce.ts +++ b/packages/grafana-data/src/transformations/transformers/reduce.ts @@ -88,7 +88,7 @@ function reduceSeriesToRows( config: {}, }); - const labels: KeyValue = {}; + const labels: KeyValue = {}; if (labelsToFields) { for (const key of distinctLabels) { labels[key] = new Array(size); @@ -101,7 +101,7 @@ function reduceSeriesToRows( } } - const calcs: KeyValue = {}; + const calcs: KeyValue = {}; for (const info of calculators) { calcs[info.id] = new Array(size); fields.push({ diff --git a/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts b/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts index 54b80c62dec..0ef14d460b7 100644 --- a/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts +++ b/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts @@ -59,11 +59,11 @@ interface OptionsUIRegistryBuilderAPI< config: OptionEditorConfig ): this; - addBooleanSwitch?(config: OptionEditorConfig): this; + addBooleanSwitch?(config: OptionEditorConfig): this; - addUnitPicker?(config: OptionEditorConfig): this; + addUnitPicker?(config: OptionEditorConfig): this; - addColorPicker?(config: OptionEditorConfig): this; + addColorPicker?(config: OptionEditorConfig): this; /** * Enables custom editor definition diff --git a/packages/grafana-data/src/types/app.ts b/packages/grafana-data/src/types/app.ts index 6ada93ab099..3b33452bd03 100644 --- a/packages/grafana-data/src/types/app.ts +++ b/packages/grafana-data/src/types/app.ts @@ -82,7 +82,7 @@ export class AppPlugin extends GrafanaPlugin extends GrafanaPlugin { diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index fa3f8ae2268..663f65888fc 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -22,7 +22,6 @@ export interface FeatureToggles { ['live-service-web-worker']?: boolean; queryOverLive?: boolean; panelTitleSearch?: boolean; - publicDashboards?: boolean; publicDashboardsEmailSharing?: boolean; publicDashboardsScene?: boolean; lokiExperimentalStreaming?: boolean; @@ -35,7 +34,6 @@ export interface FeatureToggles { autoMigratePiechartPanel?: boolean; autoMigrateWorldmapPanel?: boolean; autoMigrateStatPanel?: boolean; - autoMigrateXYChartPanel?: boolean; disableAngular?: boolean; canvasPanelNesting?: boolean; vizActions?: boolean; @@ -52,6 +50,7 @@ export interface FeatureToggles { editPanelCSVDragAndDrop?: boolean; alertingNoNormalState?: boolean; logsContextDatasourceUi?: boolean; + lokiShardSplitting?: boolean; lokiQuerySplitting?: boolean; lokiQuerySplittingConfig?: boolean; individualCookiePreferences?: boolean; @@ -79,7 +78,6 @@ export interface FeatureToggles { pluginsDetailsRightPanel?: boolean; sqlDatasourceDatabaseSelection?: boolean; recordedQueriesMulti?: boolean; - vizAndWidgetSplit?: boolean; logsExploreTableVisualisation?: boolean; awsDatasourcesTempCredentials?: boolean; transformationsRedesign?: boolean; @@ -88,6 +86,7 @@ export interface FeatureToggles { metricsSummary?: boolean; datasourceAPIServers?: boolean; grafanaAPIServerWithExperimentalAPIs?: boolean; + provisioning?: boolean; grafanaAPIServerEnsureKubectlAccess?: boolean; featureToggleAdminPage?: boolean; awsAsyncQueryCaching?: boolean; @@ -140,6 +139,7 @@ export interface FeatureToggles { dashboardSceneForViewers?: boolean; dashboardSceneSolo?: boolean; dashboardScene?: boolean; + dashboardNewLayouts?: boolean; panelFilterVariable?: boolean; pdfTables?: boolean; ssoSettingsApi?: boolean; @@ -159,8 +159,10 @@ export interface FeatureToggles { jitterAlertRulesWithinGroups?: boolean; onPremToCloudMigrations?: boolean; onPremToCloudMigrationsAlerts?: boolean; + onPremToCloudMigrationsAuthApiMig?: boolean; alertingSaveStatePeriodic?: boolean; promQLScope?: boolean; + logQLScope?: boolean; sqlExpressions?: boolean; nodeGraphDotLayout?: boolean; groupToNestedTableTransformation?: boolean; @@ -189,6 +191,7 @@ export interface FeatureToggles { preserveDashboardStateWhenNavigating?: boolean; alertingCentralAlertHistory?: boolean; pluginProxyPreserveTrailingSlash?: boolean; + sqlQuerybuilderFunctionParameters?: boolean; azureMonitorPrometheusExemplars?: boolean; pinNavItems?: boolean; authZGRPCServer?: boolean; @@ -196,7 +199,8 @@ export interface FeatureToggles { ssoSettingsLDAP?: boolean; failWrongDSUID?: boolean; zanzana?: boolean; - passScopeToDashboardApi?: boolean; + reloadDashboardsOnParamsChange?: boolean; + enableScopesInMetricsExplore?: boolean; alertingApiServer?: boolean; cloudWatchRoundUpEndTime?: boolean; cloudwatchMetricInsightsCrossAccount?: boolean; @@ -221,4 +225,21 @@ export interface FeatureToggles { unifiedStorageSearch?: boolean; pluginsSriChecks?: boolean; unifiedStorageBigObjectsSupport?: boolean; + timeRangeProvider?: boolean; + prometheusUsesCombobox?: boolean; + userStorageAPI?: boolean; + azureMonitorDisableLogLimit?: boolean; + preinstallAutoUpdate?: boolean; + dashboardSchemaV2?: boolean; + playlistsWatcher?: boolean; + passwordlessMagicLinkAuthentication?: boolean; + exploreMetricsRelatedLogs?: boolean; + enableExtensionsAdminPage?: boolean; + zipkinBackendMigration?: boolean; + enableSCIM?: boolean; + crashDetection?: boolean; + jaegerBackendMigration?: boolean; + reportingUseRawTimeRange?: boolean; + alertingUIOptimizeReducer?: boolean; + alertingNotificationsStepMode?: boolean; } diff --git a/packages/grafana-data/src/types/logs.ts b/packages/grafana-data/src/types/logs.ts index 2d363a63f7e..86df7bde61f 100644 --- a/packages/grafana-data/src/types/logs.ts +++ b/packages/grafana-data/src/types/logs.ts @@ -235,7 +235,6 @@ export type LogsVolumeCustomMetaData = { * Data sources that support supplementary queries in Explore. * This will enable users to see additional data when running original queries. * Supported supplementary queries are defined in SupplementaryQueryType enum. - * @internal */ export interface DataSourceWithSupplementaryQueriesSupport { /** diff --git a/packages/grafana-data/src/types/panel.ts b/packages/grafana-data/src/types/panel.ts index 23afeb9b95b..7cfe9263ce3 100644 --- a/packages/grafana-data/src/types/panel.ts +++ b/packages/grafana-data/src/types/panel.ts @@ -169,7 +169,7 @@ export interface PanelMenuItem { type?: 'submenu' | 'divider' | 'group'; text: string; iconClassName?: IconName; - onClick?: (event: React.MouseEvent) => void; + onClick?: (event: React.MouseEvent) => void; shortcut?: string; href?: string; subMenu?: PanelMenuItem[]; diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index d9e53bb1a7b..fcbbecf5f41 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -50,7 +50,7 @@ type PluginExtensionConfigBase = { /** * A short description */ - description: string; + description?: string; }; export type PluginExtensionAddedComponentConfig = PluginExtensionConfigBase & { @@ -75,10 +75,7 @@ export type PluginExtensionAddedComponentConfig = PluginExtensionCon component: React.ComponentType; }; -export type PluginAddedLinksConfigureFunc = ( - context: Readonly | undefined, - helpers: PluginExtensionHelpers -) => +export type PluginAddedLinksConfigureFunc = (context: Readonly | undefined) => | Partial<{ title: string; description: string; @@ -145,27 +142,11 @@ export type PluginExtensionOpenModalOptions = { height?: string | number; }; -type PluginExtensionHelpers = { - /** Checks if the app plugin (that registers the extension) is currently visible (either in the main view or in the side view) - * @experimental - */ - isAppOpened: () => boolean; -}; - export type PluginExtensionEventHelpers = { context?: Readonly; // Opens a modal dialog and renders the provided React component inside it openModal: (options: PluginExtensionOpenModalOptions) => void; - - /** Opens the app plugin (that registers the extensions) in a side view - * @experimental - */ - openAppInSideview: (context?: unknown) => void; - /** Closes the side view for the app plugin (that registers the extensions) in case it was open - * @experimental - */ - closeAppInSideview: () => void; -} & PluginExtensionHelpers; +}; // Extension Points & Contexts // -------------------------------------------------------- diff --git a/packages/grafana-data/src/types/templateVars.ts b/packages/grafana-data/src/types/templateVars.ts index b8d0de9092a..e31319b9675 100644 --- a/packages/grafana-data/src/types/templateVars.ts +++ b/packages/grafana-data/src/types/templateVars.ts @@ -70,6 +70,7 @@ export interface AdHocVariableModel extends BaseVariableModel { * Static keys that override any dynamic keys from the datasource. */ defaultKeys?: MetricFindValue[]; + allowCustomValue?: boolean; } export interface GroupByVariableModel extends VariableWithOptions { @@ -127,6 +128,7 @@ export interface VariableWithMultiSupport extends VariableWithOptions { multi: boolean; includeAll: boolean; allValue?: string | null; + allowCustomValue?: boolean; } export interface VariableWithOptions extends BaseVariableModel { diff --git a/packages/grafana-data/src/utils/OptionsUIBuilders.ts b/packages/grafana-data/src/utils/OptionsUIBuilders.ts index c7d9bf83257..b88f633ca5e 100644 --- a/packages/grafana-data/src/utils/OptionsUIBuilders.ts +++ b/packages/grafana-data/src/utils/OptionsUIBuilders.ts @@ -30,7 +30,7 @@ import { PanelOptionsEditorConfig, PanelOptionsEditorItem } from '../types/panel */ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder< TOptions, - StandardEditorProps, + StandardEditorProps, FieldConfigPropertyItem > { addNumberInput(config: FieldConfigEditorConfig) { @@ -84,7 +84,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } - addRadio(config: FieldConfigEditorConfig) { + addRadio(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, id: config.path, @@ -97,7 +97,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } - addBooleanSwitch(config: FieldConfigEditorConfig) { + addBooleanSwitch(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, id: config.path, @@ -109,7 +109,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } - addColorPicker(config: FieldConfigEditorConfig) { + addColorPicker(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, id: config.path, @@ -121,9 +121,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } - addUnitPicker( - config: FieldConfigEditorConfig - ) { + addUnitPicker(config: FieldConfigEditorConfig) { return this.addCustomEditor({ ...config, id: config.path, @@ -135,7 +133,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } - addFieldNamePicker( + addFieldNamePicker( config: FieldConfigEditorConfig ): this { return this.addCustomEditor({ @@ -149,7 +147,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder }); } - addGenericEditor( + addGenericEditor( config: FieldConfigEditorConfig, // & any... i give up! editor: (props: StandardEditorProps) => JSX.Element ): this { @@ -168,7 +166,7 @@ export class FieldConfigEditorBuilder extends OptionsUIRegistryBuilder export interface NestedValueAccess { getValue: (path: string) => any; onChange: (path: string, value: any) => void; - getContext?: (parent: StandardEditorContext) => StandardEditorContext; + getContext?: (parent: StandardEditorContext) => StandardEditorContext; } export interface NestedPanelOptions { path: string; @@ -307,7 +305,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } - addBooleanSwitch(config: PanelOptionsEditorConfig) { + addBooleanSwitch(config: PanelOptionsEditorConfig) { return this.addCustomEditor({ ...config, id: config.path, @@ -315,7 +313,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } - addColorPicker(config: PanelOptionsEditorConfig): this { + addColorPicker(config: PanelOptionsEditorConfig): this { return this.addCustomEditor({ ...config, id: config.path, @@ -324,7 +322,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } - addTimeZonePicker(config: PanelOptionsEditorConfig): this { + addTimeZonePicker(config: PanelOptionsEditorConfig): this { return this.addCustomEditor({ ...config, id: config.path, @@ -333,7 +331,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } - addUnitPicker( + addUnitPicker( config: PanelOptionsEditorConfig ): this { return this.addCustomEditor({ @@ -343,7 +341,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } - addFieldNamePicker( + addFieldNamePicker( config: PanelOptionsEditorConfig ): this { return this.addCustomEditor({ @@ -353,7 +351,7 @@ export class PanelOptionsEditorBuilder extends OptionsUIRegistryBuilde }); } - addDashboardPicker( + addDashboardPicker( config: PanelOptionsEditorConfig ): this { return this.addCustomEditor({ diff --git a/packages/grafana-data/src/utils/dataLinks.ts b/packages/grafana-data/src/utils/dataLinks.ts index 7f4a71f6aeb..0ca5e4e64f1 100644 --- a/packages/grafana-data/src/utils/dataLinks.ts +++ b/packages/grafana-data/src/utils/dataLinks.ts @@ -74,7 +74,7 @@ export function mapInternalLinkToExplore(options: LinkToExploreOptions): LinkMod /** * Generates href for internal derived field link. */ -function generateInternalHref( +function generateInternalHref( datasourceUid: string, query: T, range?: TimeRange, diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index c423d970cbb..afaacce039d 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -40,7 +40,8 @@ }, "devDependencies": { "@rollup/plugin-node-resolve": "15.3.0", - "@types/node": "20.16.13", + "@types/node": "20.16.15", + "@types/semver": "7.5.8", "esbuild": "0.24.0", "rimraf": "6.0.1", "rollup": "^4.22.4", @@ -50,6 +51,7 @@ }, "dependencies": { "@grafana/tsconfig": "^2.0.0", + "semver": "7.6.3", "tslib": "2.7.0", "typescript": "5.5.4" } diff --git a/packages/grafana-e2e-selectors/src/index.ts b/packages/grafana-e2e-selectors/src/index.ts index 8c17fa0afdf..32b22b5bb52 100644 --- a/packages/grafana-e2e-selectors/src/index.ts +++ b/packages/grafana-e2e-selectors/src/index.ts @@ -5,3 +5,4 @@ */ export * from './selectors'; export * from './types'; +export { resolveSelectors } from './resolver'; diff --git a/packages/grafana-e2e-selectors/src/resolver.test.ts b/packages/grafana-e2e-selectors/src/resolver.test.ts new file mode 100644 index 00000000000..fe294800416 --- /dev/null +++ b/packages/grafana-e2e-selectors/src/resolver.test.ts @@ -0,0 +1,68 @@ +import { resolveSelectors } from './resolver'; +import { versionedComponents } from './selectors/components'; +import { versionedPages } from './selectors/pages'; + +describe('Resolver', () => { + it('should resolve latest when no version is provided', () => { + const pages = resolveSelectors(versionedPages); + const components = resolveSelectors(versionedComponents); + expect(components.CodeEditor.container).toBe('data-testid Code editor container'); + expect(components.TimePicker.calendar.closeButton).toBe('data-testid Close time range Calendar'); + expect(components.Panels.Panel.status('Test')).toBe('data-testid Panel status Test'); + expect(components.PanelEditor.OptionsPane.fieldInput('Test')).toBe( + 'data-testid Panel editor option pane field input Test' + ); + expect(pages.Alerting.AddAlertRule.url).toBe('/alerting/new/alerting'); + expect(pages.AddDashboard.Settings.Variables.Edit.url('test')).toBe( + '/dashboard/new?orgId=1&editview=templating&editIndex=test' + ); + }); + + it('should resolve right selector versions when an older grafana version is provided', () => { + const pages = resolveSelectors(versionedPages, '10.0.14'); + const components = resolveSelectors(versionedComponents, '10.0.14'); + expect(components.CodeEditor.container).toBe('Code editor container'); + expect(components.TimePicker.calendar.closeButton).toBe('Close time range Calendar'); + expect(components.Panels.Panel.status('')).toBe('Panel status'); + expect(components.PanelEditor.OptionsPane.fieldInput('Test')).toBe( + 'data-testid Panel editor option pane field input Test' + ); + expect(pages.Alerting.AddAlertRule.url).toBe('/alerting/new'); + expect(pages.AddDashboard.Settings.Variables.Edit.url('test')).toBe( + '/dashboard/new?orgId=1&editview=templating&editIndex=test' + ); + }); + + it('should resolve the most recent selector version when a newer grafana version is provided', () => { + const pages = resolveSelectors( + { + Alerting: { + AddAlertRule: { + url: { + '11.4.0': '/alerting/new', + '11.1.0': '/alerting/old', + '9.0.15': '/alerting/new/alerting', + }, + }, + }, + }, + '25.1.0' + ); + expect(pages.Alerting.AddAlertRule.url).toBe('/alerting/new'); + }); + + it('should throw error if an invalid semver range is used in versioned selector', () => { + expect(() => + resolveSelectors({ + Alerting: { + AddAlertRule: { + url: { + abc: '/alerting/new', + '9.0.15': '/alerting/new/alerting', + }, + }, + }, + }) + ).toThrow(new Error("Invalid semver version: 'abc'")); + }); +}); diff --git a/packages/grafana-e2e-selectors/src/resolver.ts b/packages/grafana-e2e-selectors/src/resolver.ts new file mode 100644 index 00000000000..a1cd359a41f --- /dev/null +++ b/packages/grafana-e2e-selectors/src/resolver.ts @@ -0,0 +1,81 @@ +import { gte, compare, valid } from 'semver'; + +import { + FunctionSelector, + Selectors, + SelectorsOf, + StringSelector, + VersionedSelectorGroup, + VersionedSelectors, + CssSelector, + UrlSelector, + FunctionSelectorTwoArgs, +} from './types'; + +/** + * Resolves selectors based on the Grafana version + */ +export function resolveSelectors( + versionedSelectors: T, + grafanaVersion = 'latest' +): SelectorsOf { + const version = grafanaVersion.replace(/\-.*/, ''); + + return resolveSelectorGroup(versionedSelectors, version); +} + +function resolveSelectorGroup(group: T, grafanaVersion: string): SelectorsOf { + const result: Selectors = {}; + + for (const [key, value] of Object.entries(group)) { + if (isVersionedSelectorGroup(value)) { + result[key] = resolveSelectorGroup(value, grafanaVersion); + } else { + assertIsSemverValid(value, key); + result[key] = resolveSelector(value, grafanaVersion); + } + } + + return result as SelectorsOf; +} + +function isVersionedSelectorGroup( + target: VersionedSelectors | VersionedSelectorGroup +): target is VersionedSelectorGroup { + if (typeof target === 'object') { + const [first] = Object.keys(target); + return !valid(first); + } + + return false; +} + +function resolveSelector( + versionedSelector: VersionedSelectors, + grafanaVersion: string +): StringSelector | FunctionSelector | FunctionSelectorTwoArgs | CssSelector | UrlSelector { + let versionToUse; + let versions = Object.keys(versionedSelector).sort(compare); + + if (grafanaVersion === 'latest') { + return versionedSelector[versions[versions.length - 1]]; + } + + for (const version of versions) { + if (gte(grafanaVersion, version)) { + versionToUse = version; + } + } + + if (!versionToUse) { + versionToUse = versions[versions.length - 1]; + } + + return versionedSelector[versionToUse]; +} + +function assertIsSemverValid(versionedSelector: VersionedSelectors, selectorName: string) { + if (!Object.keys(versionedSelector).every((version) => valid(version))) { + throw new Error(`Invalid semver version: '${selectorName}'`); + } +} diff --git a/packages/grafana-e2e-selectors/src/selectors/README.md b/packages/grafana-e2e-selectors/src/selectors/README.md new file mode 100644 index 00000000000..ef419dac1fa --- /dev/null +++ b/packages/grafana-e2e-selectors/src/selectors/README.md @@ -0,0 +1,39 @@ +# Versioned selectors + +The selectors defined in [pages.ts](./pages.ts) and [components.ts](./components.ts) are versioned. A versioned selector consists of an object literal where value is the selector context and key is the minimum Grafana version for which the value is valid. The versioning is important in plugin end-to-end testing, as it allows them to resolve the right selector values for a given Grafana version. + +```typescript +const components = { + PanelEditor: { + content: { + '11.1.0': 'data-testid Panel editor content', // resolved for Grafana >= 11.1.0 + '9.5.0': 'Panel editor content', // resolved for Grafana >= 9.5.0 <11.1.0 + }, + } + ... +} +``` + +A few things to keep in mind: + +- Strive to use e2e selector for all components in grafana/ui. +- Don't ever delete selectors. Even though a selector may not be used in the Grafana repository, it can still be used in external plugins. +- Only create new selector in case you're creating a new piece of UI. If you're changing an existing piece of UI that already has a selector defined, you need to keep using that selector. Otherwise you might break plugin end-to-end tests. +- Prefer using string selectors in favour of function selectors. The purpose of the selectors is to provide a canonical way to select elements. + `pages.Dashboard.url('ud73s9')` is fine. + `components.Panels.Panel.title('Panel header')` is bad. + +## How to change the value of an existing selector + +1. Find the versioned selector object in [pages.ts](./pages.ts) or [components.ts](./components.ts). +2. Add a new key representing the minimum Grafana version. The version you specify should correspond to the version of Grafana where your changes will be released. In most cases, you can check the version specified in package.json of the main branch (`git show main:package.json | awk -F'"' '/"version": ".+"/{ print $4; exit; }'`), but if you know in advance that your change will be backported you can specify the version of the release with the lowest version number. The version you specify should not include tags such as `-pre` or build number. +3. Add a value for the selector. Remember that the selector needs to be backwards compatible, so you cannot change its signature. + +## How to add a new selector + +> [!CAUTION] +> If you're changing a part of the UI that already has a selector defined, you should reuse the existing selector to avoid breaking end-to-end tests in plugins. + +1. Add a new versioned selector object under an existing or new group in [pages.ts](./pages.ts) or [components.ts](./components.ts). +2. Add a new key representing the minimum Grafana version. The version you specify should correspond to the version of Grafana where this change will be released. You can check the version specified in package.json of the main branch (`git show main:package.json | awk -F'"' '/"version": ".+"/{ print $4; exit; }'`). The version you specify should not include tags such as `-pre` or build number. +3. Add a value for the selector. Prefer using string selectors as function selectors such as `selectors.components.Panels.Panel.title('Header')` require context awareness which makes them hard to use in end-to-end tests. diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index ddb683bf22c..b52ee83e585 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -4,609 +4,1234 @@ // but you still might need to select it for testing, // in that case please add the attribute data-testid={selector} in the component and // prefix your selector string with 'data-testid' so that when create the selectors we know to search for it on the right attribute + +import { VersionedSelectorGroup } from '../types'; + +import { MIN_GRAFANA_VERSION } from './constants'; + /** * Selectors grouped/defined in Components - * - * @alpha */ -export const Components = { +export const versionedComponents = { RadioButton: { - container: 'data-testid radio-button', + container: { + '10.2.3': 'data-testid radio-button', + }, }, Breadcrumbs: { - breadcrumb: (title: string) => `data-testid ${title} breadcrumb`, + breadcrumb: { + '9.4.0': (title: string) => `data-testid ${title} breadcrumb`, + }, }, TimePicker: { - openButton: 'data-testid TimePicker Open Button', - overlayContent: 'data-testid TimePicker Overlay Content', - fromField: 'data-testid Time Range from field', - toField: 'data-testid Time Range to field', - applyTimeRange: 'data-testid TimePicker submit button', - copyTimeRange: 'data-testid TimePicker copy button', - pasteTimeRange: 'data-testid TimePicker paste button', + openButton: { + [MIN_GRAFANA_VERSION]: 'data-testid TimePicker Open Button', + }, + overlayContent: { + '10.2.3': 'data-testid TimePicker Overlay Content', + }, + fromField: { + '10.2.3': 'data-testid Time Range from field', + [MIN_GRAFANA_VERSION]: 'Time Range from field', + }, + toField: { + '10.2.3': 'data-testid Time Range to field', + [MIN_GRAFANA_VERSION]: 'Time Range to field', + }, + applyTimeRange: { + [MIN_GRAFANA_VERSION]: 'data-testid TimePicker submit button', + }, + copyTimeRange: { + '10.4.0': 'data-testid TimePicker copy button', + }, + pasteTimeRange: { + '10.4.0': 'data-testid TimePicker paste button', + }, calendar: { - label: 'data-testid Time Range calendar', - openButton: 'data-testid Open time range calendar', - closeButton: 'data-testid Close time range Calendar', + label: { + '10.2.3': 'data-testid Time Range calendar', + [MIN_GRAFANA_VERSION]: 'Time Range calendar', + }, + openButton: { + '10.2.3': 'data-testid Open time range calendar', + [MIN_GRAFANA_VERSION]: 'Open time range calendar', + }, + closeButton: { + '10.2.3': 'data-testid Close time range Calendar', + [MIN_GRAFANA_VERSION]: 'Close time range Calendar', + }, + }, + absoluteTimeRangeTitle: { + [MIN_GRAFANA_VERSION]: 'data-testid-absolute-time-range-narrow', }, - absoluteTimeRangeTitle: 'data-testid-absolute-time-range-narrow', }, DataSourcePermissions: { - form: () => 'form[name="addPermission"]', - roleType: 'Role to add new permission to', - rolePicker: 'Built-in role picker', - permissionLevel: 'Permission Level', + form: { '9.5.0': () => 'form[name="addPermission"]' }, + roleType: { + '9.5.0': 'Role to add new permission to', + }, + rolePicker: { + '9.5.0': 'Built-in role picker', + }, + permissionLevel: { + '9.5.0': 'Permission Level', + }, }, DateTimePicker: { - input: 'data-testid date-time-input', + input: { + '10.2.3': 'data-testid date-time-input', + }, }, DataSource: { TestData: { QueryTab: { - scenarioSelectContainer: 'Test Data Query scenario select container', - scenarioSelect: 'Test Data Query scenario select', - max: 'TestData max', - min: 'TestData min', - noise: 'TestData noise', - seriesCount: 'TestData series count', - spread: 'TestData spread', - startValue: 'TestData start value', - drop: 'TestData drop values', + scenarioSelectContainer: { + [MIN_GRAFANA_VERSION]: 'Test Data Query scenario select container', + }, + scenarioSelect: { + [MIN_GRAFANA_VERSION]: 'Test Data Query scenario select', + }, + max: { + [MIN_GRAFANA_VERSION]: 'TestData max', + }, + min: { + [MIN_GRAFANA_VERSION]: 'TestData min', + }, + noise: { + [MIN_GRAFANA_VERSION]: 'TestData noise', + }, + seriesCount: { + [MIN_GRAFANA_VERSION]: 'TestData series count', + }, + spread: { + [MIN_GRAFANA_VERSION]: 'TestData spread', + }, + startValue: { + [MIN_GRAFANA_VERSION]: 'TestData start value', + }, + drop: { + [MIN_GRAFANA_VERSION]: 'TestData drop values', + }, }, }, DataSourceHttpSettings: { - urlInput: 'data-testid Datasource HTTP settings url', + urlInput: { + '10.4.0': 'data-testid Datasource HTTP settings url', + [MIN_GRAFANA_VERSION]: 'Datasource HTTP settings url', + }, }, Jaeger: { - traceIDInput: 'Trace ID', + traceIDInput: { + [MIN_GRAFANA_VERSION]: 'Trace ID', + }, }, Prometheus: { configPage: { - connectionSettings: 'Data source connection URL', // aria-label in grafana experimental - manageAlerts: 'prometheus-alerts-manager', // id for switch component - scrapeInterval: 'data-testid scrape interval', - queryTimeout: 'data-testid query timeout', - defaultEditor: 'data-testid default editor', - disableMetricLookup: 'disable-metric-lookup', // id for switch component - prometheusType: 'data-testid prometheus type', - prometheusVersion: 'data-testid prometheus version', - cacheLevel: 'data-testid cache level', - incrementalQuerying: 'prometheus-incremental-querying', // id for switch component - queryOverlapWindow: 'data-testid query overlap window', - disableRecordingRules: 'disable-recording-rules', // id for switch component - customQueryParameters: 'data-testid custom query parameters', - httpMethod: 'data-testid http method', - exemplarsAddButton: 'data-testid Add exemplar config button', - internalLinkSwitch: 'data-testid Internal link switch', - codeModeMetricNamesSuggestionLimit: 'data-testid code mode metric names suggestion limit', + connectionSettings: { + [MIN_GRAFANA_VERSION]: 'Data source connection URL', // aria-label in grafana experimental + }, + manageAlerts: { + '10.4.0': 'prometheus-alerts-manager', // id for switch component + }, + scrapeInterval: { + '10.4.0': 'data-testid scrape interval', + }, + queryTimeout: { + '10.4.0': 'data-testid query timeout', + }, + defaultEditor: { + '10.4.0': 'data-testid default editor', + }, + disableMetricLookup: { + '10.4.0': 'disable-metric-lookup', // id for switch component + }, + prometheusType: { + '10.4.0': 'data-testid prometheus type', + }, + prometheusVersion: { + '10.4.0': 'data-testid prometheus version', + }, + cacheLevel: { + '10.4.0': 'data-testid cache level', + }, + incrementalQuerying: { + '10.4.0': 'prometheus-incremental-querying', // id for switch component + }, + queryOverlapWindow: { + '10.4.0': 'data-testid query overlap window', + }, + disableRecordingRules: { + '10.4.0': 'disable-recording-rules', // id for switch component + }, + customQueryParameters: { + '10.4.0': 'data-testid custom query parameters', + }, + httpMethod: { + '10.4.0': 'data-testid http method', + }, + exemplarsAddButton: { + '10.3.0': 'data-testid Add exemplar config button', + [MIN_GRAFANA_VERSION]: 'Add exemplar config button', + }, + internalLinkSwitch: { + '10.3.0': 'data-testid Internal link switch', + [MIN_GRAFANA_VERSION]: 'Internal link switch', + }, + codeModeMetricNamesSuggestionLimit: { + '11.1.0': 'data-testid code mode metric names suggestion limit', + }, }, queryEditor: { - // kickstart: '', see QueryBuilder queryPatterns below - explain: 'data-testid prometheus explain switch wrapper', - editorToggle: 'data-testid QueryEditorModeToggle', // wrapper for toggle - options: 'data-testid prometheus options', // wrapper for options group - legend: 'data-testid prometheus legend wrapper', // wrapper for multiple compomnents - format: 'data-testid prometheus format', - step: 'prometheus-step', // id for autosize component - type: 'data-testid prometheus type', //wrapper for radio button group - exemplars: 'prometheus-exemplars', // id for editor switch component + explain: { + '10.4.0': 'data-testid prometheus explain switch wrapper', + }, + editorToggle: { + '10.4.0': 'data-testid QueryEditorModeToggle', // wrapper for toggle + }, + options: { + '10.4.0': 'data-testid prometheus options', // wrapper for options group + }, + legend: { + '10.4.0': 'data-testid prometheus legend wrapper', // wrapper for multiple compomnents + }, + format: { + '10.4.0': 'data-testid prometheus format', + }, + step: { + '10.4.0': 'prometheus-step', // id for autosize component + }, + type: { + '10.4.0': 'data-testid prometheus type', //wrapper for radio button group + }, + exemplars: { + '10.4.0': 'prometheus-exemplars', // id for editor switch component + }, builder: { // see QueryBuilder below for commented selectors - // labelSelect: 'data-testid Select label', - // valueSelect: 'data-testid Select value', - // matchOperatorSelect: 'data-testid Select match operator', - metricSelect: 'data-testid metric select', - hints: 'data-testid prometheus hints', // wrapper for hints component - metricsExplorer: 'data-testid metrics explorer', - queryAdvisor: 'data-testid query advisor', + metricSelect: { + '10.4.0': 'data-testid metric select', + }, + hints: { + '10.4.0': 'data-testid prometheus hints', // wrapper for hints component + }, + metricsExplorer: { + '10.4.0': 'data-testid metrics explorer', + }, + queryAdvisor: { + '10.4.0': 'data-testid query advisor', + }, }, code: { - queryField: 'data-testid prometheus query field', - metricsCountInfo: 'data-testid metrics count disclaimer', + queryField: { + '10.4.0': 'data-testid prometheus query field', + }, + metricsCountInfo: { + '11.1.0': 'data-testid metrics count disclaimer', + }, metricsBrowser: { - openButton: 'data-testid open metrics browser', - selectMetric: 'data-testid select a metric', - seriesLimit: 'data-testid series limit', - metricList: 'data-testid metric list', - labelNamesFilter: 'data-testid label names filter', - labelValuesFilter: 'data-testid label values filter', - useQuery: 'data-testid use query', - useAsRateQuery: 'data-testid use as rate query', - validateSelector: 'data-testid validate selector', - clear: 'data-testid clear', + openButton: { + '10.4.0': 'data-testid open metrics browser', + }, + selectMetric: { + '10.4.0': 'data-testid select a metric', + }, + seriesLimit: { + '10.3.1': 'data-testid series limit', + }, + metricList: { + '10.4.0': 'data-testid metric list', + }, + labelNamesFilter: { + '10.4.0': 'data-testid label names filter', + }, + labelValuesFilter: { + '10.4.0': 'data-testid label values filter', + }, + useQuery: { + '10.4.0': 'data-testid use query', + }, + useAsRateQuery: { + '10.4.0': 'data-testid use as rate query', + }, + validateSelector: { + '10.4.0': 'data-testid validate selector', + }, + clear: { + '10.4.0': 'data-testid clear', + }, }, }, }, - exemplarMarker: 'data-testid Exemplar marker', + exemplarMarker: { + '10.3.0': 'data-testid Exemplar marker', + [MIN_GRAFANA_VERSION]: 'Exemplar marker', + }, variableQueryEditor: { - queryType: 'data-testid query type', + queryType: { + '10.4.0': 'data-testid query type', + }, labelnames: { - metricRegex: 'data-testid label names metric regex', + metricRegex: { + '10.4.0': 'data-testid label names metric regex', + }, }, labelValues: { - labelSelect: 'data-testid label values label select', - // metric select see queryEditor: builder for more context - // label select for metric filtering see queryEditor: builder for more context + labelSelect: { + '10.4.0': 'data-testid label values label select', + }, }, metricNames: { - metricRegex: 'data-testid metric names metric regex', + metricRegex: { + '10.4.0': 'data-testid metric names metric regex', + }, + }, + varQueryResult: { + '10.4.0': 'data-testid variable query result', + }, + seriesQuery: { + '10.4.0': 'data-testid prometheus series query', + }, + classicQuery: { + '10.4.0': 'data-testid prometheus classic query', }, - varQueryResult: 'data-testid variable query result', - seriesQuery: 'data-testid prometheus series query', - classicQuery: 'data-testid prometheus classic query', }, annotations: { - minStep: 'prometheus-annotation-min-step', // id for autosize input - title: 'data-testid prometheus annotation title', - tags: 'data-testid prometheus annotation tags', - text: 'data-testid prometheus annotation text', - seriesValueAsTimestamp: 'data-testid prometheus annotation series value as timestamp', + minStep: { + '10.4.0': 'prometheus-annotation-min-step', // id for autosize input + }, + title: { + '10.4.0': 'data-testid prometheus annotation title', + }, + tags: { + '10.4.0': 'data-testid prometheus annotation tags', + }, + text: { + '10.4.0': 'data-testid prometheus annotation text', + }, + seriesValueAsTimestamp: { + '10.4.0': 'data-testid prometheus annotation series value as timestamp', + }, }, }, }, Menu: { - MenuComponent: (title: string) => `${title} menu`, - MenuGroup: (title: string) => `${title} menu group`, - MenuItem: (title: string) => `${title} menu item`, + MenuComponent: { + [MIN_GRAFANA_VERSION]: (title: string) => `${title} menu`, + }, + MenuGroup: { + [MIN_GRAFANA_VERSION]: (title: string) => `${title} menu group`, + }, + MenuItem: { + [MIN_GRAFANA_VERSION]: (title: string) => `${title} menu item`, + }, SubMenu: { - container: 'data-testid SubMenu container', - icon: 'data-testid SubMenu icon', + container: { + '10.3.0': 'data-testid SubMenu container', + [MIN_GRAFANA_VERSION]: 'SubMenu container', + }, + icon: { + '10.3.0': 'data-testid SubMenu icon', + [MIN_GRAFANA_VERSION]: 'SubMenu icon', + }, }, }, Panels: { Panel: { - title: (title: string) => `data-testid Panel header ${title}`, - content: 'data-testid panel content', - headerItems: (item: string) => `data-testid Panel header item ${item}`, - menuItems: (item: string) => `data-testid Panel menu item ${item}`, - menu: (title: string) => `data-testid Panel menu ${title}`, - containerByTitle: (title: string) => `${title} panel`, - headerCornerInfo: (mode: string) => `Panel header ${mode}`, - status: (status: string) => `data-testid Panel status ${status}`, - loadingBar: () => `Panel loading bar`, + title: { + [MIN_GRAFANA_VERSION]: (title: string) => `data-testid Panel header ${title}`, + }, + content: { + '11.1.0': 'data-testid panel content', + }, + headerItems: { + '10.2.0': (item: string) => `data-testid Panel header item ${item}`, + }, + menuItems: { + '9.5.0': (item: string) => `data-testid Panel menu item ${item}`, + }, + menu: { + '9.5.0': (title: string) => `data-testid Panel menu ${title}`, + }, + containerByTitle: { + [MIN_GRAFANA_VERSION]: (title: string) => `${title} panel`, + }, + headerCornerInfo: { + [MIN_GRAFANA_VERSION]: (mode: string) => `Panel header ${mode}`, + }, + status: { + '10.2.0': (status: string) => `data-testid Panel status ${status}`, + [MIN_GRAFANA_VERSION]: (_: string) => 'Panel status', + }, + loadingBar: { + '10.0.0': () => `Panel loading bar`, + }, HoverWidget: { - container: 'data-testid hover-header-container', - dragIcon: 'data-testid drag-icon', + container: { + '10.1.0': 'data-testid hover-header-container', + [MIN_GRAFANA_VERSION]: 'hover-header-container', + }, + dragIcon: { + '10.0.0': 'data-testid drag-icon', + }, + }, + PanelDataErrorMessage: { + '10.4.0': 'data-testid Panel data error message', }, - PanelDataErrorMessage: 'data-testid Panel data error message', }, Visualization: { Graph: { - container: 'Graph container', + container: { + '9.5.0': 'Graph container', + }, VisualizationTab: { - legendSection: 'Legend section', + legendSection: { + [MIN_GRAFANA_VERSION]: 'Legend section', + }, }, Legend: { - legendItemAlias: (name: string) => `gpl alias ${name}`, - showLegendSwitch: 'gpl show legend', + legendItemAlias: { + [MIN_GRAFANA_VERSION]: (name: string) => `gpl alias ${name}`, + }, + showLegendSwitch: { + [MIN_GRAFANA_VERSION]: 'gpl show legend', + }, }, xAxis: { - labels: () => 'div.flot-x-axis > div.flot-tick-label', + labels: { + [MIN_GRAFANA_VERSION]: () => 'div.flot-x-axis > div.flot-tick-label', + }, }, }, BarGauge: { - /** - * @deprecated use valueV2 from Grafana 8.3 instead - */ - value: 'Bar gauge value', - valueV2: 'data-testid Bar gauge value', + valueV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Bar gauge value', + }, }, PieChart: { - svgSlice: 'data testid Pie Chart Slice', + svgSlice: { + '10.3.0': 'data testid Pie Chart Slice', + }, }, Text: { - container: () => '.markdown-html', + container: { [MIN_GRAFANA_VERSION]: () => '.markdown-html' }, }, Table: { - header: 'table header', - footer: 'table-footer', - body: 'data-testid table body', + header: { + [MIN_GRAFANA_VERSION]: 'table header', + }, + footer: { + [MIN_GRAFANA_VERSION]: 'table-footer', + }, + body: { + '10.2.0': 'data-testid table body', + }, }, }, }, VizLegend: { - seriesName: (name: string) => `data-testid VizLegend series ${name}`, + seriesName: { + '10.3.0': (name: string) => `data-testid VizLegend series ${name}`, + }, }, Drawer: { General: { - title: (title: string) => `Drawer title ${title}`, - expand: 'Drawer expand', - contract: 'Drawer contract', - close: 'data-testid Drawer close', - rcContentWrapper: () => '.rc-drawer-content-wrapper', - subtitle: 'data-testid drawer subtitle', + title: { + [MIN_GRAFANA_VERSION]: (title: string) => `Drawer title ${title}`, + }, + expand: { + [MIN_GRAFANA_VERSION]: 'Drawer expand', + }, + contract: { + [MIN_GRAFANA_VERSION]: 'Drawer contract', + }, + close: { + '10.3.0': 'data-testid Drawer close', + [MIN_GRAFANA_VERSION]: 'Drawer close', + }, + rcContentWrapper: { '9.4.0': () => '.rc-drawer-content-wrapper' }, + subtitle: { + '10.4.0': 'data-testid drawer subtitle', + }, }, DashboardSaveDrawer: { - saveButton: 'data-testid Save dashboard drawer button', - saveAsButton: 'data-testid Save as dashboard drawer button', - saveAsTitleInput: 'Save dashboard title field', + saveButton: { + '11.1.0': 'data-testid Save dashboard drawer button', + }, + saveAsButton: { + '11.1.0': 'data-testid Save as dashboard drawer button', + }, + saveAsTitleInput: { + '11.1.0': 'Save dashboard title field', + }, }, }, PanelEditor: { General: { - content: 'data-testid Panel editor content', + content: { + '11.1.0': 'data-testid Panel editor content', + [MIN_GRAFANA_VERSION]: 'Panel editor content', + }, }, OptionsPane: { - content: 'data-testid Panel editor option pane content', - select: 'Panel editor option pane select', - fieldLabel: (type: string) => `${type} field property editor`, - fieldInput: (title: string) => `data-testid Panel editor option pane field input ${title}`, + content: { + '11.1.0': 'data-testid Panel editor option pane content', + [MIN_GRAFANA_VERSION]: 'Panel editor option pane content', + }, + select: { + [MIN_GRAFANA_VERSION]: 'Panel editor option pane select', + }, + fieldLabel: { + [MIN_GRAFANA_VERSION]: (type: string) => `${type} field property editor`, + }, + fieldInput: { + '11.0.0': (title: string) => `data-testid Panel editor option pane field input ${title}`, + }, }, - // not sure about the naming *DataPane* DataPane: { - content: 'data-testid Panel editor data pane content', + content: { + '11.1.0': 'data-testid Panel editor data pane content', + [MIN_GRAFANA_VERSION]: 'Panel editor data pane content', + }, + }, + applyButton: { + '9.2.0': 'data-testid Apply changes and go back to dashboard', + }, + toggleVizPicker: { + '10.0.0': 'data-testid toggle-viz-picker', + [MIN_GRAFANA_VERSION]: 'toggle-viz-picker', + }, + toggleVizOptions: { + '10.1.0': 'data-testid toggle-viz-options', + [MIN_GRAFANA_VERSION]: 'toggle-viz-options', + }, + toggleTableView: { + '11.1.0': 'data-testid toggle-table-view', + [MIN_GRAFANA_VERSION]: 'toggle-table-view', }, - applyButton: 'data-testid Apply changes and go back to dashboard', - toggleVizPicker: 'data-testid toggle-viz-picker', - toggleVizOptions: 'data-testid toggle-viz-options', - toggleTableView: 'data-testid toggle-table-view', // [Geomap] Map controls - showZoomField: 'Map controls Show zoom control field property editor', - showAttributionField: 'Map controls Show attribution field property editor', - showScaleField: 'Map controls Show scale field property editor', - showMeasureField: 'Map controls Show measure tools field property editor', - showDebugField: 'Map controls Show debug field property editor', + showZoomField: { + '10.2.0': 'Map controls Show zoom control field property editor', + }, + showAttributionField: { + '10.2.0': 'Map controls Show attribution field property editor', + }, + showScaleField: { + '10.2.0': 'Map controls Show scale field property editor', + }, + showMeasureField: { + '10.2.0': 'Map controls Show measure tools field property editor', + }, + showDebugField: { + '10.2.0': 'Map controls Show debug field property editor', + }, - measureButton: 'show measure tools', + measureButton: { + '9.2.0': 'show measure tools', + }, }, PanelInspector: { Data: { - content: 'Panel inspector Data content', + content: { + [MIN_GRAFANA_VERSION]: 'Panel inspector Data content', + }, }, Stats: { - content: 'Panel inspector Stats content', + content: { + [MIN_GRAFANA_VERSION]: 'Panel inspector Stats content', + }, }, Json: { - content: 'data-testid Panel inspector Json content', + content: { + '11.1.0': 'data-testid Panel inspector Json content', + [MIN_GRAFANA_VERSION]: 'Panel inspector Json content', + }, }, Query: { - content: 'Panel inspector Query content', - refreshButton: 'Panel inspector Query refresh button', - jsonObjectKeys: () => '.json-formatter-key', + content: { + [MIN_GRAFANA_VERSION]: 'Panel inspector Query content', + }, + refreshButton: { + [MIN_GRAFANA_VERSION]: 'Panel inspector Query refresh button', + }, + jsonObjectKeys: { + [MIN_GRAFANA_VERSION]: () => '.json-formatter-key', + }, }, }, Tab: { - title: (title: string) => `data-testid Tab ${title}`, - active: () => '[class*="-activeTabStyle"]', + title: { + '11.2.0': (title: string) => `data-testid Tab ${title}`, + }, + active: { [MIN_GRAFANA_VERSION]: () => '[class*="-activeTabStyle"]' }, }, RefreshPicker: { - /** - * @deprecated use runButtonV2 from Grafana 8.3 instead - */ - runButton: 'RefreshPicker run button', - /** - * @deprecated use intervalButtonV2 from Grafana 8.3 instead - */ - intervalButton: 'RefreshPicker interval button', - runButtonV2: 'data-testid RefreshPicker run button', - intervalButtonV2: 'data-testid RefreshPicker interval button', + runButtonV2: { + [MIN_GRAFANA_VERSION]: 'data-testid RefreshPicker run button', + }, + intervalButtonV2: { + [MIN_GRAFANA_VERSION]: 'data-testid RefreshPicker interval button', + }, }, QueryTab: { - content: 'Query editor tab content', - queryInspectorButton: 'Query inspector button', - queryHistoryButton: 'data-testid query-history-button', - addQuery: 'data-testid query-tab-add-query', - queryGroupTopSection: 'data-testid query group top section', - addExpression: 'data-testid query-tab-add-expression', + content: { + [MIN_GRAFANA_VERSION]: 'Query editor tab content', + }, + queryInspectorButton: { + [MIN_GRAFANA_VERSION]: 'Query inspector button', + }, + queryHistoryButton: { + '10.2.0': 'data-testid query-history-button', + [MIN_GRAFANA_VERSION]: 'query-history-button', + }, + addQuery: { + '10.2.0': 'data-testid query-tab-add-query', + [MIN_GRAFANA_VERSION]: 'Query editor add query button', + }, + queryGroupTopSection: { + '11.2.0': 'data-testid query group top section', + }, + addExpression: { + '11.2.0': 'data-testid query-tab-add-expression', + }, }, QueryHistory: { - queryText: 'Query text', + queryText: { + '9.0.0': 'Query text', + }, }, QueryEditorRows: { - rows: 'Query editor row', + rows: { + [MIN_GRAFANA_VERSION]: 'Query editor row', + }, }, QueryEditorRow: { - actionButton: (title: string) => `data-testid ${title}`, - title: (refId: string) => `Query editor row title ${refId}`, - container: (refId: string) => `Query editor row ${refId}`, + actionButton: { + '10.4.0': (title: string) => `data-testid ${title}`, + }, + title: { + [MIN_GRAFANA_VERSION]: (refId: string) => `Query editor row title ${refId}`, + }, + container: { + [MIN_GRAFANA_VERSION]: (refId: string) => `Query editor row ${refId}`, + }, }, AlertTab: { - content: 'data-testid Alert editor tab content', + content: { + '10.2.3': 'data-testid Alert editor tab content', + [MIN_GRAFANA_VERSION]: 'Alert editor tab content', + }, }, AlertRules: { - groupToggle: 'data-testid group-collapse-toggle', - toggle: 'data-testid collapse-toggle', - expandedContent: 'data-testid expanded-content', - previewButton: 'data-testid alert-rule preview-button', - ruleNameField: 'data-testid alert-rule name-field', - newFolderButton: 'data-testid alert-rule new-folder-button', - newFolderNameField: 'data-testid alert-rule name-folder-name-field', - newFolderNameCreateButton: 'data-testid alert-rule name-folder-name-create-button', - newEvaluationGroupButton: 'data-testid alert-rule new-evaluation-group-button', - newEvaluationGroupName: 'data-testid alert-rule new-evaluation-group-name', - newEvaluationGroupInterval: 'data-testid alert-rule new-evaluation-group-interval', - newEvaluationGroupCreate: 'data-testid alert-rule new-evaluation-group-create-button', + groupToggle: { + '11.0.0': 'data-testid group-collapse-toggle', + }, + toggle: { + '11.0.0': 'data-testid collapse-toggle', + }, + expandedContent: { + '11.0.0': 'data-testid expanded-content', + }, + previewButton: { + '11.1.0': 'data-testid alert-rule preview-button', + }, + ruleNameField: { + '11.1.0': 'data-testid alert-rule name-field', + }, + newFolderButton: { + '11.1.0': 'data-testid alert-rule new-folder-button', + }, + newFolderNameField: { + '11.1.0': 'data-testid alert-rule name-folder-name-field', + }, + newFolderNameCreateButton: { + '11.1.0': 'data-testid alert-rule name-folder-name-create-button', + }, + newEvaluationGroupButton: { + '11.1.0': 'data-testid alert-rule new-evaluation-group-button', + }, + newEvaluationGroupName: { + '11.1.0': 'data-testid alert-rule new-evaluation-group-name', + }, + newEvaluationGroupInterval: { + '11.1.0': 'data-testid alert-rule new-evaluation-group-interval', + }, + newEvaluationGroupCreate: { + '11.1.0': 'data-testid alert-rule new-evaluation-group-create-button', + }, }, Alert: { - /** - * @deprecated use alertV2 from Grafana 8.3 instead - */ - alert: (severity: string) => `Alert ${severity}`, - alertV2: (severity: string) => `data-testid Alert ${severity}`, + alertV2: { + [MIN_GRAFANA_VERSION]: (severity: string) => `data-testid Alert ${severity}`, + }, }, TransformTab: { - content: 'data-testid Transform editor tab content', - newTransform: (name: string) => `data-testid New transform ${name}`, - transformationEditor: (name: string) => `data-testid Transformation editor ${name}`, - transformationEditorDebugger: (name: string) => `data-testid Transformation editor debugger ${name}`, + content: { + '10.1.0': 'data-testid Transform editor tab content', + [MIN_GRAFANA_VERSION]: 'Transform editor tab content', + }, + newTransform: { + '10.1.0': (name: string) => `data-testid New transform ${name}`, + }, + transformationEditor: { + '10.1.0': (name: string) => `data-testid Transformation editor ${name}`, + }, + transformationEditorDebugger: { + '10.1.0': (name: string) => `data-testid Transformation editor debugger ${name}`, + }, }, Transforms: { - card: (name: string) => `data-testid New transform ${name}`, - disableTransformationButton: 'data-testid Disable transformation button', + card: { + '10.1.0': (name: string) => `data-testid New transform ${name}`, + }, + disableTransformationButton: { + '10.4.0': 'data-testid Disable transformation button', + }, Reduce: { - modeLabel: 'data-testid Transform mode label', - calculationsLabel: 'data-testid Transform calculations label', + modeLabel: { + '10.2.3': 'data-testid Transform mode label', + [MIN_GRAFANA_VERSION]: 'Transform mode label', + }, + calculationsLabel: { + '10.2.3': 'data-testid Transform calculations label', + [MIN_GRAFANA_VERSION]: 'Transform calculations label', + }, }, SpatialOperations: { - actionLabel: 'root Action field property editor', - locationLabel: 'root Location Mode field property editor', + actionLabel: { + '9.1.2': 'root Action field property editor', + }, + locationLabel: { + '10.2.0': 'root Location Mode field property editor', + }, location: { - autoOption: 'Auto location option', + autoOption: { + '9.1.2': 'Auto location option', + }, coords: { - option: 'Coords location option', - latitudeFieldLabel: 'root Latitude field field property editor', - longitudeFieldLabel: 'root Longitude field field property editor', + option: { + '9.1.2': 'Coords location option', + }, + latitudeFieldLabel: { + '9.1.2': 'root Latitude field field property editor', + }, + longitudeFieldLabel: { + '9.1.2': 'root Longitude field field property editor', + }, }, geohash: { - option: 'Geohash location option', - geohashFieldLabel: 'root Geohash field field property editor', + option: { + '9.1.2': 'Geohash location option', + }, + geohashFieldLabel: { + '9.1.2': 'root Geohash field field property editor', + }, }, lookup: { - option: 'Lookup location option', - lookupFieldLabel: 'root Lookup field field property editor', - gazetteerFieldLabel: 'root Gazetteer field property editor', + option: { + '9.1.2': 'Lookup location option', + }, + lookupFieldLabel: { + '9.1.2': 'root Lookup field field property editor', + }, + gazetteerFieldLabel: { + '9.1.2': 'root Gazetteer field property editor', + }, }, }, }, - searchInput: 'data-testid search transformations', - noTransformationsMessage: 'data-testid no transformations message', - addTransformationButton: 'data-testid add transformation button', - removeAllTransformationsButton: 'data-testid remove all transformations button', + searchInput: { + '10.2.3': 'data-testid search transformations', + [MIN_GRAFANA_VERSION]: 'search transformations', + }, + noTransformationsMessage: { + '10.2.3': 'data-testid no transformations message', + }, + addTransformationButton: { + '10.1.0': 'data-testid add transformation button', + [MIN_GRAFANA_VERSION]: 'add transformation button', + }, + removeAllTransformationsButton: { + '10.4.0': 'data-testid remove all transformations button', + }, }, NavBar: { Configuration: { - button: 'Configuration', + button: { + '9.5.0': 'Configuration', + }, }, Toggle: { - button: 'data-testid Toggle menu', + button: { + '10.2.3': 'data-testid Toggle menu', + [MIN_GRAFANA_VERSION]: 'Toggle menu', + }, }, Reporting: { - button: 'Reporting', + button: { + '9.5.0': 'Reporting', + }, }, }, NavMenu: { - Menu: 'data-testid navigation mega-menu', - item: 'data-testid Nav menu item', + Menu: { + '10.2.3': 'data-testid navigation mega-menu', + }, + item: { + '9.5.0': 'data-testid Nav menu item', + }, }, NavToolbar: { - container: 'data-testid Nav toolbar', - commandPaletteTrigger: 'data-testid Command palette trigger', - shareDashboard: 'data-testid Share dashboard', - markAsFavorite: 'data-testid Mark as favorite', + container: { + '9.4.0': 'data-testid Nav toolbar', + }, + commandPaletteTrigger: { + '11.4.0': 'data-testid Command palette trigger', + }, + shareDashboard: { + '11.1.0': 'data-testid Share dashboard', + }, + markAsFavorite: { + '11.1.0': 'data-testid Mark as favorite', + }, editDashboard: { - editButton: 'data-testid Edit dashboard button', - saveButton: 'data-testid Save dashboard button', - exitButton: 'data-testid Exit edit mode button', - settingsButton: 'data-testid Dashboard settings', - addRowButton: 'data-testid Add row button', - addLibraryPanelButton: 'data-testid Add a panel from the panel library button', - addVisualizationButton: 'data-testid Add new visualization menu item', - pastePanelButton: 'data-testid Paste panel button', - discardChangesButton: 'data-testid Discard changes button', - discardLibraryPanelButton: 'data-testid Discard library panel button', - unlinkLibraryPanelButton: 'data-testid Unlink library panel button', - saveLibraryPanelButton: 'data-testid Save library panel button', - backToDashboardButton: 'data-testid Back to dashboard button', + editButton: { + '11.1.0': 'data-testid Edit dashboard button', + }, + saveButton: { + '11.1.0': 'data-testid Save dashboard button', + }, + exitButton: { + '11.1.0': 'data-testid Exit edit mode button', + }, + settingsButton: { + '11.1.0': 'data-testid Dashboard settings', + }, + addRowButton: { + '11.1.0': 'data-testid Add row button', + }, + addLibraryPanelButton: { + '11.1.0': 'data-testid Add a panel from the panel library button', + }, + addVisualizationButton: { + '11.1.0': 'data-testid Add new visualization menu item', + }, + pastePanelButton: { + '11.1.0': 'data-testid Paste panel button', + }, + discardChangesButton: { + '11.1.0': 'data-testid Discard changes button', + }, + discardLibraryPanelButton: { + '11.1.0': 'data-testid Discard library panel button', + }, + unlinkLibraryPanelButton: { + '11.1.0': 'data-testid Unlink library panel button', + }, + saveLibraryPanelButton: { + '11.1.0': 'data-testid Save library panel button', + }, + backToDashboardButton: { + '11.1.0': 'data-testid Back to dashboard button', + }, }, }, PageToolbar: { - container: () => '.page-toolbar', - item: (tooltip: string) => `${tooltip}`, - itemButton: (title: string) => `data-testid ${title}`, + container: { [MIN_GRAFANA_VERSION]: () => '.page-toolbar' }, + item: { + [MIN_GRAFANA_VERSION]: (tooltip: string) => `${tooltip}`, + }, + itemButton: { + '9.5.0': (title: string) => `data-testid ${title}`, + }, }, QueryEditorToolbarItem: { - button: (title: string) => `QueryEditor toolbar item button ${title}`, + button: { + [MIN_GRAFANA_VERSION]: (title: string) => `QueryEditor toolbar item button ${title}`, + }, }, BackButton: { - backArrow: 'data-testid Go Back', + backArrow: { + '10.3.0': 'data-testid Go Back', + [MIN_GRAFANA_VERSION]: 'Go Back', + }, }, OptionsGroup: { - group: (title?: string) => (title ? `data-testid Options group ${title}` : 'data-testid Options group'), - toggle: (title?: string) => - title ? `data-testid Options group ${title} toggle` : 'data-testid Options group toggle', + group: { + '11.1.0': (title?: string) => (title ? `data-testid Options group ${title}` : 'data-testid Options group'), + [MIN_GRAFANA_VERSION]: (title?: string) => (title ? `Options group ${title}` : 'Options group'), + }, + toggle: { + '11.1.0': (title?: string) => + title ? `data-testid Options group ${title} toggle` : 'data-testid Options group toggle', + [MIN_GRAFANA_VERSION]: (title?: string) => (title ? `Options group ${title} toggle` : 'Options group toggle'), + }, }, PluginVisualization: { - item: (title: string) => `Plugin visualization item ${title}`, - current: () => '[class*="-currentVisualizationItem"]', + item: { + [MIN_GRAFANA_VERSION]: (title: string) => `Plugin visualization item ${title}`, + }, + current: { + [MIN_GRAFANA_VERSION]: () => '[class*="-currentVisualizationItem"]', + }, }, Select: { - option: 'data-testid Select option', - toggleAllOptions: 'data-testid toggle all options', - input: () => 'input[id*="time-options-input"]', - singleValue: () => 'div[class*="-singleValue"]', + menu: { + '11.4.0': 'data-testid Select menu', + }, + option: { + '11.1.0': 'data-testid Select option', + [MIN_GRAFANA_VERSION]: 'Select option', + }, + toggleAllOptions: { + '11.3.0': 'data-testid toggle all options', + }, + input: { + [MIN_GRAFANA_VERSION]: () => 'input[id*="time-options-input"]', + }, + singleValue: { + [MIN_GRAFANA_VERSION]: () => 'div[class*="-singleValue"]', + }, }, FieldConfigEditor: { - content: 'Field config editor content', + content: { + [MIN_GRAFANA_VERSION]: 'Field config editor content', + }, }, OverridesConfigEditor: { - content: 'Field overrides editor content', + content: { + [MIN_GRAFANA_VERSION]: 'Field overrides editor content', + }, }, FolderPicker: { - /** - * @deprecated use containerV2 from Grafana 8.3 instead - */ - container: 'Folder picker select container', - containerV2: 'data-testid Folder picker select container', - input: 'data-testid folder-picker-input', + containerV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Folder picker select container', + }, + input: { + '10.4.0': 'data-testid folder-picker-input', + }, }, ReadonlyFolderPicker: { - container: 'data-testid Readonly folder picker select container', + container: { + [MIN_GRAFANA_VERSION]: 'data-testid Readonly folder picker select container', + }, }, DataSourcePicker: { - container: 'data-testid Data source picker select container', - /** - * @deprecated use inputV2 instead - */ - input: () => 'input[id="data-source-picker"]', - inputV2: 'data-testid Select a data source', - dataSourceList: 'data-testid Data source list dropdown', + container: { + '10.0.0': 'data-testid Data source picker select container', + [MIN_GRAFANA_VERSION]: 'Data source picker select container', + }, + inputV2: { + '10.1.0': 'data-testid Select a data source', + [MIN_GRAFANA_VERSION]: 'Select a data source', + }, + dataSourceList: { + '10.4.0': 'data-testid Data source list dropdown', + }, advancedModal: { - dataSourceList: 'data-testid Data source list', - builtInDataSourceList: 'data-testid Built in data source list', + dataSourceList: { + '10.4.0': 'data-testid Data source list', + }, + builtInDataSourceList: { + '10.4.0': 'data-testid Built in data source list', + }, }, }, TimeZonePicker: { - /** - * @deprecated use TimeZonePicker.containerV2 from Grafana 8.3 instead - */ - container: 'Time zone picker select container', - containerV2: 'data-testid Time zone picker select container', - changeTimeSettingsButton: 'data-testid Time zone picker Change time settings button', + containerV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Time zone picker select container', + }, + changeTimeSettingsButton: { + '11.0.0': 'data-testid Time zone picker Change time settings button', + }, }, WeekStartPicker: { - /** - * @deprecated use WeekStartPicker.containerV2 from Grafana 8.3 instead - */ - container: 'Choose starting day of the week', - containerV2: 'data-testid Choose starting day of the week', - placeholder: 'Choose starting day of the week', + containerV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Choose starting day of the week', + }, + placeholder: { + [MIN_GRAFANA_VERSION]: 'Choose starting day of the week', + }, }, TraceViewer: { - spanBar: 'data-testid SpanBar--wrapper', + spanBar: { + '9.0.0': 'data-testid SpanBar--wrapper', + }, + }, + QueryField: { + container: { + '10.3.0': 'data-testid Query field', + [MIN_GRAFANA_VERSION]: 'Query field', + }, }, - QueryField: { container: 'data-testid Query field' }, QueryBuilder: { - queryPatterns: 'data-testid Query patterns', - labelSelect: 'data-testid Select label', - inputSelect: 'data-testid Select label-input', - valueSelect: 'data-testid Select value', - matchOperatorSelect: 'data-testid Select match operator', + queryPatterns: { + '10.3.0': 'data-testid Query patterns', + [MIN_GRAFANA_VERSION]: 'Query patterns', + }, + labelSelect: { + '10.3.0': 'data-testid Select label', + [MIN_GRAFANA_VERSION]: 'Select label', + }, + inputSelect: { + '11.1.0': 'data-testid Select label-input', + }, + valueSelect: { + '10.3.0': 'data-testid Select value', + [MIN_GRAFANA_VERSION]: 'Select value', + }, + matchOperatorSelect: { + '10.3.0': 'data-testid Select match operator', + [MIN_GRAFANA_VERSION]: 'Select match operator', + }, }, ValuePicker: { - button: (name: string) => `data-testid Value picker button ${name}`, - select: (name: string) => `data-testid Value picker select ${name}`, + button: { + '10.3.0': (name: string) => `data-testid Value picker button ${name}`, + }, + select: { + '10.3.0': (name: string) => `data-testid Value picker select ${name}`, + }, }, Search: { - /** - * @deprecated use sectionV2 from Grafana 8.3 instead - */ - section: 'Search section', - sectionV2: 'data-testid Search section', - /** - * @deprecated use itemsV2 from Grafana 8.3 instead - */ - items: 'Search items', - itemsV2: 'data-testid Search items', - cards: 'data-testid Search cards', - collapseFolder: (sectionId: string) => `data-testid Collapse folder ${sectionId}`, - expandFolder: (sectionId: string) => `data-testid Expand folder ${sectionId}`, - dashboardItem: (item: string) => `${Components.Search.dashboardItems} ${item}`, - dashboardCard: (item: string) => `data-testid Search card ${item}`, - folderHeader: (folderName: string) => `data-testid Folder header ${folderName}`, - folderContent: (folderName: string) => `data-testid Folder content ${folderName}`, - dashboardItems: 'data-testid Dashboard search item', + sectionV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Search section', + }, + itemsV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Search items', + }, + cards: { + [MIN_GRAFANA_VERSION]: 'data-testid Search cards', + }, + collapseFolder: { + [MIN_GRAFANA_VERSION]: (sectionId: string) => `data-testid Collapse folder ${sectionId}`, + }, + expandFolder: { + [MIN_GRAFANA_VERSION]: (sectionId: string) => `data-testid Expand folder ${sectionId}`, + }, + dashboardItem: { + [MIN_GRAFANA_VERSION]: (item: string) => `data-testid Dashboard search item ${item}`, + }, + dashboardCard: { + [MIN_GRAFANA_VERSION]: (item: string) => `data-testid Search card ${item}`, + }, + folderHeader: { + '9.3.0': (folderName: string) => `data-testid Folder header ${folderName}`, + }, + folderContent: { + '9.3.0': (folderName: string) => `data-testid Folder content ${folderName}`, + }, + dashboardItems: { + [MIN_GRAFANA_VERSION]: 'data-testid Dashboard search item', + }, }, DashboardLinks: { - container: 'data-testid Dashboard link container', - dropDown: 'data-testid Dashboard link dropdown', - link: 'data-testid Dashboard link', + container: { + [MIN_GRAFANA_VERSION]: 'data-testid Dashboard link container', + }, + dropDown: { + [MIN_GRAFANA_VERSION]: 'data-testid Dashboard link dropdown', + }, + link: { + [MIN_GRAFANA_VERSION]: 'data-testid Dashboard link', + }, }, LoadingIndicator: { - icon: 'data-testid Loading indicator', + icon: { + '10.4.0': 'data-testid Loading indicator', + [MIN_GRAFANA_VERSION]: 'Loading indicator', + }, }, CallToActionCard: { - /** - * @deprecated use buttonV2 from Grafana 8.3 instead - */ - button: (name: string) => `Call to action button ${name}`, - buttonV2: (name: string) => `data-testid Call to action button ${name}`, + buttonV2: { + [MIN_GRAFANA_VERSION]: (name: string) => `data-testid Call to action button ${name}`, + }, }, DataLinksContextMenu: { - singleLink: 'data-testid Data link', + singleLink: { + '10.3.0': 'data-testid Data link', + [MIN_GRAFANA_VERSION]: 'Data link', + }, }, CodeEditor: { - container: 'data-testid Code editor container', + container: { + '10.2.3': 'data-testid Code editor container', + [MIN_GRAFANA_VERSION]: 'Code editor container', + }, }, ReactMonacoEditor: { - editorLazy: 'data-testid ReactMonacoEditor editorLazy', + editorLazy: { + '11.1.0': 'data-testid ReactMonacoEditor editorLazy', + }, }, DashboardImportPage: { - textarea: 'data-testid-import-dashboard-textarea', - submit: 'data-testid-load-dashboard', + textarea: { + [MIN_GRAFANA_VERSION]: 'data-testid-import-dashboard-textarea', + }, + submit: { + [MIN_GRAFANA_VERSION]: 'data-testid-load-dashboard', + }, }, ImportDashboardForm: { - name: 'data-testid-import-dashboard-title', - submit: 'data-testid-import-dashboard-submit', + name: { + [MIN_GRAFANA_VERSION]: 'data-testid-import-dashboard-title', + }, + submit: { + [MIN_GRAFANA_VERSION]: 'data-testid-import-dashboard-submit', + }, }, PanelAlertTabContent: { - content: 'data-testid Unified alert editor tab content', + content: { + '10.2.3': 'data-testid Unified alert editor tab content', + [MIN_GRAFANA_VERSION]: 'Unified alert editor tab content', + }, }, VisualizationPreview: { - card: (name: string) => `data-testid suggestion-${name}`, + card: { + [MIN_GRAFANA_VERSION]: (name: string) => `data-testid suggestion-${name}`, + }, }, ColorSwatch: { - name: `data-testid-colorswatch`, + name: { + [MIN_GRAFANA_VERSION]: 'data-testid-colorswatch', + }, }, DashboardRow: { - title: (title: string) => `data-testid dashboard-row-title-${title}`, + title: { + [MIN_GRAFANA_VERSION]: (title: string) => `data-testid dashboard-row-title-${title}`, + }, }, UserProfile: { - profileSaveButton: 'data-testid-user-profile-save', - preferencesSaveButton: 'data-testid-shared-prefs-save', - orgsTable: 'data-testid-user-orgs-table', - sessionsTable: 'data-testid-user-sessions-table', - extensionPointTabs: 'data-testid-extension-point-tabs', - extensionPointTab: (tabId: string) => `data-testid-extension-point-tab-${tabId}`, + profileSaveButton: { + [MIN_GRAFANA_VERSION]: 'data-testid-user-profile-save', + }, + preferencesSaveButton: { + [MIN_GRAFANA_VERSION]: 'data-testid-shared-prefs-save', + }, + orgsTable: { + [MIN_GRAFANA_VERSION]: 'data-testid-user-orgs-table', + }, + sessionsTable: { + [MIN_GRAFANA_VERSION]: 'data-testid-user-sessions-table', + }, + extensionPointTabs: { + '10.2.3': 'data-testid-extension-point-tabs', + }, + extensionPointTab: { + '10.2.3': (tabId: string) => `data-testid-extension-point-tab-${tabId}`, + }, }, FileUpload: { - inputField: 'data-testid-file-upload-input-field', - fileNameSpan: 'data-testid-file-upload-file-name', + inputField: { + '9.0.0': 'data-testid-file-upload-input-field', + }, + fileNameSpan: { + '9.0.0': 'data-testid-file-upload-file-name', + }, }, DebugOverlay: { - wrapper: 'debug-overlay', + wrapper: { + '9.2.0': 'debug-overlay', + }, }, OrgRolePicker: { - input: 'Role', + input: { + '9.5.0': 'Role', + }, }, AnalyticsToolbarButton: { - button: 'Dashboard insights', + button: { + '9.5.0': 'Dashboard insights', + }, }, Variables: { - variableOption: 'data-testid variable-option', - variableLinkWrapper: 'data-testid variable-link-wrapper', + variableOption: { + '9.5.0': 'data-testid variable-option', + }, + variableLinkWrapper: { + '11.1.1': 'data-testid variable-link-wrapper', + }, }, Annotations: { - annotationsTypeInput: 'data-testid annotations-type-input', - annotationsChoosePanelInput: 'data-testid choose-panels-input', + annotationsTypeInput: { + '11.1.0': 'data-testid annotations-type-input', + [MIN_GRAFANA_VERSION]: 'annotations-type-input', + }, + annotationsChoosePanelInput: { + '11.1.0': 'data-testid choose-panels-input', + [MIN_GRAFANA_VERSION]: 'choose-panels-input', + }, editor: { - testButton: 'data-testid annotations-test-button', - resultContainer: 'data-testid annotations-query-result-container', + testButton: { + '11.0.0': 'data-testid annotations-test-button', + }, + resultContainer: { + '11.0.0': 'data-testid annotations-query-result-container', + }, }, }, Tooltip: { - container: 'data-testid tooltip', + container: { + '10.2.0': 'data-testid tooltip', + }, }, ReturnToPrevious: { - buttonGroup: 'data-testid dismissable button group', - backButton: 'data-testid back', - dismissButton: 'data-testid dismiss', + buttonGroup: { + '11.0.0': 'data-testid dismissable button group', + }, + backButton: { + '11.0.0': 'data-testid back', + }, + dismissButton: { + '11.0.0': 'data-testid dismiss', + }, }, SQLQueryEditor: { - selectColumn: 'data-testid select-column', - selectAggregation: 'data-testid select-aggregation', - selectAlias: 'data-testid select-alias', - filterConjunction: 'data-testid filter-conjunction', - filterField: 'data-testid filter-field', - filterOperator: 'data-testid filter-operator', - headerTableSelector: 'data-testid header-table-selector', - headerFilterSwitch: 'data-testid header-filter-switch', - headerGroupSwitch: 'data-testid header-group-switch', - headerOrderSwitch: 'data-testid header-order-switch', - headerPreviewSwitch: 'data-testid header-preview-switch', + selectColumn: { + '11.0.0': 'data-testid select-column', + }, + selectColumnInput: { '11.0.0': 'data-testid select-column-input' }, + selectFunctionParameter: { '11.0.0': (name: string) => `data-testid select-function-parameter-${name}` }, + selectAggregation: { + '11.0.0': 'data-testid select-aggregation', + }, + selectAggregationInput: { '11.0.0': 'data-testid select-aggregation-input' }, + selectAlias: { + '11.0.0': 'data-testid select-alias', + }, + selectAliasInput: { '11.0.0': 'data-testid select-alias-input' }, + selectInputParameter: { '11.0.0': 'data-testid select-input-parameter' }, + filterConjunction: { + '11.0.0': 'data-testid filter-conjunction', + }, + filterField: { + '11.0.0': 'data-testid filter-field', + }, + filterOperator: { + '11.0.0': 'data-testid filter-operator', + }, + headerTableSelector: { + '11.0.0': 'data-testid header-table-selector', + }, + headerFilterSwitch: { + '11.0.0': 'data-testid header-filter-switch', + }, + headerGroupSwitch: { + '11.0.0': 'data-testid header-group-switch', + }, + headerOrderSwitch: { + '11.0.0': 'data-testid header-order-switch', + }, + headerPreviewSwitch: { + '11.0.0': 'data-testid header-preview-switch', + }, }, EntityNotFound: { - container: 'data-testid entity-not-found', + container: { + '11.2.0': 'data-testid entity-not-found', + }, }, -}; +} satisfies VersionedSelectorGroup; + +export type VersionedComponents = typeof versionedComponents; diff --git a/packages/grafana-e2e-selectors/src/selectors/constants.ts b/packages/grafana-e2e-selectors/src/selectors/constants.ts new file mode 100644 index 00000000000..cda08ecaf1c --- /dev/null +++ b/packages/grafana-e2e-selectors/src/selectors/constants.ts @@ -0,0 +1 @@ +export const MIN_GRAFANA_VERSION = '8.5.0'; diff --git a/packages/grafana-e2e-selectors/src/selectors/index.ts b/packages/grafana-e2e-selectors/src/selectors/index.ts index 88ebc406b5f..bfe90848026 100644 --- a/packages/grafana-e2e-selectors/src/selectors/index.ts +++ b/packages/grafana-e2e-selectors/src/selectors/index.ts @@ -1,21 +1,24 @@ +import { resolveSelectors } from '../resolver'; import { E2ESelectors } from '../types'; -import { Components } from './components'; -import { Pages } from './pages'; +import { versionedComponents, VersionedComponents } from './components'; +import { versionedPages, VersionedPages } from './pages'; -/** - * Exposes selectors in package for easy use in e2e tests and in production code - * - * @alpha - */ -export const selectors: { pages: E2ESelectors; components: E2ESelectors } = { - pages: Pages, - components: Components, -}; +const Pages = resolveSelectors(versionedPages); +const Components = resolveSelectors(versionedComponents); +const selectors = { pages: Pages, components: Components }; /** * Exposes Pages, Component selectors and E2ESelectors type in package for easy use in e2e tests and in production code - * - * @alpha */ -export { Pages, Components, type E2ESelectors }; +export { + Pages, + Components, + selectors, + versionedComponents, + versionedPages, + resolveSelectors, + type VersionedPages, + type VersionedComponents, + type E2ESelectors, +}; diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index be04b0e8296..148f4fd56e2 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -1,451 +1,1018 @@ -import { Components } from './components'; +import { VersionedSelectorGroup } from '../types'; + +import { MIN_GRAFANA_VERSION } from './constants'; /** * Selectors grouped/defined in Pages - * - * @alpha */ -export const Pages = { +export const versionedPages = { + Alerting: { + AddAlertRule: { + url: { + '10.1.0': '/alerting/new/alerting', + [MIN_GRAFANA_VERSION]: '/alerting/new', + }, + }, + EditAlertRule: { + url: { + [MIN_GRAFANA_VERSION]: (alertRuleUid: string) => `alerting/${alertRuleUid}/edit`, + }, + }, + }, Login: { - url: '/login', - username: 'data-testid Username input field', - password: 'data-testid Password input field', - submit: 'data-testid Login button', - skip: 'data-testid Skip change password button', + url: { + [MIN_GRAFANA_VERSION]: '/login', + }, + username: { + '10.2.3': 'data-testid Username input field', + [MIN_GRAFANA_VERSION]: 'Username input field', + }, + password: { + '10.2.3': 'data-testid Password input field', + [MIN_GRAFANA_VERSION]: 'Password input field', + }, + submit: { + '10.2.3': 'data-testid Login button', + [MIN_GRAFANA_VERSION]: 'Login button', + }, + skip: { + '10.2.3': 'data-testid Skip change password button', + }, + }, + PasswordlessLogin: { + url: { + [MIN_GRAFANA_VERSION]: '/login/passwordless/authenticate', + }, + email: { + '10.2.3': 'data-testid Email input field', + [MIN_GRAFANA_VERSION]: 'Email input field', + }, + submit: { + '10.2.3': 'data-testid PasswordlessLogin button', + [MIN_GRAFANA_VERSION]: 'PasswordlessLogin button', + }, }, Home: { - url: '/', + url: { + [MIN_GRAFANA_VERSION]: '/', + }, }, DataSource: { - name: 'data-testid Data source settings page name input field', - delete: 'Data source settings page Delete button', - readOnly: 'data-testid Data source settings page read only message', - saveAndTest: 'data-testid Data source settings page Save and Test button', - alert: 'data-testid Data source settings page Alert', + name: { + '10.3.0': 'data-testid Data source settings page name input field', + [MIN_GRAFANA_VERSION]: 'Data source settings page name input field', + }, + delete: { + [MIN_GRAFANA_VERSION]: 'Data source settings page Delete button', + }, + readOnly: { + '10.3.0': 'data-testid Data source settings page read only message', + [MIN_GRAFANA_VERSION]: 'Data source settings page read only message', + }, + saveAndTest: { + '10.0.0': 'data-testid Data source settings page Save and Test button', + [MIN_GRAFANA_VERSION]: 'Data source settings page Save and Test button', + }, + alert: { + '10.3.0': 'data-testid Data source settings page Alert', + [MIN_GRAFANA_VERSION]: 'Data source settings page Alert', + }, }, DataSources: { - url: '/datasources', - dataSources: (dataSourceName: string) => `Data source list item ${dataSourceName}`, + url: { + [MIN_GRAFANA_VERSION]: '/datasources', + }, + dataSources: { + [MIN_GRAFANA_VERSION]: (dataSourceName: string) => `Data source list item ${dataSourceName}`, + }, }, EditDataSource: { - url: (dataSourceUid: string) => `/datasources/edit/${dataSourceUid}`, - settings: 'Datasource settings page basic settings', + url: { + '9.5.0': (dataSourceUid: string) => `/datasources/edit/${dataSourceUid}`, + }, + settings: { + '9.5.0': 'Datasource settings page basic settings', + }, }, AddDataSource: { - url: '/datasources/new', - /** @deprecated Use dataSourcePluginsV2 */ - dataSourcePlugins: (pluginName: string) => `Data source plugin item ${pluginName}`, - dataSourcePluginsV2: (pluginName: string) => `Add new data source ${pluginName}`, + url: { + [MIN_GRAFANA_VERSION]: '/datasources/new', + }, + dataSourcePluginsV2: { + '9.3.1': (pluginName: string) => `Add new data source ${pluginName}`, + [MIN_GRAFANA_VERSION]: (pluginName: string) => `Data source plugin item ${pluginName}`, + }, }, ConfirmModal: { - delete: 'data-testid Confirm Modal Danger Button', + delete: { + '10.0.0': 'data-testid Confirm Modal Danger Button', + [MIN_GRAFANA_VERSION]: 'Confirm Modal Danger Button', + }, }, AddDashboard: { - url: '/dashboard/new', - itemButton: (title: string) => `data-testid ${title}`, - addNewPanel: 'data-testid Add new panel', - addNewRow: 'data-testid Add new row', - addNewPanelLibrary: 'data-testid Add new panel from panel library', - }, - Dashboard: { - url: (uid: string) => `/d/${uid}`, - DashNav: { - /** - * @deprecated use navV2 from Grafana 8.3 instead - */ - nav: 'Dashboard navigation', - navV2: 'data-testid Dashboard navigation', - publicDashboardTag: 'data-testid public dashboard tag', - shareButton: 'data-testid share-button', - scrollContainer: 'data-testid Dashboard canvas scroll container', - newShareButton: { - container: 'data-testid new share button', - shareLink: 'data-testid new share link-button', - arrowMenu: 'data-testid new share button arrow menu', - menu: { - container: 'data-testid new share button menu', - shareInternally: 'data-testid new share button share internally', - shareExternally: 'data-testid new share button share externally', - shareSnapshot: 'data-testid new share button share snapshot', - }, - }, - NewExportButton: { - container: 'data-testid new export button', - arrowMenu: 'data-testid new export button arrow menu', - Menu: { - container: 'data-testid new export button menu', - exportAsJson: 'data-testid new export button export as json', - }, - }, - playlistControls: { - prev: 'data-testid playlist previous dashboard button', - stop: 'data-testid playlist stop dashboard button', - next: 'data-testid playlist next dashboard button', - }, + url: { + [MIN_GRAFANA_VERSION]: '/dashboard/new', }, - Controls: 'data-testid dashboard controls', - SubMenu: { - submenu: 'Dashboard submenu', - submenuItem: 'data-testid template variable', - submenuItemLabels: (item: string) => `data-testid Dashboard template variables submenu Label ${item}`, - submenuItemValueDropDownValueLinkTexts: (item: string) => - `data-testid Dashboard template variables Variable Value DropDown value link text ${item}`, - submenuItemValueDropDownDropDown: 'Variable options', - submenuItemValueDropDownOptionTexts: (item: string) => - `data-testid Dashboard template variables Variable Value DropDown option text ${item}`, - Annotations: { - annotationsWrapper: 'data-testid annotation-wrapper', - annotationLabel: (label: string) => `data-testid Dashboard annotations submenu Label ${label}`, - annotationToggle: (label: string) => `data-testid Dashboard annotations submenu Toggle ${label}`, - }, + itemButton: { + '9.5.0': (title: string) => `data-testid ${title}`, + }, + addNewPanel: { + '11.1.0': 'data-testid Add new panel', + [MIN_GRAFANA_VERSION]: 'Add new panel', + }, + itemButtonAddViz: { + [MIN_GRAFANA_VERSION]: 'Add new visualization menu item', + }, + addNewRow: { + '11.1.0': 'data-testid Add new row', + [MIN_GRAFANA_VERSION]: 'Add new row', + }, + addNewPanelLibrary: { + '11.1.0': 'data-testid Add new panel from panel library', + [MIN_GRAFANA_VERSION]: 'Add new panel from panel library', }, Settings: { - Actions: { - close: 'data-testid dashboard-settings-close', - }, - General: { - deleteDashBoard: 'data-testid Dashboard settings page delete dashboard button', - sectionItems: (item: string) => `Dashboard settings section item ${item}`, - saveDashBoard: 'Dashboard settings aside actions Save button', - saveAsDashBoard: 'Dashboard settings aside actions Save As button', - /** - * @deprecated use components.TimeZonePicker.containerV2 from Grafana 8.3 instead - */ - timezone: 'Time zone picker select container', - title: 'General', - }, Annotations: { List: { - /** - * @deprecated use addAnnotationCTAV2 from Grafana 8.3 instead - */ - addAnnotationCTA: Components.CallToActionCard.button('Add annotation query'), - addAnnotationCTAV2: Components.CallToActionCard.buttonV2('Add annotation query'), - annotations: 'data-testid list-annotations', + url: { + [MIN_GRAFANA_VERSION]: '/dashboard/new?orgId=1&editview=annotations', + }, }, - Settings: { - name: 'data-testid Annotations settings name input', - }, - NewAnnotation: { - panelFilterSelect: 'data-testid annotations-panel-filter', - showInLabel: 'data-testid show-in-label', - previewInDashboard: 'data-testid annotations-preview', - delete: 'data-testid annotations-delete', - apply: 'data-testid annotations-apply', - enable: 'data-testid annotation-enable', - hide: 'data-testid annotation-hide', + Edit: { + url: { + [MIN_GRAFANA_VERSION]: (annotationIndex: string) => + `/dashboard/new?editview=annotations&editIndex=${annotationIndex}`, + }, }, }, Variables: { List: { - /** - * @deprecated use addVariableCTAV2 from Grafana 8.3 instead - */ - addVariableCTA: Components.CallToActionCard.button('Add variable'), - addVariableCTAV2: Components.CallToActionCard.buttonV2('Add variable'), - newButton: 'Variable editor New variable button', - table: 'Variable editor Table', - tableRowNameFields: (variableName: string) => `Variable editor Table Name field ${variableName}`, - tableRowDefinitionFields: (variableName: string) => `Variable editor Table Definition field ${variableName}`, - tableRowArrowUpButtons: (variableName: string) => `Variable editor Table ArrowUp button ${variableName}`, - tableRowArrowDownButtons: (variableName: string) => `Variable editor Table ArrowDown button ${variableName}`, - tableRowDuplicateButtons: (variableName: string) => `Variable editor Table Duplicate button ${variableName}`, - tableRowRemoveButtons: (variableName: string) => `Variable editor Table Remove button ${variableName}`, + url: { + '11.3.0': '/dashboard/new?orgId=1&editview=variables', + [MIN_GRAFANA_VERSION]: '/dashboard/new?orgId=1&editview=templating', + }, }, Edit: { + url: { + [MIN_GRAFANA_VERSION]: (annotationIndex: string) => + `/dashboard/new?orgId=1&editview=templating&editIndex=${annotationIndex}`, + }, + }, + }, + }, + }, + Dashboard: { + url: { + [MIN_GRAFANA_VERSION]: (uid: string) => `/d/${uid}`, + }, + DashNav: { + nav: { + [MIN_GRAFANA_VERSION]: 'Dashboard navigation', + }, + navV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Dashboard navigation', + }, + publicDashboardTag: { + '9.1.0': 'data-testid public dashboard tag', + }, + shareButton: { + '10.4.0': 'data-testid share-button', + }, + scrollContainer: { + '11.1.0': 'data-testid Dashboard canvas scroll container', + }, + newShareButton: { + container: { + '11.1.0': 'data-testid new share button', + }, + shareLink: { + '11.1.0': 'data-testid new share link-button', + }, + arrowMenu: { + '11.1.0': 'data-testid new share button arrow menu', + }, + menu: { + container: { + '11.1.0': 'data-testid new share button menu', + }, + shareInternally: { + '11.1.0': 'data-testid new share button share internally', + }, + shareExternally: { + '11.1.1': 'data-testid new share button share externally', + }, + shareSnapshot: { + '11.2.0': 'data-testid new share button share snapshot', + }, + }, + }, + NewExportButton: { + container: { + '11.2.0': 'data-testid new export button', + }, + arrowMenu: { + '11.2.0': 'data-testid new export button arrow menu', + }, + Menu: { + container: { + '11.2.0': 'data-testid new export button menu', + }, + exportAsJson: { + '11.2.0': 'data-testid new export button export as json', + }, + }, + }, + playlistControls: { + prev: { + '11.0.0': 'data-testid playlist previous dashboard button', + }, + stop: { + '11.0.0': 'data-testid playlist stop dashboard button', + }, + next: { + '11.0.0': 'data-testid playlist next dashboard button', + }, + }, + }, + Controls: { + '11.1.0': 'data-testid dashboard controls', + }, + SubMenu: { + submenu: { + [MIN_GRAFANA_VERSION]: 'Dashboard submenu', + }, + submenuItem: { + [MIN_GRAFANA_VERSION]: 'data-testid template variable', + }, + submenuItemLabels: { + [MIN_GRAFANA_VERSION]: (item: string) => `data-testid Dashboard template variables submenu Label ${item}`, + }, + submenuItemValueDropDownValueLinkTexts: { + [MIN_GRAFANA_VERSION]: (item: string) => + `data-testid Dashboard template variables Variable Value DropDown value link text ${item}`, + }, + submenuItemValueDropDownDropDown: { + [MIN_GRAFANA_VERSION]: 'Variable options', + }, + submenuItemValueDropDownOptionTexts: { + [MIN_GRAFANA_VERSION]: (item: string) => + `data-testid Dashboard template variables Variable Value DropDown option text ${item}`, + }, + Annotations: { + annotationsWrapper: { + '10.0.0': 'data-testid annotation-wrapper', + }, + annotationLabel: { + '10.0.0': (label: string) => `data-testid Dashboard annotations submenu Label ${label}`, + }, + annotationToggle: { + '10.0.0': (label: string) => `data-testid Dashboard annotations submenu Toggle ${label}`, + }, + }, + }, + Settings: { + Actions: { + close: { + '9.5.0': 'data-testid dashboard-settings-close', + }, + }, + General: { + deleteDashBoard: { + '11.1.0': 'data-testid Dashboard settings page delete dashboard button', + }, + sectionItems: { + [MIN_GRAFANA_VERSION]: (item: string) => `Dashboard settings section item ${item}`, + }, + saveDashBoard: { + [MIN_GRAFANA_VERSION]: 'Dashboard settings aside actions Save button', + }, + saveAsDashBoard: { + [MIN_GRAFANA_VERSION]: 'Dashboard settings aside actions Save As button', + }, + title: { + '11.2.0': 'General', + }, + }, + Annotations: { + Edit: { + urlParams: { + [MIN_GRAFANA_VERSION]: (annotationIndex: string) => `editview=annotations&editIndex=${annotationIndex}`, + }, + }, + List: { + url: { + [MIN_GRAFANA_VERSION]: (dashboardUid: string) => `/d/${dashboardUid}?editview=annotations`, + }, + addAnnotationCTAV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Call to action button Add annotation query', + }, + annotations: { + '10.4.0': 'data-testid list-annotations', + }, + }, + Settings: { + name: { + '11.1.0': 'data-testid Annotations settings name input', + [MIN_GRAFANA_VERSION]: 'Annotations settings name input', + }, + }, + NewAnnotation: { + panelFilterSelect: { + '10.0.0': 'data-testid annotations-panel-filter', + }, + showInLabel: { + '11.1.0': 'data-testid show-in-label', + }, + previewInDashboard: { + '10.0.0': 'data-testid annotations-preview', + }, + delete: { + '10.4.0': 'data-testid annotations-delete', + }, + apply: { + '10.4.0': 'data-testid annotations-apply', + }, + enable: { + '10.4.0': 'data-testid annotation-enable', + }, + hide: { + '10.4.0': 'data-testid annotation-hide', + }, + }, + }, + Variables: { + List: { + url: { + [MIN_GRAFANA_VERSION]: (dashboardUid: string) => `/d/${dashboardUid}?editview=templating`, + }, + addVariableCTAV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Call to action button Add variable', + }, + newButton: { + [MIN_GRAFANA_VERSION]: 'Variable editor New variable button', + }, + table: { + [MIN_GRAFANA_VERSION]: 'Variable editor Table', + }, + tableRowNameFields: { + [MIN_GRAFANA_VERSION]: (variableName: string) => `Variable editor Table Name field ${variableName}`, + }, + tableRowDefinitionFields: { + '10.1.0': (variableName: string) => `Variable editor Table Definition field ${variableName}`, + }, + tableRowArrowUpButtons: { + [MIN_GRAFANA_VERSION]: (variableName: string) => `Variable editor Table ArrowUp button ${variableName}`, + }, + tableRowArrowDownButtons: { + [MIN_GRAFANA_VERSION]: (variableName: string) => `Variable editor Table ArrowDown button ${variableName}`, + }, + tableRowDuplicateButtons: { + [MIN_GRAFANA_VERSION]: (variableName: string) => `Variable editor Table Duplicate button ${variableName}`, + }, + tableRowRemoveButtons: { + [MIN_GRAFANA_VERSION]: (variableName: string) => `Variable editor Table Remove button ${variableName}`, + }, + }, + Edit: { + urlParams: { + [MIN_GRAFANA_VERSION]: (editIndex: string) => `editview=templating&editIndex=${editIndex}`, + }, General: { - headerLink: 'Variable editor Header link', - modeLabelNew: 'Variable editor Header mode New', + headerLink: { + [MIN_GRAFANA_VERSION]: 'Variable editor Header link', + }, + modeLabelNew: { + [MIN_GRAFANA_VERSION]: 'Variable editor Header mode New', + }, /** * @deprecated */ - modeLabelEdit: 'Variable editor Header mode Edit', - generalNameInput: 'Variable editor Form Name field', - generalNameInputV2: 'data-testid Variable editor Form Name field', - generalTypeSelect: 'Variable editor Form Type select', - generalTypeSelectV2: 'data-testid Variable editor Form Type select', - generalLabelInput: 'Variable editor Form Label field', - generalLabelInputV2: 'data-testid Variable editor Form Label field', - generalHideSelect: 'Variable editor Form Hide select', - generalHideSelectV2: 'data-testid Variable editor Form Hide select', - selectionOptionsMultiSwitch: 'data-testid Variable editor Form Multi switch', - selectionOptionsIncludeAllSwitch: 'data-testid Variable editor Form IncludeAll switch', - selectionOptionsCustomAllInput: 'data-testid Variable editor Form IncludeAll field', - previewOfValuesOption: 'data-testid Variable editor Preview of Values option', - submitButton: 'data-testid Variable editor Run Query button', - applyButton: 'data-testid Variable editor Apply button', + modeLabelEdit: { + [MIN_GRAFANA_VERSION]: 'Variable editor Header mode Edit', + }, + generalNameInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Name field', + }, + generalNameInputV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Name field', + }, + generalTypeSelect: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Type select', + }, + generalTypeSelectV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Type select', + }, + generalLabelInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Label field', + }, + generalLabelInputV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Label field', + }, + generalHideSelect: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Hide select', + }, + generalHideSelectV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Hide select', + }, + selectionOptionsAllowCustomValueSwitch: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Allow Custom Value switch', + }, + selectionOptionsMultiSwitch: { + '10.4.0': 'data-testid Variable editor Form Multi switch', + [MIN_GRAFANA_VERSION]: 'Variable editor Form Multi switch', + }, + selectionOptionsIncludeAllSwitch: { + '10.4.0': 'data-testid Variable editor Form IncludeAll switch', + [MIN_GRAFANA_VERSION]: 'Variable editor Form IncludeAll switch', + }, + selectionOptionsCustomAllInput: { + '10.4.0': 'data-testid Variable editor Form IncludeAll field', + [MIN_GRAFANA_VERSION]: 'Variable editor Form IncludeAll field', + }, + previewOfValuesOption: { + '10.4.0': 'data-testid Variable editor Preview of Values option', + [MIN_GRAFANA_VERSION]: 'Variable editor Preview of Values option', + }, + submitButton: { + '10.4.0': 'data-testid Variable editor Run Query button', + [MIN_GRAFANA_VERSION]: 'Variable editor Submit button', + }, + applyButton: { + '9.3.0': 'data-testid Variable editor Apply button', + }, }, QueryVariable: { - queryOptionsDataSourceSelect: Components.DataSourcePicker.inputV2, - queryOptionsRefreshSelect: 'Variable editor Form Query Refresh select', - queryOptionsRefreshSelectV2: 'data-testid Variable editor Form Query Refresh select', - queryOptionsRegExInput: 'Variable editor Form Query RegEx field', - queryOptionsRegExInputV2: 'data-testid Variable editor Form Query RegEx field', - queryOptionsSortSelect: 'Variable editor Form Query Sort select', - queryOptionsSortSelectV2: 'data-testid Variable editor Form Query Sort select', - queryOptionsQueryInput: 'data-testid Variable editor Form Default Variable Query Editor textarea', - valueGroupsTagsEnabledSwitch: 'Variable editor Form Query UseTags switch', - valueGroupsTagsTagsQueryInput: 'Variable editor Form Query TagsQuery field', - valueGroupsTagsTagsValuesQueryInput: 'Variable editor Form Query TagsValuesQuery field', + queryOptionsDataSourceSelect: { + '10.4.0': 'data-testid Select a data source', + '10.0.0': 'data-testid Data source picker select container', + [MIN_GRAFANA_VERSION]: 'Data source picker select container', + }, + queryOptionsRefreshSelect: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Refresh select', + }, + queryOptionsRefreshSelectV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query Refresh select', + }, + queryOptionsRegExInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Query RegEx field', + }, + queryOptionsRegExInputV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query RegEx field', + }, + queryOptionsSortSelect: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Sort select', + }, + queryOptionsSortSelectV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query Sort select', + }, + queryOptionsQueryInput: { + '10.4.0': 'data-testid Variable editor Form Default Variable Query Editor textarea', + }, + valueGroupsTagsEnabledSwitch: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Query UseTags switch', + }, + valueGroupsTagsTagsQueryInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Query TagsQuery field', + }, + valueGroupsTagsTagsValuesQueryInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Query TagsValuesQuery field', + }, }, ConstantVariable: { - constantOptionsQueryInput: 'Variable editor Form Constant Query field', - constantOptionsQueryInputV2: 'data-testid Variable editor Form Constant Query field', + constantOptionsQueryInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form Constant Query field', + }, + constantOptionsQueryInputV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Constant Query field', + }, }, DatasourceVariable: { - datasourceSelect: 'data-testid datasource variable datasource type', + datasourceSelect: { + [MIN_GRAFANA_VERSION]: 'data-testid datasource variable datasource type', + }, }, TextBoxVariable: { - textBoxOptionsQueryInput: 'Variable editor Form TextBox Query field', - textBoxOptionsQueryInputV2: 'data-testid Variable editor Form TextBox Query field', + textBoxOptionsQueryInput: { + [MIN_GRAFANA_VERSION]: 'Variable editor Form TextBox Query field', + }, + textBoxOptionsQueryInputV2: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form TextBox Query field', + }, }, CustomVariable: { - customValueInput: 'data-testid custom-variable-input', + customValueInput: { + [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-input', + }, }, IntervalVariable: { - intervalsValueInput: 'data-testid interval variable intervals input', - autoEnabledCheckbox: 'data-testid interval variable auto value checkbox', - stepCountIntervalSelect: 'data-testid interval variable step count input', - minIntervalInput: 'data-testid interval variable mininum interval input', + intervalsValueInput: { + [MIN_GRAFANA_VERSION]: 'data-testid interval variable intervals input', + }, + autoEnabledCheckbox: { + '10.4.0': 'data-testid interval variable auto value checkbox', + }, + stepCountIntervalSelect: { + '10.4.0': 'data-testid interval variable step count input', + }, + minIntervalInput: { + '10.4.0': 'data-testid interval variable mininum interval input', + }, }, GroupByVariable: { - dataSourceSelect: Components.DataSourcePicker.inputV2, - infoText: 'data-testid group by variable info text', - modeToggle: 'data-testid group by variable mode toggle', + dataSourceSelect: { + '10.4.0': 'data-testid Select a data source', + }, + infoText: { + '10.4.0': 'data-testid group by variable info text', + }, + modeToggle: { + '10.4.0': 'data-testid group by variable mode toggle', + }, }, AdHocFiltersVariable: { - datasourceSelect: Components.DataSourcePicker.inputV2, - infoText: 'data-testid ad-hoc filters variable info text', - modeToggle: 'data-testid ad-hoc filters variable mode toggle', + datasourceSelect: { + '10.4.0': 'data-testid Select a data source', + }, + infoText: { + '10.4.0': 'data-testid ad-hoc filters variable info text', + }, + modeToggle: { + '11.0.0': 'data-testid ad-hoc filters variable mode toggle', + }, }, }, }, }, Annotations: { - marker: 'data-testid annotation-marker', + marker: { + '10.0.0': 'data-testid annotation-marker', + }, }, Rows: { Repeated: { ConfigSection: { - warningMessage: 'data-testid Repeated rows warning message', + warningMessage: { + '10.2.0': 'data-testid Repeated rows warning message', + }, }, }, }, }, Dashboards: { - url: '/dashboards', - /** - * @deprecated use components.Search.dashboardItem from Grafana 8.3 instead - */ - dashboards: (title: string) => `Dashboard search item ${title}`, + url: { + [MIN_GRAFANA_VERSION]: '/dashboards', + }, + dashboards: { + '10.2.0': (title: string) => `Dashboard search item ${title}`, + }, }, SaveDashboardAsModal: { - newName: 'Save dashboard title field', - save: 'Save dashboard button', + newName: { + '10.2.0': 'Save dashboard title field', + }, + save: { + '10.2.0': 'Save dashboard button', + }, }, SaveDashboardModal: { - save: 'Dashboard settings Save Dashboard Modal Save button', - saveVariables: 'Dashboard settings Save Dashboard Modal Save variables checkbox', - saveTimerange: 'Dashboard settings Save Dashboard Modal Save timerange checkbox', - saveRefresh: 'Dashboard settings Save Dashboard Modal Save refresh checkbox', + save: { + '10.2.0': 'Dashboard settings Save Dashboard Modal Save button', + }, + saveVariables: { + '10.2.0': 'Dashboard settings Save Dashboard Modal Save variables checkbox', + }, + saveTimerange: { + '10.2.0': 'Dashboard settings Save Dashboard Modal Save timerange checkbox', + }, + saveRefresh: { + '11.1.0': 'Dashboard settings Save Dashboard Modal Save refresh checkbox', + }, }, SharePanelModal: { - linkToRenderedImage: 'Link to rendered image', + linkToRenderedImage: { + [MIN_GRAFANA_VERSION]: 'Link to rendered image', + }, }, ShareDashboardModal: { PublicDashboard: { - WillBePublicCheckbox: 'data-testid public dashboard will be public checkbox', - LimitedDSCheckbox: 'data-testid public dashboard limited datasources checkbox', - CostIncreaseCheckbox: 'data-testid public dashboard cost may increase checkbox', - PauseSwitch: 'data-testid public dashboard pause switch', - EnableAnnotationsSwitch: 'data-testid public dashboard on off switch for annotations', - CreateButton: 'data-testid public dashboard create button', - DeleteButton: 'data-testid public dashboard delete button', - CopyUrlInput: 'data-testid public dashboard copy url input', - CopyUrlButton: 'data-testid public dashboard copy url button', - SettingsDropdown: 'data-testid public dashboard settings dropdown', - TemplateVariablesWarningAlert: 'data-testid public dashboard disabled template variables alert', - UnsupportedDataSourcesWarningAlert: 'data-testid public dashboard unsupported data sources alert', - NoUpsertPermissionsWarningAlert: 'data-testid public dashboard no upsert permissions alert', - EnableTimeRangeSwitch: 'data-testid public dashboard on off switch for time range', + WillBePublicCheckbox: { + '9.1.0': 'data-testid public dashboard will be public checkbox', + }, + LimitedDSCheckbox: { + '9.1.0': 'data-testid public dashboard limited datasources checkbox', + }, + CostIncreaseCheckbox: { + '9.1.0': 'data-testid public dashboard cost may increase checkbox', + }, + PauseSwitch: { + '9.5.0': 'data-testid public dashboard pause switch', + }, + EnableAnnotationsSwitch: { + '9.3.0': 'data-testid public dashboard on off switch for annotations', + }, + CreateButton: { + '9.5.0': 'data-testid public dashboard create button', + }, + DeleteButton: { + '9.3.0': 'data-testid public dashboard delete button', + }, + CopyUrlInput: { + '9.1.0': 'data-testid public dashboard copy url input', + }, + CopyUrlButton: { + '9.1.0': 'data-testid public dashboard copy url button', + }, + SettingsDropdown: { + '10.1.0': 'data-testid public dashboard settings dropdown', + }, + TemplateVariablesWarningAlert: { + '9.1.0': 'data-testid public dashboard disabled template variables alert', + }, + UnsupportedDataSourcesWarningAlert: { + '9.5.0': 'data-testid public dashboard unsupported data sources alert', + }, + NoUpsertPermissionsWarningAlert: { + '9.5.0': 'data-testid public dashboard no upsert permissions alert', + }, + EnableTimeRangeSwitch: { + '9.4.0': 'data-testid public dashboard on off switch for time range', + }, EmailSharingConfiguration: { - Container: 'data-testid email sharing config container', - ShareType: 'data-testid public dashboard share type', - EmailSharingInput: 'data-testid public dashboard email sharing input', - EmailSharingInviteButton: 'data-testid public dashboard email sharing invite button', - EmailSharingList: 'data-testid public dashboard email sharing list', - DeleteEmail: 'data-testid public dashboard delete email button', - ReshareLink: 'data-testid public dashboard reshare link button', + Container: { + '9.5.0': 'data-testid email sharing config container', + }, + ShareType: { + '9.5.0': 'data-testid public dashboard share type', + }, + EmailSharingInput: { + '9.5.0': 'data-testid public dashboard email sharing input', + }, + EmailSharingInviteButton: { + '9.5.0': 'data-testid public dashboard email sharing invite button', + }, + EmailSharingList: { + '9.5.0': 'data-testid public dashboard email sharing list', + }, + DeleteEmail: { + '9.5.0': 'data-testid public dashboard delete email button', + }, + ReshareLink: { + '9.5.0': 'data-testid public dashboard reshare link button', + }, }, }, SnapshotScene: { - url: (key: string) => `/dashboard/snapshot/${key}`, - PublishSnapshot: 'data-testid publish snapshot button', - CopyUrlButton: 'data-testid snapshot copy url button', - CopyUrlInput: 'data-testid snapshot copy url input', + url: { + '11.1.0': (key: string) => `/dashboard/snapshot/${key}`, + }, + PublishSnapshot: { + '11.1.0': 'data-testid publish snapshot button', + }, + CopyUrlButton: { + '11.1.0': 'data-testid snapshot copy url button', + }, + CopyUrlInput: { + '11.1.0': 'data-testid snapshot copy url input', + }, }, }, ShareDashboardDrawer: { ShareInternally: { - container: 'data-testid share internally drawer container', - lockTimeRangeSwitch: 'data-testid share internally lock time range switch', - shortenUrlSwitch: 'data-testid share internally shorten url switch', - copyUrlButton: 'data-testid share internally copy url button', + container: { + '11.3.0': 'data-testid share internally drawer container', + }, + lockTimeRangeSwitch: { + '11.3.0': 'data-testid share internally lock time range switch', + }, + shortenUrlSwitch: { + '11.3.0': 'data-testid share internally shorten url switch', + }, + copyUrlButton: { + '11.3.0': 'data-testid share internally copy url button', + }, }, ShareExternally: { - container: 'data-testid share externally drawer container', - publicAlert: 'data-testid public share alert', - emailSharingAlert: 'data-testid email share alert', - shareTypeSelect: 'data-testid share externally share type select', + container: { + '11.3.0': 'data-testid share externally drawer container', + }, + publicAlert: { + '11.3.0': 'data-testid public share alert', + }, + emailSharingAlert: { + '11.3.0': 'data-testid email share alert', + }, + shareTypeSelect: { + '11.3.0': 'data-testid share externally share type select', + }, Creation: { PublicShare: { - createButton: 'data-testid public share dashboard create button', - cancelButton: 'data-testid public share dashboard cancel button', + createButton: { + '11.3.0': 'data-testid public share dashboard create button', + }, + cancelButton: { + '11.3.0': 'data-testid public share dashboard cancel button', + }, }, EmailShare: { - createButton: 'data-testid email share dashboard create button', - cancelButton: 'data-testid email share dashboard cancel button', + createButton: { + '11.3.0': 'data-testid email share dashboard create button', + }, + cancelButton: { + '11.3.0': 'data-testid email share dashboard cancel button', + }, + }, + willBePublicCheckbox: { + '11.3.0': 'data-testid share dashboard will be public checkbox', }, - willBePublicCheckbox: 'data-testid share dashboard will be public checkbox', }, Configuration: { - enableTimeRangeSwitch: 'data-testid share externally enable time range switch', - enableAnnotationsSwitch: 'data-testid share externally enable annotations switch', - copyUrlButton: 'data-testid share externally copy url button', - revokeAccessButton: 'data-testid share externally revoke access button', - toggleAccessButton: 'data-testid share externally pause or resume access button', + enableTimeRangeSwitch: { + '11.3.0': 'data-testid share externally enable time range switch', + }, + enableAnnotationsSwitch: { + '11.3.0': 'data-testid share externally enable annotations switch', + }, + copyUrlButton: { + '11.3.0': 'data-testid share externally copy url button', + }, + revokeAccessButton: { + '11.3.0': 'data-testid share externally revoke access button', + }, + toggleAccessButton: { + '11.3.0': 'data-testid share externally pause or resume access button', + }, }, }, ShareSnapshot: { - url: (key: string) => `/dashboard/snapshot/${key}`, - container: 'data-testid share snapshot drawer container', - publishSnapshot: 'data-testid share snapshot publish button', - copyUrlButton: 'data-testid share snapshot copy url button', + url: { + '11.3.0': (key: string) => `/dashboard/snapshot/${key}`, + }, + container: { + '11.3.0': 'data-testid share snapshot drawer container', + }, + publishSnapshot: { + '11.3.0': 'data-testid share snapshot publish button', + }, + copyUrlButton: { + '11.3.0': 'data-testid share snapshot copy url button', + }, }, }, ExportDashboardDrawer: { ExportAsJson: { - container: 'data-testid export as json drawer container', - codeEditor: 'data-testid export as json code editor', - exportExternallyToggle: 'data-testid export as json externally switch', - saveToFileButton: 'data-testid export as json save to file button', - copyToClipboardButton: 'data-testid export as json copy to clipboard button', - cancelButton: 'data-testid export as json cancel button', + container: { + '11.3.0': 'data-testid export as json drawer container', + }, + codeEditor: { + '11.3.0': 'data-testid export as json code editor', + }, + exportExternallyToggle: { + '11.3.0': 'data-testid export as json externally switch', + }, + saveToFileButton: { + '11.3.0': 'data-testid export as json save to file button', + }, + copyToClipboardButton: { + '11.3.0': 'data-testid export as json copy to clipboard button', + }, + cancelButton: { + '11.3.0': 'data-testid export as json cancel button', + }, }, }, PublicDashboard: { - page: 'public-dashboard-page', + page: { + '9.5.0': 'public-dashboard-page', + }, NotAvailable: { - container: 'public-dashboard-not-available', - title: 'public-dashboard-title', - pausedDescription: 'public-dashboard-paused-description', + container: { + '9.5.0': 'public-dashboard-not-available', + }, + title: { + '9.5.0': 'public-dashboard-title', + }, + pausedDescription: { + '9.5.0': 'public-dashboard-paused-description', + }, + }, + footer: { + '11.0.0': 'public-dashboard-footer', }, - footer: 'public-dashboard-footer', }, PublicDashboardScene: { - loadingPage: 'public-dashboard-scene-loading-page', - page: 'public-dashboard-scene-page', - controls: 'public-dashboard-controls', + loadingPage: { + '11.0.0': 'public-dashboard-scene-loading-page', + }, + page: { + '11.0.0': 'public-dashboard-scene-page', + }, + controls: { + '11.0.0': 'public-dashboard-controls', + }, }, RequestViewAccess: { - form: 'request-view-access-form', - recipientInput: 'request-view-access-recipient-input', - submitButton: 'request-view-access-submit-button', + form: { + '9.5.0': 'request-view-access-form', + }, + recipientInput: { + '9.5.0': 'request-view-access-recipient-input', + }, + submitButton: { + '9.5.0': 'request-view-access-submit-button', + }, }, PublicDashboardConfirmAccess: { - submitButton: 'data-testid confirm-access-submit-button', + submitButton: { + '10.2.0': 'data-testid confirm-access-submit-button', + }, }, Explore: { - url: '/explore', + url: { + [MIN_GRAFANA_VERSION]: '/explore', + }, General: { - container: 'data-testid Explore', - graph: 'Explore Graph', - table: 'Explore Table', - scrollView: 'data-testid explorer scroll view', + container: { + [MIN_GRAFANA_VERSION]: 'data-testid Explore', + }, + graph: { + [MIN_GRAFANA_VERSION]: 'Explore Graph', + }, + table: { + [MIN_GRAFANA_VERSION]: 'Explore Table', + }, + scrollView: { + '9.0.0': 'data-testid explorer scroll view', + }, }, QueryHistory: { - container: 'data-testid QueryHistory', + container: { + '11.1.0': 'data-testid QueryHistory', + }, }, }, SoloPanel: { - url: (page: string) => `/d-solo/${page}`, + url: { + [MIN_GRAFANA_VERSION]: (page: string) => `/d-solo/${page}`, + }, }, PluginsList: { - page: 'Plugins list page', - list: 'Plugins list', - listItem: 'Plugins list item', - signatureErrorNotice: 'data-testid Unsigned plugins notice', + page: { + [MIN_GRAFANA_VERSION]: 'Plugins list page', + }, + list: { + [MIN_GRAFANA_VERSION]: 'Plugins list', + }, + listItem: { + [MIN_GRAFANA_VERSION]: 'Plugins list item', + }, + signatureErrorNotice: { + '10.3.0': 'data-testid Unsigned plugins notice', + [MIN_GRAFANA_VERSION]: 'Unsigned plugins notice', + }, }, PluginPage: { - page: 'Plugin page', - signatureInfo: 'data-testid Plugin signature info', - disabledInfo: 'data-testid Plugin disabled info', + page: { + [MIN_GRAFANA_VERSION]: 'Plugin page', + }, + signatureInfo: { + '10.3.0': 'data-testid Plugin signature info', + [MIN_GRAFANA_VERSION]: 'Plugin signature info', + }, + disabledInfo: { + '10.3.0': 'data-testid Plugin disabled info', + [MIN_GRAFANA_VERSION]: 'Plugin disabled info', + }, }, PlaylistForm: { - name: 'Playlist name', - interval: 'Playlist interval', - itemDelete: 'data-testid playlist-form-delete-item', + name: { + [MIN_GRAFANA_VERSION]: 'Playlist name', + }, + interval: { + [MIN_GRAFANA_VERSION]: 'Playlist interval', + }, + itemDelete: { + '10.2.0': 'data-testid playlist-form-delete-item', + }, }, BrowseDashboards: { table: { - body: 'data-testid browse-dashboards-table', - row: (name: string) => `data-testid browse dashboards row ${name}`, - checkbox: (uid: string) => `data-testid ${uid} checkbox`, + body: { + '10.2.0': 'data-testid browse-dashboards-table', + }, + row: { + '10.2.0': (name: string) => `data-testid browse dashboards row ${name}`, + }, + checkbox: { + '10.0.0': (uid: string) => `data-testid ${uid} checkbox`, + }, }, NewFolderForm: { - form: 'data-testid new folder form', - nameInput: 'data-testid new-folder-name-input', - createButton: 'data-testid new-folder-create-button', + form: { + '10.2.0': 'data-testid new folder form', + }, + nameInput: { + '10.2.0': 'data-testid new-folder-name-input', + }, + createButton: { + '10.2.0': 'data-testid new-folder-create-button', + }, }, }, Search: { - url: '/?search=openn', + url: { + '9.3.0': '/?search=openn', + }, FolderView: { - url: '/?search=open&layout=folders', + url: { + '9.3.0': '/?search=open&layout=folders', + }, }, }, PublicDashboards: { ListItem: { - linkButton: 'public-dashboard-link-button', - configButton: 'public-dashboard-configuration-button', - trashcanButton: 'public-dashboard-remove-button', - pauseSwitch: 'data-testid public dashboard pause switch', + linkButton: { + '9.3.0': 'public-dashboard-link-button', + }, + configButton: { + '9.3.0': 'public-dashboard-configuration-button', + }, + trashcanButton: { + '9.3.0': 'public-dashboard-remove-button', + }, + pauseSwitch: { + '10.1.0': 'data-testid public dashboard pause switch', + }, }, }, UserListPage: { tabs: { - allUsers: 'data-testid all-users-tab', - orgUsers: 'data-testid org-users-tab', - anonUserDevices: 'data-testid anon-user-devices-tab', - publicDashboardsUsers: 'data-testid public-dashboards-users-tab', - users: 'data-testid users-tab', + allUsers: { + '10.0.0': 'data-testid all-users-tab', + }, + orgUsers: { + '10.0.0': 'data-testid org-users-tab', + }, + anonUserDevices: { + '10.2.3': 'data-testid anon-user-devices-tab', + }, + publicDashboardsUsers: { + '10.0.0': 'data-testid public-dashboards-users-tab', + }, + users: { + '10.0.0': 'data-testid users-tab', + }, }, org: { - url: '/org/users', + url: { + '9.5.0': '/org/users', + }, }, admin: { - url: '/admin/users', + url: { + '9.5.0': '/admin/users', + }, }, publicDashboards: { - container: 'data-testid public-dashboards-users-list', + container: { + '11.1.0': 'data-testid public-dashboards-users-list', + }, }, UserListAdminPage: { - container: 'data-testid user-list-admin-page', + container: { + '10.0.0': 'data-testid user-list-admin-page', + }, }, UsersListPage: { - container: 'data-testid users-list-page', + container: { + '10.0.0': 'data-testid users-list-page', + }, }, UserAnonListPage: { - container: 'data-testid user-anon-list-page', + container: { + '10.4.0': 'data-testid user-anon-list-page', + }, }, UsersListPublicDashboardsPage: { - container: 'data-testid users-list-public-dashboards-page', + container: { + '10.0.0': 'data-testid users-list-public-dashboards-page', + }, DashboardsListModal: { - listItem: (uid: string) => `data-testid dashboards-list-item-${uid}`, + listItem: { + '10.0.0': (uid: string) => `data-testid dashboards-list-item-${uid}`, + }, }, }, }, ProfilePage: { - url: '/profile', + url: { + '10.2.0': '/profile', + }, }, -}; + Plugin: { + url: { + [MIN_GRAFANA_VERSION]: (pluginId: string) => `/plugins/${pluginId}`, + }, + }, +} satisfies VersionedSelectorGroup; + +export type VersionedPages = typeof versionedPages; diff --git a/packages/grafana-e2e-selectors/src/types/selectors.ts b/packages/grafana-e2e-selectors/src/types/selectors.ts index 6b9e9a02294..036d6f36d4d 100644 --- a/packages/grafana-e2e-selectors/src/types/selectors.ts +++ b/packages/grafana-e2e-selectors/src/types/selectors.ts @@ -1,42 +1,67 @@ /** * A string selector - * - * @alpha */ export type StringSelector = string; /** - * A function selector with an argument - * - * @alpha + * A function selector with one argument */ export type FunctionSelector = (id: string) => string; +/** + * A function selector with two arguments + */ +export type FunctionSelectorTwoArgs = (arg1: string, arg2: string) => string; + /** * A function selector without argument - * - * @alpha */ export type CssSelector = () => string; -/** - * @alpha - */ export interface Selectors { - [key: string]: StringSelector | FunctionSelector | CssSelector | UrlSelector | Selectors; + [key: string]: StringSelector | FunctionSelector | FunctionSelectorTwoArgs | CssSelector | UrlSelector | Selectors; } -/** - * @alpha - */ export type E2ESelectors = { [P in keyof S]: S[P]; }; -/** - * @alpha - */ export interface UrlSelector extends Selectors { url: string | FunctionSelector; } + +export type VersionedFunctionSelector1 = Record; + +export type VersionedFunctionSelector2 = Record; + +export type VersionedStringSelector = Record; + +export type VersionedCssSelector = Record; + +export type VersionedUrlSelector = Record; + +export type VersionedSelectors = + | VersionedFunctionSelector1 + | VersionedFunctionSelector2 + | VersionedStringSelector + | VersionedCssSelector + | VersionedUrlSelector; + +export type VersionedSelectorGroup = { + [property: string]: VersionedSelectors | VersionedSelectorGroup; +}; + +export type SelectorsOf = { + [Property in keyof T]: T[Property] extends VersionedFunctionSelector1 + ? FunctionSelector + : T[Property] extends VersionedFunctionSelector2 + ? FunctionSelectorTwoArgs + : T[Property] extends VersionedStringSelector + ? StringSelector + : T[Property] extends VersionedCssSelector + ? CssSelector + : T[Property] extends VersionedUrlSelector + ? UrlSelector + : SelectorsOf; +}; diff --git a/packages/grafana-eslint-rules/package.json b/packages/grafana-eslint-rules/package.json index 596158c9a68..06e02805a6f 100644 --- a/packages/grafana-eslint-rules/package.json +++ b/packages/grafana-eslint-rules/package.json @@ -14,11 +14,11 @@ "directory": "packages/grafana-eslint-rules" }, "dependencies": { - "@typescript-eslint/utils": "^6.0.0" + "@typescript-eslint/utils": "^8.9.0" }, "devDependencies": { - "@typescript-eslint/types": "^6.0.0", - "eslint": "8.57.0", + "@typescript-eslint/types": "^8.9.0", + "eslint": "9.14.0", "tslib": "2.7.0" }, "private": true diff --git a/packages/grafana-eslint-rules/rules/no-aria-label-e2e-selectors.cjs b/packages/grafana-eslint-rules/rules/no-aria-label-e2e-selectors.cjs index 3662c0abfea..c8a71bdfbed 100644 --- a/packages/grafana-eslint-rules/rules/no-aria-label-e2e-selectors.cjs +++ b/packages/grafana-eslint-rules/rules/no-aria-label-e2e-selectors.cjs @@ -39,7 +39,7 @@ const rule = createRule({ const identifiers = findIdentifiers(node.value.expression); for (const identifier of identifiers) { - const scope = context.getScope(); + const scope = context.sourceCode.getScope(node); // Find the actual "scoped variable" to inspect it's import // This is relatively fragile, and will fail to find the import if the variable is reassigned diff --git a/packages/grafana-eslint-rules/rules/no-untranslated-strings.cjs b/packages/grafana-eslint-rules/rules/no-untranslated-strings.cjs index b1e90de21f4..3c780ed2f84 100644 --- a/packages/grafana-eslint-rules/rules/no-untranslated-strings.cjs +++ b/packages/grafana-eslint-rules/rules/no-untranslated-strings.cjs @@ -9,7 +9,7 @@ const noUntranslatedStrings = createRule({ create(context) { return { JSXText(node) { - const ancestors = context.getAncestors(); + const ancestors = context.sourceCode.getAncestors(node); const isEmpty = !node.value.trim(); const hasTransAncestor = ancestors.some((ancestor) => { return ( diff --git a/packages/grafana-eslint-rules/rules/theme-token-usage.cjs b/packages/grafana-eslint-rules/rules/theme-token-usage.cjs index abb56ea6b49..506f8db8c59 100644 --- a/packages/grafana-eslint-rules/rules/theme-token-usage.cjs +++ b/packages/grafana-eslint-rules/rules/theme-token-usage.cjs @@ -8,7 +8,7 @@ const themeTokenUsage = createRule({ return { Identifier: function (node) { if (node.name === 'theme') { - const ancestors = context.getAncestors().reverse(); + const ancestors = context.sourceCode.getAncestors(node).reverse(); const paths = []; let lastAncestor = null; for (const ancestor of ancestors) { diff --git a/packages/grafana-eslint-rules/tests/no-aria-label-e2e-selectors.test.js b/packages/grafana-eslint-rules/tests/no-aria-label-e2e-selectors.test.js index 5fdd4c86390..60ef0a89644 100644 --- a/packages/grafana-eslint-rules/tests/no-aria-label-e2e-selectors.test.js +++ b/packages/grafana-eslint-rules/tests/no-aria-label-e2e-selectors.test.js @@ -3,11 +3,13 @@ import { RuleTester } from 'eslint'; import noAriaLabelE2ESelector from '../rules/no-aria-label-e2e-selectors.cjs'; RuleTester.setDefaultConfig({ - parserOptions: { + languageOptions: { ecmaVersion: 2018, sourceType: 'module', - ecmaFeatures: { - jsx: true, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, }, }, }); diff --git a/packages/grafana-eslint-rules/tests/no-border-radius-literal.test.js b/packages/grafana-eslint-rules/tests/no-border-radius-literal.test.js index 33699113983..a4a90c150c6 100644 --- a/packages/grafana-eslint-rules/tests/no-border-radius-literal.test.js +++ b/packages/grafana-eslint-rules/tests/no-border-radius-literal.test.js @@ -3,11 +3,13 @@ import { RuleTester } from 'eslint'; import noBorderRadiusLiteral from '../rules/no-border-radius-literal.cjs'; RuleTester.setDefaultConfig({ - parserOptions: { + languageOptions: { ecmaVersion: 2018, sourceType: 'module', - ecmaFeatures: { - jsx: true, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, }, }, }); diff --git a/packages/grafana-flamegraph/.eslintrc b/packages/grafana-flamegraph/.eslintrc deleted file mode 100644 index 0dbc8077ffc..00000000000 --- a/packages/grafana-flamegraph/.eslintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "rules": { - "no-restricted-imports": [ - "error", - { - "patterns": ["@grafana/runtime", "@grafana/e2e", "@grafana/e2e-selectors/*"] - } - ] - }, - "overrides": [ - { - "files": ["**/*.{test,story}.{ts,tsx}"], - "rules": { - "no-restricted-imports": "off", - "react/prop-types": "off" - } - } - ] -} diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 05bcf27a7db..0edf8356a2f 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -43,10 +43,10 @@ "not IE 11" ], "dependencies": { - "@emotion/css": "11.13.4", + "@emotion/css": "11.13.5", "@grafana/data": "11.4.0-pre", "@grafana/ui": "11.4.0-pre", - "@leeoniya/ufuzzy": "1.0.14", + "@leeoniya/ufuzzy": "1.0.16", "d3": "^7.8.5", "lodash": "4.17.21", "react": "18.2.0", @@ -56,9 +56,9 @@ "tslib": "2.7.0" }, "devDependencies": { - "@babel/core": "7.25.8", - "@babel/preset-env": "7.25.8", - "@babel/preset-react": "7.25.7", + "@babel/core": "7.26.0", + "@babel/preset-env": "7.26.0", + "@babel/preset-react": "7.25.9", "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "15.3.0", "@testing-library/dom": "10.4.0", @@ -67,8 +67,8 @@ "@testing-library/user-event": "14.5.2", "@types/d3": "^7", "@types/jest": "^29.5.4", - "@types/lodash": "4.17.12", - "@types/node": "20.16.13", + "@types/lodash": "4.17.13", + "@types/node": "20.16.15", "@types/react": "18.3.3", "@types/react-virtualized-auto-sizer": "1.0.4", "@types/tinycolor2": "1.4.6", diff --git a/packages/grafana-flamegraph/src/FlameGraph/dataTransform.ts b/packages/grafana-flamegraph/src/FlameGraph/dataTransform.ts index fb7b921f4dd..51b41f97397 100644 --- a/packages/grafana-flamegraph/src/FlameGraph/dataTransform.ts +++ b/packages/grafana-flamegraph/src/FlameGraph/dataTransform.ts @@ -237,7 +237,7 @@ export function checkFields(data: DataFrame): CheckFieldsResult | undefined { for (const field of fields) { const [name, types] = field; - const frameField = data.fields.find((f) => f.name === name); + const frameField = data?.fields.find((f) => f.name === name); if (!frameField) { missingFields.push(name); continue; diff --git a/packages/grafana-icons/package.json b/packages/grafana-icons/package.json index 71f7dbb073b..caba46ada87 100644 --- a/packages/grafana-icons/package.json +++ b/packages/grafana-icons/package.json @@ -34,7 +34,7 @@ "build": "yarn generate && rollup -c rollup.config.ts --configPlugin esbuild" }, "devDependencies": { - "@babel/core": "7.25.8", + "@babel/core": "7.26.0", "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-typescript": "^12.1.0", @@ -45,7 +45,7 @@ "@svgr/plugin-prettier": "^8.1.0", "@svgr/plugin-svgo": "^8.1.0", "@types/babel__core": "^7", - "@types/node": "20.16.13", + "@types/node": "20.16.15", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "esbuild": "0.24.0", diff --git a/packages/grafana-icons/svg/asserts.svg b/packages/grafana-icons/svg/asserts.svg index 31a10a9236b..225853a3ed2 100644 --- a/packages/grafana-icons/svg/asserts.svg +++ b/packages/grafana-icons/svg/asserts.svg @@ -1,14 +1,3 @@ - - - - - - - - - - - - - + + diff --git a/packages/grafana-o11y-ds-frontend/package.json b/packages/grafana-o11y-ds-frontend/package.json index 9c9638720c2..543d303bd14 100644 --- a/packages/grafana-o11y-ds-frontend/package.json +++ b/packages/grafana-o11y-ds-frontend/package.json @@ -17,14 +17,14 @@ "typecheck": "tsc --emitDeclarationOnly false --noEmit" }, "dependencies": { - "@emotion/css": "11.13.4", + "@emotion/css": "11.13.5", "@grafana/data": "11.4.0-pre", "@grafana/e2e-selectors": "11.4.0-pre", - "@grafana/experimental": "2.1.2", + "@grafana/experimental": "2.1.4", "@grafana/runtime": "11.4.0-pre", "@grafana/schema": "11.4.0-pre", "@grafana/ui": "11.4.0-pre", - "react-select": "5.8.1", + "react-select": "5.8.3", "react-use": "17.5.1", "rxjs": "7.8.1", "tslib": "2.7.0" @@ -36,7 +36,7 @@ "@testing-library/react": "16.0.1", "@testing-library/user-event": "14.5.2", "@types/jest": "^29.5.4", - "@types/node": "20.16.13", + "@types/node": "20.16.15", "@types/react": "18.3.3", "@types/systemjs": "6.15.1", "jest": "^29.6.4", diff --git a/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx b/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx index 9af49fd9ae5..d94e09b0411 100644 --- a/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/TraceToLogs/TraceToLogsSettings.tsx @@ -126,6 +126,7 @@ export function TraceToLogsSettings({ options, onOptionsChange }: Props) { datasourceUid: ds.uid, }) } + onClear={() => updateTracesToLogs({ datasourceUid: undefined })} /> diff --git a/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx b/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx index 26359c41cba..98cff44f304 100644 --- a/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/TraceToMetrics/TraceToMetricsSettings.tsx @@ -57,24 +57,14 @@ export function TraceToMetricsSettings({ options, onOptionsChange }: Props) { datasourceUid: ds.uid, }) } - /> - - {options.jsonData.tracesToMetrics?.datasourceUid ? ( - - ) : null} + }) + } + /> + diff --git a/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx b/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx index ef51352916b..9b2d5ca31cc 100644 --- a/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx +++ b/packages/grafana-o11y-ds-frontend/src/TraceToProfiles/TraceToProfilesSettings.tsx @@ -81,6 +81,12 @@ export function TraceToProfilesSettings({ options, onOptionsChange }: Props) { datasourceUid: ds.uid, }); }} + onClear={() => { + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToProfiles', { + ...options.jsonData.tracesToProfiles, + datasourceUid: undefined, + }); + }} /> diff --git a/packages/grafana-plugin-configs/package.json b/packages/grafana-plugin-configs/package.json index 1455884b66b..ee0fa912cc0 100644 --- a/packages/grafana-plugin-configs/package.json +++ b/packages/grafana-plugin-configs/package.json @@ -11,7 +11,7 @@ "@swc/core": "1.4.2", "@types/eslint": "9.6.1", "copy-webpack-plugin": "12.0.2", - "eslint": "8.57.0", + "eslint": "9.14.0", "eslint-webpack-plugin": "4.2.0", "fork-ts-checker-webpack-plugin": "9.0.2", "glob": "11.0.0", diff --git a/packages/grafana-plugin-configs/webpack.config.ts b/packages/grafana-plugin-configs/webpack.config.ts index d78b9cd3097..14432d95939 100644 --- a/packages/grafana-plugin-configs/webpack.config.ts +++ b/packages/grafana-plugin-configs/webpack.config.ts @@ -212,6 +212,7 @@ const config = async (env: Record): Promise => { path.basename(process.cwd()), '.eslintcache' ), + configType: 'flat', }), ] : []), diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 3712962f02c..9a45874a415 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -36,21 +36,21 @@ "postpack": "mv package.json.bak package.json" }, "dependencies": { - "@emotion/css": "11.13.4", - "@floating-ui/react": "0.26.25", + "@emotion/css": "11.13.5", + "@floating-ui/react": "0.26.28", "@grafana/data": "11.4.0-pre", - "@grafana/experimental": "2.1.2", - "@grafana/faro-web-sdk": "1.10.2", + "@grafana/experimental": "2.1.4", + "@grafana/faro-web-sdk": "1.12.2", "@grafana/runtime": "11.4.0-pre", "@grafana/schema": "11.4.0-pre", "@grafana/ui": "11.4.0-pre", "@hello-pangea/dnd": "17.0.0", - "@leeoniya/ufuzzy": "1.0.14", + "@leeoniya/ufuzzy": "1.0.16", "@lezer/common": "1.2.3", "@lezer/highlight": "1.2.1", "@lezer/lr": "1.4.2", - "@prometheus-io/lezer-promql": "0.54.1", - "@reduxjs/toolkit": "2.2.8", + "@prometheus-io/lezer-promql": "0.55.1", + "@reduxjs/toolkit": "2.3.0", "d3": "7.9.0", "date-fns": "3.6.0", "debounce-promise": "3.1.2", @@ -58,14 +58,14 @@ "lodash": "4.17.21", "lru-cache": "10.2.2", "marked": "12.0.2", - "marked-mangle": "1.1.9", + "marked-mangle": "1.1.10", "moment": "2.30.1", "moment-timezone": "0.5.46", "monaco-promql": "1.7.4", "pluralize": "8.0.0", "prismjs": "1.29.0", "react-highlight-words": "0.20.0", - "react-select": "5.8.1", + "react-select": "5.8.3", "react-use": "17.5.1", "react-window": "1.8.10", "rxjs": "7.8.1", @@ -81,40 +81,40 @@ "@rollup/plugin-image": "3.0.3", "@rollup/plugin-node-resolve": "15.3.0", "@swc/core": "1.4.2", - "@swc/helpers": "0.5.13", + "@swc/helpers": "0.5.15", "@testing-library/dom": "10.4.0", - "@testing-library/jest-dom": "6.5.0", + "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.0.1", "@testing-library/user-event": "14.5.2", "@types/d3": "7.4.3", "@types/debounce-promise": "3.1.9", "@types/eslint": "9.6.1", - "@types/jest": "29.5.13", - "@types/jquery": "3.5.31", - "@types/lodash": "4.17.12", - "@types/node": "20.16.13", + "@types/jest": "29.5.14", + "@types/jquery": "3.5.32", + "@types/lodash": "4.17.13", + "@types/node": "20.16.15", "@types/pluralize": "^0.0.33", - "@types/prismjs": "1.26.4", + "@types/prismjs": "1.26.5", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/react-highlight-words": "0.20.0", "@types/react-window": "1.8.8", "@types/semver": "7.5.8", "@types/uuid": "9.0.8", - "@typescript-eslint/eslint-plugin": "6.21.0", - "@typescript-eslint/parser": "6.21.0", + "@typescript-eslint/eslint-plugin": "8.9.0", + "@typescript-eslint/parser": "8.9.0", "copy-webpack-plugin": "12.0.2", "css-loader": "7.1.2", "esbuild": "0.24.0", - "eslint": "8.57.0", + "eslint": "9.14.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "28.8.3", - "eslint-plugin-jsdoc": "48.11.0", - "eslint-plugin-jsx-a11y": "6.10.1", - "eslint-plugin-lodash": "7.4.0", - "eslint-plugin-react": "7.37.1", - "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "28.9.0", + "eslint-plugin-jsdoc": "50.5.0", + "eslint-plugin-jsx-a11y": "6.10.2", + "eslint-plugin-lodash": "8.0.0", + "eslint-plugin-react": "7.37.2", + "eslint-plugin-react-hooks": "5.0.0", "eslint-webpack-plugin": "4.2.0", "fork-ts-checker-webpack-plugin": "9.0.2", "glob": "11.0.0", @@ -130,8 +130,8 @@ "rollup-plugin-dts": "^6.1.1", "rollup-plugin-esbuild": "6.1.1", "rollup-plugin-node-externals": "^7.1.3", - "sass": "1.79.5", - "sass-loader": "16.0.2", + "sass": "1.80.7", + "sass-loader": "16.0.3", "style-loader": "4.0.0", "testing-library-selector": "0.3.1", "ts-node": "10.9.2", diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts index 26d425befa2..32bfcb14efc 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts @@ -3,8 +3,8 @@ import { config } from '@grafana/runtime'; import { SUGGESTIONS_LIMIT } from '../../../language_provider'; import { FUNCTIONS } from '../../../promql'; -import { getCompletions } from './completions'; -import { DataProvider, DataProviderParams } from './data_provider'; +import { filterMetricNames, getCompletions } from './completions'; +import { DataProvider, type DataProviderParams } from './data_provider'; import type { Situation } from './situation'; const history: string[] = ['previous_metric_name_1', 'previous_metric_name_2', 'previous_metric_name_3']; @@ -41,6 +41,133 @@ afterEach(() => { jest.restoreAllMocks(); }); +describe('filterMetricNames', () => { + const sampleMetrics = [ + 'http_requests_total', + 'http_requests_failed', + 'node_cpu_seconds_total', + 'node_memory_usage_bytes', + 'very_long_metric_name_with_many_underscores_and_detailed_description', + 'metric_name_1_with_extra_terms_included', + ]; + + describe('empty input', () => { + it('should return all metrics up to limit when input is empty', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: '', + limit: 3, + }); + expect(result).toEqual(sampleMetrics.slice(0, 3)); + }); + + it('should return all metrics when input is whitespace', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: ' ', + limit: 3, + }); + expect(result).toEqual(sampleMetrics.slice(0, 3)); + }); + }); + + describe('simple searches (≤ 4 terms)', () => { + it('should match exact strings', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'http_requests_total', + limit: 10, + }); + expect(result).toContainEqual('http_requests_total'); + }); + + it('should match with single character errors', () => { + // substitution + let result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'http_requezts_total', // 's' replaced with 'z' + limit: 10, + }); + expect(result).toContainEqual('http_requests_total'); + + // ransposition + result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'http_reqeust_total', // 'ue' swapped + limit: 10, + }); + expect(result).toContainEqual('http_requests_total'); + + // deletion + result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'http_reqests_total', // missing 'u' + limit: 10, + }); + expect(result).toContainEqual('http_requests_total'); + + // insertion + result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'http_reqquests_total', // extra 'q' + limit: 10, + }); + expect(result).toContainEqual('http_requests_total'); + }); + + it('should match partial strings', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'requests', // partial match + limit: 10, + }); + expect(result).toContainEqual('http_requests_total'); + expect(result).toContainEqual('http_requests_failed'); + }); + + it('should not match with multiple errors', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'htp_reqests_total', // two errors: missing 't' and missing 'u' + limit: 10, + }); + expect(result).not.toContainEqual('http_requests_total'); + }); + }); + + describe('complex searches (> 4 terms)', () => { + it('should use substring matching for each term', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'metric name 1 with extra terms', + limit: 10, + }); + expect(result).toContainEqual('metric_name_1_with_extra_terms_included'); + }); + + it('should return empty array when no metrics match all terms', () => { + const result = filterMetricNames({ + metricNames: sampleMetrics, + inputText: 'metric name 1 with nonexistent terms', + limit: 10, + }); + expect(result).toHaveLength(0); + }); + + it('should stop searching after limit is reached', () => { + const manyMetrics = Array.from({ length: 10 }, (_, i) => `metric_name_${i}_with_terms`); + + const result = filterMetricNames({ + metricNames: manyMetrics, + inputText: 'metric name with terms other words', // > 4 terms + limit: 3, + }); + + expect(result.length).toBeLessThanOrEqual(3); + }); + }); +}); + type MetricNameSituation = Extract; const metricNameCompletionSituations = ['AT_ROOT', 'IN_FUNCTION', 'EMPTY'] as MetricNameSituation[]; @@ -74,22 +201,22 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat expect(completions?.length).toBeLessThanOrEqual(expectedCompletionsCount); }); - it('should limit completions for metric names when the number of metric names is greater than the limit', async () => { + it('should limit completions for metric names when the number exceeds the limit', async () => { const situation: Situation = { type: situationType, }; const expectedCompletionsCount = getSuggestionCountForSituation(situationType, metrics.beyondLimit.length); jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValue(metrics.beyondLimit); - // No text input - dataProvider.monacoSettings.setInputInRange(''); + // Complex query + dataProvider.monacoSettings.setInputInRange('metric name one two three four five'); let completions = await getCompletions(situation, dataProvider); - expect(completions).toHaveLength(expectedCompletionsCount); + expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount); - // With text input (use fuzzy search) - dataProvider.monacoSettings.setInputInRange('name_1'); + // Simple query with fuzzy match + dataProvider.monacoSettings.setInputInRange('metric_name_'); completions = await getCompletions(situation, dataProvider); - expect(completions?.length).toBeLessThanOrEqual(expectedCompletionsCount); + expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount); }); it('should enable autocomplete suggestions update when the number of metric names is greater than the limit', async () => { @@ -115,4 +242,35 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat await getCompletions(situation, dataProvider); expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(true); }); + + it('should handle complex queries efficiently', async () => { + const situation: Situation = { + type: situationType, + }; + + const testMetrics = ['metric_name_1', 'metric_name_2', 'metric_name_1_with_extra_terms', 'unrelated_metric']; + jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValue(testMetrics); + + // Test with a complex query (> 4 terms) + dataProvider.monacoSettings.setInputInRange('metric name 1 with extra terms more'); + const completions = await getCompletions(situation, dataProvider); + + const metricCompletions = completions.filter((c) => c.type === 'METRIC_NAME'); + expect(metricCompletions.some((c) => c.label === 'metric_name_1_with_extra_terms')).toBe(true); + }); + + it('should handle multiple term queries efficiently', async () => { + const situation: Situation = { + type: situationType, + }; + + jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValue(metrics.beyondLimit); + + // Test with multiple terms + dataProvider.monacoSettings.setInputInRange('metric name 1 2 3 4 5'); + const completions = await getCompletions(situation, dataProvider); + + const expectedCompletionsCount = getSuggestionCountForSituation(situationType, metrics.beyondLimit.length); + expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount); + }); }); diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts index d93338c5ea5..95c16738d55 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts @@ -22,7 +22,31 @@ type Completion = { triggerOnInsert?: boolean; }; -const metricNamesSearchClient = new UFuzzy({ intraMode: 1 }); +const metricNamesSearch = { + // see https://github.com/leeoniya/uFuzzy?tab=readme-ov-file#how-it-works for details + multiInsert: new UFuzzy({ intraMode: 0 }), + singleError: new UFuzzy({ intraMode: 1 }), +}; + +interface MetricFilterOptions { + metricNames: string[]; + inputText: string; + limit: number; +} + +export function filterMetricNames({ metricNames, inputText, limit }: MetricFilterOptions): string[] { + if (!inputText?.trim()) { + return metricNames.slice(0, limit); + } + + const terms = metricNamesSearch.multiInsert.split(inputText); // e.g. 'some_metric_name or-another' -> ['some', 'metric', 'name', 'or', 'another'] + const isComplexSearch = terms.length > 4; + const fuzzyResults = isComplexSearch + ? metricNamesSearch.multiInsert.filter(metricNames, inputText) // for complex searches, prioritize performance by using MultiInsert fuzzy search + : metricNamesSearch.singleError.filter(metricNames, inputText); // for simple searches, prioritize flexibility by using SingleError fuzzy search + + return fuzzyResults ? fuzzyResults.slice(0, limit).map((idx) => metricNames[idx]) : []; +} // we order items like: history, functions, metrics function getAllMetricNamesCompletions(dataProvider: DataProvider): Completion[] { @@ -36,11 +60,11 @@ function getAllMetricNamesCompletions(dataProvider: DataProvider): Completion[] monacoSettings.enableAutocompleteSuggestionsUpdate(); if (monacoSettings.inputInRange) { - metricNames = - metricNamesSearchClient - .filter(metricNames, monacoSettings.inputInRange) - ?.slice(0, dataProvider.metricNamesSuggestionLimit) - .map((idx) => metricNames[idx]) ?? []; + metricNames = filterMetricNames({ + metricNames, + inputText: monacoSettings.inputInRange, + limit: dataProvider.metricNamesSuggestionLimit, + }); } else { metricNames = metricNames.slice(0, dataProvider.metricNamesSuggestionLimit); } diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/situation.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/situation.ts index 0bc0d18cdd5..c7420522b4c 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/situation.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/situation.ts @@ -15,6 +15,7 @@ import { MatrixSelector, Neq, NeqRegex, + NumberDurationLiteralInDurationContext, parser, PromQL, StringLiteral, @@ -190,7 +191,7 @@ const RESOLVERS: Resolver[] = [ fun: resolveLabelMatcher, }, { - path: [ERROR_NODE_NAME, MatrixSelector], + path: [ERROR_NODE_NAME, NumberDurationLiteralInDurationContext, MatrixSelector], fun: resolveDurations, }, { @@ -460,10 +461,10 @@ export function getSituation(text: string, pos: number): Situation | null { } /** - PromQL - Expr - VectorSelector - LabelMatchers + PromQL + Expr + VectorSelector + LabelMatchers */ const tree = parser.parse(text); diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/validation.test.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/validation.test.ts index e467257e23d..3768457dfce 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/validation.test.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/validation.test.ts @@ -49,10 +49,10 @@ describe('Monaco Query Validation', () => { query = 'max_over_time(deriv(rate(distance_covered_total[5s])[30s5s])[10m:])'; expect(validateQuery(query, query, [query], parser)).toEqual([ { - endColumn: 60, + endColumn: 59, endLineNumber: 1, - error: 'rate(distance_covered_total[5s])[30s5s]', - startColumn: 21, + error: '5s', + startColumn: 57, startLineNumber: 1, }, ]); diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index 00edfe60784..ce24ddcb93f 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -97,7 +97,6 @@ export class PrometheusDatasource basicAuth: any; withCredentials: boolean; interval: string; - queryTimeout: string | undefined; httpMethod: string; languageProvider: PrometheusLanguageProvider; exemplarTraceIdDestinations: ExemplarTraceIdDestination[] | undefined; @@ -127,7 +126,6 @@ export class PrometheusDatasource this.basicAuth = instanceSettings.basicAuth; this.withCredentials = Boolean(instanceSettings.withCredentials); this.interval = instanceSettings.jsonData.timeInterval || '15s'; - this.queryTimeout = instanceSettings.jsonData.queryTimeout; this.httpMethod = instanceSettings.jsonData.httpMethod || 'GET'; this.exemplarTraceIdDestinations = instanceSettings.jsonData.exemplarTraceIdDestinations; this.hasIncrementalQuery = instanceSettings.jsonData.incrementalQuerying ?? false; @@ -407,6 +405,7 @@ export class PrometheusDatasource ...processedTarget, refId: processedTarget.refId + InstantQueryRefIdIndex, range: false, + exemplar: false, } ); } else { diff --git a/packages/grafana-prometheus/src/query_hints.test.ts b/packages/grafana-prometheus/src/query_hints.test.ts index 34ceb13cf84..a5bd75b0f81 100644 --- a/packages/grafana-prometheus/src/query_hints.test.ts +++ b/packages/grafana-prometheus/src/query_hints.test.ts @@ -216,13 +216,11 @@ describe('getQueryHints()', () => { const datasource = mock as PrometheusDatasource; let hints = getQueryHints('foo', series, datasource); - expect(hints!.length).toBe(6); + expect(hints!.length).toBe(3); const hintsString = JSON.stringify(hints); expect(hintsString).toContain('ADD_HISTOGRAM_AVG'); expect(hintsString).toContain('ADD_HISTOGRAM_COUNT'); - expect(hintsString).toContain('ADD_HISTOGRAM_SUM'); - expect(hintsString).toContain('ADD_HISTOGRAM_FRACTION'); - expect(hintsString).toContain('ADD_HISTOGRAM_AVG'); + expect(hintsString).toContain('ADD_HISTOGRAM_QUANTILE'); }); it('returns no hints for native histogram when there are native histogram functions in the query', () => { diff --git a/packages/grafana-prometheus/src/query_hints.ts b/packages/grafana-prometheus/src/query_hints.ts index 15fb2fdede2..3fcdd66e0d2 100644 --- a/packages/grafana-prometheus/src/query_hints.ts +++ b/packages/grafana-prometheus/src/query_hints.ts @@ -50,9 +50,20 @@ export function getQueryHints(query: string, series?: unknown[], datasource?: Pr if (nativeHistogramNameMetric) { // add hints: - // histogram_avg, histogram_count, histogram_sum, histogram_fraction, histogram_stddev, histogram_stdvar + // histogram_quantile, histogram_avg, histogram_count const label = 'Selected metric is a native histogram.'; hints.push( + { + type: 'HISTOGRAM_QUANTILE', + label, + fix: { + label: 'Consider calculating aggregated quantile by adding histogram_quantile().', + action: { + type: 'ADD_HISTOGRAM_QUANTILE', + query, + }, + }, + }, { type: 'HISTOGRAM_AVG', label, @@ -74,52 +85,6 @@ export function getQueryHints(query: string, series?: unknown[], datasource?: Pr query, }, }, - }, - { - type: 'HISTOGRAM_SUM', - label, - fix: { - label: 'Consider calculating the sum of observations by adding histogram_sum().', - action: { - type: 'ADD_HISTOGRAM_SUM', - query, - }, - }, - }, - { - type: 'HISTOGRAM_FRACTION', - label, - fix: { - label: - 'Consider calculating the estimated fraction of observations between the provided lower and upper values by adding histogram_fraction().', - action: { - type: 'ADD_HISTOGRAM_FRACTION', - query, - }, - }, - }, - { - type: 'HISTOGRAM_STDDEV', - label, - fix: { - label: - 'Consider calculating the estimated standard deviation of observations by adding histogram_stddev().', - action: { - type: 'ADD_HISTOGRAM_STDDEV', - query, - }, - }, - }, - { - type: 'HISTOGRAM_STDVAR', - label, - fix: { - label: 'Consider calculating the estimated standard variance of observations by adding histogram_stdvar().', - action: { - type: 'ADD_HISTOGRAM_STDVAR', - query, - }, - }, } ); } diff --git a/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx b/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx index a98cc3a44c1..41ffb6f66bb 100644 --- a/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx +++ b/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx @@ -77,19 +77,26 @@ export const QueryPatternsModal = (props: Props) => { Kick start your query by selecting one of these queries. You can then continue to complete your query.
{Object.values(PromQueryPatternType).map((patternType) => { + const isOpen = openTabs.includes(patternType); return ( + onToggle={() => { + const action = isOpen ? 'close' : 'open'; + reportInteraction(`grafana_prom_kickstart_toggle_pattern_card`, { + action, + patternType, + }); + setOpenTabs((tabs) => // close tab if it's already open, otherwise open it tabs.includes(patternType) ? tabs.filter((t) => t !== patternType) : [...tabs, patternType] - ) - } + ); + }} >
{promQueryModeller diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx new file mode 100644 index 00000000000..d23fda227f7 --- /dev/null +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.test.tsx @@ -0,0 +1,153 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import '@testing-library/jest-dom'; + +import { DataSourceInstanceSettings, MetricFindValue } from '@grafana/data'; +import { config } from '@grafana/runtime'; + +import { PrometheusDatasource } from '../../datasource'; +import { PromOptions } from '../../types'; + +import { MetricCombobox, MetricComboboxProps } from './MetricCombobox'; + +describe('MetricCombobox', () => { + beforeAll(() => { + const mockGetBoundingClientRect = jest.fn(() => ({ + width: 120, + height: 120, + top: 0, + left: 0, + bottom: 0, + right: 0, + })); + + Object.defineProperty(Element.prototype, 'getBoundingClientRect', { + value: mockGetBoundingClientRect, + }); + }); + + const instanceSettings = { + url: 'proxied', + id: 1, + user: 'test', + password: 'mupp', + jsonData: { httpMethod: 'GET' }, + } as unknown as DataSourceInstanceSettings; + + const mockDatasource = new PrometheusDatasource(instanceSettings); + const mockValues = [{ label: 'random_metric' }, { label: 'unique_metric' }, { label: 'more_unique_metric' }]; + + // Mock metricFindQuery which will call backend API + mockDatasource.metricFindQuery = jest.fn((query: string) => { + // return Promise.resolve([]); + // Use the label values regex to get the values inside the label_values function call + const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/; + const queryValueArray = query.match(labelValuesRegex) as RegExpMatchArray; + const queryValueRaw = queryValueArray[1]; + + // Remove the wrapping regex + const queryValue = queryValueRaw.substring(queryValueRaw.indexOf('".*') + 3, queryValueRaw.indexOf('.*"')); + + // Run the regex that we'd pass into prometheus API against the strings in the test + return Promise.resolve( + mockValues + .filter((value) => value.label.match(queryValue)) + .map((result) => { + return { + text: result.label, + }; + }) as MetricFindValue[] + ); + }); + + const mockOnChange = jest.fn(); + const mockOnGetMetrics = jest.fn(() => Promise.resolve(mockValues.map((v) => ({ value: v.label })))); + + const defaultProps: MetricComboboxProps = { + metricLookupDisabled: false, + query: { + metric: '', + labels: [], + operations: [], + }, + onChange: mockOnChange, + onGetMetrics: mockOnGetMetrics, + datasource: mockDatasource, + labelsFilters: [], + variableEditor: false, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', () => { + render(); + expect(screen.getByPlaceholderText('Select metric')).toBeInTheDocument(); + }); + + it('fetches top metrics when the combobox is opened ', async () => { + render(); + + const combobox = screen.getByPlaceholderText('Select metric'); + await userEvent.click(combobox); + + expect(mockOnGetMetrics).toHaveBeenCalledTimes(1); + + const item = await screen.findByRole('option', { name: 'random_metric' }); + expect(item).toBeInTheDocument(); + }); + + it('fetches metrics for the users query', async () => { + render(); + + const combobox = screen.getByPlaceholderText('Select metric'); + await userEvent.click(combobox); + await userEvent.type(combobox, 'unique'); + + const item = await screen.findByRole('option', { name: 'unique_metric' }); + expect(item).toBeInTheDocument(); + + const negativeItem = screen.queryByRole('option', { name: 'random_metric' }); + expect(negativeItem).not.toBeInTheDocument(); + }); + + it('calls onChange with the correct value when a metric is selected', async () => { + render(); + + const combobox = screen.getByPlaceholderText('Select metric'); + await userEvent.click(combobox); + + const item = await screen.findByRole('option', { name: 'random_metric' }); + await userEvent.click(item); + + expect(mockOnChange).toHaveBeenCalledWith({ metric: 'random_metric', labels: [], operations: [] }); + }); + + it("doesn't show the metrics explorer button by default", () => { + render(); + expect(screen.queryByRole('button', { name: /open metrics explorer/i })).not.toBeInTheDocument(); + }); + + describe('when metrics explorer toggle is enabled', () => { + beforeAll(() => { + jest.replaceProperty(config, 'featureToggles', { + prometheusMetricEncyclopedia: true, + }); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('opens the metrics explorer when the button is clicked', async () => { + render( Promise.resolve([])} />); + + const button = screen.getByRole('button', { name: /open metrics explorer/i }); + await userEvent.click(button); + + expect(screen.getByText('Metrics explorer')).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx new file mode 100644 index 00000000000..f5c3c4808f5 --- /dev/null +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx @@ -0,0 +1,169 @@ +import { useCallback, useState } from 'react'; + +import { SelectableValue } from '@grafana/data'; +import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/experimental'; +import { config } from '@grafana/runtime'; +import { Button, InlineField, InlineFieldRow } from '@grafana/ui'; +import { Combobox, ComboboxOption } from '@grafana/ui/src/components/Combobox/Combobox'; + +import { PrometheusDatasource } from '../../datasource'; +import { regexifyLabelValuesQueryString } from '../parsingUtils'; +import { QueryBuilderLabelFilter } from '../shared/types'; +import { PromVisualQuery } from '../types'; + +import { MetricsModal } from './metrics-modal'; + +export interface MetricComboboxProps { + metricLookupDisabled: boolean; + query: PromVisualQuery; + onChange: (query: PromVisualQuery) => void; + onGetMetrics: () => Promise; + datasource: PrometheusDatasource; + labelsFilters: QueryBuilderLabelFilter[]; + onBlur?: () => void; + variableEditor?: boolean; +} + +export function MetricCombobox({ + datasource, + query, + onChange, + onGetMetrics, + labelsFilters, + variableEditor, +}: Readonly) { + const [metricsModalOpen, setMetricsModalOpen] = useState(false); + + /** + * Gets label_values response from prometheus API for current autocomplete query string and any existing labels filters + */ + const getMetricLabels = useCallback( + async (query: string) => { + const results = await datasource.metricFindQuery(formatKeyValueStringsForLabelValuesQuery(query, labelsFilters)); + + const resultsOptions = results.map((result) => { + return { + label: result.text, + value: result.text, + }; + }); + return resultsOptions; + }, + [datasource, labelsFilters] + ); + + const onComboboxChange = useCallback( + (opt: ComboboxOption | null) => { + onChange({ ...query, metric: opt?.value ?? '' }); + }, + [onChange, query] + ); + + const loadOptions = useCallback( + async (input: string): Promise => { + const metrics = input.length ? await getMetricLabels(input) : await onGetMetrics(); + + return metrics.map((option) => ({ + label: option.label ?? option.value, + value: option.value, + })); + }, + [getMetricLabels, onGetMetrics] + ); + + const loadMetricsExplorerMetrics = useCallback(async () => { + const allMetrics = await onGetMetrics(); + const metrics: string[] = []; + for (const metric of allMetrics) { + if (metric.value) { + metrics.push(metric.value); + } + } + + return metrics; + }, [onGetMetrics]); + + const metricsExplorerEnabled = config.featureToggles.prometheusMetricEncyclopedia; + + const asyncSelect = () => { + return ( + + + + {metricsExplorerEnabled ? ( +
} + > + {asyncSelect()} + + + ) : ( + + {asyncSelect()} + + )} + + ); +} + +export const formatPrometheusLabelFiltersToString = ( + queryString: string, + labelsFilters: QueryBuilderLabelFilter[] | undefined +): string => { + const filterArray = labelsFilters ? formatPrometheusLabelFilters(labelsFilters) : []; + + return `label_values({__name__=~".*${queryString}"${filterArray ? filterArray.join('') : ''}},__name__)`; +}; + +export const formatPrometheusLabelFilters = (labelsFilters: QueryBuilderLabelFilter[]): string[] => { + return labelsFilters.map((label) => { + return `,${label.label}="${label.value}"`; + }); +}; + +/** + * Reformat the query string and label filters to return all valid results for current query editor state + */ +const formatKeyValueStringsForLabelValuesQuery = (query: string, labelsFilters?: QueryBuilderLabelFilter[]): string => { + const queryString = regexifyLabelValuesQueryString(query); + + return formatPrometheusLabelFiltersToString(queryString, labelsFilters); +}; diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx index bdfdd52f3f0..e983eb23be9 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx @@ -12,12 +12,12 @@ import { config } from '@grafana/runtime'; import { AsyncSelect, Button, - CustomScrollbar, FormatOptionLabelMeta, getSelectStyles, Icon, InlineField, InlineFieldRow, + ScrollContainer, SelectMenuOptions, useStyles2, useTheme2, @@ -245,15 +245,9 @@ export function MetricSelect({ style={{ maxHeight: Math.round(maxHeight * 0.9) }} aria-label="Select options menu" > - + {children} - + {optionsLoaded && (
diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx index ce91d04bbdb..ae6353e8b1a 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx @@ -2,6 +2,7 @@ import { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; +import { config } from '@grafana/runtime'; import { PrometheusDatasource } from '../../datasource'; import { getMetadataString } from '../../language_provider'; @@ -12,6 +13,7 @@ import { QueryBuilderLabelFilter } from '../shared/types'; import { PromVisualQuery } from '../types'; import { LabelFilters } from './LabelFilters'; +import { MetricCombobox } from './MetricCombobox'; import { MetricSelect } from './MetricSelect'; export interface MetricsLabelsSectionProps { @@ -192,9 +194,11 @@ export function MetricsLabelsSection({ return withTemplateVariableOptions(getMetrics(datasource, query)); }, [datasource, query, withTemplateVariableOptions]); + const MetricSelectComponent = config.featureToggles.prometheusUsesCombobox ? MetricCombobox : MetricSelect; + return ( <> - ((props) => { setExplain(e.currentTarget.checked); }; + const handleOpenQueryPatternsModal = useCallback(() => { + reportInteraction('grafana_prometheus_open_kickstart_clicked', { + app: app ?? '', + }); + setQueryPatternsModalOpen(true); + }, [app]); + return ( <> ((props) => { data-testid={selectors.components.QueryBuilder.queryPatterns} variant="secondary" size="sm" - onClick={() => setQueryPatternsModalOpen((prevValue) => !prevValue)} + onClick={handleOpenQueryPatternsModal} > Kick start your query diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx index ae7eae785b6..53051953c81 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx @@ -52,7 +52,7 @@ export type MetricsModalProps = { query: PromVisualQuery; onClose: () => void; onChange: (query: PromVisualQuery) => void; - initialMetrics: string[]; + initialMetrics: string[] | (() => Promise); }; export const MetricsModal = (props: MetricsModalProps) => { @@ -70,7 +70,11 @@ export const MetricsModal = (props: MetricsModalProps) => { // *** Loading Gif dispatch(setIsLoading(true)); - const data: MetricsModalMetadata = await setMetrics(datasource, query, initialMetrics); + // Because Combobox in MetricsCombobox doesn't use the same lifecycle as Select to open the Metrics Explorer + // it might not have loaded any metrics yet, so it instead passes in an async function to get the metrics + const metrics = typeof initialMetrics === 'function' ? await initialMetrics() : initialMetrics; + + const data: MetricsModalMetadata = await setMetrics(datasource, query, metrics); dispatch( buildMetrics({ isLoading: false, diff --git a/packages/grafana-prometheus/src/querybuilder/operationUtils.ts b/packages/grafana-prometheus/src/querybuilder/operationUtils.ts index a0bda0d598f..bf5bc98c0ad 100644 --- a/packages/grafana-prometheus/src/querybuilder/operationUtils.ts +++ b/packages/grafana-prometheus/src/querybuilder/operationUtils.ts @@ -96,8 +96,8 @@ export function rangeRendererLeftWithParams( function renderParams(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) { return (model.params ?? []).map((value, index) => { const paramDef = def.params[index]; - if (paramDef.type === 'string') { - return '"' + value + '"'; + if (paramDef?.type === 'string') { + return `"${value}"`; } return value; diff --git a/packages/grafana-prometheus/src/querybuilder/parsing.ts b/packages/grafana-prometheus/src/querybuilder/parsing.ts index ecdf9044d7e..3e81d154d0e 100644 --- a/packages/grafana-prometheus/src/querybuilder/parsing.ts +++ b/packages/grafana-prometheus/src/querybuilder/parsing.ts @@ -14,7 +14,7 @@ import { LabelName, MatchingModifierClause, MatchOp, - NumberLiteral, + NumberDurationLiteral, On, ParenExpr, parser, @@ -334,7 +334,7 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont break; } - case NumberLiteral: { + case NumberDurationLiteral: { op.params.push(parseFloat(getString(expr, node))); break; } @@ -369,8 +369,8 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) { const opDef = binaryScalarOperatorToOperatorName[op]; - const leftNumber = left.type.id === NumberLiteral; - const rightNumber = right.type.id === NumberLiteral; + const leftNumber = left.type.id === NumberDurationLiteral; + const rightNumber = right.type.id === NumberDurationLiteral; const rightBinary = right.type.id === BinaryExpr; @@ -389,7 +389,7 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) { // Due to the way binary ops are parsed we can get a binary operation on the right that starts with a number which // is a factor for a current binary operation. So we have to add it as an operation now. const leftMostChild = getLeftMostChild(right); - if (leftMostChild?.type.id === NumberLiteral) { + if (leftMostChild?.type.id === NumberDurationLiteral) { visQuery.operations.push(makeBinOp(opDef, expr, leftMostChild, !!binModifier?.isBool)); } diff --git a/packages/grafana-prometheus/tsconfig.json b/packages/grafana-prometheus/tsconfig.json index 169e37aca11..bfab9da4959 100644 --- a/packages/grafana-prometheus/tsconfig.json +++ b/packages/grafana-prometheus/tsconfig.json @@ -6,7 +6,8 @@ "emitDeclarationOnly": true, "isolatedModules": true, "allowJs": true, - "rootDirs": ["."] + "rootDirs": ["."], + "resolveJsonModule": true }, "exclude": ["dist/**/*"], "extends": "@grafana/tsconfig", diff --git a/packages/grafana-runtime/.eslintrc b/packages/grafana-runtime/.eslintrc deleted file mode 100644 index 566825e111c..00000000000 --- a/packages/grafana-runtime/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "no-restricted-imports": ["error", { "patterns": ["@grafana/runtime", "@grafana/data/*", "@grafana/ui/*", "@grafana/e2e/*"] }] - } -} diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 2cf8dacaeab..d3965fdc82a 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -57,8 +57,8 @@ "@testing-library/user-event": "14.5.2", "@types/angular": "1.8.9", "@types/history": "4.7.11", - "@types/jest": "29.5.13", - "@types/lodash": "4.17.12", + "@types/jest": "29.5.14", + "@types/lodash": "4.17.13", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/systemjs": "6.15.1", diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 299db15ec35..95f175fcca5 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -140,7 +140,6 @@ export class GrafanaBootConfig implements GrafanaConfig { pluginCatalogPreinstalledPlugins: PreinstalledPlugin[] = []; pluginsCDNBaseURL = ''; expressionsEnabled = false; - customTheme?: undefined; awsAllowedAuthProviders: string[] = []; awsAssumeRoleEnabled = false; azure: AzureSettings = { @@ -183,6 +182,7 @@ export class GrafanaBootConfig implements GrafanaConfig { rudderstackSdkUrl: undefined; rudderstackConfigUrl: undefined; rudderstackIntegrationsUrl: undefined; + analyticsConsoleReporting = false; sqlConnectionLimits = { maxOpenConns: 100, maxIdleConns: 100, diff --git a/packages/grafana-runtime/src/services/LocationService.tsx b/packages/grafana-runtime/src/services/LocationService.tsx index 6a148b15482..f3a26c5b110 100644 --- a/packages/grafana-runtime/src/services/LocationService.tsx +++ b/packages/grafana-runtime/src/services/LocationService.tsx @@ -1,5 +1,6 @@ import * as H from 'history'; import React, { useContext } from 'react'; +import { BehaviorSubject, Observable } from 'rxjs'; import { deprecationWarning, UrlQueryMap, urlUtil } from '@grafana/data'; import { attachDebugger, createLogger } from '@grafana/ui'; @@ -21,6 +22,7 @@ export interface LocationService { getHistory: () => H.History; getSearch: () => URLSearchParams; getSearchObject: () => UrlQueryMap; + getLocationObservable: () => Observable; /** * This is from the old LocationSrv interface @@ -31,6 +33,7 @@ export interface LocationService { /** @internal */ export class HistoryWrapper implements LocationService { private readonly history: H.History; + private locationObservable: BehaviorSubject; constructor(history?: H.History) { // If no history passed create an in memory one if being called from test @@ -40,6 +43,12 @@ export class HistoryWrapper implements LocationService { ? H.createMemoryHistory({ initialEntries: ['/'] }) : H.createBrowserHistory({ basename: config.appSubUrl ?? '/' })); + this.locationObservable = new BehaviorSubject(this.history.location); + + this.history.listen((location) => { + this.locationObservable.next(location); + }); + this.partial = this.partial.bind(this); this.push = this.push.bind(this); this.replace = this.replace.bind(this); @@ -48,6 +57,10 @@ export class HistoryWrapper implements LocationService { this.getLocation = this.getLocation.bind(this); } + getLocationObservable() { + return this.locationObservable.asObservable(); + } + getHistory() { return this.history; } diff --git a/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts b/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts index 1597fb25b39..05f4579d0ba 100644 --- a/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts +++ b/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts @@ -21,17 +21,29 @@ export function useSidecar_EXPERIMENTAL() { throw new Error('No SidecarContext found'); } - const activePluginId = useObservable(service.activePluginIdObservable, service.activePluginId); const initialContext = useObservable(service.initialContextObservable, service.initialContext); + const activePluginId = useObservable(service.activePluginIdObservable, service.activePluginId); + const locationService = service.getLocationService(); return { activePluginId, initialContext, + locationService, // TODO: currently this allows anybody to open any app, in the future we should probably scope this to the // current app but that means we will need to incorporate this better into the plugin platform APIs which // we will do once the functionality is reasonably stable - openApp: (pluginId: string) => service.openApp(pluginId), - closeApp: (pluginId: string) => service.closeApp(pluginId), - isAppOpened: (pluginId: string) => service.isAppOpened(pluginId), + openApp: (pluginId: string, context?: unknown) => { + return service.openApp(pluginId, context); + }, + openAppV2: (pluginId: string, path?: string) => { + return service.openAppV2(pluginId, path); + }, + openAppV3: (options: { pluginId: string; path?: string; follow?: boolean }) => { + return service.openAppV3(options); + }, + closeApp: () => service.closeApp(), + isAppOpened: (pluginId: string) => { + return service.isAppOpened(pluginId); + }, }; } diff --git a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts new file mode 100644 index 00000000000..b41e0f7f70e --- /dev/null +++ b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts @@ -0,0 +1,123 @@ +import * as H from 'history'; + +import { config } from '../config'; + +import { HistoryWrapper } from './LocationService'; +import { SidecarService_EXPERIMENTAL } from './SidecarService_EXPERIMENTAL'; + +describe('SidecarService_EXPERIMENTAL', () => { + let mainLocationService: HistoryWrapper; + let sidecarService: SidecarService_EXPERIMENTAL; + + beforeAll(() => { + config.featureToggles.appSidecar = true; + }); + + afterAll(() => { + config.featureToggles.appSidecar = false; + }); + + beforeEach(() => { + mainLocationService = new HistoryWrapper(H.createMemoryHistory({ initialEntries: ['/explore'] })); + sidecarService = new SidecarService_EXPERIMENTAL(mainLocationService); + }); + + it('has the correct state after opening and closing an app', () => { + sidecarService.openApp('pluginId', { filter: 'test' }); + + expect(sidecarService.activePluginId).toBe('pluginId'); + expect(sidecarService.initialContext).toMatchObject({ filter: 'test' }); + expect(sidecarService.getLocationService().getLocation().pathname).toBe('/a/pluginId'); + + sidecarService.closeApp(); + expect(sidecarService.activePluginId).toBe(undefined); + expect(sidecarService.initialContext).toBe(undefined); + expect(sidecarService.getLocationService().getLocation().pathname).toBe('/'); + }); + + it('has the correct state after opening and closing an app v2', () => { + sidecarService.openAppV2('pluginId', '/test'); + + expect(sidecarService.activePluginId).toBe('pluginId'); + expect(sidecarService.getLocationService().getLocation().pathname).toBe('/a/pluginId/test'); + + sidecarService.closeApp(); + expect(sidecarService.activePluginId).toBe(undefined); + expect(sidecarService.initialContext).toBe(undefined); + expect(sidecarService.getLocationService().getLocation().pathname).toBe('/'); + }); + + it('has the correct state after opening and closing an app v3', () => { + sidecarService.openAppV3({ pluginId: 'pluginId', path: '/test' }); + + expect(sidecarService.activePluginId).toBe('pluginId'); + expect(sidecarService.getLocationService().getLocation().pathname).toBe('/a/pluginId/test'); + + sidecarService.closeApp(); + expect(sidecarService.activePluginId).toBe(undefined); + expect(sidecarService.initialContext).toBe(undefined); + expect(sidecarService.getLocationService().getLocation().pathname).toBe('/'); + }); + + it('reports correct opened state', () => { + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + + sidecarService.openApp('pluginId'); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + + sidecarService.closeApp(); + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + }); + + it('reports correct opened state v2', () => { + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + + sidecarService.openAppV2('pluginId'); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + + sidecarService.closeApp(); + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + }); + + it('reports correct opened state v3', () => { + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + + sidecarService.openAppV3({ pluginId: 'pluginId' }); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + + sidecarService.closeApp(); + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + }); + + it('autocloses on not allowed routes', () => { + sidecarService.openAppV3({ pluginId: 'pluginId' }); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + mainLocationService.push('/config'); + + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + }); + + it('autocloses on when changing route', () => { + sidecarService.openAppV3({ pluginId: 'pluginId' }); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + mainLocationService.push('/a/other-app'); + + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + }); + + it('does not autocloses when set to follow', () => { + sidecarService.openAppV3({ pluginId: 'pluginId', follow: true }); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + mainLocationService.push('/a/other-app'); + + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + }); + + it('autocloses on not allowed routes when set to follow', () => { + sidecarService.openAppV3({ pluginId: 'pluginId', follow: true }); + expect(sidecarService.isAppOpened('pluginId')).toBe(true); + mainLocationService.push('/config'); + + expect(sidecarService.isAppOpened('pluginId')).toBe(false); + }); +}); diff --git a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts index dd7fa8bc6ee..2656f1d7448 100644 --- a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts +++ b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts @@ -1,16 +1,25 @@ -import { BehaviorSubject } from 'rxjs'; +import * as H from 'history'; +import { pick } from 'lodash'; +import { BehaviorSubject, map, Observable } from 'rxjs'; +import { reportInteraction } from '../analytics/utils'; import { config } from '../config'; -import { locationService } from './LocationService'; +import { HistoryWrapper, locationService as mainLocationService, LocationService } from './LocationService'; -interface Options { - localStorageKey?: string; -} +// Only allow sidecar to be opened on these routes. It does not seem to make sense to keep the sidecar opened on +// config/admin pages for example. +// At this moment let's be restrictive about where the sidecar can show and add more routes if there is a need. +const ALLOW_ROUTES = [ + /(^\/d\/)/, // dashboards + /^\/explore/, // explore + explore metrics + /^\/a\/[^\/]+/, // app plugins + /^\/alerting/, +]; /** * This is a service that handles state and operation of a sidecar feature (sideview to render a second app in grafana). - * At this moment this is highly experimental and if used should be understand to break easily with newer versions. + * At this moment this is highly experimental and if used should be understood to break easily with newer versions. * None of this functionality works without a feature toggle `appSidecar` being enabled. * * Right now this being in a single service is more of a practical tradeoff for easier isolation in the future these @@ -19,18 +28,27 @@ interface Options { * @experimental */ export class SidecarService_EXPERIMENTAL { - private _activePluginId: BehaviorSubject; private _initialContext: BehaviorSubject; - private localStorageKey: string | undefined; - constructor(options: Options) { - this.localStorageKey = options.localStorageKey; - let initialId = undefined; - if (this.localStorageKey) { - initialId = localStorage.getItem(this.localStorageKey) || undefined; - } - this._activePluginId = new BehaviorSubject(initialId); + private sidecarLocationService: LocationService; + private mainLocationService: LocationService; + + // If true we don't close the sidecar when user navigates to another app or part of Grafana from where the sidecar + // was opened. + private follow = false; + + // Keep track of where the sidecar was originally opened for autoclose behaviour. + private mainLocationWhenOpened: string | undefined; + + private mainOnAllowedRoute = false; + + constructor(mainLocationService: LocationService) { this._initialContext = new BehaviorSubject(undefined); + this.mainLocationService = mainLocationService; + this.sidecarLocationService = new HistoryWrapper( + createLocationStorageHistory({ storageKey: 'grafana.sidecar.history' }) + ); + this.handleMainLocationChanges(); } private assertFeatureEnabled() { @@ -42,6 +60,48 @@ export class SidecarService_EXPERIMENTAL { return true; } + private updateMainLocationWhenOpened() { + const pathname = this.mainLocationService.getLocation().pathname; + for (const route of ALLOW_ROUTES) { + const match = pathname.match(route)?.[0]; + if (match) { + this.mainLocationWhenOpened = match; + return; + } + } + } + + /** + * Every time the main location changes we check if we should keep the sidecar open or close it based on list + * of allowed routes and also based on the follow flag when opening the app. + */ + private handleMainLocationChanges() { + this.mainOnAllowedRoute = ALLOW_ROUTES.some((prefix) => + this.mainLocationService.getLocation().pathname.match(prefix) + ); + + this.mainLocationService.getLocationObservable().subscribe((location) => { + if (!this.activePluginId) { + return; + } + this.mainOnAllowedRoute = ALLOW_ROUTES.some((prefix) => location.pathname.match(prefix)); + + if (!this.mainOnAllowedRoute) { + this.closeApp(); + return; + } + + // We check if we moved to some other app or part of grafana from where we opened the sidecar. + const isTheSameLocation = Boolean( + this.mainLocationWhenOpened && location.pathname.startsWith(this.mainLocationWhenOpened) + ); + + if (!(isTheSameLocation || this.follow)) { + this.closeApp(); + } + }); + } + /** * Get current app id of the app in sidecar. This is most probably provisional. In the future * this should be driven by URL addressing so that routing for the apps don't change. Useful just internally @@ -50,7 +110,11 @@ export class SidecarService_EXPERIMENTAL { * @experimental */ get activePluginIdObservable() { - return this._activePluginId.asObservable(); + return this.sidecarLocationService.getLocationObservable().pipe( + map((val) => { + return getPluginIdFromUrl(val?.pathname || ''); + }) + ); } /** @@ -74,44 +138,86 @@ export class SidecarService_EXPERIMENTAL { * @experimental */ get activePluginId() { - return this._activePluginId.getValue(); + return getPluginIdFromUrl(this.sidecarLocationService.getLocation().pathname); + } + + getLocationService() { + return this.sidecarLocationService; } /** * Opens an app in a sidecar. You can also pass some context object that will be then available to the app. + * @deprecated * @experimental */ openApp(pluginId: string, context?: unknown) { - if (!this.assertFeatureEnabled()) { + if (!(this.assertFeatureEnabled() && this.mainOnAllowedRoute)) { return; } - if (this.localStorageKey) { - localStorage.setItem(this.localStorageKey, pluginId); + this._initialContext.next(context); + this.openAppV3({ pluginId, follow: false }); + } + + /** + * Opens an app in a sidecar. You can also relative path inside the app to open. + * @deprecated + * @experimental + */ + openAppV2(pluginId: string, path?: string) { + this.openAppV3({ pluginId, path, follow: false }); + } + + /** + * Opens an app in a sidecar. You can also relative path inside the app to open. + * @param options.pluginId Plugin ID of the app to open + * @param options.path Relative path inside the app to open + * @param options.follow If true, the sidecar will stay open even if the main location change to another app or + * Grafana section + * + * @experimental + */ + openAppV3(options: { pluginId: string; path?: string; follow?: boolean }) { + if (!(this.assertFeatureEnabled() && this.mainOnAllowedRoute)) { + return; } - this._activePluginId.next(pluginId); - this._initialContext.next(context); + this.follow = options.follow || false; + + this.updateMainLocationWhenOpened(); + this.sidecarLocationService.push({ pathname: `/a/${options.pluginId}${options.path || ''}` }); + reportInteraction('sidecar_service_open_app', { pluginId: options.pluginId, follow: options.follow }); } /** * @experimental */ - closeApp(pluginId: string) { + closeApp() { if (!this.assertFeatureEnabled()) { return; } - if (this._activePluginId.getValue() === pluginId) { - if (this.localStorageKey) { - localStorage.removeItem(this.localStorageKey); - } - this._activePluginId.next(undefined); - this._initialContext.next(undefined); - } + + this.follow = false; + this.mainLocationWhenOpened = undefined; + this._initialContext.next(undefined); + this.sidecarLocationService.replace({ pathname: '/' }); + + reportInteraction('sidecar_service_close_app'); } /** - * This is mainly useful inside an app extensions which are executed outside of the main app context but can work - * differently depending whether their app is currently rendered or not. + * This is mainly useful inside an app extensions which are executed outside the main app context but can work + * differently depending on whether their app is currently rendered or not. + * + * This is also true only in case a sidecar is opened. In other cases, just to check if a single app is opened + * probably does not make sense. + * + * This means these are the states and the result of this function: + * Single app is opened: false (may seem strange from considering the function name, but the main point of + * this is to recognize when the app needs to do specific alteration in context of running next to second app) + * 2 apps are opened and pluginId is the one in the main window: true + * 2 apps are opened and pluginId is the one in the sidecar window: true + * 2 apps are opened and pluginId is not one of those: false + * * @experimental */ isAppOpened(pluginId: string) { @@ -119,24 +225,25 @@ export class SidecarService_EXPERIMENTAL { return false; } - if (this._activePluginId.getValue() === pluginId || getMainAppPluginId() === pluginId) { - return true; - } - - return false; + const result = !!(this.activePluginId && (this.activePluginId === pluginId || getMainAppPluginId() === pluginId)); + reportInteraction('sidecar_service_is_app_opened', { pluginId, isOpened: result }); + return result; } } -export const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL({ - localStorageKey: 'grafana.sidecar.activePluginId', -}); +const pluginIdUrlRegex = /a\/([^\/]+)/; +function getPluginIdFromUrl(url: string) { + return url.match(pluginIdUrlRegex)?.[1]; +} // The app plugin that is "open" in the main Grafana view function getMainAppPluginId() { - const { pathname } = locationService.getLocation(); + // TODO: not great but we have to get a handle on the other locationService used for the main view and easiest way + // right now is through this global singleton + const { pathname } = mainLocationService.getLocation(); // A naive way to sort of simulate core features being an app and having an appID - let mainApp = pathname.match(/\/a\/([^/]+)/)?.[1]; + let mainApp = getPluginIdFromUrl(pathname); if (!mainApp && pathname.match(/\/explore/)) { mainApp = 'explore'; } @@ -147,3 +254,88 @@ function getMainAppPluginId() { return mainApp || 'unknown'; } + +type LocalStorageHistoryOptions = { + storageKey: string; +}; + +interface LocationStorageHistory extends H.MemoryHistory { + getLocationObservable(): Observable; +} + +/** + * Simple wrapper over the memory history that persists the location in the localStorage. + * + * @param options + */ +function createLocationStorageHistory(options: LocalStorageHistoryOptions): LocationStorageHistory { + const storedLocation = localStorage.getItem(options.storageKey); + const initialEntry = storedLocation ? JSON.parse(storedLocation) : '/'; + const locationSubject = new BehaviorSubject(initialEntry); + const memoryHistory = H.createMemoryHistory({ initialEntries: [initialEntry] }); + + let currentLocation = memoryHistory.location; + + function maybeUpdateLocation() { + if (memoryHistory.location !== currentLocation) { + localStorage.setItem( + options.storageKey, + JSON.stringify(pick(memoryHistory.location, 'pathname', 'search', 'hash')) + ); + currentLocation = memoryHistory.location; + locationSubject.next(memoryHistory.location); + } + } + + // This creates a sort of proxy over the memory location just to add the localStorage persistence and the location + // observer. We could achieve the same effect by a listener but that would create a memory leak as there would be no + // reasonable way to unsubcribe the listener later on. + // Another issue is that react router for some reason does not care about proper `this` binding and just calls these + // as normal functions. So if this were to be a class we would still need to bind each of these methods to the + // instance so at that moment this just seems easier. + return { + ...memoryHistory, + // Getter aren't destructured as getter but as values, so they have to be still here even though we are not + // modifying them. + get index() { + return memoryHistory.index; + }, + get entries() { + return memoryHistory.entries; + }, + get length() { + return memoryHistory.length; + }, + get action() { + return memoryHistory.action; + }, + get location() { + return memoryHistory.location; + }, + push(location: H.Path | H.LocationDescriptor, state?: H.LocationState) { + memoryHistory.push(location, state); + maybeUpdateLocation(); + }, + replace(location: H.Path | H.LocationDescriptor, state?: H.LocationState) { + memoryHistory.replace(location, state); + maybeUpdateLocation(); + }, + go(n: number) { + memoryHistory.go(n); + maybeUpdateLocation(); + }, + goBack() { + memoryHistory.goBack(); + maybeUpdateLocation(); + }, + goForward() { + memoryHistory.goForward(); + maybeUpdateLocation(); + }, + getLocationObservable() { + return locationSubject.asObservable(); + }, + }; +} + +export const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL(mainLocationService); diff --git a/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts b/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts index 04d7293bab8..2ce4b88e85f 100644 --- a/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts +++ b/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts @@ -19,6 +19,7 @@ import { AdHocVariableFilter, } from '@grafana/data'; +import { reportInteraction } from '../analytics/utils'; import { config } from '../config'; import { BackendSrvRequest, @@ -355,8 +356,17 @@ class DataSourceWithBackend< headers: this.getRequestHeaders(), }) ) - .then((v: FetchResponse) => v.data) - .catch((err) => err.data); + .then((v: FetchResponse) => v.data) + .catch((err) => { + let properties: Record = { + plugin_id: this.meta?.id || '', + plugin_version: this.meta?.info?.version || '', + datasource_healthcheck_status: err?.data?.status || 'error', + datasource_healthcheck_message: err?.data?.message || '', + }; + reportInteraction('datasource_health_check_completed', properties); + return err.data; + }); } /** diff --git a/packages/grafana-schema/.eslintrc b/packages/grafana-schema/.eslintrc deleted file mode 100644 index 7a796b6e54f..00000000000 --- a/packages/grafana-schema/.eslintrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "rules": { - "no-restricted-imports": ["error", { "patterns": ["@grafana/*"] }] - }, - "overrides": [ - { - "files": ["**/*.test.{ts,tsx}"], - "rules": { - "no-restricted-imports": "off" - } - } - ] -} diff --git a/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts index 1c5e39ee716..756f2c575cc 100644 --- a/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts @@ -25,6 +25,10 @@ export interface GrafanaPyroscopeDataQuery extends common.DataQuery { * Specifies the query label selectors. */ labelSelector: string; + /** + * Sets the maximum number of time series. + */ + limit?: number; /** * Sets the maximum number of nodes in the flamegraph. */ diff --git a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts index 5e31328c325..1fbf39c775d 100644 --- a/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/logs/panelcfg/x/LogsPanelCfg_types.gen.ts @@ -17,6 +17,8 @@ export interface Options { displayedFields?: Array; enableLogDetails: boolean; isFilterLabelActive?: unknown; + logRowMenuIconsAfter?: unknown; + logRowMenuIconsBefore?: unknown; /** * TODO: figure out how to define callbacks */ diff --git a/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts index b8d2b5ffc49..a9b2c7ecaee 100644 --- a/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/loki/dataquery/x/LokiDataQuery_types.gen.ts @@ -33,6 +33,7 @@ export enum SupportingQueryType { export enum LokiQueryDirection { Backward = 'backward', Forward = 'forward', + Scan = 'scan', } export interface LokiDataQuery extends common.DataQuery { diff --git a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts index 357969cb116..7e1b4a63d64 100644 --- a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts @@ -21,6 +21,11 @@ export interface ArcOption { field?: string; } +export enum ZoomMode { + Cooperative = 'cooperative', + Greedy = 'greedy', +} + export interface Options { edges?: { /** @@ -46,4 +51,8 @@ export interface Options { */ arcs?: Array; }; + /** + * How to handle zoom/scroll events in the node graph + */ + zoomMode?: ZoomMode; } diff --git a/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts index 6583afdedb2..44bffb757b5 100644 --- a/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts @@ -98,7 +98,10 @@ export enum SearchTableType { * static fields are pre-set in the UI, dynamic fields are added by the user */ export enum TraceqlSearchScope { + Event = 'event', + Instrumentation = 'instrumentation', Intrinsic = 'intrinsic', + Link = 'link', Resource = 'resource', Span = 'span', Unscoped = 'unscoped', diff --git a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts index 8e98dfaa449..5e5267a9be4 100644 --- a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts @@ -12,66 +12,85 @@ import * as common from '@grafana/schema'; export const pluginVersion = "11.4.0-pre"; -/** - * Auto is "table" in the UI - */ +export enum PointShape { + Circle = 'circle', + Square = 'square', +} + export enum SeriesMapping { Auto = 'auto', Manual = 'manual', } -export enum ScatterShow { +export enum XYShowMode { Lines = 'lines', Points = 'points', PointsAndLines = 'points+lines', } /** - * Configuration for the Table/Auto mode + * NOTE: (copied from dashboard_kind.cue, since not exported) + * Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. + * It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. */ -export interface XYDimensionConfig { - exclude?: Array; - frame: number; - x?: string; +export interface MatcherConfig { + /** + * The matcher id. This is used to find the matcher implementation from registry. + */ + id: string; + /** + * The matcher options. This is specific to the matcher implementation. + */ + options?: unknown; } -export const defaultXYDimensionConfig: Partial = { - exclude: [], +export const defaultMatcherConfig: Partial = { + id: '', }; export interface FieldConfig extends common.HideableFieldConfig, common.AxisConfig { - label?: common.VisibilityMode; - labelValue?: common.TextDimensionConfig; - lineColor?: common.ColorDimensionConfig; + fillOpacity?: number; lineStyle?: common.LineStyle; lineWidth?: number; - pointColor?: common.ColorDimensionConfig; - pointSize?: common.ScaleDimensionConfig; - show?: ScatterShow; + pointShape?: PointShape; + pointSize?: { + fixed?: number; + min?: number; + max?: number; + }; + pointStrokeWidth?: number; + show?: XYShowMode; } export const defaultFieldConfig: Partial = { - label: common.VisibilityMode.Auto, - show: ScatterShow.Points, + fillOpacity: 50, + show: XYShowMode.Points, }; -export interface ScatterSeriesConfig extends FieldConfig { - frame?: number; - name?: string; - x?: string; - y?: string; +export interface XYSeriesConfig { + color?: { + matcher: MatcherConfig; + }; + frame?: { + matcher: MatcherConfig; + }; + name?: { + fixed?: string; + }; + size?: { + matcher: MatcherConfig; + }; + x?: { + matcher: MatcherConfig; + }; + y?: { + matcher: MatcherConfig; + }; } export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip { - /** - * Table Mode (auto) - */ - dims: XYDimensionConfig; - /** - * Manual Mode - */ - series: Array; - seriesMapping?: SeriesMapping; + mapping: SeriesMapping; + series: Array; } export const defaultOptions: Partial = { diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts index a67ab34ae38..2a0a2de17ba 100644 --- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts @@ -130,6 +130,10 @@ export interface VariableModel { * Custom all value */ allValue?: string; + /** + * Allow custom values to be entered in the variable + */ + allowCustomValue?: boolean; /** * Shows current selected variable text/value on the dashboard */ @@ -194,6 +198,7 @@ export interface VariableModel { } export const defaultVariableModel: Partial = { + allowCustomValue: true, includeAll: false, multi: false, options: [], diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen.ts new file mode 100644 index 00000000000..23dfe83a255 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen.ts @@ -0,0 +1,1249 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface DashboardV2Spec { + // Unique numeric identifier for the dashboard. + // `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances. + id?: number; + // Title of dashboard. + title: string; + // Description of dashboard. + description?: string; + // Configuration of dashboard cursor sync behavior. + // "Off" for no shared crosshair or tooltip (default). + // "Crosshair" for shared crosshair. + // "Tooltip" for shared crosshair AND shared tooltip. + cursorSync: DashboardCursorSync; + // When set to true, the dashboard will redraw panels at an interval matching the pixel width. + // This will keep data "moving left" regardless of the query refresh rate. This setting helps + // avoid dashboards presenting stale live data. + liveNow?: boolean; + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + preload: boolean; + // Whether a dashboard is editable or not. + editable?: boolean; + // Links with references to other dashboards or external websites. + links: DashboardLink[]; + // Tags associated with dashboard. + tags?: string[]; + timeSettings: TimeSettingsSpec; + // Configured template variables. + variables: (QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind)[]; + // |* more element types in the future + elements: Record; + annotations: AnnotationQueryKind[]; + layout: GridLayoutKind; + // Version of the JSON schema, incremented each time a Grafana update brings + // changes to said schema. + // version: will rely on k8s resource versioning, via metadata.resorceVersion + // revision?: int // for plugins only + // gnetId?: string // ??? Wat is this used for? + schemaVersion: number; +} + +export const defaultDashboardV2Spec = (): DashboardV2Spec => ({ + title: "", + cursorSync: DashboardCursorSync.Off, + preload: false, + editable: true, + links: [], + timeSettings: defaultTimeSettingsSpec(), + variables: [], + elements: {}, + annotations: [], + layout: defaultGridLayoutKind(), + schemaVersion: 39, +}); + +export interface AnnotationPanelFilter { + // Should the specified panels be included or excluded + exclude?: boolean; + // Panel IDs that should be included or excluded + ids: number[]; +} + +export const defaultAnnotationPanelFilter = (): AnnotationPanelFilter => ({ + exclude: false, + ids: [], +}); + +// "Off" for no shared crosshair or tooltip (default). +// "Crosshair" for shared crosshair. +// "Tooltip" for shared crosshair AND shared tooltip. +export enum DashboardCursorSync { + Off = "Off", + Crosshair = "Crosshair", + Tooltip = "Tooltip", +} + +export const defaultDashboardCursorSync = (): DashboardCursorSync => (DashboardCursorSync.Off); + +// Links with references to other dashboards or external resources +export interface DashboardLink { + // Title to display with the link + title: string; + // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) + // FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType` + type: DashboardLinkType; + // Icon name to be displayed with the link + icon: string; + // Tooltip to display when the user hovers their mouse over it + tooltip: string; + // Link URL. Only required/valid if the type is link + url?: string; + // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards + tags: string[]; + // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards + asDropdown: boolean; + // If true, the link will be opened in a new tab + targetBlank: boolean; + // If true, includes current template variables values in the link as query params + includeVars: boolean; + // If true, includes current time range in the link as query params + keepTime: boolean; +} + +export const defaultDashboardLink = (): DashboardLink => ({ + title: "", + type: DashboardLinkType.Link, + icon: "", + tooltip: "", + tags: [], + asDropdown: false, + targetBlank: false, + includeVars: false, + keepTime: false, +}); + +export interface DataSourceRef { + // The plugin type-id + type?: string; + // Specific datasource instance + uid?: string; +} + +export const defaultDataSourceRef = (): DataSourceRef => ({ +}); + +// Transformations allow to manipulate data returned by a query before the system applies a visualization. +// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, +// use the output of one transformation as the input to another transformation, etc. +export interface DataTransformerConfig { + // Unique identifier of transformer + id: string; + // Disabled transformations are skipped + disabled?: boolean; + // Optional frame matcher. When missing it will be applied to all results + filter?: MatcherConfig; + // Where to pull DataFrames from as input to transformation + // replaced with common.DataTopic + topic?: "series" | "annotations" | "alertStates"; + // Options to be passed to the transformer + // Valid options depend on the transformer id + options: any; +} + +export const defaultDataTransformerConfig = (): DataTransformerConfig => ({ + id: "", + options: {}, +}); + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +export interface FieldConfigSource { + // Defaults are the options applied to all fields. + defaults: FieldConfig; + // Overrides are the options applied to specific fields overriding the defaults. + overrides: { + matcher: MatcherConfig; + properties: DynamicConfigValue[]; + }[]; +} + +export const defaultFieldConfigSource = (): FieldConfigSource => ({ + defaults: defaultFieldConfig(), + overrides: [], +}); + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +export interface FieldConfig { + // The display value for this field. This supports template variables blank is auto + displayName?: string; + // This can be used by data sources that return and explicit naming structure for values and labels + // When this property is configured, this value is used rather than the default naming strategy. + displayNameFromDS?: string; + // Human readable field metadata + description?: string; + // An explicit path to the field in the datasource. When the frame meta includes a path, + // This will default to `${frame.meta.path}/${field.name} + // + // When defined, this value can be used as an identifier within the datasource scope, and + // may be used to update the results + path?: string; + // True if data source can write a value to the path. Auth/authz are supported separately + writeable?: boolean; + // True if data source field supports ad-hoc filters + filterable?: boolean; + // Unit a field should use. The unit you select is applied to all fields except time. + // You can use the units ID availables in Grafana or a custom unit. + // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts + // As custom unit, you can use the following formats: + // `suffix:` for custom unit that should go after value. + // `prefix:` for custom unit that should go before value. + // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. + // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. + // `count:` for a custom count unit. + // `currency:` for custom a currency unit. + unit?: string; + // Specify the number of decimals Grafana includes in the rendered value. + // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. + // For example 1.1234 will display as 1.12 and 100.456 will display as 100. + // To display all decimals, set the unit to `String`. + decimals?: number; + // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + min?: number; + // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + max?: number; + // Convert input values into a display string + mappings?: ValueMapping[]; + // Map numeric values to states + thresholds?: ThresholdsConfig; + // Panel color configuration + color?: FieldColor; + // The behavior when clicking on a result + links?: any[]; + // Alternative to empty string + noValue?: string; + // custom is specified by the FieldConfig field + // in panel plugin schemas. + custom?: Record; +} + +export const defaultFieldConfig = (): FieldConfig => ({ +}); + +export interface DynamicConfigValue { + id: string; + value?: any; +} + +export const defaultDynamicConfigValue = (): DynamicConfigValue => ({ + id: "", +}); + +// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. +// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. +export interface MatcherConfig { + // The matcher id. This is used to find the matcher implementation from registry. + id: string; + // The matcher options. This is specific to the matcher implementation. + options?: any; +} + +export const defaultMatcherConfig = (): MatcherConfig => ({ + id: "", +}); + +export interface Threshold { + value: number | null; + color: string; +} + +export const defaultThreshold = (): Threshold => ({ + value: 0, + color: "", +}); + +export enum ThresholdsMode { + Absolute = "absolute", + Percentage = "percentage", +} + +export const defaultThresholdsMode = (): ThresholdsMode => (ThresholdsMode.Absolute); + +export interface ThresholdsConfig { + mode: ThresholdsMode; + steps: Threshold[]; +} + +export const defaultThresholdsConfig = (): ThresholdsConfig => ({ + mode: ThresholdsMode.Absolute, + steps: [], +}); + +export type ValueMapping = ValueMap | RangeMap | RegexMap | SpecialValueMap; + +export const defaultValueMapping = (): ValueMapping => (defaultValueMap()); + +// Supported value mapping types +// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. +export enum MappingType { + Value = "value", + Range = "range", + Regex = "regex", + Special = "special", +} + +export const defaultMappingType = (): MappingType => (MappingType.Value); + +// Maps text values to a color or different display text and color. +// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +export interface ValueMap { + type: "value"; + // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } + options: Record; +} + +export const defaultValueMap = (): ValueMap => ({ + type: "value", + options: {}, +}); + +// Maps numerical ranges to a display text and color. +// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +export interface RangeMap { + type: "range"; + // Range to match against and the result to apply when the value is within the range + options: { + // Min value of the range. It can be null which means -Infinity + from: number | null; + // Max value of the range. It can be null which means +Infinity + to: number | null; + // Config to apply when the value is within the range + result: ValueMappingResult; + }; +} + +export const defaultRangeMap = (): RangeMap => ({ + type: "range", + options: { + from: 0, + to: 0, + result: defaultValueMappingResult(), +}, +}); + +// Maps regular expressions to replacement text and a color. +// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +export interface RegexMap { + type: "regex"; + // Regular expression to match against and the result to apply when the value matches the regex + options: { + // Regular expression to match against + pattern: string; + // Config to apply when the value matches the regex + result: ValueMappingResult; + }; +} + +export const defaultRegexMap = (): RegexMap => ({ + type: "regex", + options: { + pattern: "", + result: defaultValueMappingResult(), +}, +}); + +// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. +// See SpecialValueMatch to see the list of special values. +// For example, you can configure a special value mapping so that null values appear as N/A. +export interface SpecialValueMap { + type: "special"; + options: { + // Special value to match against + match: SpecialValueMatch; + // Config to apply when the value matches the special value + result: ValueMappingResult; + }; +} + +export const defaultSpecialValueMap = (): SpecialValueMap => ({ + type: "special", + options: { + match: SpecialValueMatch.True, + result: defaultValueMappingResult(), +}, +}); + +// Special value types supported by the `SpecialValueMap` +export enum SpecialValueMatch { + True = "true", + False = "false", + Null = "null", + NotANumber = "nan", + NullNan = "null+nan", + Empty = "empty", +} + +export const defaultSpecialValueMatch = (): SpecialValueMatch => (SpecialValueMatch.True); + +// Result used as replacement with text and color when the value matches +export interface ValueMappingResult { + // Text to display when the value matches + text?: string; + // Text to use when the value matches + color?: string; + // Icon to display when the value matches. Only specific visualizations. + icon?: string; + // Position in the mapping array. Only used internally. + index?: number; +} + +export const defaultValueMappingResult = (): ValueMappingResult => ({ +}); + +// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. +// Continuous color interpolates a color using the percentage of a value relative to min and max. +// Accepted values are: +// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold +// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations +// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations +// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode +// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode +// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode +// `continuous-YlRd`: Continuous Yellow-Red palette mode +// `continuous-BlPu`: Continuous Blue-Purple palette mode +// `continuous-YlBl`: Continuous Yellow-Blue palette mode +// `continuous-blues`: Continuous Blue palette mode +// `continuous-reds`: Continuous Red palette mode +// `continuous-greens`: Continuous Green palette mode +// `continuous-purples`: Continuous Purple palette mode +// `shades`: Shades of a single color. Specify a single color, useful in an override rule. +// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. +export enum FieldColorModeId { + Thresholds = "thresholds", + PaletteClassic = "palette-classic", + PaletteClassicByName = "palette-classic-by-name", + ContinuousGrYlRd = "continuous-GrYlRd", + ContinuousRdYlGr = "continuous-RdYlGr", + ContinuousBlYlRd = "continuous-BlYlRd", + ContinuousYlRd = "continuous-YlRd", + ContinuousBlPu = "continuous-BlPu", + ContinuousYlBl = "continuous-YlBl", + ContinuousBlues = "continuous-blues", + ContinuousReds = "continuous-reds", + ContinuousGreens = "continuous-greens", + ContinuousPurples = "continuous-purples", + Fixed = "fixed", + Shades = "shades", +} + +export const defaultFieldColorModeId = (): FieldColorModeId => (FieldColorModeId.Thresholds); + +// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. +export enum FieldColorSeriesByMode { + Min = "min", + Max = "max", + Last = "last", +} + +export const defaultFieldColorSeriesByMode = (): FieldColorSeriesByMode => (FieldColorSeriesByMode.Min); + +// Map a field to a color. +export interface FieldColor { + // The main color scheme mode. + mode: FieldColorModeId; + // The fixed color value for fixed or shades color modes. + fixedColor?: string; + // Some visualizations need to know how to assign a series color from by value color schemes. + seriesBy?: FieldColorSeriesByMode; +} + +export const defaultFieldColor = (): FieldColor => ({ + mode: FieldColorModeId.Thresholds, +}); + +// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) +export enum DashboardLinkType { + Link = "link", + Dashboards = "dashboards", +} + +export const defaultDashboardLinkType = (): DashboardLinkType => (DashboardLinkType.Link); + +// --- Common types --- +export interface Kind { + kind: string; + spec: any; + metadata?: any; +} + +export const defaultKind = (): Kind => ({ + kind: "", + spec: {}, +}); + +// --- Kinds --- +export interface VizConfigSpec { + pluginVersion: string; + options: Record; + fieldConfig: FieldConfigSource; +} + +export const defaultVizConfigSpec = (): VizConfigSpec => ({ + pluginVersion: "", + options: {}, + fieldConfig: defaultFieldConfigSource(), +}); + +export interface VizConfigKind { + // The kind of a VizConfigKind is the plugin ID + kind: string; + spec: VizConfigSpec; +} + +export const defaultVizConfigKind = (): VizConfigKind => ({ + kind: "", + spec: defaultVizConfigSpec(), +}); + +export interface AnnotationQuerySpec { + datasource: DataSourceRef; + query: DataQueryKind; + // TODO: Should be figured out based on datasource (Grafana ds) + // builtIn?: int + // Below are currently existing options for annotation queries + enable: boolean; + filter: AnnotationPanelFilter; + hide: boolean; + iconColor: string; + name: string; +} + +export const defaultAnnotationQuerySpec = (): AnnotationQuerySpec => ({ + datasource: defaultDataSourceRef(), + query: defaultDataQueryKind(), + enable: false, + filter: defaultAnnotationPanelFilter(), + hide: false, + iconColor: "", + name: "", +}); + +export interface AnnotationQueryKind { + kind: "AnnotationQuery"; + spec: AnnotationQuerySpec; +} + +export const defaultAnnotationQueryKind = (): AnnotationQueryKind => ({ + kind: "AnnotationQuery", + spec: defaultAnnotationQuerySpec(), +}); + +export interface QueryOptionsSpec { + timeFrom?: string; + maxDataPoints?: number; + timeShift?: string; + queryCachingTTL?: number; + interval?: string; + cacheTimeout?: string; +} + +export const defaultQueryOptionsSpec = (): QueryOptionsSpec => ({ +}); + +export interface DataQueryKind { + // The kind of a DataQueryKind is the datasource type + kind: string; + spec: Record; +} + +export const defaultDataQueryKind = (): DataQueryKind => ({ + kind: "", + spec: {}, +}); + +export interface PanelQuerySpec { + query: DataQueryKind; + datasource: DataSourceRef; + refId: string; + hidden: boolean; +} + +export const defaultPanelQuerySpec = (): PanelQuerySpec => ({ + query: defaultDataQueryKind(), + datasource: defaultDataSourceRef(), + refId: "", + hidden: false, +}); + +export interface PanelQueryKind { + kind: "PanelQuery"; + spec: PanelQuerySpec; +} + +export const defaultPanelQueryKind = (): PanelQueryKind => ({ + kind: "PanelQuery", + spec: defaultPanelQuerySpec(), +}); + +export interface TransformationKind { + // The kind of a TransformationKind is the transformation ID + kind: string; + spec: DataTransformerConfig; +} + +export const defaultTransformationKind = (): TransformationKind => ({ + kind: "", + spec: defaultDataTransformerConfig(), +}); + +export interface QueryGroupSpec { + queries: PanelQueryKind[]; + transformations: TransformationKind[]; + queryOptions: QueryOptionsSpec; +} + +export const defaultQueryGroupSpec = (): QueryGroupSpec => ({ + queries: [], + transformations: [], + queryOptions: defaultQueryOptionsSpec(), +}); + +export interface QueryGroupKind { + kind: "QueryGroup"; + spec: QueryGroupSpec; +} + +export const defaultQueryGroupKind = (): QueryGroupKind => ({ + kind: "QueryGroup", + spec: defaultQueryGroupSpec(), +}); + +// Time configuration +// It defines the default time config for the time picker, the refresh picker for the specific dashboard. +export interface TimeSettingsSpec { + // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". + timezone?: string; + // Start time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + from: string; + // End time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + to: string; + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + // v1: refresh + autoRefresh: string; + // Interval options available in the refresh picker dropdown. + // v1: timepicker.refresh_intervals + autoRefreshIntervals: string[]; + // Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. + // v1: timepicker.time_options , not exposed in the UI + quickRanges: string[]; + // Whether timepicker is visible or not. + // v1: timepicker.hidden + hideTimepicker: boolean; + // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". + weekStart: string; + // The month that the fiscal year starts on. 0 = January, 11 = December + fiscalYearStartMonth: number; + // Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. + // v1: timepicker.nowDelay + nowDelay?: string; +} + +export const defaultTimeSettingsSpec = (): TimeSettingsSpec => ({ + timezone: "browser", + from: "now-6h", + to: "now", + autoRefresh: "", + autoRefreshIntervals: [ +"5s", +"10s", +"30s", +"1m", +"5m", +"15m", +"30m", +"1h", +"2h", +"1d", +], + quickRanges: [ +"5m", +"15m", +"1h", +"6h", +"12h", +"24h", +"2d", +"7d", +"30d", +], + hideTimepicker: false, + weekStart: "", + fiscalYearStartMonth: 0, +}); + +export interface GridLayoutItemSpec { + x: number; + y: number; + width: number; + height: number; + // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference + element: ElementReference; +} + +export const defaultGridLayoutItemSpec = (): GridLayoutItemSpec => ({ + x: 0, + y: 0, + width: 0, + height: 0, + element: defaultElementReference(), +}); + +export interface GridLayoutItemKind { + kind: "GridLayoutItem"; + spec: GridLayoutItemSpec; +} + +export const defaultGridLayoutItemKind = (): GridLayoutItemKind => ({ + kind: "GridLayoutItem", + spec: defaultGridLayoutItemSpec(), +}); + +export interface GridLayoutSpec { + items: GridLayoutItemKind[]; +} + +export const defaultGridLayoutSpec = (): GridLayoutSpec => ({ + items: [], +}); + +export interface GridLayoutKind { + kind: "GridLayout"; + spec: GridLayoutSpec; +} + +export const defaultGridLayoutKind = (): GridLayoutKind => ({ + kind: "GridLayout", + spec: defaultGridLayoutSpec(), +}); + +export interface PanelSpec { + uid: string; + title: string; + description: string; + links: DashboardLink[]; + data: QueryGroupKind; + vizConfig: VizConfigKind; +} + +export const defaultPanelSpec = (): PanelSpec => ({ + uid: "", + title: "", + description: "", + links: [], + data: defaultQueryGroupKind(), + vizConfig: defaultVizConfigKind(), +}); + +export interface PanelKind { + kind: "Panel"; + spec: PanelSpec; +} + +export const defaultPanelKind = (): PanelKind => ({ + kind: "Panel", + spec: defaultPanelSpec(), +}); + +export interface ElementReference { + kind: "ElementReference"; + name: string; +} + +export const defaultElementReference = (): ElementReference => ({ + kind: "ElementReference", + name: "", +}); + +// Variable types +export type VariableValue = VariableValueSingle | VariableValueSingle[]; + +export const defaultVariableValue = (): VariableValue => (defaultVariableValueSingle()); + +export type VariableValueSingle = string | boolean | number | CustomVariableValue; + +export const defaultVariableValueSingle = (): VariableValueSingle => (""); + +// Custom formatter variable +export interface CustomFormatterVariable { + name: string; + type: VariableType; + multi: boolean; + includeAll: boolean; +} + +export const defaultCustomFormatterVariable = (): CustomFormatterVariable => ({ + name: "", + type: VariableType.Query, + multi: false, + includeAll: false, +}); + +// Custom variable value +export interface CustomVariableValue { + // The format name or function used in the expression + formatter: string | VariableCustomFormatterFn; +} + +export const defaultCustomVariableValue = (): CustomVariableValue => ({ + formatter: "", +}); + +// Custom formatter function +export interface VariableCustomFormatterFn { + value: any; + legacyVariableModel: { + name: string; + type: VariableType; + multi: boolean; + includeAll: boolean; + }; + legacyDefaultFormatter?: VariableCustomFormatterFn; +} + +export const defaultVariableCustomFormatterFn = (): VariableCustomFormatterFn => ({ + value: {}, + legacyVariableModel: { + name: "", + type: VariableType.Query, + multi: false, + includeAll: false, +}, +}); + +// Dashboard variable type +// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. +// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). +// `constant`: Define a hidden constant. +// `datasource`: Quickly change the data source for an entire dashboard. +// `interval`: Interval variables represent time spans. +// `textbox`: Display a free text input field with an optional default value. +// `custom`: Define the variable options manually using a comma-separated list. +// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables +export enum VariableType { + Query = "query", + Adhoc = "adhoc", + Groupby = "groupby", + Constant = "constant", + Datasource = "datasource", + Interval = "interval", + Textbox = "textbox", + Custom = "custom", + System = "system", + Snapshot = "snapshot", +} + +export const defaultVariableType = (): VariableType => (VariableType.Query); + +// Sort variable options +// Accepted values are: +// `disabled`: No sorting +// `alphabeticalAsc`: Alphabetical ASC +// `alphabeticalDesc`: Alphabetical DESC +// `numericalAsc`: Numerical ASC +// `numericalDesc`: Numerical DESC +// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC +// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC +// `naturalAsc`: Natural ASC +// `naturalDesc`: Natural DESC +// VariableSort enum with default value +export enum VariableSort { + Disabled = "disabled", + AlphabeticalAsc = "alphabeticalAsc", + AlphabeticalDesc = "alphabeticalDesc", + NumericalAsc = "numericalAsc", + NumericalDesc = "numericalDesc", + AlphabeticalCaseInsensitiveAsc = "alphabeticalCaseInsensitiveAsc", + AlphabeticalCaseInsensitiveDesc = "alphabeticalCaseInsensitiveDesc", + NaturalAsc = "naturalAsc", + NaturalDesc = "naturalDesc", +} + +export const defaultVariableSort = (): VariableSort => (VariableSort.Disabled); + +// Options to config when to refresh a variable +// `never`: Never refresh the variable +// `onDashboardLoad`: Queries the data source every time the dashboard loads. +// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes. +export enum VariableRefresh { + Never = "never", + OnDashboardLoad = "onDashboardLoad", + OnTimeRangeChanged = "onTimeRangeChanged", +} + +export const defaultVariableRefresh = (): VariableRefresh => (VariableRefresh.Never); + +// Determine if the variable shows on dashboard +// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing). +export enum VariableHide { + DontHide = "dontHide", + HideLabel = "hideLabel", + HideVariable = "hideVariable", +} + +export const defaultVariableHide = (): VariableHide => (VariableHide.DontHide); + +// FIXME: should we introduce this? --- Variable value option +export interface VariableValueOption { + label: string; + value: VariableValueSingle; + group?: string; +} + +export const defaultVariableValueOption = (): VariableValueOption => ({ + label: "", + value: defaultVariableValueSingle(), +}); + +// Variable option specification +export interface VariableOption { + // Whether the option is selected or not + selected?: boolean; + // Text to be displayed for the option + text: string | string[]; + // Value of the option + value: string | string[]; +} + +export const defaultVariableOption = (): VariableOption => ({ + text: "", + value: "", +}); + +// Query variable specification +export interface QueryVariableSpec { + name: string; + current: VariableOption; + label?: string; + hide: VariableHide; + refresh: VariableRefresh; + skipUrlSync: boolean; + description?: string; + datasource: DataSourceRef; + query: string | DataQueryKind; + regex: string; + sort: VariableSort; + definition?: string; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + placeholder?: string; +} + +export const defaultQueryVariableSpec = (): QueryVariableSpec => ({ + name: "", + current: { text: "", value: "", }, + hide: VariableHide.DontHide, + refresh: VariableRefresh.Never, + skipUrlSync: false, + datasource: defaultDataSourceRef(), + query: "", + regex: "", + sort: VariableSort.Disabled, + options: [], + multi: false, + includeAll: false, +}); + +// Query variable kind +export interface QueryVariableKind { + kind: "QueryVariable"; + spec: QueryVariableSpec; +} + +export const defaultQueryVariableKind = (): QueryVariableKind => ({ + kind: "QueryVariable", + spec: defaultQueryVariableSpec(), +}); + +// Text variable specification +export interface TextVariableSpec { + name: string; + current: VariableOption; + query: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultTextVariableSpec = (): TextVariableSpec => ({ + name: "", + current: { text: "", value: "", }, + query: "", + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Text variable kind +export interface TextVariableKind { + kind: "TextVariable"; + spec: TextVariableSpec; +} + +export const defaultTextVariableKind = (): TextVariableKind => ({ + kind: "TextVariable", + spec: defaultTextVariableSpec(), +}); + +// Constant variable specification +export interface ConstantVariableSpec { + name: string; + query: string; + current: VariableOption; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultConstantVariableSpec = (): ConstantVariableSpec => ({ + name: "", + query: "", + current: { text: "", value: "", }, + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Constant variable kind +export interface ConstantVariableKind { + kind: "ConstantVariable"; + spec: ConstantVariableSpec; +} + +export const defaultConstantVariableKind = (): ConstantVariableKind => ({ + kind: "ConstantVariable", + spec: defaultConstantVariableSpec(), +}); + +// Datasource variable specification +export interface DatasourceVariableSpec { + name: string; + pluginId: string; + refresh: VariableRefresh; + regex: string; + current: VariableOption; + defaultOptionEnabled: boolean; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultDatasourceVariableSpec = (): DatasourceVariableSpec => ({ + name: "", + pluginId: "", + refresh: VariableRefresh.Never, + regex: "", + current: { text: "", value: "", }, + defaultOptionEnabled: false, + options: [], + multi: false, + includeAll: false, + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Datasource variable kind +export interface DatasourceVariableKind { + kind: "DatasourceVariable"; + spec: DatasourceVariableSpec; +} + +export const defaultDatasourceVariableKind = (): DatasourceVariableKind => ({ + kind: "DatasourceVariable", + spec: defaultDatasourceVariableSpec(), +}); + +// Interval variable specification +export interface IntervalVariableSpec { + name: string; + query: string; + current: VariableOption; + options: VariableOption[]; + auto: boolean; + auto_min: string; + auto_count: number; + refresh: VariableRefresh; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultIntervalVariableSpec = (): IntervalVariableSpec => ({ + name: "", + query: "", + current: { text: "", value: "", }, + options: [], + auto: false, + auto_min: "", + auto_count: 0, + refresh: VariableRefresh.Never, + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Interval variable kind +export interface IntervalVariableKind { + kind: "IntervalVariable"; + spec: IntervalVariableSpec; +} + +export const defaultIntervalVariableKind = (): IntervalVariableKind => ({ + kind: "IntervalVariable", + spec: defaultIntervalVariableSpec(), +}); + +// Custom variable specification +export interface CustomVariableSpec { + name: string; + query: string; + current: VariableOption; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultCustomVariableSpec = (): CustomVariableSpec => ({ + name: "", + query: "", + current: defaultVariableOption(), + options: [], + multi: false, + includeAll: false, + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Custom variable kind +export interface CustomVariableKind { + kind: "CustomVariable"; + spec: CustomVariableSpec; +} + +export const defaultCustomVariableKind = (): CustomVariableKind => ({ + kind: "CustomVariable", + spec: defaultCustomVariableSpec(), +}); + +// GroupBy variable specification +export interface GroupByVariableSpec { + name: string; + datasource: DataSourceRef; + current: VariableOption; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultGroupByVariableSpec = (): GroupByVariableSpec => ({ + name: "", + datasource: defaultDataSourceRef(), + current: { text: "", value: "", }, + options: [], + multi: false, + includeAll: false, + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Group variable kind +export interface GroupByVariableKind { + kind: "GroupByVariable"; + spec: GroupByVariableSpec; +} + +export const defaultGroupByVariableKind = (): GroupByVariableKind => ({ + kind: "GroupByVariable", + spec: defaultGroupByVariableSpec(), +}); + +// Adhoc variable specification +export interface AdhocVariableSpec { + name: string; + datasource: DataSourceRef; + baseFilters: AdHocFilterWithLabels[]; + filters: AdHocFilterWithLabels[]; + defaultKeys: MetricFindValue[]; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultAdhocVariableSpec = (): AdhocVariableSpec => ({ + name: "", + datasource: defaultDataSourceRef(), + baseFilters: [], + filters: [], + defaultKeys: [], + hide: VariableHide.DontHide, + skipUrlSync: false, +}); + +// Define the MetricFindValue type +export interface MetricFindValue { + text: string; + value?: string | number; + group?: string; + expandable?: boolean; +} + +export const defaultMetricFindValue = (): MetricFindValue => ({ + text: "", +}); + +// Define the AdHocFilterWithLabels type +export interface AdHocFilterWithLabels { + key: string; + operator: string; + value: string; + values?: string[]; + keyLabel?: string; + valueLabels?: string[]; + forceEdit?: boolean; + // @deprecated + condition?: string; +} + +export const defaultAdHocFilterWithLabels = (): AdHocFilterWithLabels => ({ + key: "", + operator: "", + value: "", +}); + +// Adhoc variable kind +export interface AdhocVariableKind { + kind: "AdhocVariable"; + spec: AdhocVariableSpec; +} + +export const defaultAdhocVariableKind = (): AdhocVariableKind => ({ + kind: "AdhocVariable", + spec: defaultAdhocVariableSpec(), +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue new file mode 100644 index 00000000000..e7922f46eae --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.schema.cue @@ -0,0 +1,789 @@ +package dashboard + +DashboardV2Spec: { + // Unique numeric identifier for the dashboard. + // `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances. + id?: int64 + + // Title of dashboard. + title: string + + // Description of dashboard. + description?: string + + // Configuration of dashboard cursor sync behavior. + // "Off" for no shared crosshair or tooltip (default). + // "Crosshair" for shared crosshair. + // "Tooltip" for shared crosshair AND shared tooltip. + cursorSync: DashboardCursorSync + + // When set to true, the dashboard will redraw panels at an interval matching the pixel width. + // This will keep data "moving left" regardless of the query refresh rate. This setting helps + // avoid dashboards presenting stale live data. + liveNow?: bool + + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + preload: bool + + // Whether a dashboard is editable or not. + editable?: bool | *true + + // Links with references to other dashboards or external websites. + links: [...DashboardLink] + + // Tags associated with dashboard. + tags?: [...string] + + timeSettings: TimeSettingsSpec + + // Configured template variables. + variables: [...QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind] + + elements: [ElementReference.name]: PanelKind // |* more element types in the future + + annotations: [...AnnotationQueryKind] + + layout: GridLayoutKind + + // Version of the JSON schema, incremented each time a Grafana update brings + // changes to said schema. + schemaVersion: uint16 | *39 + + + // version: will rely on k8s resource versioning, via metadata.resorceVersion + // revision?: int // for plugins only + // gnetId?: string // ??? Wat is this used for? +} + + +AnnotationPanelFilter: { + // Should the specified panels be included or excluded + exclude?: bool | *false + + // Panel IDs that should be included or excluded + ids: [...uint8] +} + +// "Off" for no shared crosshair or tooltip (default). +// "Crosshair" for shared crosshair. +// "Tooltip" for shared crosshair AND shared tooltip. +DashboardCursorSync: "Off" | "Crosshair" | "Tooltip" + +// Links with references to other dashboards or external resources +DashboardLink: { + // Title to display with the link + title: string + // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) + // FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType` + type: DashboardLinkType + // Icon name to be displayed with the link + icon: string + // Tooltip to display when the user hovers their mouse over it + tooltip: string + // Link URL. Only required/valid if the type is link + url?: string + // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards + tags: [...string] + // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards + asDropdown: bool | *false + // If true, the link will be opened in a new tab + targetBlank: bool | *false + // If true, includes current template variables values in the link as query params + includeVars: bool | *false + // If true, includes current time range in the link as query params + keepTime: bool | *false +} + +DataSourceRef: { + // The plugin type-id + type?: string + + // Specific datasource instance + uid?: string +} + +// Transformations allow to manipulate data returned by a query before the system applies a visualization. +// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, +// use the output of one transformation as the input to another transformation, etc. +DataTransformerConfig: { + // Unique identifier of transformer + id: string + // Disabled transformations are skipped + disabled?: bool + // Optional frame matcher. When missing it will be applied to all results + filter?: MatcherConfig + // Where to pull DataFrames from as input to transformation + topic?: "series" | "annotations" | "alertStates" // replaced with common.DataTopic + // Options to be passed to the transformer + // Valid options depend on the transformer id + options: _ +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +FieldConfigSource: { + // Defaults are the options applied to all fields. + defaults: FieldConfig + // Overrides are the options applied to specific fields overriding the defaults. + overrides: [...{ + matcher: MatcherConfig + properties: [...DynamicConfigValue] + }] +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +FieldConfig: { + // The display value for this field. This supports template variables blank is auto + displayName?: string + + // This can be used by data sources that return and explicit naming structure for values and labels + // When this property is configured, this value is used rather than the default naming strategy. + displayNameFromDS?: string + + // Human readable field metadata + description?: string + + // An explicit path to the field in the datasource. When the frame meta includes a path, + // This will default to `${frame.meta.path}/${field.name} + // + // When defined, this value can be used as an identifier within the datasource scope, and + // may be used to update the results + path?: string + + // True if data source can write a value to the path. Auth/authz are supported separately + writeable?: bool + + // True if data source field supports ad-hoc filters + filterable?: bool + + // Unit a field should use. The unit you select is applied to all fields except time. + // You can use the units ID availables in Grafana or a custom unit. + // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts + // As custom unit, you can use the following formats: + // `suffix:` for custom unit that should go after value. + // `prefix:` for custom unit that should go before value. + // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. + // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. + // `count:` for a custom count unit. + // `currency:` for custom a currency unit. + unit?: string + + // Specify the number of decimals Grafana includes in the rendered value. + // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. + // For example 1.1234 will display as 1.12 and 100.456 will display as 100. + // To display all decimals, set the unit to `String`. + decimals?: number + + // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + min?: number + // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + max?: number + + // Convert input values into a display string + mappings?: [...ValueMapping] + + // Map numeric values to states + thresholds?: ThresholdsConfig + + // Panel color configuration + color?: FieldColor + + // The behavior when clicking on a result + links?: [...] + + // Alternative to empty string + noValue?: string + + // custom is specified by the FieldConfig field + // in panel plugin schemas. + custom?: {...} +} + +DynamicConfigValue: { + id: string | *"" + value?: _ +} + +// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. +// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. +MatcherConfig: { + // The matcher id. This is used to find the matcher implementation from registry. + id: string | *"" + // The matcher options. This is specific to the matcher implementation. + options?: _ +} + +Threshold: { + value: number | null + color: string +} + +ThresholdsMode: "absolute" | "percentage" + +ThresholdsConfig: { + mode: ThresholdsMode + steps: [...Threshold] +} + +ValueMapping: ValueMap | RangeMap | RegexMap | SpecialValueMap + +// Supported value mapping types +// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. +MappingType: "value" | "range" | "regex" | "special" + +// Maps text values to a color or different display text and color. +// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +ValueMap: { + type: MappingType & "value" + // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } + options: [string]: ValueMappingResult +} + +// Maps numerical ranges to a display text and color. +// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +RangeMap: { + type: MappingType & "range" + // Range to match against and the result to apply when the value is within the range + options: { + // Min value of the range. It can be null which means -Infinity + from: float64 | null + // Max value of the range. It can be null which means +Infinity + to: float64 | null + // Config to apply when the value is within the range + result: ValueMappingResult + } +} + +// Maps regular expressions to replacement text and a color. +// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +RegexMap: { + type: MappingType & "regex" + // Regular expression to match against and the result to apply when the value matches the regex + options: { + // Regular expression to match against + pattern: string + // Config to apply when the value matches the regex + result: ValueMappingResult + } +} + +// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. +// See SpecialValueMatch to see the list of special values. +// For example, you can configure a special value mapping so that null values appear as N/A. +SpecialValueMap: { + type: MappingType & "special" + options: { + // Special value to match against + match: SpecialValueMatch + // Config to apply when the value matches the special value + result: ValueMappingResult + } +} + +// Special value types supported by the `SpecialValueMap` +SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" + +// Result used as replacement with text and color when the value matches +ValueMappingResult: { + // Text to display when the value matches + text?: string + // Text to use when the value matches + color?: string + // Icon to display when the value matches. Only specific visualizations. + icon?: string + // Position in the mapping array. Only used internally. + index?: int32 +} + +// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. +// Continuous color interpolates a color using the percentage of a value relative to min and max. +// Accepted values are: +// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold +// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations +// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations +// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode +// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode +// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode +// `continuous-YlRd`: Continuous Yellow-Red palette mode +// `continuous-BlPu`: Continuous Blue-Purple palette mode +// `continuous-YlBl`: Continuous Yellow-Blue palette mode +// `continuous-blues`: Continuous Blue palette mode +// `continuous-reds`: Continuous Red palette mode +// `continuous-greens`: Continuous Green palette mode +// `continuous-purples`: Continuous Purple palette mode +// `shades`: Shades of a single color. Specify a single color, useful in an override rule. +// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. +FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" + +// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. +FieldColorSeriesByMode: "min" | "max" | "last" + +// Map a field to a color. +FieldColor: { + // The main color scheme mode. + mode: FieldColorModeId + // The fixed color value for fixed or shades color modes. + fixedColor?: string + // Some visualizations need to know how to assign a series color from by value color schemes. + seriesBy?: FieldColorSeriesByMode +} + +// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) +DashboardLinkType: "link" | "dashboards" + +// --- Common types --- +Kind: { + kind: string, + spec: _ + metadata?: _ +} + +// --- Kinds --- +VizConfigSpec: { + pluginVersion: string + options: [string]: _ + fieldConfig: FieldConfigSource +} + +VizConfigKind: { + // The kind of a VizConfigKind is the plugin ID + kind: string + spec: VizConfigSpec +} + +AnnotationQuerySpec: { + datasource: DataSourceRef + query: DataQueryKind + + // TODO: Should be figured out based on datasource (Grafana ds) + // builtIn?: int + // Below are currently existing options for annotation queries + enable: bool + filter: AnnotationPanelFilter + hide: bool + iconColor: string + name: string +} + +AnnotationQueryKind: { + kind: "AnnotationQuery" + spec: AnnotationQuerySpec +} + +QueryOptionsSpec: { + timeFrom?: string + maxDataPoints?: int + timeShift?: string + queryCachingTTL?: int + interval?: string + cacheTimeout?: string +} + +DataQueryKind: { + // The kind of a DataQueryKind is the datasource type + kind: string + spec: [string]: _ +} + +PanelQuerySpec: { + query: DataQueryKind + datasource: DataSourceRef + + refId: string + hidden: bool +} + +PanelQueryKind: { + kind: "PanelQuery" + spec: PanelQuerySpec +} + +TransformationKind: { + // The kind of a TransformationKind is the transformation ID + kind: string + spec: DataTransformerConfig +} + +QueryGroupSpec: { + queries: [...PanelQueryKind] + transformations: [...TransformationKind] + queryOptions: QueryOptionsSpec +} + +QueryGroupKind: { + kind: "QueryGroup" + spec: QueryGroupSpec +} + +// Time configuration +// It defines the default time config for the time picker, the refresh picker for the specific dashboard. +TimeSettingsSpec: { + // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". + timezone?: string | *"browser" + // Start time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + from: string | *"now-6h" + // End time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + to: string | *"now" + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + autoRefresh: string // v1: refresh + // Interval options available in the refresh picker dropdown. + autoRefreshIntervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] // v1: timepicker.refresh_intervals + // Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. + quickRanges: [...string] | *["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] // v1: timepicker.time_options , not exposed in the UI + // Whether timepicker is visible or not. + hideTimepicker: bool // v1: timepicker.hidden + // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". + weekStart: string + // The month that the fiscal year starts on. 0 = January, 11 = December + fiscalYearStartMonth: int + // Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. + nowDelay?: string // v1: timepicker.nowDelay +} + +GridLayoutItemSpec: { + x: int + y: int + width: int + height: int + element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference +} + +GridLayoutItemKind: { + kind: "GridLayoutItem" + spec: GridLayoutItemSpec +} + +GridLayoutSpec: { + items: [...GridLayoutItemKind] +} + +GridLayoutKind: { + kind: "GridLayout" + spec: GridLayoutSpec +} + +PanelSpec: { + uid: string + title: string + description: string + links: [...DashboardLink] + data: QueryGroupKind + vizConfig: VizConfigKind +} + +PanelKind: { + kind: "Panel" + spec: PanelSpec +} + +ElementReference: { + kind: "ElementReference" + name: string +} + + + + +// Start FIXME: variables - in CUE PR - this are things that should be added into the cue schema +// TODO: properties such as `hide`, `skipUrlSync`, `multi` are type boolean, and in the old schema they are conditional, +// should we make them conditional in the new schema as well? or should we make them required but default to false? + +// Variable types +VariableValue: VariableValueSingle | [...VariableValueSingle] + +VariableValueSingle: string | bool | number | CustomVariableValue + +// Custom formatter variable +CustomFormatterVariable: { + name: string + type: VariableType + multi: bool + includeAll: bool +} + +// Custom variable value +CustomVariableValue: { + // The format name or function used in the expression + formatter: *null | string | VariableCustomFormatterFn +} + +// Custom formatter function +VariableCustomFormatterFn: { + value: _ + legacyVariableModel: { + name: string + type: VariableType + multi: bool + includeAll: bool + } + legacyDefaultFormatter?: VariableCustomFormatterFn +} + +// Dashboard variable type + // `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. + // `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). + // `constant`: Define a hidden constant. + // `datasource`: Quickly change the data source for an entire dashboard. + // `interval`: Interval variables represent time spans. + // `textbox`: Display a free text input field with an optional default value. + // `custom`: Define the variable options manually using a comma-separated list. + // `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables +VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | + "system" | "snapshot" + +// Sort variable options +// Accepted values are: +// `disabled`: No sorting +// `alphabeticalAsc`: Alphabetical ASC +// `alphabeticalDesc`: Alphabetical DESC +// `numericalAsc`: Numerical ASC +// `numericalDesc`: Numerical DESC +// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC +// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC +// `naturalAsc`: Natural ASC +// `naturalDesc`: Natural DESC +// VariableSort enum with default value +VariableSort: "disabled" | "alphabeticalAsc" | "alphabeticalDesc" | "numericalAsc" | "numericalDesc" | "alphabeticalCaseInsensitiveAsc" | "alphabeticalCaseInsensitiveDesc" | "naturalAsc" | "naturalDesc" + +// Options to config when to refresh a variable +// `never`: Never refresh the variable +// `onDashboardLoad`: Queries the data source every time the dashboard loads. +// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes. +VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged" + +// Determine if the variable shows on dashboard +// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing). +VariableHide: *"dontHide" | "hideLabel" | "hideVariable" + + +// FIXME: should we introduce this? --- Variable value option +VariableValueOption: { + label: string + value: VariableValueSingle + group?: string +} + +// Variable option specification +VariableOption: { + // Whether the option is selected or not + selected?: bool + // Text to be displayed for the option + text: string | [...string] + // Value of the option +value: string | [...string] +} + +// Query variable specification +QueryVariableSpec: { + name: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + label?: string + hide: VariableHide + refresh: VariableRefresh + skipUrlSync: bool | *false + description?: string + datasource: DataSourceRef | *{} + query: string | DataQueryKind | *"" + regex: string | *"" + sort: VariableSort + definition?: string + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + placeholder?: string +} + +// Query variable kind +QueryVariableKind: { + kind: "QueryVariable" + spec: QueryVariableSpec +} + +// Text variable specification +TextVariableSpec: { + name: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + query: string | *"" + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Text variable kind +TextVariableKind: { + kind: "TextVariable" + spec: TextVariableSpec +} + +// Constant variable specification +ConstantVariableSpec: { + name: string | *"" + query: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Constant variable kind +ConstantVariableKind: { + kind: "ConstantVariable" + spec: ConstantVariableSpec +} + +// Datasource variable specification +DatasourceVariableSpec: { + name: string | *"" + pluginId: string | *"" + refresh: VariableRefresh + regex: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + defaultOptionEnabled: bool | *false + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Datasource variable kind +DatasourceVariableKind: { + kind: "DatasourceVariable" + spec: DatasourceVariableSpec +} + +// Interval variable specification +IntervalVariableSpec: { + name: string | *"" + query: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + options: [...VariableOption] | *[] + auto: bool | *false + auto_min: string | *"" + auto_count: int | *0 + refresh: VariableRefresh + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Interval variable kind +IntervalVariableKind: { + kind: "IntervalVariable" + spec: IntervalVariableSpec +} + +// Custom variable specification +CustomVariableSpec: { + name: string | *"" + query: string | *"" + current: VariableOption + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Custom variable kind +CustomVariableKind: { + kind: "CustomVariable" + spec: CustomVariableSpec +} + +// GroupBy variable specification +GroupByVariableSpec: { + name: string | *"" + datasource: DataSourceRef | *{} + current: VariableOption | *{ + text: "" + value: "" + } + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Group variable kind +GroupByVariableKind: { + kind: "GroupByVariable" + spec: GroupByVariableSpec +} + +// Adhoc variable specification +AdhocVariableSpec: { + name: string | *"" + datasource: DataSourceRef | *{} + baseFilters: [...AdHocFilterWithLabels] | *[] + filters: [...AdHocFilterWithLabels] | *[] + defaultKeys: [...MetricFindValue] | *[] + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Define the MetricFindValue type +MetricFindValue: { + text: string + value?: string | number + group?: string + expandable?: bool +} + +// Define the AdHocFilterWithLabels type +AdHocFilterWithLabels: { + key: string, + operator: string, + value: string, + values?: [...string], + keyLabel?: string, + valueLabels?: [...string], + forceEdit?: bool, + // @deprecated + condition?: string, +} + +// Adhoc variable kind +AdhocVariableKind: { + kind: "AdhocVariable" + spec: AdhocVariableSpec +} diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts new file mode 100644 index 00000000000..5b98aaf0854 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts @@ -0,0 +1,150 @@ +import { DashboardCursorSync, DashboardV2Spec } from './dashboard.gen'; + +export const handyTestingSchema: DashboardV2Spec = { + id: 1, + title: 'Default Dashboard', + description: 'This is a default dashboard', + cursorSync: DashboardCursorSync.Off, + liveNow: false, + preload: false, + editable: true, + links: [], + tags: [], + schemaVersion: 39, + timeSettings: { + timezone: 'browser', + from: 'now-6h', + to: 'now', + autoRefresh: '10s', + autoRefreshIntervals: ['10s', '1m', '5m', '15m', '30m', '1h', '6h', '12h', '1d'], + quickRanges: ['now/d', 'now/w', 'now/M', 'now/y'], + hideTimepicker: false, + weekStart: 'sunday', + fiscalYearStartMonth: 1, + }, + + elements: { + timeSeriesTest: { + kind: 'Panel', + spec: { + title: 'Time Series Test', + description: 'This is a test panel', + uid: 'timeSeriesTest', + links: [], + data: { + kind: 'QueryGroup', + spec: { + queries: [ + { + kind: 'PanelQuery', + spec: { + query: { + kind: 'prometheus', + spec: { + query: 'up', + }, + }, + datasource: { uid: 'gdev-prometheus', type: 'prometheus' }, + hidden: false, + refId: 'A', + }, + }, + ], + transformations: [ + { + kind: 'limit', + spec: { + id: 'limit', // id is competing w/ kind + options: { + limit: 10, + }, + }, + }, + ], + queryOptions: { + maxDataPoints: 100, + cacheTimeout: '1m', + }, + }, + }, + vizConfig: { + kind: 'timeseries', + spec: { + pluginVersion: '11.0.0', + options: {}, + fieldConfig: { + defaults: {}, + overrides: [], + }, + }, + }, + }, + }, + }, + layout: { + kind: 'GridLayout', + spec: { + items: [ + { + kind: 'GridLayoutItem', + spec: { + element: { kind: 'ElementReference', name: 'timeSeriesTest' }, + x: 0, + y: 0, + width: 12, + height: 6, + }, + }, + ], + }, + }, + variables: [], + annotations: [ + { + kind: 'AnnotationQuery', + spec: { + datasource: { type: 'datasource', uid: 'grafana' }, + query: { + kind: 'grafana', + spec: { + queryType: 'timeRegions', + matchAny: false, + timeRegion: { + from: '12:27', + fromDayOfWeek: 2, + timezone: 'browser', + to: '11:30', + toDayOfWeek: 2, + }, + }, + }, + enable: true, + filter: { + ids: [], + }, + hide: false, + iconColor: 'blue', + name: 'Grafana annotations', + }, + }, + { + kind: 'AnnotationQuery', + spec: { + datasource: { uid: 'gdev-prometheus', type: 'prometheus' }, + query: { + kind: 'prometheus', + spec: { + query: 'up', + }, + }, + enable: true, + filter: { + ids: [], + }, + hide: false, + iconColor: 'red', + name: 'Prometheus annotations', + }, + }, + ], +}; diff --git a/packages/grafana-sql/package.json b/packages/grafana-sql/package.json index 304e33ce459..e86d2e86918 100644 --- a/packages/grafana-sql/package.json +++ b/packages/grafana-sql/package.json @@ -14,10 +14,10 @@ "typecheck": "tsc --emitDeclarationOnly false --noEmit" }, "dependencies": { - "@emotion/css": "11.13.4", + "@emotion/css": "11.13.5", "@grafana/data": "11.4.0-pre", "@grafana/e2e-selectors": "11.4.0-pre", - "@grafana/experimental": "2.1.2", + "@grafana/experimental": "2.1.4", "@grafana/runtime": "11.4.0-pre", "@grafana/ui": "11.4.0-pre", "@react-awesome-query-builder/ui": "6.6.3", @@ -25,7 +25,7 @@ "lodash": "4.17.21", "react": "18.2.0", "react-dom": "18.2.0", - "react-select": "5.8.1", + "react-select": "5.8.3", "react-use": "17.5.1", "react-virtualized-auto-sizer": "1.0.24", "rxjs": "7.8.1", @@ -41,8 +41,8 @@ "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "14.5.2", "@types/jest": "^29.5.4", - "@types/lodash": "4.17.12", - "@types/node": "20.16.13", + "@types/lodash": "4.17.13", + "@types/node": "20.16.15", "@types/react": "18.3.3", "@types/react-dom": "18.2.25", "@types/react-virtualized-auto-sizer": "1.0.4", diff --git a/packages/grafana-sql/src/components/QueryEditor.tsx b/packages/grafana-sql/src/components/QueryEditor.tsx index 3adffb79efb..487c4f2c1c9 100644 --- a/packages/grafana-sql/src/components/QueryEditor.tsx +++ b/packages/grafana-sql/src/components/QueryEditor.tsx @@ -14,11 +14,11 @@ import { QueryHeader, QueryHeaderProps } from './QueryHeader'; import { RawEditor } from './query-editor-raw/RawEditor'; import { VisualEditor } from './visual-query-builder/VisualEditor'; -interface SqlQueryEditorProps extends QueryEditorProps { +export interface SqlQueryEditorProps extends QueryEditorProps { queryHeaderProps?: Pick; } -export function SqlQueryEditor({ +export default function SqlQueryEditor({ datasource, query, onChange, diff --git a/packages/grafana-sql/src/components/QueryEditorLazy.tsx b/packages/grafana-sql/src/components/QueryEditorLazy.tsx new file mode 100644 index 00000000000..ed76b6a492c --- /dev/null +++ b/packages/grafana-sql/src/components/QueryEditorLazy.tsx @@ -0,0 +1,27 @@ +import { css } from '@emotion/css'; +import { lazy, Suspense } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { LoadingPlaceholder, useStyles2 } from '@grafana/ui'; + +import type { SqlQueryEditorProps } from './QueryEditor'; +const QueryEditor = lazy(() => import(/* webpackChunkName: "sql-query-editor" */ './QueryEditor')); + +export function SqlQueryEditorLazy(props: SqlQueryEditorProps) { + const styles = useStyles2(getStyles); + + return ( + }> + + + ); +} + +const getStyles = (theme: GrafanaTheme2) => { + return { + container: css({ + marginBottom: 'unset', + marginLeft: theme.spacing(1), + }), + }; +}; diff --git a/packages/grafana-sql/src/components/SqlComponents.testHelpers.ts b/packages/grafana-sql/src/components/SqlComponents.testHelpers.ts index df20f537a2d..917769b50a1 100644 --- a/packages/grafana-sql/src/components/SqlComponents.testHelpers.ts +++ b/packages/grafana-sql/src/components/SqlComponents.testHelpers.ts @@ -5,7 +5,7 @@ import { DB, SQLQuery, SQLSelectableValue, ValidationResults } from '../types'; import { DatasetSelectorProps } from './DatasetSelector'; import { TableSelectorProps } from './TableSelector'; -const buildMockDB = (): DB => ({ +export const buildMockDB = (): DB => ({ datasets: jest.fn(() => Promise.resolve(['dataset1', 'dataset2'])), tables: jest.fn((_ds: string | undefined) => Promise.resolve(['table1', 'table2'])), fields: jest.fn((_query: SQLQuery, _order?: boolean) => Promise.resolve([])), @@ -13,6 +13,7 @@ const buildMockDB = (): DB => ({ Promise.resolve({ query: { refId: '123' }, error: '', isError: false, isValid: true }) ), dsID: jest.fn(() => 1234), + functions: jest.fn(() => []), getEditorLanguageDefinition: jest.fn(() => ({ id: '4567' })), toRawSql: (_query: SQLQuery) => '', }); diff --git a/packages/grafana-sql/src/components/visual-query-builder/SQLSelectRow.tsx b/packages/grafana-sql/src/components/visual-query-builder/SQLSelectRow.tsx deleted file mode 100644 index b0febbeb747..00000000000 --- a/packages/grafana-sql/src/components/visual-query-builder/SQLSelectRow.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { SelectableValue, toOption } from '@grafana/data'; - -import { COMMON_AGGREGATE_FNS } from '../../constants'; -import { QueryWithDefaults } from '../../defaults'; -import { DB, SQLQuery } from '../../types'; -import { useSqlChange } from '../../utils/useSqlChange'; - -import { SelectRow } from './SelectRow'; - -interface SQLSelectRowProps { - fields: SelectableValue[]; - query: QueryWithDefaults; - onQueryChange: (query: SQLQuery) => void; - db: DB; -} - -export function SQLSelectRow({ fields, query, onQueryChange, db }: SQLSelectRowProps) { - const { onSqlChange } = useSqlChange({ query, onQueryChange, db }); - const functions = [...COMMON_AGGREGATE_FNS, ...(db.functions?.() || [])].map(toOption); - - return ( - - ); -} diff --git a/packages/grafana-sql/src/components/visual-query-builder/SelectColumn.tsx b/packages/grafana-sql/src/components/visual-query-builder/SelectColumn.tsx new file mode 100644 index 00000000000..774ef7501f8 --- /dev/null +++ b/packages/grafana-sql/src/components/visual-query-builder/SelectColumn.tsx @@ -0,0 +1,30 @@ +import { useId } from 'react'; + +import { SelectableValue } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; +import { EditorField } from '@grafana/experimental'; +import { Select } from '@grafana/ui'; + +interface Props { + columns: Array>; + onParameterChange: (value?: string) => void; + value: SelectableValue | null; +} + +export function SelectColumn({ columns, onParameterChange, value }: Props) { + const selectInputId = useId(); + + return ( + + onParameterChange(index)(e.currentTarget.value)} + value={param.name} + aria-label={`Parameter ${index} for column ${columnIndex}`} + data-testid={selectors.components.SQLQueryEditor.selectInputParameter} + addonAfter={ + )} {canReadLDAPUser && ( - Debug LDAP Mapping + Debug LDAP Mapping )}
diff --git a/public/app/features/admin/UserListAdminPage.tsx b/public/app/features/admin/UserListAdminPage.tsx index 1456a8844f4..72f7aa49431 100644 --- a/public/app/features/admin/UserListAdminPage.tsx +++ b/public/app/features/admin/UserListAdminPage.tsx @@ -7,7 +7,7 @@ import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { LinkButton, RadioButtonGroup, useStyles2, FilterInput, EmptyState } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { contextSrv } from 'app/core/core'; -import { t } from 'app/core/internationalization'; +import { t, Trans } from 'app/core/internationalization'; import { AccessControlAction, StoreState, UserFilter } from '../../types'; @@ -94,7 +94,7 @@ const UserListAdminPageUnConnected = ({ ))} {contextSrv.hasPermission(AccessControlAction.UsersCreate) && ( - New user + New user )}
diff --git a/public/app/features/admin/UserListPage.test.tsx b/public/app/features/admin/UserListPage.test.tsx index 86e34581877..a59246fca23 100644 --- a/public/app/features/admin/UserListPage.test.tsx +++ b/public/app/features/admin/UserListPage.test.tsx @@ -38,7 +38,6 @@ const renderPage = () => { const enableEmailSharing = () => { config.featureToggles.publicDashboardsEmailSharing = true; - config.featureToggles.publicDashboards = true; config.licenseInfo = { ...config.licenseInfo, enabledFeatures: { publicDashboardsEmailSharing: true } }; }; diff --git a/public/app/features/admin/UserListPublicDashboardPage/DashboardsListModalButton.tsx b/public/app/features/admin/UserListPublicDashboardPage/DashboardsListModalButton.tsx index 9cbcc10c5e7..62081fe419d 100644 --- a/public/app/features/admin/UserListPublicDashboardPage/DashboardsListModalButton.tsx +++ b/public/app/features/admin/UserListPublicDashboardPage/DashboardsListModalButton.tsx @@ -57,7 +57,7 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis )} - • + {'•'} { }; const getStyles = (theme: GrafanaTheme2) => ({ - modal: css` - width: 590px; - `, - loading: css` - display: flex; - justify-content: center; - `, - listItem: css` - display: flex; - flex-direction: column; - gap: ${theme.spacing(0.5)}; - `, - divider: css` - margin: ${theme.spacing(1.5, 0)}; - color: ${theme.colors.text.secondary}; - `, - urlsContainer: css` - display: flex; - gap: ${theme.spacing(0.5)}; + modal: css({ + width: '590px', + }), + loading: css({ + display: 'flex', + justifyContent: 'center', + }), + listItem: css({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(0.5), + }), + divider: css({ + margin: theme.spacing(1.5, 0), + color: theme.colors.text.secondary, + }), + urlsContainer: css({ + display: 'flex', + gap: theme.spacing(0.5), - ${theme.breakpoints.down('sm')} { - flex-direction: column; - } - `, - urlsDivider: css` - color: ${theme.colors.text.secondary}; - ${theme.breakpoints.down('sm')} { - display: none; - } - `, - dashboardTitle: css` - font-size: ${theme.typography.body.fontSize}; - font-weight: ${theme.typography.fontWeightBold}; - margin-bottom: 0; - `, - url: css` - font-size: ${theme.typography.body.fontSize}; - `, + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + }, + }), + urlsDivider: css({ + color: theme.colors.text.secondary, + [theme.breakpoints.down('sm')]: { + display: 'none', + }, + }), + dashboardTitle: css({ + fontSize: theme.typography.body.fontSize, + fontWeight: theme.typography.fontWeightBold, + marginBottom: 0, + }), + url: css({ + fontSize: theme.typography.body.fontSize, + }), }); diff --git a/public/app/features/admin/UserListPublicDashboardPage/UserListPublicDashboardPage.tsx b/public/app/features/admin/UserListPublicDashboardPage/UserListPublicDashboardPage.tsx index 8e4da1a16ab..0034b8aec3b 100644 --- a/public/app/features/admin/UserListPublicDashboardPage/UserListPublicDashboardPage.tsx +++ b/public/app/features/admin/UserListPublicDashboardPage/UserListPublicDashboardPage.tsx @@ -59,7 +59,14 @@ export const UserListPublicDashboardPage = () => { {user.lastSeenAtAge} - {user.totalDashboards} dashboard(s) + + + {{ count: user.totalDashboards }} dashboards + + diff --git a/public/app/features/admin/UserOrgs.tsx b/public/app/features/admin/UserOrgs.tsx index 66ecda8c441..2e384aa51de 100644 --- a/public/app/features/admin/UserOrgs.tsx +++ b/public/app/features/admin/UserOrgs.tsx @@ -19,6 +19,7 @@ import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { fetchRoleOptions, updateUserRoles } from 'app/core/components/RolePicker/api'; import { OrgPicker, OrgSelectItem } from 'app/core/components/Select/OrgPicker'; import { contextSrv } from 'app/core/core'; +import { t, Trans } from 'app/core/internationalization'; import { AccessControlAction, Organization, OrgRole, Role, UserDTO, UserOrg } from 'app/types'; import { OrgRolePicker } from './OrgRolePicker'; @@ -60,7 +61,9 @@ export class UserOrgs extends PureComponent { const canAddToOrg = contextSrv.hasPermission(AccessControlAction.OrgUsersAdd) && !isExternalUser; return (
-

Organizations

+

+ Organizations +

@@ -80,7 +83,7 @@ export class UserOrgs extends PureComponent {
{canAddToOrg && ( )}
@@ -242,7 +245,7 @@ class UnThemedOrgRow extends PureComponent { onCancel={this.onCancelClick} onConfirm={this.onOrgRemove} > - Remove from organization + {t('admin.user-orgs.remove-button', 'Remove from organization')} )} @@ -383,10 +386,10 @@ export class AddToOrgModal extends PureComponent @@ -438,17 +441,19 @@ export function ChangeOrgButton({ interactive={true} content={
- This user's role is not editable because it is synchronized from your auth provider. Refer to - the  - - Grafana authentication docs - -  for details. + + This user's role is not editable because it is synchronized from your auth provider. Refer to + the  + + Grafana authentication docs + +  for details. +
} > @@ -465,7 +470,7 @@ export function ChangeOrgButton({ onConfirm={onOrgRoleSave} disabled={isExternalUser} > - Change role + {t('admin.user-orgs.change-role-button', 'Change role')} )} @@ -486,17 +491,19 @@ export const ExternalUserTooltip = ({ lockMessage }: ExternalUserTooltipProps) = interactive={true} content={
- This user's built-in role is not editable because it is synchronized from your auth provider. Refer to - the  - - Grafana authentication docs - -  for details. + + This user's built-in role is not editable because it is synchronized from your auth provider. Refer + to the  + + Grafana authentication docs + +  for details. +
} > diff --git a/public/app/features/admin/UserPermissions.tsx b/public/app/features/admin/UserPermissions.tsx index 0ebd00f0366..0999b343453 100644 --- a/public/app/features/admin/UserPermissions.tsx +++ b/public/app/features/admin/UserPermissions.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { ConfirmButton, RadioButtonGroup, Icon, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; +import { t, Trans } from 'app/core/internationalization'; import { ExternalUserTooltip } from 'app/features/admin/UserOrgs'; import { AccessControlAction } from 'app/types'; @@ -39,11 +40,15 @@ export function UserPermissions({ isGrafanaAdmin, isExternalUser, lockMessage, o return (
-

Permissions

+

+ Permissions +

- + {isEditing ? ( )} @@ -72,7 +77,7 @@ export function UserPermissions({ isGrafanaAdmin, isExternalUser, lockMessage, o onCancel={onCancelClick} confirmText="Change" > - Change + {t('admin.user-permissions.change-button', 'Change')} )} {isExternalUser && ( @@ -89,10 +94,10 @@ export function UserPermissions({ isGrafanaAdmin, isExternalUser, lockMessage, o } const getTooltipStyles = (theme: GrafanaTheme2) => ({ - lockMessageClass: css` - display: flex; - justify-content: flex-end; - font-style: italic; - margin-right: ${theme.spacing(0.6)}; - `, + lockMessageClass: css({ + display: 'flex', + justifyContent: 'flex-end', + fontStyle: 'italic', + marginRight: theme.spacing(0.6), + }), }); diff --git a/public/app/features/admin/UserProfile.tsx b/public/app/features/admin/UserProfile.tsx index 34ee8a40cd0..2614bd794a0 100644 --- a/public/app/features/admin/UserProfile.tsx +++ b/public/app/features/admin/UserProfile.tsx @@ -4,15 +4,16 @@ import * as React from 'react'; import { Button, ConfirmButton, ConfirmModal, Input, LegacyInputStatus, Stack } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; +import { t, Trans } from 'app/core/internationalization'; import { AccessControlAction, UserDTO } from 'app/types'; interface Props { user: UserDTO; onUserUpdate: (user: UserDTO) => void; - onUserDelete: (userId: number) => void; - onUserDisable: (userId: number) => void; - onUserEnable: (userId: number) => void; + onUserDelete: (userUid: string) => void; + onUserDisable: (userUid: string) => void; + onUserEnable: (userUid: string) => void; onPasswordChange(password: string): void; } @@ -43,11 +44,11 @@ export function UserProfile({ } }; - const handleUserDelete = () => onUserDelete(user.id); + const handleUserDelete = () => onUserDelete(user.uid); - const handleUserDisable = () => onUserDisable(user.id); + const handleUserDisable = () => onUserDisable(user.uid); - const handleUserEnable = () => onUserEnable(user.id); + const handleUserEnable = () => onUserEnable(user.uid); const onUserNameChange = (newValue: string) => { onUserUpdate({ @@ -82,11 +83,14 @@ export function UserProfile({ return (
-

User information

+

+ User information +

Grafana Admin + Grafana Admin + {isGrafanaAdmin ? ( <> - Yes + Yes ) : ( - <>No + No )}
+ - Enable user + Enable user )} {!user.isDisabled && canDisable && ( <> - Edit + {t('admin.user-profile.edit-button', 'Edit')} @@ -296,15 +300,15 @@ interface LockedRowProps { } export const LockedRow = ({ label, value, lockMessage }: LockedRowProps) => { - const lockMessageClass = css` - font-style: italic; - margin-right: 0.6rem; - `; + const lockMessageClass = css({ + fontStyle: 'italic', + marginRight: '0.6rem', + }); const labelClass = cx( 'width-16', - css` - font-weight: 500; - ` + css({ + fontWeight: 500, + }) ); return ( diff --git a/public/app/features/admin/UserSessions.tsx b/public/app/features/admin/UserSessions.tsx index 9197d962ce7..6ed87f37ca0 100644 --- a/public/app/features/admin/UserSessions.tsx +++ b/public/app/features/admin/UserSessions.tsx @@ -1,7 +1,9 @@ import { createRef, PureComponent } from 'react'; import { ConfirmButton, ConfirmModal, Button, Stack } from '@grafana/ui'; +import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; import { contextSrv } from 'app/core/core'; +import { t, Trans } from 'app/core/internationalization'; import { formatDate } from 'app/core/internationalization/dates'; import { AccessControlAction, UserSession } from 'app/types'; @@ -51,16 +53,29 @@ class BaseUserSessions extends PureComponent { return (
-

Sessions

+

+ Sessions +

- - - - + + + + + @@ -71,6 +86,9 @@ class BaseUserSessions extends PureComponent { + @@ -91,7 +109,7 @@ class BaseUserSessions extends PureComponent {
{canLogout && sessions.length > 0 && ( )} - No anonymous users found. + + No anonymous users found. + )} diff --git a/public/app/features/admin/Users/OrgUsersTable.tsx b/public/app/features/admin/Users/OrgUsersTable.tsx index 98fc52462ac..2ae1d20872f 100644 --- a/public/app/features/admin/Users/OrgUsersTable.tsx +++ b/public/app/features/admin/Users/OrgUsersTable.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react'; import { OrgRole } from '@grafana/data'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; +import { config } from '@grafana/runtime'; import { Avatar, Box, @@ -20,8 +21,10 @@ import { } from '@grafana/ui'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { fetchRoleOptions, updateUserRoles } from 'app/core/components/RolePicker/api'; +import { RolePickerBadges } from 'app/core/components/RolePickerDrawer/RolePickerBadges'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; import { contextSrv } from 'app/core/core'; +import { Trans } from 'app/core/internationalization'; import { AccessControlAction, OrgUser, Role } from 'app/types'; import { OrgRolePicker } from '../OrgRolePicker'; @@ -111,7 +114,21 @@ export const OrgUsersTable = ({ id: 'lastSeenAtAge', header: 'Last active', cell: ({ cell: { value } }: Cell<'lastSeenAtAge'>) => { - return <>{value && <>{value === '10 years' ? Never : value}}; + return ( + <> + {value && ( + <> + {value === '10 years' ? ( + + Never + + ) : ( + value + )} + + )} + + ); }, sortType: (a, b) => new Date(a.original.lastSeenAt).getTime() - new Date(b.original.lastSeenAt).getTime(), }, @@ -127,6 +144,10 @@ export const OrgUsersTable = ({ } }; + if (config.featureToggles.rolePickerDrawer) { + return ; + } + return contextSrv.licensedAccessControlEnabled() ? ( - This user's role is not editable because it is synchronized from your auth provider. Refer to - the  - - Grafana authentication docs - -  for details. + + This user's role is not editable because it is synchronized from your auth provider. Refer + to the  + + Grafana authentication docs + +  for details. +
} > diff --git a/public/app/features/admin/Users/UsersTable.test.tsx b/public/app/features/admin/Users/UsersTable.test.tsx new file mode 100644 index 00000000000..667aca52b89 --- /dev/null +++ b/public/app/features/admin/Users/UsersTable.test.tsx @@ -0,0 +1,69 @@ +import { render } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom-v5-compat'; + +import { UserDTO } from '../../../types'; + +import { UsersTable, UsersTableProps } from './UsersTable'; + +const setup = (propOverrides?: object) => { + const props: UsersTableProps = { + users: [] as UserDTO[], + showPaging: true, + totalPages: 1, + onChangePage: jest.fn(), + currentPage: 1, + fetchData: jest.fn(), + }; + + Object.assign(props, propOverrides); + + render( + + + + ); +}; + +describe('Render', () => { + it('should render component', () => { + expect(() => setup()).not.toThrow(); + }); + + it('should render when user has licensed role None', () => { + expect(() => + setup({ + users: [ + { + id: 1, + uid: '1', + login: '1', + email: '1', + name: '1', + isGrafanaAdmin: false, + isDisabled: false, + licensedRole: 'None', + }, + ], + }) + ).not.toThrow(); + }); + + it('should render when user belongs to org', () => { + expect(() => + setup({ + users: [ + { + id: 1, + uid: '1', + login: '1', + email: '1', + name: '1', + isGrafanaAdmin: false, + isDisabled: false, + orgs: [{ name: 'test', url: 'test' }], + }, + ], + }) + ).not.toThrow(); + }); +}); diff --git a/public/app/features/admin/Users/UsersTable.tsx b/public/app/features/admin/Users/UsersTable.tsx index 1fcc84276a6..c397dccf6b7 100644 --- a/public/app/features/admin/Users/UsersTable.tsx +++ b/public/app/features/admin/Users/UsersTable.tsx @@ -16,13 +16,14 @@ import { Tooltip, } from '@grafana/ui'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; +import { Trans } from 'app/core/internationalization'; import { UserDTO } from 'app/types'; import { OrgUnits } from './OrgUnits'; type Cell = CellProps; -interface UsersTableProps { +export interface UsersTableProps { users: UserDTO[]; showPaging?: boolean; totalPages: number; @@ -54,7 +55,7 @@ export const UsersTable = ({ header: 'Login', cell: ({ row: { original } }: Cell<'login'>) => { return ( - + {original.login} ); @@ -101,9 +102,9 @@ export const UsersTable = ({ cell: ({ cell: { value } }: Cell<'licensedRole'>) => { return value === 'None' ? ( - Not assigned{' '} + Not assigned - + ) : ( @@ -121,7 +122,21 @@ export const UsersTable = ({ iconName: 'question-circle', }, cell: ({ cell: { value } }: Cell<'lastSeenAtAge'>) => { - return <>{value && <>{value === '10 years' ? Never : value}}; + return ( + <> + {value && ( + <> + {value === '10 years' ? ( + + Never + + ) : ( + value + )} + + )} + + ); }, sortType: (a, b) => new Date(a.original.lastSeenAt!).getTime() - new Date(b.original.lastSeenAt!).getTime(), }, @@ -146,7 +161,7 @@ export const UsersTable = ({ variant="secondary" size="sm" icon="pen" - href={`admin/users/edit/${original.id}`} + href={`admin/users/edit/${original.uid}`} aria-label={`Edit user ${original.name}`} tooltip={'Edit user'} /> diff --git a/public/app/features/admin/api.ts b/public/app/features/admin/api.ts index 49542e53b36..0db8de7ed13 100644 --- a/public/app/features/admin/api.ts +++ b/public/app/features/admin/api.ts @@ -19,7 +19,10 @@ export const getOrgUsers = async (orgId: UrlQueryValue, page: number) => { export const getUsersRoles = async (orgId: number, users: OrgUser[]) => { const userIds = users.map((u) => u.userId); - const roles = await getBackendSrv().post(`/api/access-control/users/roles/search`, { userIds, orgId }); + const roles = await getBackendSrv().post(`/api/access-control/users/roles/search?includeMapped=true`, { + userIds, + orgId, + }); users.forEach((u) => { u.roles = roles ? roles[u.userId] || [] : []; }); diff --git a/public/app/features/admin/ldap/LdapConnectionStatus.tsx b/public/app/features/admin/ldap/LdapConnectionStatus.tsx index 647688111f8..281013fe76b 100644 --- a/public/app/features/admin/ldap/LdapConnectionStatus.tsx +++ b/public/app/features/admin/ldap/LdapConnectionStatus.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { Alert, CellProps, Column, Icon, InteractiveTable, Stack, Text, Tooltip } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { AppNotificationSeverity, LdapConnectionInfo, LdapServerInfo } from 'app/types'; interface Props { @@ -54,7 +55,7 @@ export const LdapConnectionStatus = ({ ldapConnectionInfo }: Props) => {
- LDAP Connection + LDAP Connection serverInfo.host + serverInfo.port} /> @@ -83,7 +84,7 @@ export const LdapErrorBox = ({ ldapConnectionInfo }: LdapConnectionErrorProps) = const errorElements = connectionErrors.map((info, index) => (
- {info.host}:{info.port} + {`${info.host}:${info.port}`}
{info.error} diff --git a/public/app/features/admin/ldap/LdapPage.tsx b/public/app/features/admin/ldap/LdapPage.tsx index d7eaade9b1a..7f8a2ee5f63 100644 --- a/public/app/features/admin/ldap/LdapPage.tsx +++ b/public/app/features/admin/ldap/LdapPage.tsx @@ -7,6 +7,7 @@ import { featureEnabled } from '@grafana/runtime'; import { Alert, Button, Field, Input, Stack } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { contextSrv } from 'app/core/core'; +import { Trans } from 'app/core/internationalization'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { AppNotificationSeverity, @@ -118,7 +119,9 @@ export const LdapPage = ({ {canReadLDAPUser && (
-

Test user mapping

+

+ Test user mapping +

- Run + Run } /> diff --git a/public/app/features/admin/ldap/LdapSyncInfo.tsx b/public/app/features/admin/ldap/LdapSyncInfo.tsx index b51441d98e0..11c3001fc91 100644 --- a/public/app/features/admin/ldap/LdapSyncInfo.tsx +++ b/public/app/features/admin/ldap/LdapSyncInfo.tsx @@ -1,5 +1,6 @@ import { dateTimeFormat } from '@grafana/data'; import { InteractiveTable, Text } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { SyncInfo } from 'app/types'; interface Props { @@ -29,7 +30,9 @@ export const LdapSyncInfo = ({ ldapSyncInfo }: Props) => { return (
- LDAP Synchronization + + LDAP Synchronization + sync.syncAttribute} />
); diff --git a/public/app/features/admin/ldap/LdapUserGroups.tsx b/public/app/features/admin/ldap/LdapUserGroups.tsx index b5e35ea6285..890ec4aee56 100644 --- a/public/app/features/admin/ldap/LdapUserGroups.tsx +++ b/public/app/features/admin/ldap/LdapUserGroups.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { Tooltip, Icon, InteractiveTable, type CellProps, Column } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { LdapRole } from 'app/types'; interface Props { @@ -28,10 +29,12 @@ export const LdapUserGroups = ({ groups }: Props) => { cell: (props: CellProps) => props.value || ( <> - No match{' '} - - - + + No match{' '} + + + + ), }, diff --git a/public/app/features/admin/ldap/LdapUserInfo.tsx b/public/app/features/admin/ldap/LdapUserInfo.tsx index 115c41f78c0..5652594e8ca 100644 --- a/public/app/features/admin/ldap/LdapUserInfo.tsx +++ b/public/app/features/admin/ldap/LdapUserInfo.tsx @@ -1,4 +1,5 @@ import { Box, Stack, Text } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { LdapUser } from 'app/types'; import { LdapUserGroups } from './LdapUserGroups'; @@ -21,7 +22,9 @@ export const LdapUserInfo = ({ ldapUser }: Props) => { ) : ( - No teams found via LDAP + + No teams found via LDAP + )} diff --git a/public/app/features/admin/ldap/LdapUserPermissions.tsx b/public/app/features/admin/ldap/LdapUserPermissions.tsx index 96071801be9..9a851ecc1c5 100644 --- a/public/app/features/admin/ldap/LdapUserPermissions.tsx +++ b/public/app/features/admin/ldap/LdapUserPermissions.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react'; import * as React from 'react'; import { Column, Icon, InteractiveTable } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; import { LdapPermissions } from 'app/types'; interface Props { @@ -33,9 +34,9 @@ export const LdapUserPermissions = ({ permissions }: Props) => { { permission: 'Grafana admin', value: permissions.isGrafanaAdmin ? ( - <> + Yes - + ) : ( 'No' ), @@ -43,13 +44,13 @@ export const LdapUserPermissions = ({ permissions }: Props) => { { permission: 'Status', value: permissions.isDisabled ? ( - <> + Inactive - + ) : ( - <> + Active - + ), }, ], diff --git a/public/app/features/admin/partials/admin_home.html b/public/app/features/admin/partials/admin_home.html deleted file mode 100644 index a3432e61dc0..00000000000 --- a/public/app/features/admin/partials/admin_home.html +++ /dev/null @@ -1,12 +0,0 @@ - - -
- -
- Grafana is a multi-tenant system where most can be configured per organization. These - admin pages are for server admins where you can manage orgs, & all users across all orgs. -
- -
- -
diff --git a/public/app/features/admin/partials/edit_org.html b/public/app/features/admin/partials/edit_org.html deleted file mode 100644 index 104398ca9be..00000000000 --- a/public/app/features/admin/partials/edit_org.html +++ /dev/null @@ -1,52 +0,0 @@ - - -
-

Edit Organization

- - -
- Name - -
- -
- -
- - -

Organization Users

- -
Last seenLogged onIP addressBrowser and OS + Last seen + + Logged on + + IP address + + Browser and OS + + Identity Provider +
{formatDate(session.createdAt, { dateStyle: 'long' })} {session.clientIp} {`${session.browser} on ${session.os} ${session.osVersion}`} + {session.authModule && } + {canLogout && ( { confirmVariant="destructive" onConfirm={this.onSessionRevoke(session.id)} > - Force logout + {t('admin.user-sessions.force-logout-button', 'Force logout')} )}
- - - - - - - - - - - - -
UsernameEmailRole
{{orgUser.login}}{{orgUser.email}} -
- - - -
-
- - - -
-
- -