diff --git a/.betterer.results b/.betterer.results index 4db1152f170..4f0262e4ecb 100644 --- a/.betterer.results +++ b/.betterer.results @@ -7,12 +7,8 @@ exports[`better eslint`] = { value: `{ "packages/grafana-data/src/dataframe/ArrayDataFrame.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, "Do not use any type assertions.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"] + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "packages/grafana-data/src/dataframe/CircularDataFrame.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -45,18 +41,19 @@ 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, "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"], [0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"], - [0, 0, 0, "Unexpected any. Specify a different type.", "12"], - [0, 0, 0, "Do not use any type assertions.", "13"], - [0, 0, 0, "Unexpected any. Specify a different type.", "14"], + [0, 0, 0, "Unexpected any. Specify a different type.", "11"], + [0, 0, 0, "Do not use any type assertions.", "12"], + [0, 0, 0, "Unexpected any. Specify a different type.", "13"], + [0, 0, 0, "Do not use any type assertions.", "14"], [0, 0, 0, "Unexpected any. Specify a different type.", "15"], - [0, 0, 0, "Do not use any type assertions.", "16"] + [0, 0, 0, "Unexpected any. Specify a different type.", "16"], + [0, 0, 0, "Do not use any type assertions.", "17"] ], "packages/grafana-data/src/dataframe/dimensions.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -74,24 +71,24 @@ exports[`better eslint`] = { [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.", "6"], - [0, 0, 0, "Do not use any type assertions.", "7"], + [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, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"], - [0, 0, 0, "Unexpected any. Specify a different type.", "12"], + [0, 0, 0, "Do not use any type assertions.", "10"], + [0, 0, 0, "Unexpected any. Specify a different type.", "11"], + [0, 0, 0, "Do not use any type assertions.", "12"], [0, 0, 0, "Unexpected any. Specify a different type.", "13"], [0, 0, 0, "Do not use any type assertions.", "14"], - [0, 0, 0, "Do not use any type assertions.", "15"], - [0, 0, 0, "Unexpected any. Specify a different type.", "16"], + [0, 0, 0, "Unexpected any. Specify a different type.", "15"], + [0, 0, 0, "Do not use any type assertions.", "16"], [0, 0, 0, "Do not use any type assertions.", "17"], - [0, 0, 0, "Unexpected any. Specify a different type.", "18"], + [0, 0, 0, "Do not use any type assertions.", "18"], [0, 0, 0, "Do not use any type assertions.", "19"], [0, 0, 0, "Do not use any type assertions.", "20"], [0, 0, 0, "Do not use any type assertions.", "21"], - [0, 0, 0, "Unexpected any. Specify a different type.", "22"], - [0, 0, 0, "Unexpected any. Specify a different type.", "23"] + [0, 0, 0, "Do not use any type assertions.", "22"], + [0, 0, 0, "Do not use any type assertions.", "23"] ], "packages/grafana-data/src/datetime/datemath.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -266,25 +263,31 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "packages/grafana-data/src/transformations/transformers/calculateField.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.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], "packages/grafana-data/src/transformations/transformers/ensureColumns.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "packages/grafana-data/src/transformations/transformers/groupBy.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "packages/grafana-data/src/transformations/transformers/groupingToMatrix.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/groupingToMatrix.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "packages/grafana-data/src/transformations/transformers/histogram.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], + "packages/grafana-data/src/transformations/transformers/joinDataFrames.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "packages/grafana-data/src/transformations/transformers/merge.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -294,9 +297,7 @@ exports[`better eslint`] = { ], "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"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Do not use any type assertions.", "3"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -381,12 +382,10 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "25"], [0, 0, 0, "Unexpected any. Specify a different type.", "26"], [0, 0, 0, "Unexpected any. Specify a different type.", "27"], - [0, 0, 0, "Unexpected any. Specify a different type.", "28"], - [0, 0, 0, "Unexpected any. Specify a different type.", "29"] + [0, 0, 0, "Unexpected any. Specify a different type.", "28"] ], "packages/grafana-data/src/types/explore.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"] ], "packages/grafana-data/src/types/fieldOverrides.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -431,9 +430,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"] ], - "packages/grafana-data/src/types/logsVolume.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "packages/grafana-data/src/types/options.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -487,8 +483,19 @@ exports[`better eslint`] = { ], "packages/grafana-data/src/types/vector.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "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, "Unexpected any. Specify a different type.", "6"], + [0, 0, 0, "Do not use any type assertions.", "7"], + [0, 0, 0, "Unexpected any. Specify a different type.", "8"], + [0, 0, 0, "Unexpected any. Specify a different type.", "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"] ], "packages/grafana-data/src/utils/OptionsUIBuilders.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -616,10 +623,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], - "packages/grafana-data/src/utils/logs.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], "packages/grafana-data/src/utils/testdata/testTheme.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] @@ -655,20 +658,43 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "2"] ], "packages/grafana-data/src/vector/ArrayVector.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [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-data/src/vector/AsNumberVector.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], + "packages/grafana-data/src/vector/BinaryOperationVector.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] ], "packages/grafana-data/src/vector/CircularVector.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "packages/grafana-data/src/vector/ConstantVector.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] ], "packages/grafana-data/src/vector/FormattedVector.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] ], "packages/grafana-data/src/vector/FunctionalVector.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.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "Unexpected any. Specify a different type.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"], + [0, 0, 0, "Unexpected any. Specify a different type.", "7"], + [0, 0, 0, "Unexpected any. Specify a different type.", "8"], + [0, 0, 0, "Unexpected any. Specify a different type.", "9"], + [0, 0, 0, "Unexpected any. Specify a different type.", "10"] + ], + "packages/grafana-data/src/vector/IndexVector.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] ], "packages/grafana-data/src/vector/SortedVector.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -815,8 +841,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "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.", "5"] ], "packages/grafana-runtime/src/utils/plugin.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -841,103 +866,15 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Do not use any type assertions.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"] - ], - "packages/grafana-toolkit/src/cli/tasks/component.create.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], - "packages/grafana-toolkit/src/cli/tasks/package.build.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-toolkit/src/cli/tasks/plugin.ci.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "packages/grafana-toolkit/src/cli/tasks/plugin.utils.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-toolkit/src/cli/tasks/plugin/bundle.managed.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "packages/grafana-toolkit/src/cli/tasks/plugin/create.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-toolkit/src/cli/tasks/searchTestDataSetup.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"], - [0, 0, 0, "Unexpected any. Specify a different type.", "8"], - [0, 0, 0, "Unexpected any. Specify a different type.", "9"], - [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Unexpected any. Specify a different type.", "11"] + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"], + [0, 0, 0, "Do not use any type assertions.", "6"] ], "packages/grafana-toolkit/src/cli/tasks/task.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"] ], - "packages/grafana-toolkit/src/cli/utils/githubRelease.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-toolkit/src/cli/utils/prompt.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-toolkit/src/cli/utils/useSpinner.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], - "packages/grafana-toolkit/src/config/jest.plugin.config.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "packages/grafana-toolkit/src/config/react-inlinesvg.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "packages/grafana-toolkit/src/config/utils/pluginValidation.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], - "packages/grafana-toolkit/src/config/webpack.plugin.config.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"] - ], - "packages/grafana-toolkit/src/plugins/manifest.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "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"] - ], - "packages/grafana-toolkit/src/plugins/types.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "packages/grafana-toolkit/src/plugins/utils.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], - "packages/grafana-toolkit/src/plugins/workflow.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], "packages/grafana-ui/src/components/Card/Card.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -953,9 +890,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"] ], - "packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] - ], "packages/grafana-ui/src/components/DataLinks/DataLinkInput.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -1008,9 +942,7 @@ exports[`better eslint`] = { [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"] ], "packages/grafana-ui/src/components/Drawer/Drawer.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", "0"] ], "packages/grafana-ui/src/components/Dropdown/ButtonSelect.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -1099,16 +1031,6 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/Layout/Layout.story.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "packages/grafana-ui/src/components/Logs/LogRowContextProvider.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"] - ], - "packages/grafana-ui/src/components/Logs/LogRows.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], - "packages/grafana-ui/src/components/Logs/logParser.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] - ], "packages/grafana-ui/src/components/MatchersUI/FieldValueMatcher.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -1541,6 +1463,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "5"], [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], + "public/app/core/components/AppChrome/SectionNav/SectionNavItem.tsx:5381": [ + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + ], "public/app/core/components/DynamicImports/SafeDynamicImport.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -1554,11 +1479,6 @@ exports[`better eslint`] = { [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"] ], - "public/app/core/components/NavBar/NavBarItemMenuTrigger.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"] - ], "public/app/core/components/OptionsUI/registry.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -1648,9 +1568,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "85"], [0, 0, 0, "Unexpected any. Specify a different type.", "86"] ], - "public/app/core/components/PageNew/SectionNavItem.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] - ], "public/app/core/components/PanelTypeFilter/PanelTypeFilter.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -1712,8 +1629,8 @@ exports[`better eslint`] = { "public/app/core/services/backend_srv.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Do not use any type assertions.", "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"], @@ -2229,7 +2146,10 @@ exports[`better eslint`] = { [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"] ], "public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsEdit.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", "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"] ], "public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -2569,7 +2489,9 @@ 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, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"], + [0, 0, 0, "Unexpected any. Specify a different type.", "7"] ], "public/app/features/dashboard/state/PanelModel.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -2672,15 +2594,12 @@ exports[`better eslint`] = { "public/app/features/datasources/components/BasicSettings.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], - "public/app/features/datasources/components/ButtonRow.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"] - ], "public/app/features/datasources/components/DataSourceReadOnlyMessage.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], "public/app/features/datasources/components/DataSourceTestingStatus.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"] ], "public/app/features/datasources/components/DataSourceTypeCard.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] @@ -2694,8 +2613,11 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], "public/app/features/datasources/state/actions.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.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], "public/app/features/datasources/state/navModel.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -2730,8 +2652,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/features/dimensions/types.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/features/dimensions/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -2748,9 +2669,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], - "public/app/features/explore/LogsContainer.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/features/explore/LogsMetaRow.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -2826,9 +2744,25 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], + "public/app/features/explore/state/history.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], + "public/app/features/explore/state/main.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/explore/state/query.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], "public/app/features/explore/state/time.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/explore/state/time.ts:5381": [ + [0, 0, 0, "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/features/explore/state/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -2857,9 +2791,6 @@ exports[`better eslint`] = { "public/app/features/geo/format/geojson.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/features/geo/format/utils.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/features/geo/gazetteer/gazetteer.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -2948,15 +2879,6 @@ exports[`better eslint`] = { "public/app/features/live/index.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/logs/components/LogRowContextProvider.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Do not use any type assertions.", "3"] - ], - "public/app/features/logs/components/LogRows.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/features/logs/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -3135,27 +3057,20 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "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.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"], [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Do not use any type assertions.", "9"], - [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"], + [0, 0, 0, "Unexpected any. Specify a different type.", "9"], + [0, 0, 0, "Do not use any type assertions.", "10"], + [0, 0, 0, "Unexpected any. Specify a different type.", "11"], [0, 0, 0, "Do not use any type assertions.", "12"], [0, 0, 0, "Do not use any type assertions.", "13"], - [0, 0, 0, "Unexpected any. Specify a different type.", "14"], + [0, 0, 0, "Do not use any type assertions.", "14"], [0, 0, 0, "Unexpected any. Specify a different type.", "15"], [0, 0, 0, "Unexpected any. Specify a different type.", "16"], - [0, 0, 0, "Unexpected any. Specify a different type.", "17"], - [0, 0, 0, "Unexpected any. Specify a different type.", "18"], - [0, 0, 0, "Do not use any type assertions.", "19"], - [0, 0, 0, "Unexpected any. Specify a different type.", "20"], - [0, 0, 0, "Do not use any type assertions.", "21"], - [0, 0, 0, "Unexpected any. Specify a different type.", "22"], - [0, 0, 0, "Unexpected any. Specify a different type.", "23"], - [0, 0, 0, "Do not use any type assertions.", "24"] + [0, 0, 0, "Do not use any type assertions.", "17"] ], "public/app/features/plugins/importPanelPlugin.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -3215,10 +3130,6 @@ exports[`better eslint`] = { "public/app/features/query/components/QueryGroupOptions.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/query/components/SavedQueryPicker.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"] - ], "public/app/features/query/state/DashboardQueryRunner/AlertStatesWorker.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -3295,8 +3206,7 @@ exports[`better eslint`] = { ], "public/app/features/search/service/bluge.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, "Do not use any type assertions.", "1"] ], "public/app/features/search/service/sql.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -3839,8 +3749,7 @@ exports[`better eslint`] = { "public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/datasource/azuremonitor/azure_log_analytics/azure_log_analytics_datasource.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -3983,10 +3892,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/plugins/datasource/cloudwatch/components/MetricsQueryEditor/Alias.tsx: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/cloudwatch/datasource.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -4086,6 +3991,21 @@ 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/datasource/elasticsearch/LegacyQueryRunner.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "Unexpected any. Specify a different type.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"], + [0, 0, 0, "Unexpected any. Specify a different type.", "7"], + [0, 0, 0, "Unexpected any. Specify a different type.", "8"], + [0, 0, 0, "Unexpected any. Specify a different type.", "9"], + [0, 0, 0, "Unexpected any. Specify a different type.", "10"], + [0, 0, 0, "Do not use any type assertions.", "11"], + [0, 0, 0, "Unexpected any. Specify a different type.", "12"] + ], "public/app/plugins/datasource/elasticsearch/QueryBuilder.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -4163,22 +4083,7 @@ exports[`better eslint`] = { [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, "Do not use any type assertions.", "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.", "30"] + [0, 0, 0, "Unexpected any. Specify a different type.", "15"] ], "public/app/plugins/datasource/elasticsearch/hooks/useStatelessReducer.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -4218,9 +4123,10 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "6"], [0, 0, 0, "Do not use any type assertions.", "7"], [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Unexpected any. Specify a different type.", "9"], + [0, 0, 0, "Do not use any type assertions.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"] + [0, 0, 0, "Unexpected any. Specify a different type.", "11"], + [0, 0, 0, "Do not use any type assertions.", "12"] ], "public/app/plugins/datasource/graphite/components/FunctionEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -4593,10 +4499,7 @@ exports[`better eslint`] = { ], "public/app/plugins/datasource/jaeger/testResponse.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/plugins/datasource/jaeger/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -4638,13 +4541,12 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], [0, 0, 0, "Unexpected any. Specify a different type.", "5"], - [0, 0, 0, "Unexpected any. Specify a different type.", "6"], - [0, 0, 0, "Unexpected any. Specify a different type.", "7"] + [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], - "public/app/plugins/datasource/loki/getDerivedFields.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + "public/app/plugins/datasource/loki/queryUtils.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/plugins/datasource/loki/querybuilder/binaryScalarOperations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -4816,7 +4718,7 @@ exports[`better eslint`] = { [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, "Do not use any type assertions.", "22"], + [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"], @@ -4825,9 +4727,7 @@ exports[`better eslint`] = { [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.", "30"], - [0, 0, 0, "Unexpected any. Specify a different type.", "31"], - [0, 0, 0, "Unexpected any. Specify a different type.", "32"], - [0, 0, 0, "Unexpected any. Specify a different type.", "33"] + [0, 0, 0, "Unexpected any. Specify a different type.", "31"] ], "public/app/plugins/datasource/prometheus/language_provider.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -5068,10 +4968,11 @@ exports[`better eslint`] = { "public/app/plugins/datasource/testdata/QueryEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "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, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], "public/app/plugins/datasource/testdata/components/RandomWalkEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -5097,15 +4998,12 @@ exports[`better eslint`] = { ], "public/app/plugins/datasource/testdata/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] - ], - "public/app/plugins/datasource/testdata/module.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/plugins/datasource/testdata/nodeGraphUtils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/datasource/testdata/runStreams.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -5126,15 +5024,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "3"], [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], - "public/app/plugins/datasource/zipkin/utils/testData.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/plugins/datasource/zipkin/utils/testResponse.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/plugins/datasource/zipkin/utils/transforms.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -5236,6 +5128,9 @@ exports[`better eslint`] = { "public/app/plugins/panel/canvas/editor/TreeNavigationEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/plugins/panel/canvas/editor/connectionEditor.tsx:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "public/app/plugins/panel/canvas/editor/elementEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -5699,8 +5594,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"] + [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], "public/app/plugins/panel/heatmap/palettes.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -5802,7 +5696,9 @@ exports[`better eslint`] = { ], "public/app/plugins/panel/nodeGraph/utils.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.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], "public/app/plugins/panel/piechart/PieChart.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] @@ -5988,20 +5884,23 @@ 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, "Do not use any type assertions.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Do not use any type assertions.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], [0, 0, 0, "Do not use any type assertions.", "6"], [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Unexpected any. Specify a different type.", "9"], - [0, 0, 0, "Do not use any type assertions.", "10"], - [0, 0, 0, "Unexpected any. Specify a different type.", "11"], - [0, 0, 0, "Do not use any type assertions.", "12"], - [0, 0, 0, "Unexpected any. Specify a different type.", "13"], + [0, 0, 0, "Do not use any type assertions.", "9"], + [0, 0, 0, "Unexpected any. Specify a different type.", "10"], + [0, 0, 0, "Do not use any type assertions.", "11"], + [0, 0, 0, "Unexpected any. Specify a different type.", "12"], + [0, 0, 0, "Do not use any type assertions.", "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.", "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"] ], "public/app/plugins/panel/timeseries/plugins/ExemplarMarker.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] @@ -6175,9 +6074,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/test/jest-setup.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.", "0"] ], "public/test/jest-shim.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -6281,9 +6178,6 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.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/Typography/Typography.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/VizLayout/VizLayout.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 2e45d61a756..140dd9509ae 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -23,17 +23,18 @@ $(BRA): $(BINGO_DIR)/bra.mod @echo "(re)installing $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bra.mod -o=$(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 "github.com/unknwon/bra" -CUE := $(GOBIN)/cue-v0.5.0-beta.2 +CUE := $(GOBIN)/cue-v0.5.0 $(CUE): $(BINGO_DIR)/cue.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/cue-v0.5.0-beta.2" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cue.mod -o=$(GOBIN)/cue-v0.5.0-beta.2 "cuelang.org/go/cmd/cue" + @echo "(re)installing $(GOBIN)/cue-v0.5.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cue.mod -o=$(GOBIN)/cue-v0.5.0 "cuelang.org/go/cmd/cue" DRONE := $(GOBIN)/drone-v1.5.0 $(DRONE): $(BINGO_DIR)/drone.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/drone-v1.5.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=drone.mod -o=$(GOBIN)/drone-v1.5.0 "github.com/drone/drone-cli/drone" + @# Manual modification: CGo is disabled to make the Drone CLI build successfully on Darwin/arm64 machines. + @cd $(BINGO_DIR) && CGO_ENABLED=0 GOWORK=off $(GO) build -mod=mod -modfile=drone.mod -o=$(GOBIN)/drone-v1.5.0 "github.com/drone/drone-cli/drone" GOLANGCI_LINT := $(GOBIN)/golangci-lint-v1.51.2 $(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod diff --git a/.bingo/cue.mod b/.bingo/cue.mod index 79ccdfb391c..6e13b6d3eec 100644 --- a/.bingo/cue.mod +++ b/.bingo/cue.mod @@ -2,4 +2,4 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT go 1.19 -require cuelang.org/go v0.5.0-beta.2 // cmd/cue +require cuelang.org/go v0.5.0 // cmd/cue diff --git a/.bingo/cue.sum b/.bingo/cue.sum index f59e3b21cb6..5e9012b2946 100644 --- a/.bingo/cue.sum +++ b/.bingo/cue.sum @@ -2,29 +2,57 @@ cuelang.org/go v0.4.3 h1:W3oBBjDTm7+IZfCKZAmC8uDG0eYfJL4Pp/xbbCMKaVo= cuelang.org/go v0.4.3/go.mod h1:7805vR9H+VoBNdWFdI7jyDR3QLUPp4+naHfbcgp55HI= cuelang.org/go v0.5.0-beta.2 h1:am5M7jGvNTJ0rnjrFNyvE7fucL/wRqb0emK4XxdThQI= cuelang.org/go v0.5.0-beta.2/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= +cuelang.org/go v0.5.0 h1:D6N0UgTGJCOxFKU8RU+qYvavKNsVc/+ZobmifStVJzU= +cuelang.org/go v0.5.0/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc h1:gSVONBi2HWMFXCa9jFdYvYk7IwW/mTLxWOF7rXS4LO0= github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4= +github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ= golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9 h1:VtCrPQXM5Wo9l7XN64SjBMczl48j8mkP+2e3OhYlz+0= +golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20200612220849-54c614fe050c h1:g6oFfz6Cmw68izP3xsdud3Oxu145IPkeFzyRg58AKHM= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 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/.bingo/variables.env b/.bingo/variables.env index a1dde2f98e8..394897e5690 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -10,7 +10,7 @@ fi BRA="${GOBIN}/bra-v0.0.0-20200517080246-1e3013ecaff8" -CUE="${GOBIN}/cue-v0.5.0-beta.2" +CUE="${GOBIN}/cue-v0.5.0" DRONE="${GOBIN}/drone-v1.5.0" diff --git a/.drone.star b/.drone.star index c0a804ddf8d..ec10d154fba 100644 --- a/.drone.star +++ b/.drone.star @@ -20,11 +20,19 @@ load( "publish_npm_pipelines", "publish_packages_pipeline", ) +load( + "scripts/drone/rgm.star", + "rgm", +) load( "scripts/drone/pipelines/publish_images.star", "publish_image_pipelines_public", "publish_image_pipelines_security", ) +load( + "scripts/drone/pipelines/ci_images.star", + "publish_ci_windows_test_image_pipeline", +) load("scripts/drone/pipelines/github.star", "publish_github_pipeline") load("scripts/drone/pipelines/aws_marketplace.star", "publish_aws_marketplace_pipeline") load("scripts/drone/version.star", "version_branch_pipelines") @@ -38,10 +46,6 @@ def main(_ctx): oss_pipelines() + enterprise_pipelines() + enterprise2_pipelines() + - enterprise2_pipelines( - prefix = "custom-", - trigger = {"event": ["custom"]}, - ) + publish_image_pipelines_public() + publish_image_pipelines_security() + publish_github_pipeline("public") + @@ -51,9 +55,11 @@ def main(_ctx): publish_artifacts_pipelines("public") + publish_npm_pipelines() + publish_packages_pipeline() + + rgm() + artifacts_page_pipeline() + version_branch_pipelines() + integration_test_pipelines() + + publish_ci_windows_test_image_pipeline() + cronjobs() + secrets() ) diff --git a/.drone.yml b/.drone.yml index 9c5f364a85e..dbeddd1e2fd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -24,7 +24,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build verify-drone @@ -74,13 +74,13 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build verify-starlark . depends_on: - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: lint-starlark trigger: event: @@ -127,13 +127,13 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn betterer ci depends_on: - yarn-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: betterer-frontend - commands: - is_fork=$(curl "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" @@ -153,7 +153,7 @@ steps: GITHUB_TOKEN: from_secret: github_token failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - yarn run ci:test-frontend @@ -162,7 +162,7 @@ steps: - clone-enterprise environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-frontend trigger: event: @@ -216,7 +216,7 @@ steps: GITHUB_TOKEN: from_secret: github_token failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - echo $DRONE_RUNNER_NAME @@ -225,7 +225,7 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn run prettier:check @@ -237,7 +237,7 @@ steps: - clone-enterprise environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: lint-frontend trigger: event: @@ -291,7 +291,7 @@ steps: GITHUB_TOKEN: from_secret: github_token failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - echo $DRONE_RUNNER_NAME @@ -302,7 +302,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -312,7 +312,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - clone-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -321,25 +321,27 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - clone-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend - commands: - - go test -run Integration -covermode=atomic -timeout=5m ./pkg/... + - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend-integration trigger: event: @@ -371,6 +373,56 @@ environment: image_pull_secrets: - dockerconfigjson kind: pipeline +name: pr-oss-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: [] + image: grafana/grafana-ci-windows-test:0.1.0 + 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: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +trigger: + event: + - pull_request + paths: + exclude: + - docs/** + - '*.md' + - pkg/** + - packaging/** + - go.sum + - go.mod + include: [] +type: docker +volumes: +- host: + path: //./pipe/docker_engine/ + name: docker +--- +clone: + retries: 3 +depends_on: [] +environment: + EDITION: oss +image_pull_secrets: +- dockerconfigjson +kind: pipeline name: pr-lint-backend node: type: no-parallel @@ -388,7 +440,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - is_fork=$(curl "https://$GITHUB_TOKEN@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" @@ -408,13 +460,13 @@ steps: GITHUB_TOKEN: from_secret: github_token failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - make gen-go depends_on: - clone-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update && apt-get install make @@ -423,7 +475,7 @@ steps: - wire-install environment: CGO_ENABLED: "1" - image: golang:1.20.1 + image: golang:1.20.4 name: lint-backend trigger: event: @@ -478,7 +530,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -487,7 +539,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -495,18 +547,18 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" @@ -529,7 +581,7 @@ steps: from_secret: github_token_pr TEST_TAG: v0.0.0-test failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: trigger-test-release when: paths: @@ -556,7 +608,7 @@ steps: depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend - commands: - ./bin/build build-frontend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} @@ -565,16 +617,18 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} + - yarn packages:pack + - ./scripts/validate-npm-packages.sh depends_on: - compile-build-cmd - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition oss @@ -582,7 +636,7 @@ steps: - compile-build-cmd - yarn-install environment: null - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - . scripts/build/gpg-test-vars.sh && ./bin/build package --jobs 8 --edition oss @@ -593,7 +647,7 @@ steps: - build-frontend - build-frontend-packages environment: null - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package - commands: - ./scripts/grafana-server/start-server @@ -606,7 +660,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: grafana-server - commands: - apt-get install -y netcat @@ -709,7 +763,7 @@ steps: - build-frontend-packages environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-storybook when: paths: @@ -720,7 +774,7 @@ steps: - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - yarn wait-on http://$HOST:$PORT @@ -793,9 +847,21 @@ services: MYSQL_ROOT_PASSWORD: rootpass MYSQL_USER: grafana image: mysql:5.7.39 - name: mysql + name: mysql57 volumes: - - name: mysql + - name: mysql57 + path: /var/lib/mysql +- commands: + - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password + environment: + MYSQL_DATABASE: grafana_tests + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_USER: grafana + image: mysql:8.0.32 + name: mysql80 + volumes: + - name: mysql80 path: /var/lib/mysql - environment: {} image: redis:6.2.11-alpine @@ -822,7 +888,7 @@ steps: GITHUB_TOKEN: from_secret: github_token failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mkdir -p bin @@ -835,7 +901,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -849,7 +915,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - clone-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -858,13 +924,13 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - clone-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update @@ -872,32 +938,51 @@ steps: - dockerize -wait tcp://postgres:5432 -timeout 120s - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: postgres-integration-tests - commands: - apt-get update - apt-get install -yq default-mysql-client - - dockerize -wait tcp://mysql:3306 -timeout 120s - - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql -P 3306 -u root + - dockerize -wait tcp://mysql57:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql57 -P 3306 -u root -prootpass - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql - image: grafana/build-container:1.7.2 - name: mysql-integration-tests + MYSQL_HOST: mysql57 + image: grafana/build-container:1.7.4 + name: mysql-5.7-integration-tests +- commands: + - apt-get update + - apt-get install -yq default-mysql-client + - dockerize -wait tcp://mysql80:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql80 -P 3306 -u root + -prootpass + - go clean -testcache + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) + depends_on: + - wire-install + environment: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: mysql80 + image: grafana/build-container:1.7.4 + name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s - go clean -testcache @@ -906,7 +991,7 @@ steps: - wire-install environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -916,7 +1001,7 @@ steps: - wire-install environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: memcached-integration-tests trigger: event: @@ -941,7 +1026,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -968,7 +1056,7 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - |- @@ -980,7 +1068,7 @@ steps: wan" > words_to_ignore.txt - codespell -I words_to_ignore.txt docs/ - rm words_to_ignore.txt - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: codespell - commands: - yarn run prettier:checkDocs @@ -988,7 +1076,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: lint-docs - commands: - mkdir -p /hugo/content/docs/grafana/latest @@ -1032,13 +1120,13 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build shellcheck depends_on: - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: shellcheck trigger: event: @@ -1079,7 +1167,7 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - |- @@ -1091,7 +1179,7 @@ steps: wan" > words_to_ignore.txt - codespell -I words_to_ignore.txt docs/ - rm words_to_ignore.txt - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: codespell - commands: - yarn run prettier:checkDocs @@ -1099,7 +1187,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: lint-docs - commands: - mkdir -p /hugo/content/docs/grafana/latest @@ -1152,13 +1240,13 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn betterer ci depends_on: - yarn-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: betterer-frontend - commands: - yarn run ci:test-frontend @@ -1166,7 +1254,7 @@ steps: - yarn-install environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-frontend trigger: branch: main @@ -1206,7 +1294,7 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn run prettier:check @@ -1217,7 +1305,7 @@ steps: - yarn-install environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: lint-frontend trigger: branch: main @@ -1259,7 +1347,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1268,7 +1356,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1276,25 +1364,27 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend - commands: - - go test -run Integration -covermode=atomic -timeout=5m ./pkg/... + - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend-integration trigger: branch: main @@ -1336,12 +1426,12 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - make gen-go depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update && apt-get install make @@ -1350,7 +1440,7 @@ steps: - wire-install environment: CGO_ENABLED: "1" - image: golang:1.20.1 + image: golang:1.20.4 name: lint-backend - commands: - ./bin/build verify-drone @@ -1404,7 +1494,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1413,7 +1503,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1421,25 +1511,25 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - ./bin/build build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend - commands: - ./bin/build build-frontend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} @@ -1448,16 +1538,18 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} + - yarn packages:pack + - ./scripts/validate-npm-packages.sh depends_on: - compile-build-cmd - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition oss @@ -1467,7 +1559,7 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --sign @@ -1485,7 +1577,7 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package - commands: - ./scripts/grafana-server/start-server @@ -1498,7 +1590,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: grafana-server - commands: - apt-get install -y netcat @@ -1601,7 +1693,7 @@ steps: - build-frontend-packages environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-storybook when: paths: @@ -1612,7 +1704,7 @@ steps: - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - yarn wait-on http://$HOST:$PORT @@ -1656,7 +1748,7 @@ steps: GRAFANA_MISC_STATS_API_KEY: from_secret: grafana_misc_stats_api_key failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: publish-frontend-metrics when: repo: @@ -1728,7 +1820,7 @@ steps: repo: - grafana/grafana - commands: - - ./scripts/circle-release-canary-packages.sh + - ./scripts/publish-npm-packages.sh --dist-tag 'canary' --registry 'https://registry.npmjs.org' depends_on: - end-to-end-tests-dashboards-suite - end-to-end-tests-panels-suite @@ -1737,9 +1829,12 @@ steps: environment: NPM_TOKEN: from_secret: npm_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: release-canary-npm-packages when: + paths: + include: + - packages/** repo: - grafana/grafana - commands: @@ -1819,9 +1914,21 @@ services: MYSQL_ROOT_PASSWORD: rootpass MYSQL_USER: grafana image: mysql:5.7.39 - name: mysql + name: mysql57 volumes: - - name: mysql + - name: mysql57 + path: /var/lib/mysql +- commands: + - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password + environment: + MYSQL_DATABASE: grafana_tests + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_USER: grafana + image: mysql:8.0.32 + name: mysql80 + volumes: + - name: mysql80 path: /var/lib/mysql - environment: {} image: redis:6.2.11-alpine @@ -1841,7 +1948,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -1854,7 +1961,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1862,13 +1969,13 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update @@ -1876,32 +1983,51 @@ steps: - dockerize -wait tcp://postgres:5432 -timeout 120s - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: postgres-integration-tests - commands: - apt-get update - apt-get install -yq default-mysql-client - - dockerize -wait tcp://mysql:3306 -timeout 120s - - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql -P 3306 -u root + - dockerize -wait tcp://mysql57:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql57 -P 3306 -u root -prootpass - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql - image: grafana/build-container:1.7.2 - name: mysql-integration-tests + MYSQL_HOST: mysql57 + image: grafana/build-container:1.7.4 + name: mysql-5.7-integration-tests +- commands: + - apt-get update + - apt-get install -yq default-mysql-client + - dockerize -wait tcp://mysql80:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql80 -P 3306 -u root + -prootpass + - go clean -testcache + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) + depends_on: + - wire-install + environment: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: mysql80 + image: grafana/build-container:1.7.4 + name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s - go clean -testcache @@ -1910,7 +2036,7 @@ steps: - wire-install environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -1920,7 +2046,7 @@ steps: - wire-install environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: memcached-integration-tests trigger: branch: main @@ -1939,7 +2065,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -2009,7 +2138,127 @@ trigger: type: docker volumes: - host: - path: /var/run/docker.sock + path: //./pipe/docker_engine/ + name: docker +--- +clone: + retries: 3 +depends_on: [] +environment: + EDITION: oss +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: main-oss-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: [] + image: grafana/grafana-ci-windows-test:0.1.0 + 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: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +trigger: + branch: main + event: + - push + paths: + exclude: + - '*.md' + - docs/** + - latest.json +type: docker +volumes: +- host: + path: //./pipe/docker_engine/ + name: docker +--- +clone: + disable: true +depends_on: [] +environment: + EDITION: enterprise +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: main-enterprise-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: + - $$ProgressPreference = "SilentlyContinue" + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.30/windows/grabpl.exe + -OutFile grabpl.exe + image: grafana/ci-wix:0.1.1 + name: grabpl +- commands: + - git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git" + - cd grafana-enterprise + - git checkout main + environment: + GITHUB_TOKEN: + from_secret: github_token + image: grafana/ci-wix:0.1.1 + name: clone +- commands: + - cp -r grafana-enterprise C:\App\grafana-enterprise + - rm -r -force grafana-enterprise + - cp grabpl.exe C:\App\grabpl.exe + - rm -force grabpl.exe + - C:\App\grabpl.exe init-enterprise --github-token $$env:GITHUB_TOKEN C:\App\grafana-enterprise + - cp C:\App\grabpl.exe grabpl.exe + depends_on: + - clone + environment: + GITHUB_TOKEN: + from_secret: github_token + image: grafana/ci-wix:0.1.1 + name: windows-init +- commands: + - go install github.com/google/wire/cmd/wire@v0.5.0 + - wire gen -tags enterprise ./pkg/server + depends_on: + - windows-init + image: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +trigger: + branch: main + event: + - push + paths: + exclude: + - '*.md' + - docs/** + - latest.json +type: docker +volumes: +- host: + path: //./pipe/docker_engine/ name: docker --- clone: @@ -2158,32 +2407,32 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build build-backend --jobs 8 --edition oss ${DRONE_TAG} depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend - commands: - ./bin/build build-frontend --jobs 8 --edition oss ${DRONE_TAG} @@ -2192,7 +2441,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition oss ${DRONE_TAG} @@ -2201,7 +2450,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition oss @@ -2211,7 +2460,7 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build package --jobs 8 --edition oss --sign ${DRONE_TAG} @@ -2229,14 +2478,14 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - ./bin/build build-docker --edition oss --shouldSave @@ -2275,7 +2524,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: grafana-server - commands: - apt-get install -y netcat @@ -2352,7 +2601,7 @@ steps: - build-frontend-packages environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-storybook when: event: @@ -2411,7 +2660,7 @@ steps: from_secret: gcp_key PRERELEASE_BUCKET: from_secret: prerelease_bucket - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: store-npm-packages trigger: event: @@ -2457,13 +2706,13 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn betterer ci depends_on: - yarn-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: betterer-frontend - commands: - yarn run ci:test-frontend @@ -2471,7 +2720,7 @@ steps: - yarn-install environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-frontend trigger: event: @@ -2513,7 +2762,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -2522,7 +2771,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2530,25 +2779,27 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend - commands: - - go test -run Integration -covermode=atomic -timeout=5m ./pkg/... + - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend-integration trigger: event: @@ -2565,12 +2816,132 @@ volumes: path: /var/run/docker.sock name: docker --- +clone: + retries: 3 +depends_on: [] +environment: + EDITION: oss +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: release-oss-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: [] + image: grafana/grafana-ci-windows-test:0.1.0 + 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: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +trigger: + event: + exclude: + - promote + ref: + exclude: + - refs/tags/*-cloud* + include: + - refs/tags/v* +type: docker +volumes: +- host: + path: //./pipe/docker_engine/ + name: docker +--- +clone: + disable: true +depends_on: [] +environment: + EDITION: enterprise +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: release-enterprise-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: + - $$ProgressPreference = "SilentlyContinue" + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.30/windows/grabpl.exe + -OutFile grabpl.exe + image: grafana/ci-wix:0.1.1 + name: grabpl +- commands: + - git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git" + - cd grafana-enterprise + - git checkout ${DRONE_TAG} + environment: + GITHUB_TOKEN: + from_secret: github_token + image: grafana/ci-wix:0.1.1 + name: clone +- commands: + - cp -r grafana-enterprise C:\App\grafana-enterprise + - rm -r -force grafana-enterprise + - cp grabpl.exe C:\App\grabpl.exe + - rm -force grabpl.exe + - C:\App\grabpl.exe init-enterprise --github-token $$env:GITHUB_TOKEN C:\App\grafana-enterprise + - cp C:\App\grabpl.exe grabpl.exe + depends_on: + - clone + environment: + GITHUB_TOKEN: + from_secret: github_token + image: grafana/ci-wix:0.1.1 + name: windows-init +- commands: + - go install github.com/google/wire/cmd/wire@v0.5.0 + - wire gen -tags enterprise ./pkg/server + depends_on: + - windows-init + image: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +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: - release-oss-build-e2e-publish - release-oss-test-frontend -- release-oss-test-backend +- release-oss-test-backend-windows environment: EDITION: oss image_pull_secrets: @@ -2628,7 +2999,7 @@ trigger: type: docker volumes: - host: - path: /var/run/docker.sock + path: //./pipe/docker_engine/ name: docker --- clone: @@ -2664,7 +3035,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -2681,7 +3052,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -2689,19 +3060,19 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - make gen-go depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -2711,7 +3082,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2720,14 +3091,14 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - ./bin/build build-backend --jobs 8 --edition enterprise ${DRONE_TAG} depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend - commands: - ./bin/build build-frontend --jobs 8 --edition enterprise ${DRONE_TAG} @@ -2736,7 +3107,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition enterprise ${DRONE_TAG} @@ -2745,7 +3116,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition enterprise @@ -2755,7 +3126,7 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build package --jobs 8 --edition enterprise --sign ${DRONE_TAG} @@ -2773,14 +3144,14 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - ./bin/build build-docker --edition enterprise --shouldSave @@ -2820,7 +3191,7 @@ steps: ARCH: linux-amd64 PORT: 3001 RUNDIR: scripts/grafana-server/tmp-grafana-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: grafana-server - commands: - apt-get install -y netcat @@ -2955,7 +3326,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -2972,7 +3343,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - echo $DRONE_RUNNER_NAME @@ -2982,14 +3353,14 @@ steps: - yarn install --immutable depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn betterer ci depends_on: - init-enterprise - yarn-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: betterer-frontend - commands: - yarn run ci:test-frontend @@ -2998,7 +3369,7 @@ steps: - yarn-install environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-frontend trigger: event: @@ -3038,7 +3409,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mkdir -p bin @@ -3061,7 +3432,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - echo $DRONE_RUNNER_NAME @@ -3073,7 +3444,7 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -3083,7 +3454,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -3092,25 +3463,27 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend - commands: - - go test -run Integration -covermode=atomic -timeout=5m ./pkg/... + - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend-integration trigger: event: @@ -3210,7 +3583,7 @@ trigger: type: docker volumes: - host: - path: /var/run/docker.sock + path: //./pipe/docker_engine/ name: docker --- clone: @@ -3246,7 +3619,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -3263,7 +3636,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -3271,19 +3644,19 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - make gen-go depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -3293,7 +3666,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - ./bin/build build-frontend --jobs 8 --edition enterprise ${DRONE_TAG} @@ -3302,7 +3675,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition enterprise ${DRONE_TAG} @@ -3311,7 +3684,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition enterprise @@ -3321,14 +3694,14 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build build-backend --jobs 8 --edition enterprise2 ${DRONE_TAG} depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend-enterprise2 - commands: - ./bin/build package --jobs 8 --edition enterprise2 --sign ${DRONE_TAG} @@ -3346,7 +3719,7 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package-enterprise2 - commands: - ./bin/build upload-cdn --edition enterprise2 @@ -3366,7 +3739,7 @@ steps: - cp dist/*.tar.gz* packaging/docker/ depends_on: - package-enterprise2 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - ./bin/build build-docker --edition enterprise2 --shouldSave @@ -3463,255 +3836,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 temp: medium: memory ---- -clone: - disable: true -depends_on: [] -environment: - EDITION: enterprise2 -image_pull_secrets: -- dockerconfigjson -kind: pipeline -name: custom-release-enterprise2-build-e2e-publish -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.30/grabpl - - chmod +x bin/grabpl - image: byrnedo/alpine-curl:0.1.8 - name: grabpl -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.17.1 - name: identify-runner -- commands: - - git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" - - cd grafana-enterprise - - git checkout ${DRONE_TAG} - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/build-container:1.7.2 - name: clone-enterprise -- commands: - - mv bin/grabpl /tmp/ - - rmdir bin - - mv grafana-enterprise /tmp/ - - /tmp/grabpl init-enterprise --github-token $${GITHUB_TOKEN} /tmp/grafana-enterprise - ${DRONE_TAG} - - mv /tmp/grafana-enterprise/deployment_tools_config.json deployment_tools_config.json - - mkdir bin - - mv /tmp/grabpl bin/ - depends_on: - - clone-enterprise - - grabpl - environment: - GITHUB_TOKEN: - from_secret: github_token - image: grafana/build-container:1.7.2 - name: init-enterprise -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: - - init-enterprise - environment: - CGO_ENABLED: 0 - image: golang:1.20.1 - name: compile-build-cmd -- commands: - - make gen-go - depends_on: - - init-enterprise - image: grafana/build-container:1.7.2 - name: wire-install -- commands: - - yarn install --immutable - depends_on: - - init-enterprise - image: grafana/build-container:1.7.2 - name: yarn-install -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - CODEGEN_VERIFY=1 make gen-cue - depends_on: - - init-enterprise - image: grafana/build-container:1.7.2 - name: verify-gen-cue -- commands: - - ./bin/build build-frontend --jobs 8 --edition enterprise ${DRONE_TAG} - depends_on: - - compile-build-cmd - - yarn-install - environment: - NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 - name: build-frontend -- commands: - - ./bin/build build-frontend-packages --jobs 8 --edition enterprise ${DRONE_TAG} - depends_on: - - compile-build-cmd - - yarn-install - environment: - NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 - name: build-frontend-packages -- commands: - - ./bin/build build-plugins --jobs 8 --edition enterprise - depends_on: - - compile-build-cmd - - yarn-install - environment: - GRAFANA_API_KEY: - from_secret: grafana_api_key - image: grafana/build-container:1.7.2 - name: build-plugins -- commands: - - ./bin/build build-backend --jobs 8 --edition enterprise2 ${DRONE_TAG} - depends_on: - - wire-install - - compile-build-cmd - image: grafana/build-container:1.7.2 - name: build-backend-enterprise2 -- commands: - - ./bin/build package --jobs 8 --edition enterprise2 --sign ${DRONE_TAG} - depends_on: - - build-plugins - - build-backend-enterprise2 - - build-frontend - - build-frontend-packages - environment: - GPG_KEY_PASSWORD: - from_secret: packages_gpg_passphrase - GPG_PRIV_KEY: - from_secret: packages_gpg_private_key - GPG_PUB_KEY: - from_secret: packages_gpg_public_key - GRAFANA_API_KEY: - from_secret: grafana_api_key - image: grafana/build-container:1.7.2 - name: package-enterprise2 -- commands: - - ./bin/build upload-cdn --edition enterprise2 - depends_on: - - package-enterprise2 - environment: - ENTERPRISE2_CDN_PATH: - from_secret: enterprise2-cdn-path - GCP_KEY: - from_secret: gcp_key - PRERELEASE_BUCKET: - from_secret: prerelease_bucket - image: grafana/grafana-ci-deploy:1.3.3 - name: upload-cdn-assets-enterprise2 -- commands: - - ls dist/*.tar.gz* - - cp dist/*.tar.gz* packaging/docker/ - depends_on: - - package-enterprise2 - image: grafana/build-container:1.7.2 - name: copy-packages-for-docker -- commands: - - ./bin/build build-docker --edition enterprise2 --shouldSave - depends_on: - - copy-packages-for-docker - - compile-build-cmd - environment: - DOCKER_ENTERPRISE2_REPO: - from_secret: docker_enterprise2_repo - GCP_KEY: - from_secret: gcp_key - image: google/cloud-sdk - name: build-docker-images - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/build build-docker --edition enterprise2 --shouldSave --ubuntu - depends_on: - - copy-packages-for-docker - - compile-build-cmd - environment: - DOCKER_ENTERPRISE2_REPO: - from_secret: docker_enterprise2_repo - GCP_KEY: - from_secret: gcp_key - image: google/cloud-sdk - name: build-docker-images-ubuntu - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/build artifacts docker fetch --edition enterprise2 - depends_on: - - build-docker-images - - build-docker-images-ubuntu - environment: - DOCKER_ENTERPRISE2_REPO: - from_secret: docker_enterprise2_repo - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USER: - from_secret: docker_username - GCP_KEY: - from_secret: gcp_key - image: google/cloud-sdk - name: fetch-images-enterprise2 - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/build artifacts docker publish-enterprise2 --dockerhub-repo $${DOCKER_ENTERPRISE2_REPO} - depends_on: - - fetch-images-enterprise2 - environment: - DOCKER_ENTERPRISE2_REPO: - from_secret: docker_enterprise2_repo - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USER: - from_secret: docker_username - GCP_KEY: - from_secret: gcp_key_hg - image: google/cloud-sdk - name: publish-images-enterprise2 - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/build upload-packages --edition enterprise2 - depends_on: - - package-enterprise2 - environment: - GCP_KEY: - from_secret: gcp_key - PRERELEASE_BUCKET: - from_secret: prerelease_bucket - image: grafana/grafana-ci-deploy:1.3.3 - name: upload-packages-enterprise2 -trigger: - event: - - custom -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: postgres - temp: - medium: memory -- name: mysql +- name: mysql80 temp: medium: memory --- @@ -3746,7 +3874,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3842,7 +3970,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition enterprise @@ -3921,7 +4049,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition enterprise @@ -3990,7 +4118,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition enterprise2 @@ -4053,7 +4181,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition enterprise2 @@ -4117,7 +4245,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build publish aws --image grafana/grafana-enterprise --repo grafana-labs/grafanaenterprise @@ -4168,10 +4296,10 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - - ./bin/build artifacts publish --security --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET} + - ./bin/build artifacts packages --security --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET} depends_on: - compile-build-cmd environment: @@ -4183,10 +4311,21 @@ steps: from_secret: prerelease_bucket SECURITY_DEST_BUCKET: from_secret: security_dest_bucket + image: grafana/grafana-ci-deploy:1.3.3 + name: publish-artifacts +- commands: + - ./bin/build artifacts static-assets --tag ${DRONE_TAG} + depends_on: + - compile-build-cmd + environment: + GCP_KEY: + from_secret: gcp_key + PRERELEASE_BUCKET: + from_secret: prerelease_bucket STATIC_ASSET_EDITIONS: from_secret: static_asset_editions image: grafana/grafana-ci-deploy:1.3.3 - name: publish-artifacts + name: publish-static-assets trigger: event: - promote @@ -4219,10 +4358,10 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - - ./bin/build artifacts publish --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET} + - ./bin/build artifacts packages --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET} depends_on: - compile-build-cmd environment: @@ -4234,10 +4373,32 @@ steps: from_secret: prerelease_bucket SECURITY_DEST_BUCKET: from_secret: security_dest_bucket + image: grafana/grafana-ci-deploy:1.3.3 + name: publish-artifacts +- commands: + - ./bin/build artifacts static-assets --tag ${DRONE_TAG} + depends_on: + - compile-build-cmd + environment: + GCP_KEY: + from_secret: gcp_key + PRERELEASE_BUCKET: + from_secret: prerelease_bucket STATIC_ASSET_EDITIONS: from_secret: static_asset_editions image: grafana/grafana-ci-deploy:1.3.3 - name: publish-artifacts + name: publish-static-assets +- commands: + - ./bin/build artifacts storybook --tag ${DRONE_TAG} + depends_on: + - compile-build-cmd + environment: + GCP_KEY: + from_secret: gcp_key + PRERELEASE_BUCKET: + from_secret: prerelease_bucket + image: grafana/grafana-ci-deploy:1.3.3 + name: publish-storybook trigger: event: - promote @@ -4270,12 +4431,12 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - ./bin/build artifacts npm retrieve --tag ${DRONE_TAG} @@ -4299,7 +4460,7 @@ steps: NPM_TOKEN: from_secret: npm_token failure: ignore - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: release-npm-packages trigger: event: @@ -4336,7 +4497,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - depends_on: - compile-build-cmd @@ -4427,7 +4588,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - depends_on: - compile-build-cmd @@ -4494,6 +4655,106 @@ volumes: path: /var/run/docker.sock name: docker --- +clone: + retries: 3 +depends_on: +- main-test-backend +- main-test-frontend +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: '[RGM] Build and upload a grafana.tar.gz to a prerelease bucket when merging + to main' +node: + type: no-parallel +platform: + arch: amd64 + os: linux +services: [] +steps: +- commands: + - git clone https://github.com/grafana/grafana-build.git rgm + failure: ignore + image: alpine/git + name: clone-rgm +- commands: + - apk update && apk add docker + - export GRAFANA_DIR=$$(pwd) + - cd rgm && ./scripts/drone_publish_main.sh + environment: + DESTINATION: + from_secret: destination + GCP_KEY_BASE64: + from_secret: gcp_key_base64 + GITHUB_TOKEN: + from_secret: github_token + failure: ignore + image: golang:1.20.3-alpine + name: rgm-build + volumes: + - name: docker + path: /var/run/docker.sock +trigger: + branch: main + event: + - push +type: docker +volumes: +- host: + path: /var/run/docker.sock + name: docker +--- +clone: + retries: 3 +depends_on: [] +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: '[RGM] Build and upload a grafana.tar.gz to a prerelease bucket when tagging' +node: + type: no-parallel +platform: + arch: amd64 + os: linux +services: [] +steps: +- commands: + - git clone https://github.com/grafana/grafana-build.git rgm + failure: ignore + image: alpine/git + name: clone-rgm +- commands: + - apk update && apk add docker + - export GRAFANA_DIR=$$(pwd) + - cd rgm && ./scripts/drone_publish_tag.sh + environment: + DESTINATION: + from_secret: destination + GCP_KEY_BASE64: + from_secret: gcp_key_base64 + GITHUB_TOKEN: + from_secret: github_token + failure: ignore + image: golang:1.20.3-alpine + name: rgm-build + 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: disable: true depends_on: [] @@ -4523,7 +4784,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -4540,7 +4801,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -4548,7 +4809,7 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build artifacts-page @@ -4557,7 +4818,7 @@ steps: environment: GCP_KEY: from_secret: gcp_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: artifacts-page trigger: event: @@ -4602,32 +4863,32 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend - commands: - ./bin/build build-frontend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} @@ -4636,16 +4897,18 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} + - yarn packages:pack + - ./scripts/validate-npm-packages.sh depends_on: - compile-build-cmd - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition oss @@ -4655,7 +4918,7 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --sign @@ -4673,14 +4936,14 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - ./bin/build build-docker --edition oss --shouldSave @@ -4719,7 +4982,7 @@ steps: environment: ARCH: linux-amd64 PORT: 3001 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: grafana-server - commands: - apt-get install -y netcat @@ -4796,7 +5059,7 @@ steps: - build-frontend-packages environment: NODE_OPTIONS: --max_old_space_size=4096 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-storybook when: paths: @@ -4844,7 +5107,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -4877,13 +5143,13 @@ steps: - commands: - yarn install --immutable depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn betterer ci depends_on: - yarn-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: betterer-frontend - commands: - yarn run ci:test-frontend @@ -4891,7 +5157,7 @@ steps: - yarn-install environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-frontend trigger: ref: @@ -4927,7 +5193,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -4936,7 +5202,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4944,25 +5210,27 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend - commands: - - go test -run Integration -covermode=atomic -timeout=5m ./pkg/... + - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend-integration trigger: ref: @@ -4981,6 +5249,114 @@ environment: image_pull_secrets: - dockerconfigjson kind: pipeline +name: release-branch-oss-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: [] + image: grafana/grafana-ci-windows-test:0.1.0 + 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: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +trigger: + ref: + - refs/heads/v[0-9]* +type: docker +volumes: +- host: + path: //./pipe/docker_engine/ + name: docker +--- +clone: + disable: true +depends_on: [] +environment: + EDITION: enterprise +image_pull_secrets: +- dockerconfigjson +kind: pipeline +name: release-branch-enterprise-test-backend-windows +platform: + arch: amd64 + os: windows + version: "1809" +services: [] +steps: +- commands: + - $$ProgressPreference = "SilentlyContinue" + - Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.30/windows/grabpl.exe + -OutFile grabpl.exe + image: grafana/ci-wix:0.1.1 + name: grabpl +- commands: + - git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git" + - cd grafana-enterprise + - git checkout $$env:DRONE_BRANCH + environment: + GITHUB_TOKEN: + from_secret: github_token + image: grafana/ci-wix:0.1.1 + name: clone +- commands: + - cp -r grafana-enterprise C:\App\grafana-enterprise + - rm -r -force grafana-enterprise + - cp grabpl.exe C:\App\grabpl.exe + - rm -force grabpl.exe + - C:\App\grabpl.exe init-enterprise --github-token $$env:GITHUB_TOKEN C:\App\grafana-enterprise + - cp C:\App\grabpl.exe grabpl.exe + depends_on: + - clone + environment: + GITHUB_TOKEN: + from_secret: github_token + image: grafana/ci-wix:0.1.1 + name: windows-init +- commands: + - go install github.com/google/wire/cmd/wire@v0.5.0 + - wire gen -tags enterprise ./pkg/server + depends_on: + - windows-init + image: grafana/grafana-ci-windows-test:0.1.0 + name: wire-install +- commands: + - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... + depends_on: + - wire-install + failure: ignore + image: grafana/grafana-ci-windows-test:0.1.0 + name: test-backend +trigger: + ref: + - refs/heads/v[0-9]* +type: docker +volumes: +- host: + path: //./pipe/docker_engine/ + name: docker +--- +clone: + retries: 3 +depends_on: [] +environment: + EDITION: oss +image_pull_secrets: +- dockerconfigjson +kind: pipeline name: release-branch-oss-integration-tests node: type: no-parallel @@ -5004,9 +5380,21 @@ services: MYSQL_ROOT_PASSWORD: rootpass MYSQL_USER: grafana image: mysql:5.7.39 - name: mysql + name: mysql57 volumes: - - name: mysql + - name: mysql57 + path: /var/lib/mysql +- commands: + - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password + environment: + MYSQL_DATABASE: grafana_tests + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_USER: grafana + image: mysql:8.0.32 + name: mysql80 + volumes: + - name: mysql80 path: /var/lib/mysql - environment: {} image: redis:6.2.11-alpine @@ -5032,7 +5420,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -5040,13 +5428,13 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update @@ -5054,32 +5442,51 @@ steps: - dockerize -wait tcp://postgres:5432 -timeout 120s - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: postgres-integration-tests - commands: - apt-get update - apt-get install -yq default-mysql-client - - dockerize -wait tcp://mysql:3306 -timeout 120s - - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql -P 3306 -u root + - dockerize -wait tcp://mysql57:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql57 -P 3306 -u root -prootpass - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql - image: grafana/build-container:1.7.2 - name: mysql-integration-tests + MYSQL_HOST: mysql57 + image: grafana/build-container:1.7.4 + name: mysql-5.7-integration-tests +- commands: + - apt-get update + - apt-get install -yq default-mysql-client + - dockerize -wait tcp://mysql80:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql80 -P 3306 -u root + -prootpass + - go clean -testcache + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) + depends_on: + - wire-install + environment: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: mysql80 + image: grafana/build-container:1.7.4 + name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s - go clean -testcache @@ -5088,7 +5495,7 @@ steps: - wire-install environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -5098,7 +5505,7 @@ steps: - wire-install environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: memcached-integration-tests trigger: ref: @@ -5111,7 +5518,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -5120,7 +5530,7 @@ clone: depends_on: - release-branch-oss-build-e2e-publish - release-branch-oss-test-frontend -- release-branch-oss-test-backend +- release-branch-oss-test-backend-windows environment: EDITION: oss image_pull_secrets: @@ -5168,7 +5578,7 @@ trigger: type: docker volumes: - host: - path: /var/run/docker.sock + path: //./pipe/docker_engine/ name: docker --- clone: @@ -5204,7 +5614,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -5220,7 +5630,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -5228,19 +5638,19 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - make gen-go depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -5250,7 +5660,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -5259,14 +5669,14 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - ./bin/build build-backend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend - commands: - ./bin/build build-frontend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} @@ -5275,16 +5685,18 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} + - yarn packages:pack + - ./scripts/validate-npm-packages.sh depends_on: - compile-build-cmd - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition enterprise @@ -5294,7 +5706,7 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build package --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} @@ -5313,14 +5725,14 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package - commands: - ls dist/*.tar.gz* - cp dist/*.tar.gz* packaging/docker/ depends_on: - package - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - ./bin/build build-docker --edition enterprise --shouldSave @@ -5360,7 +5772,7 @@ steps: ARCH: linux-amd64 PORT: 3001 RUNDIR: scripts/grafana-server/tmp-grafana-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: grafana-server - commands: - apt-get install -y netcat @@ -5468,7 +5880,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -5501,7 +5916,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -5517,7 +5932,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - echo $DRONE_RUNNER_NAME @@ -5527,14 +5942,14 @@ steps: - yarn install --immutable depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - yarn betterer ci depends_on: - init-enterprise - yarn-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: betterer-frontend - commands: - yarn run ci:test-frontend @@ -5543,7 +5958,7 @@ steps: - yarn-install environment: TEST_MAX_WORKERS: 50% - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-frontend trigger: ref: @@ -5577,7 +5992,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mkdir -p bin @@ -5599,7 +6014,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - echo $DRONE_RUNNER_NAME @@ -5611,7 +6026,7 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -5621,7 +6036,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -5630,25 +6045,27 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - go test -tags requires_buildifer -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend - commands: - - go test -run Integration -covermode=atomic -timeout=5m ./pkg/... + - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: test-backend-integration trigger: ref: @@ -5690,9 +6107,21 @@ services: MYSQL_ROOT_PASSWORD: rootpass MYSQL_USER: grafana image: mysql:5.7.39 - name: mysql + name: mysql57 volumes: - - name: mysql + - name: mysql57 + path: /var/lib/mysql +- commands: + - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password + environment: + MYSQL_DATABASE: grafana_tests + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_USER: grafana + image: mysql:8.0.32 + name: mysql80 + volumes: + - name: mysql80 path: /var/lib/mysql - environment: {} image: redis:6.2.11-alpine @@ -5718,7 +6147,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -5734,7 +6163,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -5744,7 +6173,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -5753,13 +6182,13 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update @@ -5767,32 +6196,51 @@ steps: - dockerize -wait tcp://postgres:5432 -timeout 120s - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: postgres-integration-tests - commands: - apt-get update - apt-get install -yq default-mysql-client - - dockerize -wait tcp://mysql:3306 -timeout 120s - - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql -P 3306 -u root + - dockerize -wait tcp://mysql57:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql57 -P 3306 -u root -prootpass - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql - image: grafana/build-container:1.7.2 - name: mysql-integration-tests + MYSQL_HOST: mysql57 + image: grafana/build-container:1.7.4 + name: mysql-5.7-integration-tests +- commands: + - apt-get update + - apt-get install -yq default-mysql-client + - dockerize -wait tcp://mysql80:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql80 -P 3306 -u root + -prootpass + - go clean -testcache + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) + depends_on: + - wire-install + environment: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: mysql80 + image: grafana/build-container:1.7.4 + name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s - go clean -testcache @@ -5801,7 +6249,7 @@ steps: - wire-install environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -5811,7 +6259,7 @@ steps: - wire-install environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: memcached-integration-tests trigger: ref: @@ -5824,7 +6272,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -5901,7 +6352,7 @@ trigger: type: docker volumes: - host: - path: /var/run/docker.sock + path: //./pipe/docker_engine/ name: docker --- clone: @@ -5937,7 +6388,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -5953,7 +6404,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd @@ -5961,19 +6412,19 @@ steps: - init-enterprise environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - make gen-go depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - yarn install --immutable depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: yarn-install - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -5983,7 +6434,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - ./bin/build build-frontend --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} @@ -5992,16 +6443,18 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend - commands: - ./bin/build build-frontend-packages --jobs 8 --edition enterprise --build-id ${DRONE_BUILD_NUMBER} + - yarn packages:pack + - ./scripts/validate-npm-packages.sh depends_on: - compile-build-cmd - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-frontend-packages - commands: - ./bin/build build-plugins --jobs 8 --edition enterprise @@ -6011,7 +6464,7 @@ steps: environment: GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-plugins - commands: - ./bin/build build-backend --jobs 8 --edition enterprise2 --build-id ${DRONE_BUILD_NUMBER} @@ -6019,7 +6472,7 @@ steps: depends_on: - wire-install - compile-build-cmd - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: build-backend-enterprise2 - commands: - ./bin/build package --jobs 8 --edition enterprise2 --build-id ${DRONE_BUILD_NUMBER} @@ -6038,7 +6491,7 @@ steps: from_secret: packages_gpg_public_key GRAFANA_API_KEY: from_secret: grafana_api_key - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: package-enterprise2 - commands: - ./bin/build upload-cdn --edition enterprise2 @@ -6058,7 +6511,7 @@ steps: - cp dist/*.tar.gz* packaging/docker/ depends_on: - package-enterprise2 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: copy-packages-for-docker - commands: - ./bin/build build-docker --edition enterprise2 --shouldSave @@ -6149,7 +6602,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -6184,9 +6640,21 @@ services: MYSQL_ROOT_PASSWORD: rootpass MYSQL_USER: grafana image: mysql:5.7.39 - name: mysql + name: mysql57 volumes: - - name: mysql + - name: mysql57 + path: /var/lib/mysql +- commands: + - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password + environment: + MYSQL_DATABASE: grafana_tests + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_USER: grafana + image: mysql:8.0.32 + name: mysql80 + volumes: + - name: mysql80 path: /var/lib/mysql - environment: {} image: redis:6.2.11-alpine @@ -6212,7 +6680,7 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -6220,13 +6688,13 @@ steps: in output.' - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update @@ -6234,32 +6702,51 @@ steps: - dockerize -wait tcp://postgres:5432 -timeout 120s - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: postgres-integration-tests - commands: - apt-get update - apt-get install -yq default-mysql-client - - dockerize -wait tcp://mysql:3306 -timeout 120s - - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql -P 3306 -u root + - dockerize -wait tcp://mysql57:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql57 -P 3306 -u root -prootpass - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql - image: grafana/build-container:1.7.2 - name: mysql-integration-tests + MYSQL_HOST: mysql57 + image: grafana/build-container:1.7.4 + name: mysql-5.7-integration-tests +- commands: + - apt-get update + - apt-get install -yq default-mysql-client + - dockerize -wait tcp://mysql80:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql80 -P 3306 -u root + -prootpass + - go clean -testcache + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) + depends_on: + - wire-install + environment: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: mysql80 + image: grafana/build-container:1.7.4 + name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s - go clean -testcache @@ -6268,7 +6755,7 @@ steps: - wire-install environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -6278,7 +6765,7 @@ steps: - wire-install environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: memcached-integration-tests trigger: event: @@ -6292,7 +6779,10 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 + temp: + medium: memory +- name: mysql80 temp: medium: memory --- @@ -6327,9 +6817,21 @@ services: MYSQL_ROOT_PASSWORD: rootpass MYSQL_USER: grafana image: mysql:5.7.39 - name: mysql + name: mysql57 volumes: - - name: mysql + - name: mysql57 + path: /var/lib/mysql +- commands: + - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password + environment: + MYSQL_DATABASE: grafana_tests + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_USER: grafana + image: mysql:8.0.32 + name: mysql80 + volumes: + - name: mysql80 path: /var/lib/mysql - environment: {} image: redis:6.2.11-alpine @@ -6355,7 +6857,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: clone-enterprise - commands: - mv bin/grabpl /tmp/ @@ -6372,7 +6874,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: init-enterprise - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -6382,7 +6884,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -6391,13 +6893,13 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - init-enterprise - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: verify-gen-jsonnet - commands: - make gen-go depends_on: - verify-gen-cue - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: wire-install - commands: - apt-get update @@ -6405,32 +6907,51 @@ steps: - dockerize -wait tcp://postgres:5432 -timeout 120s - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: postgres-integration-tests - commands: - apt-get update - apt-get install -yq default-mysql-client - - dockerize -wait tcp://mysql:3306 -timeout 120s - - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql -P 3306 -u root + - dockerize -wait tcp://mysql57:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql57 -P 3306 -u root -prootpass - go clean -testcache - - go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic - -timeout=5m {}' + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install environment: GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql - image: grafana/build-container:1.7.2 - name: mysql-integration-tests + MYSQL_HOST: mysql57 + image: grafana/build-container:1.7.4 + name: mysql-5.7-integration-tests +- commands: + - apt-get update + - apt-get install -yq default-mysql-client + - dockerize -wait tcp://mysql80:3306 -timeout 120s + - cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h mysql80 -P 3306 -u root + -prootpass + - go clean -testcache + - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find + ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' + | grep -o '\(.*\)/' | sort -u) + depends_on: + - wire-install + environment: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: mysql80 + image: grafana/build-container:1.7.4 + name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379/0 -timeout 120s - go clean -testcache @@ -6439,7 +6960,7 @@ steps: - wire-install environment: REDIS_URL: redis://redis:6379/0 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -6449,7 +6970,7 @@ steps: - wire-install environment: MEMCACHED_HOSTS: memcached:11211 - image: grafana/build-container:1.7.2 + image: grafana/build-container:1.7.4 name: memcached-integration-tests trigger: event: @@ -6463,9 +6984,60 @@ volumes: - name: postgres temp: medium: memory -- name: mysql +- name: mysql57 temp: medium: memory +- name: mysql80 + temp: + medium: memory +--- +clone: + disable: true +depends_on: [] +image_pull_secrets: +- dockerconfigjson +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 @@ -6602,7 +7174,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.20.1 + image: golang:1.20.4 name: compile-build-cmd - commands: - ./bin/build publish grafana-com --edition oss @@ -6752,7 +7324,25 @@ get: kind: secret name: enterprise2_security_prefix --- +get: + name: gcp_service_account_base64 + path: infra/data/ci/grafana-release-eng/rgm +kind: secret +name: gcp_key_base64 +--- +get: + name: destination + path: infra/data/ci/grafana-release-eng/rgm +kind: secret +name: destination +--- +get: + name: pat + path: infra/data/ci/github/grafanabot +kind: secret +name: github_token +--- kind: signature -hmac: 54bded4dc7c3ffbbf3859a4f8f6364f59f3b6e0696152a0b835211eca7119e50 +hmac: dc3090ab1f4561e295654e1aa01e61a3fbb8bbe17eb5c935f3e808f545a75b39 ... diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59d3c75414e..d0da194624f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -168,7 +168,7 @@ /devenv/dev-dashboards-without-uid/ @grafana/dashboards-squad /devenv/dev-dashboards/ @grafana/dashboards-squad /devenv/docker/blocks/alert_webhook_listener/ @grafana/alerting-squad-backend -/devenv/docker/blocks/clickhouse/ @grafana/partner-plugins +/devenv/docker/blocks/clickhouse/ @grafana/partner-datasources /devenv/docker/blocks/collectd/ @grafana/observability-metrics /devenv/docker/blocks/grafana/ @grafana/grafana-as-code /devenv/docker/blocks/graphite/ @grafana/observability-metrics @@ -193,6 +193,7 @@ /devenv/docker/blocks/postgres_tests/ @grafana/grafana-bi-squad /devenv/docker/blocks/prometheus/ @grafana/observability-metrics /devenv/docker/blocks/prometheus_random_data/ @grafana/observability-metrics +/devenv/docker/blocks/pyroscope/ @grafana/observability-traces-and-profiling /devenv/docker/blocks/redis/ @bergquist /devenv/docker/blocks/sensugo/ @grafana/backend-platform /devenv/docker/blocks/slow_proxy/ @bergquist @@ -231,8 +232,8 @@ # OSS Plugin Partnerships backend code /pkg/tsdb/cloudwatch/ @grafana/aws-datasources -/pkg/tsdb/azuremonitor/ @grafana/partner-plugins -/pkg/tsdb/cloudmonitoring/ @grafana/partner-plugins +/pkg/tsdb/azuremonitor/ @grafana/partner-datasources +/pkg/tsdb/cloudmonitoring/ @grafana/partner-datasources # Observability backend code /pkg/tsdb/prometheus/ @grafana/observability-metrics @@ -284,9 +285,6 @@ /pkg/tsdb/testdatasource/ @grafana/plugins-platform-backend /pkg/services/pluginsintegration/pluginsettings/ @grafana/plugins-platform-backend -# Dashboard previews / crawler (behind feature flag) -/pkg/services/thumbs/ @grafana/grafana-app-platform-squad - # Backend code docs /contribute/backend/ @grafana/backend-platform @@ -295,19 +293,27 @@ /public/locales/ @grafana/grafana-frontend-platform /public/app/core/internationalization/ @grafana/grafana-frontend-platform /e2e/ @grafana/grafana-frontend-platform -/e2e/cloud-plugins-suite/ @grafana/partner-plugins -/packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad +/e2e/cloud-plugins-suite/ @grafana/partner-datasources +/packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend /packages/grafana-e2e-selectors/ @grafana/grafana-frontend-platform /packages/grafana-e2e/ @grafana/grafana-frontend-platform /packages/grafana-toolkit/ @grafana/plugins-platform-frontend /packages/grafana-ui/.storybook/ @grafana/plugins-platform-frontend /packages/grafana-ui/src/components/ @grafana/grafana-frontend-platform /packages/grafana-ui/src/components/DateTimePickers/ @grafana/grafana-frontend-platform -/packages/grafana-ui/src/components/GraphNG/ @grafana/dataviz-squad -/packages/grafana-ui/src/components/Logs/ @grafana/observability-logs /packages/grafana-ui/src/components/Table/ @grafana/grafana-bi-squad +/packages/grafana-ui/src/components/Gauge/ @grafana/grafana-bi-squad +/packages/grafana-ui/src/components/BarGauge/ @grafana/grafana-bi-squad +/packages/grafana-ui/src/components/GraphNG/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/Graph/ @grafana/dataviz-squad /packages/grafana-ui/src/components/TimeSeries/ @grafana/dataviz-squad /packages/grafana-ui/src/components/uPlot/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/DataLinks/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/ValuePicker/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/VizLayout/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/VizLegend/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/VizRepeater/ @grafana/dataviz-squad +/packages/grafana-ui/src/components/VizTooltip/ @grafana/dataviz-squad /packages/grafana-ui/src/utils/storybook/ @grafana/plugins-platform-frontend /packages/grafana-data/src/**/*logs* @grafana/observability-logs /plugins-bundled/ @grafana/plugins-platform-frontend @@ -346,6 +352,7 @@ lerna.json @grafana/frontend-ops /public/app/core/components/Layers/ @grafana/dataviz-squad /public/app/features/all.ts @grafana/grafana-frontend-platform /public/app/features/admin/ @grafana/grafana-authnz-team +/public/app/features/auth-config/ @grafana/grafana-authnz-team /public/app/features/annotations/ @grafana/grafana-frontend-platform /public/app/features/api-keys/ @grafana/grafana-frontend-platform /public/app/features/canvas/ @grafana/dataviz-squad @@ -356,11 +363,10 @@ lerna.json @grafana/frontend-ops /public/app/features/datasources/ @grafana/plugins-platform-frontend /public/app/features/dimensions/ @grafana/dataviz-squad /public/app/features/dataframe-import/ @grafana/grafana-bi-squad -/public/app/features/datasource-drawer/ @grafana/grafana-bi-squad /public/app/features/explore/ @grafana/explore-squad /public/app/features/expressions/ @grafana/observability-metrics /public/app/features/folders/ @grafana/grafana-frontend-platform -/public/app/features/inspector/ @grafana/grafana-frontend-platform +/public/app/features/inspector/ @grafana/dashboards-squad /public/app/features/invites/ @grafana/grafana-frontend-platform /public/app/features/geo/ @grafana/dataviz-squad /public/app/features/library-panels/ @grafana/grafana-frontend-platform @@ -377,6 +383,7 @@ lerna.json @grafana/frontend-ops /public/app/features/query/ @grafana/dashboards-squad /public/app/features/sandbox/ @grafana/grafana-frontend-platform /public/app/features/scenes/ @grafana/dashboards-squad +/public/app/features/browse-dashboards/ @grafana/grafana-frontend-platform /public/app/features/search/ @grafana/grafana-frontend-platform /public/app/features/serviceaccounts/ @grafana/grafana-authnz-team /public/app/features/storage/ @grafana/grafana-app-platform-squad @@ -392,6 +399,7 @@ lerna.json @grafana/frontend-ops /public/app/plugins/panel/bargauge/ @grafana/grafana-bi-squad /public/app/plugins/panel/dashlist/ @grafana/grafana-frontend-platform /public/app/plugins/panel/debug/ @ryantxu +/public/app/plugins/panel/datagrid/ @grafana/grafana-bi-squad /public/app/plugins/panel/gauge/ @grafana/grafana-bi-squad /public/app/plugins/panel/gettingstarted/ @grafana/grafana-frontend-platform /public/app/plugins/panel/graph/ @grafana/dataviz-squad @@ -425,7 +433,6 @@ lerna.json @grafana/frontend-ops /public/app/types/ @grafana/grafana-frontend-platform /public/dashboards/ @grafana/dashboards-squad /public/fonts/ @grafana/alerting-squad-frontend -/public/emails/ @grafana/grafana-frontend-platform /public/gazetteer/ @ryantxu /public/img/ @grafana/grafana-frontend-platform /public/lib/ @grafana/grafana-frontend-platform @@ -457,6 +464,8 @@ lerna.json @grafana/frontend-ops /scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend /scripts/ci-* @grafana/grafana-delivery /scripts/circle-* @grafana/grafana-delivery +/scripts/publish-npm-packages.sh @grafana/grafana-delivery @grafana/plugins-platform-frontend +/scripts/validate-npm-packages.sh @grafana/grafana-delivery @grafana/plugins-platform-frontend /scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/grafana-bi-squad /scripts/cli/ @grafana/grafana-frontend-platform /scripts/clean-git-or-error.sh @grafana/grafana-as-code @@ -472,15 +481,14 @@ lerna.json @grafana/frontend-ops /scripts/trigger_docker_build.sh @grafana/grafana-delivery /scripts/trigger_grafana_packer.sh @grafana/grafana-delivery /scripts/trigger_windows_build.sh @grafana/grafana-delivery -/scripts/validate-devenv-dashboards.sh @grafana/grafana-delivery /scripts/verify-repo-update/ @grafana/grafana-delivery /scripts/webpack/ @grafana/frontend-ops /scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform .pa11yci.conf.js @grafana/grafana-frontend-platform .pa11yci-pr.conf.js @grafana/grafana-frontend-platform -.betterer.results @joshhunt -.betterer.ts @joshhunt +.betterer.results @grafanabot +.betterer.ts @grafana/grafana-frontend-platform # @grafana/ui component documentation *.mdx @grafana/plugins-platform-frontend @@ -494,7 +502,7 @@ lerna.json @grafana/frontend-ops /public/app/plugins/datasource/elasticsearch/ @grafana/observability-logs /public/app/plugins/datasource/grafana/ @grafana/grafana-frontend-platform /public/app/plugins/datasource/testdata/ @grafana/plugins-platform-frontend -/public/app/plugins/datasource/azuremonitor/ @grafana/partner-plugins +/public/app/plugins/datasource/azuremonitor/ @grafana/partner-datasources /public/app/plugins/datasource/graphite/ @grafana/observability-metrics /public/app/plugins/datasource/influxdb/ @grafana/observability-metrics /public/app/plugins/datasource/jaeger/ @grafana/observability-traces-and-profiling @@ -505,7 +513,7 @@ lerna.json @grafana/frontend-ops /public/app/plugins/datasource/opentsdb/ @grafana/backend-platform /public/app/plugins/datasource/postgres/ @grafana/grafana-bi-squad /public/app/plugins/datasource/prometheus/ @grafana/observability-metrics -/public/app/plugins/datasource/cloud-monitoring/ @grafana/partner-plugins +/public/app/plugins/datasource/cloud-monitoring/ @grafana/partner-datasources /public/app/plugins/datasource/zipkin/ @grafana/observability-traces-and-profiling /public/app/plugins/datasource/tempo/ @grafana/observability-traces-and-profiling /public/app/plugins/datasource/phlare/ @grafana/observability-traces-and-profiling @@ -524,6 +532,7 @@ lerna.json @grafana/frontend-ops /pkg/services/anonymous/ @grafana/grafana-authnz-team /pkg/services/auth/ @grafana/grafana-authnz-team /pkg/services/authn/ @grafana/grafana-authnz-team +/pkg/services/signingkeys/ @grafana/grafana-authnz-team /pkg/services/dashboards/accesscontrol.go @grafana/grafana-authnz-team /pkg/services/datasources/permissions/ @grafana/grafana-authnz-team /pkg/services/guardian/ @grafana/grafana-authnz-team @@ -541,6 +550,7 @@ lerna.json @grafana/frontend-ops # Grafana Operator Experience Team /pkg/infra/httpclient/httpclientprovider/sigv4_middleware.go @grafana/grafana-operator-experience-squad /pkg/infra/httpclient/httpclientprovider/sigv4_middleware_test.go @grafana/grafana-operator-experience-squad +/pkg/services/caching/ @grafana/grafana-operator-experience-squad # Kind definitions /kinds/dashboard @grafana/dashboards-squad @@ -573,7 +583,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/backport.yml @grafana/grafana-delivery /.github/workflows/bump-version.yml @grafana/grafana-delivery /.github/workflows/close-milestone.yml @grafana/grafana-delivery -/.github/workflows/cloud-data-sources-code-coverage.yml @grafana/partner-plugins @grafana/aws-datasources +/.github/workflows/cloud-data-sources-code-coverage.yml @grafana/partner-datasources @grafana/aws-datasources /.github/workflows/codeowners-validator.yml @tolzhabayev /.github/workflows/codeql-analysis.yml @DanCech /.github/workflows/commands.yml @torkelo @@ -602,6 +612,12 @@ embed.go @grafana/grafana-as-code /.github/workflows/update-changelog.yml @grafana/grafana-delivery /.github/workflows/snyk.yml @grafana/security-team +# Generated files not requiring owner approval +/packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot +/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @grafanabot +/pkg/services/featuremgmt/toggles_gen.csv @grafanabot +/pkg/services/featuremgmt/toggles_gen.go @grafanabot +/public/emails/ @grafanabot # Conf /conf/defaults.ini @torkelo diff --git a/.github/ISSUE_TEMPLATE/0-bug-report.yaml b/.github/ISSUE_TEMPLATE/0-bug-report.yaml new file mode 100644 index 00000000000..56476cb716c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/0-bug-report.yaml @@ -0,0 +1,177 @@ +name: New Bug Report +description: File a bug report +title: "Product-Area-Here: short description of bug here" +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please try to give your issue a good title. Try using the product-area where you are having an issue and a brief description of the problem. Like this: + - `Dashboards: Template Variables break when I do X` or + - `Alerting: message templating plus Slack channel breaks when I do X` + - type: markdown + attributes: + value: | + **HINT:** Have you tried [searching](https://github.com/grafana/grafana/issues) for similar issues? Duplicate issues are common. + + **Are you reporting a security vulnerability?** [Submit it here instead](https://github.com/grafana/grafana/security/policy). + - type: markdown + attributes: + value: | + # + - type: textarea + id: bug-describe + attributes: + label: | + + # What went wrong? + + description: | + # + Describe your bug. What happened? What did you expect to happen? + + **Hot Tip:** Record your screen and add it here as a gif. + placeholder: Tell us what you see! + value: | + **What happened**: + + - + + **What did you expect to happen**: + + - + + validations: + required: true + - type: markdown + attributes: + value: | + # + - type: textarea + id: bug-repro + attributes: + label: | + + # How do we reproduce it? + + description: | + # + + Whenever possible, please provide **detailed** steps for reproducing your bug. + + **This is very helpful info** + placeholder: "Step 1:..." + value: | + **Step 1**: + + - Open Grafana and do X + + **Step 2**: + + - Now click button Y + + **Step 3**: + + - Wait for the browser to crash. Error message says: "Error..." + validations: + required: true + - type: markdown + attributes: + value: | + # + - type: input + id: gf-version + attributes: + label: | + + # What Grafana version are you using? + + description: | + # + - [How do I find my Grafana version info?](https://community.grafana.com/t/how-to-find-your-grafana-version-info-3-different-ways/86857) + placeholder: "ex: v9.5.0, or v9.5.0-cloud.5.a016665c (b2a5d45589)" + validations: + required: true + - type: markdown + attributes: + value: | + # + # + # Optional Questions: + # + - type: textarea + id: get-help + attributes: + label: | + + ## Optional Questions: + + ### Is the bug inside a Dashboard Panel? + + description: | + If the bug appears inside a [dashboard panel](https://grafana.com/docs/grafana/latest/panels-visualizations/#panels-and-visualizations), please use the ["Get-Help" feature](https://grafana.com/docs/grafana/latest/troubleshooting/send-panel-to-grafana-support/). Select **copy to clipboard** and paste the data below. + No need for backticks--this text will get formatted as a code-block. + + What's a [dashboard panel](https://grafana.com/docs/grafana/latest/panels-visualizations/#panels-and-visualizations)? + placeholder: Copy "get-help" data here + value: Copy the panel's ["get-help" data](https://grafana.com/docs/grafana/latest/troubleshooting/send-panel-to-grafana-support/) here + - type: dropdown + id: gf-deployment + attributes: + label: Grafana Platform? + description: | + **How** are you running/deploying Grafana? + options: + - I use Grafana Cloud + - Docker + - Kubernetes + - A package manager (APT, YUM, BREW, etc.) + - A downloaded binary + - Other + - I don't know + validations: + required: false + - type: input + id: user-os + attributes: + label: User's OS? + description: What operating system are you running locally? + placeholder: "ex. MacOS Big Sur 11.7, or Ubuntu 20.04..." + - type: input + id: user-browser + attributes: + label: User's Browser? + description: Is the bug occuring in Grafana's user-interface? If so, what browsers are you seeing the problem on? You may choose more than one. + placeholder: "ex. Google Chrome Version 112.0.5615.137 (Official Build) (arm64)..." + - type: dropdown + id: regression + attributes: + label: Is this a Regression? + description: | + A regression means that the feature was working, then you upgraded, and now it's broken. + options: + - 'No' + - 'Yes' + validations: + required: false + - type: input + id: gf-datasource + attributes: + label: Are Datasources involved? + description: | + Is this issue specific to a datasource plugin / plugins? Please list all that apply: + placeholder: "ex. Elasticsearch 5.0.0 or Infinity 1.4.1 ..." + - type: markdown + attributes: + value: | + # + # + ## Anything else to add? + # + - type: textarea + id: extra + attributes: + label: | + Anything else to add? + description: Add any extra information here \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/1-bug_report.md b/.github/ISSUE_TEMPLATE/1-bug_report.md deleted file mode 100644 index a2fecc3893c..00000000000 --- a/.github/ISSUE_TEMPLATE/1-bug_report.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Bug report -about: Report a bug you found when using Grafana -labels: 'type: bug' ---- - - - -**What happened**: - -**What you expected to happen**: - -**How to reproduce it (as minimally and precisely as possible)**: - -**Anything else we need to know?**: - -**Environment**: -- Grafana version: -- Data source type & version: -- OS Grafana is installed on: -- User OS & Browser: -- Grafana plugins: -- Others: diff --git a/.github/ISSUE_TEMPLATE/1-staff_issues.md b/.github/ISSUE_TEMPLATE/1-staff_issues.md new file mode 100644 index 00000000000..48aa91e08c9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1-staff_issues.md @@ -0,0 +1,5 @@ +--- +name: Staff Issues +about: Blank issue for Grafana staff members +labels: 'internal' +--- diff --git a/.github/ISSUE_TEMPLATE/8-Saga_contribution.yaml b/.github/ISSUE_TEMPLATE/8-Saga_contribution.yaml new file mode 100644 index 00000000000..5624b43c122 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/8-Saga_contribution.yaml @@ -0,0 +1,65 @@ +--- +name: Saga contribution template +description: Contribute to the design system. +title: "Saga: " +labels: + - area/grafana/ui, design-system +body: + - type: markdown + attributes: + value: | + By using this template, you help expand our component and pattern solutions within the design system and let others reuse your work! You can contribute bug or design defect fixes, component, and pattern enhancements, or even net-new components! This template brings visibility to the great work everyone already does and in turn helps reduce duplicate efforts. + Thank you! + - type: checkboxes + attributes: + label: What type of contribution is this? + description: null + options: + - label: Fix - bug, design defect, typo, documentation conciseness + - label: Unification - combining similar components/patterns into a single + solution + - label: Proposal - net-new component, additions, variants + validations: + required: true + - type: textarea + id: background + attributes: + label: Why is this needed? + description: Explain the use case and where it can be used. + placeholder: null + validations: + required: true + - type: textarea + id: use_case + attributes: + label: Where is this or where can it be used? + description: Explain the current/future use case, be sure to include areas of impact + placeholder: null + validations: + required: true + - type: textarea + attributes: + label: Deliverables + description: Add a checklist of deliverables here. You can later add links to + each deliverable. + value: | + - Figma mockup + - Storybook story + - … + - type: checkboxes + attributes: + label: Contribution guiding principles + description: Use this checklist of guiding principles to ensure your + contribution makes Grafana a better product + options: + - label: Universal - not specific to a single product feature; able to be used + product-wide + - label: Accessible - inclusive to all types of users and assistive tools + - label: Flexible - thoughtfully built and designed to solve various + configurations and situations + - label: Coherent - follows existing visual styling and patterns + - label: Defined - documented clearly and concisely for ease of understanding + - label: Distinct - solves a unique gap or problem that cannot be solved with + current solutions + validations: + required: false diff --git a/.github/bot.md b/.github/bot.md index c08f03d1953..ec745bcf93a 100644 --- a/.github/bot.md +++ b/.github/bot.md @@ -23,7 +23,7 @@ Metrics are configured in [metrics-collector.json](https://github.com/grafana/gr ## Backport PR To automatically backport a PR to a release branch like v7.3.x add a label named `backport v7.3.x`. The label name should follow the pattern `backport `. Once merged grafanabot will automatically -try to cherry-pick the PR merge commit into that branch and open a PR. It will sync the milestone with the source PR so make sure the source PR also is assigned the milestone for the patch release. If the PR is already merged you can still add this label and trigger the backport automation. +try to cherry-pick the PR merge commit into that branch and open a PR. You must then add the milestone to your backport PR. If there are merge conflicts the bot will write a comment on the source PR saying the cherry-pick failed. In this case you have to do the cherry pick and backport PR manually. diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a652df0a8ad..bdb71164b90 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -16,22 +16,6 @@ "systemjs", "ts-loader", // we should remove ts-loader and use babel-loader instead "ora", // we should bump this once we move to esm modules - - // Sentry deprecated in favor of Grafana Faro for frontend logging. - // Major effort required to upgrade to latest Sentry, not worthwhile - "@sentry/browser", - "@sentry/types", - "@sentry/utils", - - // dep updates blocked by React 18 - "@testing-library/dom", - "@testing-library/react", - "@types/react", - "@types/react-dom", - "@types/react-test-renderer", - "react", - "react-dom", - "react-test-renderer" ], "includePaths": ["package.json", "packages/**"], "ignorePaths": ["packages/grafana-toolkit/package.json", "emails/**", "plugins-bundled/**", "**/mocks/**"], diff --git a/.github/teams.yml b/.github/teams.yml index 00259652bee..366c2471cd8 100644 --- a/.github/teams.yml +++ b/.github/teams.yml @@ -7,5 +7,5 @@ test: # Alerting team area/alerting: - github-board: 52 - channel-label: C02B9MXQE0J + channel-label: C028MCV4R7C + exclude-github-team: alerting-squad diff --git a/.github/workflows/cloud-data-sources-code-coverage.yml b/.github/workflows/cloud-data-sources-code-coverage.yml index a99d0409468..dbd1d2bcb01 100644 --- a/.github/workflows/cloud-data-sources-code-coverage.yml +++ b/.github/workflows/cloud-data-sources-code-coverage.yml @@ -14,7 +14,7 @@ on: jobs: workflow-call: - uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.18 + uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.19 with: frontend-path-regexp: public\/app\/plugins\/datasource\/(azuremonitor|cloud-monitoring|cloudwatch) backend-path-regexp: pkg\/tsdb\/(azuremonitor|cloudmonitoring|cloudwatch) diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index ac8c19754ae..10e9ca5a114 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -1,4 +1,4 @@ -name: Notify Slack channel based on issue label +name: Notify Slack channel based on new issue label on: issues: @@ -14,37 +14,28 @@ jobs: - name: "Determine which team to notify" run: | # Default to null values. - BOARD="null" CHANNEL="null" + TEAM="null" echo "${{ github.event.label.name }} label added" export CURRENT_LABEL="${{ github.event.label.name }}" # Enable the use of the label in yq evaluations # yq is installed by default in ubuntu-latest if [[ $(yq e 'keys | .[] | select(. == env(CURRENT_LABEL))' teams.yml ) ]]; then - # Check if we have a board set to use. - if [[ $(yq '.[env(CURRENT_LABEL)] | has("github-board")' teams.yml ) == true ]]; then - BOARD=$(yq '.[env(CURRENT_LABEL)].github-board' teams.yml) - echo "Ready to add issue to Grafana board ${BOARD}" - fi # Check if we have a channel set to notify on comments. if [[ $(yq '.[env(CURRENT_LABEL)] | has("channel-label")' teams.yml ) == true ]]; then CHANNEL=$(yq '.[env(CURRENT_LABEL)].channel-label' teams.yml) echo "Ready to send issue to channel ID ${CHANNEL}" fi + + if [[ $(yq '.[env(CURRENT_LABEL)] | has("exclude-github-team")' teams.yml ) == true ]]; then + TEAM=$(yq '.[env(CURRENT_LABEL)].exclude-github-team' teams.yml) + echo "Will not send issue to channel if issue author is part of the team ${TEAM}" + fi fi - # set environment for next step - echo "BOARD=${BOARD}" >> $GITHUB_ENV + # set environment for next steps echo "CHANNEL=${CHANNEL}" >> $GITHUB_ENV - - - name: "Add to GitHub board" - if: ${{ env.BOARD != 'null' }} - uses: leonsteinhaeuser/project-beta-automations@v2.1.0 - with: - project_id: ${{ env.BOARD }} - organization: grafana - resource_node_id: ${{ github.event.issue.node_id }} - gh_token: ${{ secrets.GITHUB_TOKEN }} + echo "TEAM=${TEAM}" >> $GITHUB_ENV - name: "Prepare payload" uses: frabert/replace-string-action@v2.0 @@ -56,15 +47,24 @@ jobs: replace-with: "'" flags: 'g' + - name: "Check that issue author is not part of the team" + if: ${{ env.TEAM != 'null' }} + uses: tspascoal/get-user-teams-membership@v2 + id: checkUserMember + with: + username: ${{ github.event.issue.user.login }} + team: "${{ env.TEAM }}" + GITHUB_TOKEN: ${{ secrets.GH_BOT_ACCESS_TOKEN }} + - name: "Send Slack notification" - if: ${{ env.CHANNEL != 'null' }} + if: ${{ (env.CHANNEL != 'null') && ((steps.checkUserMember.outputs.isTeamMember == 'false') || (env.TEAM != 'null')) }} uses: slackapi/slack-github-action@v1.23.0 with: payload: > { "icon_emoji": ":grafana:", "username": "Grafana issue labeled", - "text": "Issue \"${{ steps.preparePayload.outputs.replaced }}\" labeled \"${{ github.event.label.name }}\": ${{ github.event.issue.html_url }}", + "text": "Issue \"${{ steps.preparePayload.outputs.replaced }}\" labeled \"${{ github.event.label.name }}\": ${{ github.event.issue.html_url }}, please triage.", "channel": "${{ env.CHANNEL }}" } env: diff --git a/.github/workflows/ox-code-coverage.yml b/.github/workflows/ox-code-coverage.yml index 4961bc20444..faeaad2dd78 100644 --- a/.github/workflows/ox-code-coverage.yml +++ b/.github/workflows/ox-code-coverage.yml @@ -15,7 +15,7 @@ on: jobs: workflow-call: - uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.18 + uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.19 with: frontend-path-regexp: public\/app\/features\/(explore|correlations)|public\/app\/plugins\/datasource\/(loki|elasticsearch) backend-path-regexp: pkg\/services\/(queryhistory)|pkg\/tsdb\/(loki|elasticsearch) diff --git a/.github/workflows/scripts/json-file-to-job-output.js b/.github/workflows/scripts/json-file-to-job-output.js index 0f6dd1e65ff..f176f79aa5b 100644 --- a/.github/workflows/scripts/json-file-to-job-output.js +++ b/.github/workflows/scripts/json-file-to-job-output.js @@ -1,7 +1,7 @@ module.exports = async ({ core, filePath }) => { try { - const fs = require('fs'); - const content = await readFile(fs, filePath); + const fs = require('fs').promises; + const content = await fs.readFile(filepath) const result = JSON.parse(content); core.startGroup('Parsing json file...'); @@ -15,13 +15,4 @@ module.exports = async ({ core, filePath }) => { } catch (error) { core.restFailed(error.message); } -} - -async function readFile(fs, path) { - return new Promise((resolve, reject) => { - fs.readFile(path, (error, data) => { - if (error) return reject(error); - return resolve(data); - }); - }); } \ No newline at end of file diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml index 2566d11cf56..7419b80796a 100644 --- a/.github/workflows/update-changelog.yml +++ b/.github/workflows/update-changelog.yml @@ -5,21 +5,23 @@ on: version: required: true description: 'Needs to match, exactly, the name of a milestone. The version to be released please respect: major.minor.patch or major.minor.patch-beta format. example: 7.4.3 or 7.4.3-beta1' + skip_pr: + required: false + default: "0" + skip_community_post: + required: false + default: "0" jobs: main: runs-on: ubuntu-latest steps: - - name: Checkout Actions - uses: actions/checkout@v3 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - - name: Run update changelog (manually invoked) - uses: ./actions/update-changelog + - name: Run update changelog (manually invoked) + uses: grafana/grafana-github-actions-go/update-changelog@main with: token: ${{ secrets.GH_BOT_ACCESS_TOKEN }} - metricsWriteAPIKey: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} - grafanabotForumKey: ${{ secrets.GRAFANABOT_FORUM_KEY }} + version: ${{ inputs.version }} + metrics_api_key: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} + community_api_key: ${{ secrets.GRAFANABOT_FORUM_KEY }} + community_api_username: grafanabot + skip_pr: ${{ inputs.skip_pr }} + skip_community_post: ${{ inputs.skip_community_post }} diff --git a/.gitignore b/.gitignore index 39b7a1e2ae4..fbfb6a6396f 100644 --- a/.gitignore +++ b/.gitignore @@ -139,7 +139,7 @@ pkg/services/quota/quotaimpl/storage/storage.json /packages/**/package.tgz /packages/grafana-toolkit/sass ## CI places the packages in a different location -/npm-artifacts/*.tgz +/npm-artifacts # Ignore frontend build manifest manifest.json diff --git a/.prettierrc.js b/.prettierrc.js index c14684b0835..a4df7366a3a 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,3 +1,5 @@ module.exports = { - ...require('@grafana/toolkit/src/config/prettier.plugin.config.json'), + trailingComma: 'es5', + singleQuote: true, + printWidth: 120, }; diff --git a/.yarn/patches/@storybook-addon-docs-npm-6.5.16-56ecbd77e7.patch b/.yarn/patches/@storybook-addon-docs-npm-6.5.16-56ecbd77e7.patch new file mode 100644 index 00000000000..5fc706f7d5d --- /dev/null +++ b/.yarn/patches/@storybook-addon-docs-npm-6.5.16-56ecbd77e7.patch @@ -0,0 +1,14 @@ +diff --git a/dist/ts3.9/blocks/DocsContainer.d.ts b/dist/ts3.9/blocks/DocsContainer.d.ts +index be330e44bebb02eaf2c92d365d4e7dc1da452465..6c8b1d42bea2e184456e2757eb2ee20076ba43b3 100644 +--- a/dist/ts3.9/blocks/DocsContainer.d.ts ++++ b/dist/ts3.9/blocks/DocsContainer.d.ts +@@ -1,7 +1,8 @@ +-import { FunctionComponent } from 'react'; ++import { FunctionComponent, ReactNode } from 'react'; + import { AnyFramework } from '@storybook/csf'; + import { DocsContextProps } from './DocsContext'; + export interface DocsContainerProps { + context: DocsContextProps; ++ children?: ReactNode; + } + export declare const DocsContainer: FunctionComponent; diff --git a/.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch b/.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch new file mode 100644 index 00000000000..1ea29362cf1 --- /dev/null +++ b/.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch @@ -0,0 +1,12 @@ +diff --git a/index.d.ts b/index.d.ts +index d116f54d6da12d24b48e24ff3636c9066059aa58..93290945d8b1818cab893d6466179b33869a47b9 100644 +--- a/index.d.ts ++++ b/index.d.ts +@@ -25,6 +25,7 @@ export type SplitPaneProps = { + pane2Style?: React.CSSProperties; + resizerClassName?: string; + step?: number; ++ children?: React.ReactNode; + }; + + export type SplitPaneState = { diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c35d787414..6f2b1a90cf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,275 @@ + + +# 9.5.1 (2023-04-26) + +### Features and enhancements + +- **Chore:** Upgrade Go to 1.20.3. [#66831](https://github.com/grafana/grafana/issues/66831), [@sakjur](https://github.com/sakjur) + + + + +# 9.5.0 (2023-04-04) + +### Features and enhancements + +- **API keys:** Add deprecation to api keys. [#65948](https://github.com/grafana/grafana/pull/65948), [@eleijonmarck](https://github.com/eleijonmarck) +- **API:** Enable serving Swagger UI by default and add docs and guidelines. [#63489](https://github.com/grafana/grafana/pull/63489), [@papagian](https://github.com/papagian) +- **API:** Permit Cache-Control (browser caching) for datasource resources. [#62033](https://github.com/grafana/grafana/pull/62033), [@kylebrandt](https://github.com/kylebrandt) +- **Accessibility:** Make row actions keyboard accessible. [#63367](https://github.com/grafana/grafana/pull/63367), [@ashharrison90](https://github.com/ashharrison90) +- **Admin/Plugins:** Set category filter in connections link. [#64393](https://github.com/grafana/grafana/pull/64393), [@mikkancso](https://github.com/mikkancso) +- **Alerting:** Add CustomDetails field in PagerDuty contact point. [#64860](https://github.com/grafana/grafana/pull/64860), [@santihernandezc](https://github.com/santihernandezc) +- **Alerting:** Add dashboard and panel links to rule and instance annotations. [#63243](https://github.com/grafana/grafana/pull/63243), [@konrad147](https://github.com/konrad147) +- **Alerting:** Add filter and remove funcs for custom labels and annotations. [#63437](https://github.com/grafana/grafana/pull/63437), [@grobinson-grafana](https://github.com/grobinson-grafana) +- **Alerting:** Add fuzzy search to alert list view. [#63931](https://github.com/grafana/grafana/pull/63931), [@konrad147](https://github.com/konrad147) +- **Alerting:** Add metrics for active receiver and integrations. [#64050](https://github.com/grafana/grafana/pull/64050), [@gotjosh](https://github.com/gotjosh) +- **Alerting:** Better printing of labels. [#63348](https://github.com/grafana/grafana/pull/63348), [@grobinson-grafana](https://github.com/grobinson-grafana) +- **Alerting:** Create new state history "fanout" backend that dispatches to multiple other backends at once. [#64774](https://github.com/grafana/grafana/pull/64774), [@alexweav](https://github.com/alexweav) +- **Alerting:** Enable preview for recording rules. [#63260](https://github.com/grafana/grafana/pull/63260), [@VikaCep](https://github.com/VikaCep) +- **Alerting:** Fetch all applied alerting configurations. [#65728](https://github.com/grafana/grafana/pull/65728), [@santihernandezc](https://github.com/santihernandezc) +- **Alerting:** Introduce proper feature toggles for common state history backend combinations. [#65497](https://github.com/grafana/grafana/pull/65497), [@alexweav](https://github.com/alexweav) +- **Alerting:** Make time range query parameters not required when querying Loki. [#62985](https://github.com/grafana/grafana/pull/62985), [@alexweav](https://github.com/alexweav) +- **Alerting:** New notification policies view. [#61952](https://github.com/grafana/grafana/pull/61952), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** No longer index state history log streams by instance labels. [#65474](https://github.com/grafana/grafana/pull/65474), [@alexweav](https://github.com/alexweav) +- **Alerting:** Respect "For" Duration for NoData alerts. [#65574](https://github.com/grafana/grafana/pull/65574), [@JacobsonMT](https://github.com/JacobsonMT) +- **Alerting:** Support filtering rules by multiple datasources. [#64355](https://github.com/grafana/grafana/pull/64355), [@VikaCep](https://github.com/VikaCep) +- **Alerting:** Switch to snappy-compressed-protobuf for outgoing push requests to Loki. [#65077](https://github.com/grafana/grafana/pull/65077), [@alexweav](https://github.com/alexweav) +- **Angular:** Prevent angular from loading when disabled. [#65755](https://github.com/grafana/grafana/pull/65755), [@torkelo](https://github.com/torkelo) +- **Auth:** Add Generic oauth skip org role sync setting. [#62418](https://github.com/grafana/grafana/pull/62418), [@eleijonmarck](https://github.com/eleijonmarck) +- **Auth:** Add feature flag to move token rotation to client. [#65060](https://github.com/grafana/grafana/pull/65060), [@kalleep](https://github.com/kalleep) +- **Auth:** Show user sync external Authentication status. [#62721](https://github.com/grafana/grafana/pull/62721), [@lokeswaran-aj](https://github.com/lokeswaran-aj) +- **Backend:** Use sdk version 0.148.0. [#62822](https://github.com/grafana/grafana/pull/62822), [@kylebrandt](https://github.com/kylebrandt) +- **Chore:** Add stat for remote cache config. [#64276](https://github.com/grafana/grafana/pull/64276), [@DanCech](https://github.com/DanCech) +- **Chore:** Replace short UID generation with more standard UUIDs. [#62731](https://github.com/grafana/grafana/pull/62731), [@ryantxu](https://github.com/ryantxu) +- **Chore:** Use DOMPurify to sanitize strings rather than js-xss. [#62787](https://github.com/grafana/grafana/pull/62787), [@KristianGrafana](https://github.com/KristianGrafana) +- **CloudMonitoring:** Add possibility to use path for private key. [#65252](https://github.com/grafana/grafana/pull/65252), [@zoltanbedi](https://github.com/zoltanbedi) +- **CloudWatch Logs:** Update default timeout to 30m. [#63155](https://github.com/grafana/grafana/pull/63155), [@ashnove](https://github.com/ashnove) +- **CloudWatch:** Add AWS/IotSiteWise namespace and metrics. [#63534](https://github.com/grafana/grafana/pull/63534), [@iwysiu](https://github.com/iwysiu) +- **CloudWatch:** Add account support to variable queries. [#63822](https://github.com/grafana/grafana/pull/63822), [@iwysiu](https://github.com/iwysiu) +- **CloudWatch:** Make deeplinks work for us-gov and china regions. [#64080](https://github.com/grafana/grafana/pull/64080), [@iwysiu](https://github.com/iwysiu) +- **Cloudwatch:** Add MeteredIOBytes metric for EFS. [#64793](https://github.com/grafana/grafana/pull/64793), [@xlagg5](https://github.com/xlagg5) +- **Command Palette:** Display dashboard location. [#63807](https://github.com/grafana/grafana/pull/63807), [@ashharrison90](https://github.com/ashharrison90) +- **Command palette:** Enable folder searching. [#62663](https://github.com/grafana/grafana/pull/62663), [@ashharrison90](https://github.com/ashharrison90) +- **Connections:** Turn on feature toggle by default. [#64885](https://github.com/grafana/grafana/pull/64885), [@mikkancso](https://github.com/mikkancso) +- **Cookies:** Provide a mechanism for per user control over cookies. [#61566](https://github.com/grafana/grafana/pull/61566), [@sakjur](https://github.com/sakjur) +- **Dashboard Datasource:** Update Query List & Improve UX. [#64429](https://github.com/grafana/grafana/pull/64429), [@codeincarnate](https://github.com/codeincarnate) +- **Dashboard:** Add a feature that creates a table panel when a spreadsheet file is dropped on the dashboard. [#62688](https://github.com/grafana/grafana/pull/62688), [@oscarkilhed](https://github.com/oscarkilhed) +- **Dashboard:** Add new visualization/row/library panel/pasted panel is now a dropdown menu. [#65361](https://github.com/grafana/grafana/pull/65361), [@polibb](https://github.com/polibb) +- **Dashboard:** Add value format for requests per minute. [#62258](https://github.com/grafana/grafana/pull/62258), [@dwradcliffe](https://github.com/dwradcliffe) +- **Dashboard:** Empty/No Panels dashboard with a new design. [#65161](https://github.com/grafana/grafana/pull/65161), [@polibb](https://github.com/polibb) +- **Dashboard:** When dashboard is not found show message instead of empty page. [#65508](https://github.com/grafana/grafana/pull/65508), [@polibb](https://github.com/polibb) +- **Dashboards:** Enable feature flag `newPanelChromeUI` by default. [#65593](https://github.com/grafana/grafana/pull/65593), [@axelavargas](https://github.com/axelavargas) +- **Dataplane:** Support timeSeriesLong without transform. [#62732](https://github.com/grafana/grafana/pull/62732), [@bohandley](https://github.com/bohandley) +- **Datasources:** Add user_agent header customization for outgoing HTTP requests. [#63769](https://github.com/grafana/grafana/pull/63769), [@zhichli](https://github.com/zhichli) +- **Datasources:** Use getDefaultQuery in annotations editors. [#61870](https://github.com/grafana/grafana/pull/61870), [@idastambuk](https://github.com/idastambuk) +- **Docs:** Add documentation on how to debug backend plugins. [#64814](https://github.com/grafana/grafana/pull/64814), [@xnyo](https://github.com/xnyo) +- **Docs:** Deprecate dashboard previews. [#65698](https://github.com/grafana/grafana/pull/65698), [@ArturWierzbicki](https://github.com/ArturWierzbicki) +- **Elasticsearch:** Detect Elasticsearch version. [#63341](https://github.com/grafana/grafana/pull/63341), [@gabor](https://github.com/gabor) +- **Elasticsearch:** Run Explore queries trough data source backend. [#65339](https://github.com/grafana/grafana/pull/65339), [@ivanahuckova](https://github.com/ivanahuckova) +- **Explore:** Add range option to internal data links. [#64063](https://github.com/grafana/grafana/pull/64063), [@connorlindsey](https://github.com/connorlindsey) +- **Explore:** Add transformations to correlation data links. [#61799](https://github.com/grafana/grafana/pull/61799), [@gelicia](https://github.com/gelicia) +- **Explore:** Support mixed data sources for supplementary query. [#63036](https://github.com/grafana/grafana/pull/63036), [@ifrost](https://github.com/ifrost) +- **Extensions:** Expose an enum for available placements. [#64586](https://github.com/grafana/grafana/pull/64586), [@leventebalogh](https://github.com/leventebalogh) +- **Feat:** Changing link destination for get more plugins. [#63517](https://github.com/grafana/grafana/pull/63517), [@tolzhabayev](https://github.com/tolzhabayev) +- **Feat:** Linking to plugin details page rather than externally for new datasources. [#63499](https://github.com/grafana/grafana/pull/63499), [@tolzhabayev](https://github.com/tolzhabayev) +- **FieldMatchers:** Add match by value (reducer). [#64477](https://github.com/grafana/grafana/pull/64477), [@leeoniya](https://github.com/leeoniya) +- **Flame graph:** Add context menu. [#62705](https://github.com/grafana/grafana/pull/62705), [@joey-grafana](https://github.com/joey-grafana) +- **Flame graph:** Add metadata above flame graph. [#61921](https://github.com/grafana/grafana/pull/61921), [@joey-grafana](https://github.com/joey-grafana) +- **Geomap:** Improve tooltip url for photos layer. [#63487](https://github.com/grafana/grafana/pull/63487), [@adela-almasan](https://github.com/adela-almasan) +- **Geomap:** Release night / day layer. [#63435](https://github.com/grafana/grafana/pull/63435), [@adela-almasan](https://github.com/adela-almasan) +- **InfluxDB:** Move database information into jsondata. [#62308](https://github.com/grafana/grafana/pull/62308), [@itsmylife](https://github.com/itsmylife) +- **Jaeger and Zipkin:** Config & docs upgrade. [#64250](https://github.com/grafana/grafana/pull/64250), [@joey-grafana](https://github.com/joey-grafana) +- **LDAP:** Allow setting minimum TLS version and accepted ciphers. [#63646](https://github.com/grafana/grafana/pull/63646), [@Jguer](https://github.com/Jguer) +- **Licensing:** Allow server admin user to log in even if the active user limit is reached. (Enterprise) +- **Live:** Remove (alpha) ability to configure live pipelines. [#65138](https://github.com/grafana/grafana/pull/65138), [@ryantxu](https://github.com/ryantxu) +- **Logger:** Add feature toggle for errors in HTTP request logs. [#64425](https://github.com/grafana/grafana/pull/64425), [@sakjur](https://github.com/sakjur) +- **Login:** Allow custom name and icon for social providers. [#63297](https://github.com/grafana/grafana/pull/63297), [@jkroepke](https://github.com/jkroepke) +- **Logs Panel:** Refactor style generation to improve rendering performance. [#62599](https://github.com/grafana/grafana/pull/62599), [@matyax](https://github.com/matyax) +- **Logs:** Add millisecond to timestamp in log line. [#64372](https://github.com/grafana/grafana/pull/64372), [@svennergr](https://github.com/svennergr) +- **Logs:** Rename dedup to deduplicate. [#62944](https://github.com/grafana/grafana/pull/62944), [@gwdawson](https://github.com/gwdawson) +- **Loki Query Editor:** Make Monaco the default editor. [#62247](https://github.com/grafana/grafana/pull/62247), [@matyax](https://github.com/matyax) +- **Loki:** Add `unpack` query builder hint. [#65608](https://github.com/grafana/grafana/pull/65608), [@svennergr](https://github.com/svennergr) +- **Loki:** Add descriptions to query builder operations. [#64046](https://github.com/grafana/grafana/pull/64046), [@gwdawson](https://github.com/gwdawson) +- **Loki:** Add placeholder to the loki query editor. [#62773](https://github.com/grafana/grafana/pull/62773), [@gwdawson](https://github.com/gwdawson) +- **Loki:** Always fetch for new label keys in the QueryBuilder. [#64597](https://github.com/grafana/grafana/pull/64597), [@svennergr](https://github.com/svennergr) +- **Loki:** Display error with label filter conflicts. [#63109](https://github.com/grafana/grafana/pull/63109), [@gwdawson](https://github.com/gwdawson) +- **Loki:** Improve the display of loki query stats. [#63623](https://github.com/grafana/grafana/pull/63623), [@gwdawson](https://github.com/gwdawson) +- **MSSQL/Postgres:** List views in table dropdown as well. [#62867](https://github.com/grafana/grafana/pull/62867), [@zoltanbedi](https://github.com/zoltanbedi) +- **MSSQL:** Update forked go-mssqldb dependency. [#65658](https://github.com/grafana/grafana/pull/65658), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **Metrics:** Update comment to mention folders instead of dashboards. [#63312](https://github.com/grafana/grafana/pull/63312), [@monteiro-renato](https://github.com/monteiro-renato) +- **Navigation:** Enable new navigation by default. [#65335](https://github.com/grafana/grafana/pull/65335), [@ashharrison90](https://github.com/ashharrison90) +- **NodeGraph:** Support icons for nodes. [#60989](https://github.com/grafana/grafana/pull/60989), [@aocenas](https://github.com/aocenas) +- **Notifications:** Enable display of trace ID by default. [#64884](https://github.com/grafana/grafana/pull/64884), [@ashharrison90](https://github.com/ashharrison90) +- **Packaging:** Start Grafana service after InfluxDB. [#64090](https://github.com/grafana/grafana/pull/64090), [@MichaIng](https://github.com/MichaIng) +- **Panel Header:** Add CancelQuery option to panel header. [#64796](https://github.com/grafana/grafana/pull/64796), [@axelavargas](https://github.com/axelavargas) +- **Panel:** Show multiple errors info in the inspector. [#64340](https://github.com/grafana/grafana/pull/64340), [@andresmgot](https://github.com/andresmgot) +- **PanelChrome:** Add option to show actions on the right side (actions = leftItems). [#65762](https://github.com/grafana/grafana/pull/65762), [@torkelo](https://github.com/torkelo) +- **Phlare:** Allow variables in labelSelector (in query). [#64324](https://github.com/grafana/grafana/pull/64324), [@joey-grafana](https://github.com/joey-grafana) +- **Plugin:** Skip preloading disabled app plugins. [#63083](https://github.com/grafana/grafana/pull/63083), [@mckn](https://github.com/mckn) +- **Plugins:** Add optional logger for plugin requests sent to backend plugins. [#62981](https://github.com/grafana/grafana/pull/62981), [@bergquist](https://github.com/bergquist) +- **Plugins:** Extend panel menu with commands from plugins. [#63802](https://github.com/grafana/grafana/pull/63802), [@mckn](https://github.com/mckn) +- **Plugins:** Extend panel menu with links from plugins. [#63089](https://github.com/grafana/grafana/pull/63089), [@jackw](https://github.com/jackw) +- **Plugins:** Improve instrumentation by adding metrics and tracing. [#61035](https://github.com/grafana/grafana/pull/61035), [@xnyo](https://github.com/xnyo) +- **Plugins:** Support for distributed tracing in backend plugins SDK. [#63714](https://github.com/grafana/grafana/pull/63714), [@xnyo](https://github.com/xnyo) +- **Plugins:** Support for link extensions. [#61663](https://github.com/grafana/grafana/pull/61663), [@mckn](https://github.com/mckn) +- **Profiling:** Enable flame graph & Phlare/Parca data sources for all users. [#63488](https://github.com/grafana/grafana/pull/63488), [@joey-grafana](https://github.com/joey-grafana) +- **Prometheus Datasource:** Improve Prom query variable editor. [#58292](https://github.com/grafana/grafana/pull/58292), [@bohandley](https://github.com/bohandley) +- **Prometheus Metrics:** Add missing stat_total_teams metric. [#65133](https://github.com/grafana/grafana/pull/65133), [@gamab](https://github.com/gamab) +- **Prometheus/Loki:** Run query explicitly instead of onblur in panel edit. [#64815](https://github.com/grafana/grafana/pull/64815), [@torkelo](https://github.com/torkelo) +- **Prometheus:** Browser resource caching. [#60711](https://github.com/grafana/grafana/pull/60711), [@gtk-grafana](https://github.com/gtk-grafana) +- **Prometheus:** Improve prometheus query variable editor. [#63529](https://github.com/grafana/grafana/pull/63529), [@bohandley](https://github.com/bohandley) +- **Prometheus:** Use $\_\_rate_interval for rate queries generated by metric browser. [#65386](https://github.com/grafana/grafana/pull/65386), [@ivanahuckova](https://github.com/ivanahuckova) +- **Pubdash:** Email sharing handle dashboard deleted. [#64247](https://github.com/grafana/grafana/pull/64247), [@owensmallwood](https://github.com/owensmallwood) +- **Pubdash:** Email sharing handle dashboard deleted. (Enterprise) +- **PublicDashboards:** Backfills share column with default value. [#63407](https://github.com/grafana/grafana/pull/63407), [@owensmallwood](https://github.com/owensmallwood) +- **PublicDashboards:** Configuration modal redesign. [#63211](https://github.com/grafana/grafana/pull/63211), [@juanicabanas](https://github.com/juanicabanas) +- **PublicDashboards:** Email sharing. [#63762](https://github.com/grafana/grafana/pull/63762), [@juanicabanas](https://github.com/juanicabanas) +- **PublicDashboards:** Email sharing. (Enterprise) +- **PublicDashboards:** Enable creation when dashboard has template variables. [#64560](https://github.com/grafana/grafana/pull/64560), [@juanicabanas](https://github.com/juanicabanas) +- **PublicDashboards:** Paused or deleted public dashboard screen. [#63970](https://github.com/grafana/grafana/pull/63970), [@juanicabanas](https://github.com/juanicabanas) +- **PublicDashboards:** Viewer can request and claim magic link. (Enterprise) +- **QueryHistory:** Improve handling of mixed datasource entries. [#62214](https://github.com/grafana/grafana/pull/62214), [@Elfo404](https://github.com/Elfo404) +- **Rendering:** Experimental support to use JWTs as auth method. [#60841](https://github.com/grafana/grafana/pull/60841), [@joanlopez](https://github.com/joanlopez) +- **Reports:** Improve the UI for the new navigation. (Enterprise) +- **SQL Datasources:** Add back help content. [#65383](https://github.com/grafana/grafana/pull/65383), [@zoltanbedi](https://github.com/zoltanbedi) +- **Schema:** Remove exclusion for timeseries and update imports. [#65242](https://github.com/grafana/grafana/pull/65242), [@ryantxu](https://github.com/ryantxu) +- **Search:** Improvements for starred dashboard search. [#64758](https://github.com/grafana/grafana/pull/64758), [@IevaVasiljeva](https://github.com/IevaVasiljeva) +- **Select:** Show icon in the grafana/ui Select component. [#63827](https://github.com/grafana/grafana/pull/63827), [@ryantxu](https://github.com/ryantxu) +- **Service accounts:** Creation logic simplification. [#63884](https://github.com/grafana/grafana/pull/63884), [@IevaVasiljeva](https://github.com/IevaVasiljeva) +- **Service accounts:** Remove Add API keys buttons and remove one state of migrating for API keys tab. [#63411](https://github.com/grafana/grafana/pull/63411), [@eleijonmarck](https://github.com/eleijonmarck) +- **SplitOpen:** Update API to accept multiple queries. [#62245](https://github.com/grafana/grafana/pull/62245), [@ivanahuckova](https://github.com/ivanahuckova) +- **Stat Panel:** Add an option for a non-gradient/solid background. [#65052](https://github.com/grafana/grafana/pull/65052), [@baldm0mma](https://github.com/baldm0mma) +- **Stat:** Add ability to remove default single-color background gradient. [#64353](https://github.com/grafana/grafana/pull/64353), [@baldm0mma](https://github.com/baldm0mma) +- **SupportBundles:** Add OAuth bundle collectors. [#64810](https://github.com/grafana/grafana/pull/64810), [@Jguer](https://github.com/Jguer) +- **Table Panel:** Add ability to use text color for value or hide value in gauge cell. [#61477](https://github.com/grafana/grafana/pull/61477), [@torkelo](https://github.com/torkelo) +- **Table:** Introduce sparkline cell type. [#63182](https://github.com/grafana/grafana/pull/63182), [@domasx2](https://github.com/domasx2) +- **Tempo:** Config and doc updates. [#64017](https://github.com/grafana/grafana/pull/64017), [@joey-grafana](https://github.com/joey-grafana) +- **Tempo:** Update service graph view and docs. [#64875](https://github.com/grafana/grafana/pull/64875), [@joey-grafana](https://github.com/joey-grafana) +- **TraceView:** Add key and url escaping of json tag values. [#64331](https://github.com/grafana/grafana/pull/64331), [@aocenas](https://github.com/aocenas) +- **TraceView:** Reworked header. [#63105](https://github.com/grafana/grafana/pull/63105), [@joey-grafana](https://github.com/joey-grafana) +- **Tracing:** Add more detail to HTTP Outgoing Request. [#64757](https://github.com/grafana/grafana/pull/64757), [@bboreham](https://github.com/bboreham) +- **Tracing:** Docs and config improvements for Tempo/Jaeger/Zipkin. [#65255](https://github.com/grafana/grafana/pull/65255), [@joey-grafana](https://github.com/joey-grafana) +- **Tracing:** Support multiple OTel propagators. [#61199](https://github.com/grafana/grafana/pull/61199), [@hairyhenderson](https://github.com/hairyhenderson) +- **Transformations:** Support time format when converting time to strings. [#63826](https://github.com/grafana/grafana/pull/63826), [@ryantxu](https://github.com/ryantxu) +- **Transformers:** Support adding the row index using calculate field transformer. [#65148](https://github.com/grafana/grafana/pull/65148), [@ryantxu](https://github.com/ryantxu) +- **Units:** Format currency with negative before the symbol. [#65152](https://github.com/grafana/grafana/pull/65152), [@ryantxu](https://github.com/ryantxu) + +### Bug fixes + +- **API:** Fix "Updated by" Column in dashboard versions table. [#65351](https://github.com/grafana/grafana/pull/65351), [@papagian](https://github.com/papagian) +- **AccessControl:** Allow editors to access GET /api/datasources. [#66343](https://github.com/grafana/grafana/pull/66343), [@mgyongyosi](https://github.com/mgyongyosi) +- **Alerting:** Add "backend" label to state history writes metrics. [#65395](https://github.com/grafana/grafana/pull/65395), [@alexweav](https://github.com/alexweav) +- **Alerting:** Add alert instance labels to Loki log lines in addition to stream labels. [#65403](https://github.com/grafana/grafana/pull/65403), [@alexweav](https://github.com/alexweav) +- **Alerting:** Elide requests to Loki if nothing should be recorded. [#65011](https://github.com/grafana/grafana/pull/65011), [@alexweav](https://github.com/alexweav) +- **Alerting:** Fix DatasourceUID and RefID missing for DatasourceNoData alerts. [#66733](https://github.com/grafana/grafana/pull/66733), [@grobinson-grafana](https://github.com/grobinson-grafana) +- **Alerting:** Fix ambiguous handling of equals in labels when bucketing Loki state history streams. [#65013](https://github.com/grafana/grafana/pull/65013), [@alexweav](https://github.com/alexweav) +- **Alerting:** Fix attachment of external labels to Loki state history log streams. [#65140](https://github.com/grafana/grafana/pull/65140), [@alexweav](https://github.com/alexweav) +- **Alerting:** Fix creating a recording rule when having multiple datasources. [#66415](https://github.com/grafana/grafana/pull/66415), [@VikaCep](https://github.com/VikaCep) +- **Alerting:** Fix explore link in alert detail view. [#66106](https://github.com/grafana/grafana/pull/66106), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix share URL for Prometheus rules on subpath. [#66752](https://github.com/grafana/grafana/pull/66752), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Fix stats that display alert count when using unified alerting. [#64852](https://github.com/grafana/grafana/pull/64852), [@gotjosh](https://github.com/gotjosh) +- **Alerting:** Hide mute timing actions when dealing with vanilla prometheus. [#66457](https://github.com/grafana/grafana/pull/66457), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Paginate result previews. [#65257](https://github.com/grafana/grafana/pull/65257), [@gillesdemey](https://github.com/gillesdemey) +- **Alerting:** Prometheus-compatible Alertmanager timings editor. [#64526](https://github.com/grafana/grafana/pull/64526), [@konrad147](https://github.com/konrad147) +- **Alerting:** Update scheduler to get updates only from database. [#64635](https://github.com/grafana/grafana/pull/64635), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Use a completely isolated context for state history writes. [#64989](https://github.com/grafana/grafana/pull/64989), [@alexweav](https://github.com/alexweav) +- **Alerting:** Use displayNameFromDS if available in preview. [#65342](https://github.com/grafana/grafana/pull/65342), [@gillesdemey](https://github.com/gillesdemey) +- **Annotation List:** Fix panel not updating when variable is changed. [#65899](https://github.com/grafana/grafana/pull/65899), [@JoaoSilvaGrafana](https://github.com/JoaoSilvaGrafana) +- **Annotations:** Ignore unique constraint violations for tags. [#65935](https://github.com/grafana/grafana/pull/65935), [@sakjur](https://github.com/sakjur) +- **Auth:** Fix orgrole picker disabled if isSynced user. [#64033](https://github.com/grafana/grafana/pull/64033), [@eleijonmarck](https://github.com/eleijonmarck) +- **AzureMonitor:** Fix Log Analytics portal links. [#65482](https://github.com/grafana/grafana/pull/65482), [@aangelisc](https://github.com/aangelisc) +- **BrowseDashboards:** Fix move to General folder not working. [#65653](https://github.com/grafana/grafana/pull/65653), [@joshhunt](https://github.com/joshhunt) +- **Catalog:** Show install error with incompatible version. [#65059](https://github.com/grafana/grafana/pull/65059), [@andresmgot](https://github.com/andresmgot) +- **Chore:** Update Grafana to use Alertmanager v0.25.1-0.20230308154952-78fedf89728b. [#64778](https://github.com/grafana/grafana/pull/64778), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **CloudMonitoring:** Add project selector for MQL editor[fix]. [#65567](https://github.com/grafana/grafana/pull/65567), [@alyssabull](https://github.com/alyssabull) +- **CloudWatch Logs:** Fix running logs queries with expressions. [#65306](https://github.com/grafana/grafana/pull/65306), [@iwysiu](https://github.com/iwysiu) +- **CloudWatch Logs:** Fix to make log queries use a relative time if available. [#65236](https://github.com/grafana/grafana/pull/65236), [@kevinwcyu](https://github.com/kevinwcyu) +- **CloudWatch Logs:** Revert "Queries in an expression should run synchronously (#64443)". [#65036](https://github.com/grafana/grafana/pull/65036), [@fridgepoet](https://github.com/fridgepoet) +- **CloudWatch:** Fix cachedQueries insights not being updated for metric queries. [#65495](https://github.com/grafana/grafana/pull/65495), [@kevinwcyu](https://github.com/kevinwcyu) +- **Cloudwatch:** Pass refId from query for expression queries. [#66147](https://github.com/grafana/grafana/pull/66147), [@idastambuk](https://github.com/idastambuk) +- **Dashboards:** Evaluate provisioned dashboard titles in a backwards compatible way. [#65184](https://github.com/grafana/grafana/pull/65184), [@sakjur](https://github.com/sakjur) +- **Dashboards:** Fix Mobile support dashboard issues on new iOS 16.3. [#65542](https://github.com/grafana/grafana/pull/65542), [@axelavargas](https://github.com/axelavargas) +- **Dashboards:** Fix broken internal data links. [#66567](https://github.com/grafana/grafana/pull/66567), [@ifrost](https://github.com/ifrost) +- **Database:** Don't sleep 10ms before every request. [#64832](https://github.com/grafana/grafana/pull/64832), [@bboreham](https://github.com/bboreham) +- **Elasticsearch:** Fix processing of response with multiple group by for alerting. [#65165](https://github.com/grafana/grafana/pull/65165), [@ivanahuckova](https://github.com/ivanahuckova) +- **Elasticsearch:** Handle multiple annotation structures. [#66762](https://github.com/grafana/grafana/pull/66762), [@gabor](https://github.com/gabor) +- **Email:** Mark HTML comments as "safe" in email templates. [#64546](https://github.com/grafana/grafana/pull/64546), [@gillesdemey](https://github.com/gillesdemey) +- **Emails:** Preserve HTML comments. (Enterprise) +- **ErrorHandling:** Fixes issues with bad error messages. [#63775](https://github.com/grafana/grafana/pull/63775), [@torkelo](https://github.com/torkelo) +- **ErrorView:** Better detection of no-data responses. [#65477](https://github.com/grafana/grafana/pull/65477), [@leeoniya](https://github.com/leeoniya) +- **Explore:** Make `DataSourcePicker` visible on small screens. [#65149](https://github.com/grafana/grafana/pull/65149), [@abdulhdr1](https://github.com/abdulhdr1) +- **Fix:** DataLinks from data sources override user defined data link. [#65996](https://github.com/grafana/grafana/pull/65996), [@axelavargas](https://github.com/axelavargas) +- **Fix:** Top table rendering and update docs. [#64497](https://github.com/grafana/grafana/pull/64497), [@joey-grafana](https://github.com/joey-grafana) +- **Frontend:** Fix broken links in /plugins when pathname has a trailing slash. [#64348](https://github.com/grafana/grafana/pull/64348), [@gassiss](https://github.com/gassiss) +- **Geomap:** Fix route layer zoom behavior. [#63409](https://github.com/grafana/grafana/pull/63409), [@drew08t](https://github.com/drew08t) +- **Google Cloud Monitoring:** Fix project variable. [#66524](https://github.com/grafana/grafana/pull/66524), [@asimpson](https://github.com/asimpson) +- **HeatMap:** Sort y buckets when all bucket names are numeric. [#65322](https://github.com/grafana/grafana/pull/65322), [@leeoniya](https://github.com/leeoniya) +- **InfluxDB:** Fix querying with hardcoded retention policy. [#66466](https://github.com/grafana/grafana/pull/66466), [@itsmylife](https://github.com/itsmylife) +- **InfluxDB:** Fix sending retention policy with InfluxQL queries. [#63820](https://github.com/grafana/grafana/pull/63820), [@itsmylife](https://github.com/itsmylife) +- **KVStore:** Include database field in migration. [#62790](https://github.com/grafana/grafana/pull/62790), [@zoltanbedi](https://github.com/zoltanbedi) +- **LDAP:** Always synchronize Server Admin role through role sync if role sync is enabled. [#58820](https://github.com/grafana/grafana/pull/58820), [@IevaVasiljeva](https://github.com/IevaVasiljeva) +- **Library panels:** Ensure pagination controls are always correctly displayed. [#65283](https://github.com/grafana/grafana/pull/65283), [@ashharrison90](https://github.com/ashharrison90) +- **Loki:** Fix autocomplete situations with multiple escaped quotes. [#65520](https://github.com/grafana/grafana/pull/65520), [@svennergr](https://github.com/svennergr) +- **MegaMenu:** Fixes mega menu showing scroll indicator when it shouldn't. [#65452](https://github.com/grafana/grafana/pull/65452), [@torkelo](https://github.com/torkelo) +- **Navigation:** Redirect to root page when switching organization. [#66655](https://github.com/grafana/grafana/pull/66655), [@ashharrison90](https://github.com/ashharrison90) +- **Navigation:** Scrolled hamburger menu links now navigate correctly in Safari. [#66261](https://github.com/grafana/grafana/pull/66261), [@ashharrison90](https://github.com/ashharrison90) +- **NestedFolders:** Fix nested folder deletion. [#63572](https://github.com/grafana/grafana/pull/63572), [@ying-jeanne](https://github.com/ying-jeanne) +- **New Panel Header:** Fix when clicking submenu item the parent menu item onClick get's triggered. [#65691](https://github.com/grafana/grafana/pull/65691), [@axelavargas](https://github.com/axelavargas) +- **Phlare:** Fix error when there are no profileTypes to send from backend. [#65455](https://github.com/grafana/grafana/pull/65455), [@aocenas](https://github.com/aocenas) +- **PieChart:** Show long labels properly. [#65699](https://github.com/grafana/grafana/pull/65699), [@mdvictor](https://github.com/mdvictor) +- **PluginExtensions:** Fixed issue with incorrect type being exposed when configuring an extension. [#65910](https://github.com/grafana/grafana/pull/65910), [@mckn](https://github.com/mckn) +- **Plugins:** Ensure proxy route bodies are valid JSON. [#61771](https://github.com/grafana/grafana/pull/61771), [@wbrowne](https://github.com/wbrowne) +- **Plugins:** Fix width for README pages with tables. [#66872](https://github.com/grafana/grafana/pull/66872), [@andresmgot](https://github.com/andresmgot) +- **Plugins:** Markdown fetch retry with lowercase. [#65384](https://github.com/grafana/grafana/pull/65384), [@wbrowne](https://github.com/wbrowne) +- **Plugins:** Skip instrumenting plugin build info for core and bundled plugins. [#66105](https://github.com/grafana/grafana/pull/66105), [@wbrowne](https://github.com/wbrowne) +- **PublicDashboards:** Query collapsed panels inside rows. [#64779](https://github.com/grafana/grafana/pull/64779), [@evictorero](https://github.com/evictorero) +- **Query Splitting:** Fix for handling queries with no requestId. [#66161](https://github.com/grafana/grafana/pull/66161), [@domasx2](https://github.com/domasx2) +- **SQL Datasources:** Fix variable throwing error if query returns no data. [#65937](https://github.com/grafana/grafana/pull/65937), [@mdvictor](https://github.com/mdvictor) +- **SQL Datasources:** Prevent Call Stack Overflows with Large Numbers of Values for Variable. [#64937](https://github.com/grafana/grafana/pull/64937), [@codeincarnate](https://github.com/codeincarnate) +- **SQLStore:** Fix SQLite error propagation if query retries are disabled. [#64904](https://github.com/grafana/grafana/pull/64904), [@papagian](https://github.com/papagian) +- **Stat Panel:** Fix issue with clipping text values. [#64300](https://github.com/grafana/grafana/pull/64300), [@JoaoSilvaGrafana](https://github.com/JoaoSilvaGrafana) +- **Table Panel:** Fix panel migration for options cell type. [#66305](https://github.com/grafana/grafana/pull/66305), [@ryantxu](https://github.com/ryantxu) +- **Table:** Fix migrations from old angular table for cell color modes. [#65760](https://github.com/grafana/grafana/pull/65760), [@torkelo](https://github.com/torkelo) +- **Table:** Fixes issue with pagination summary causing scrollbar. [#65189](https://github.com/grafana/grafana/pull/65189), [@torkelo](https://github.com/torkelo) +- **Table:** Fixes table panel gauge alignment. [#64994](https://github.com/grafana/grafana/pull/64994), [@torkelo](https://github.com/torkelo) +- **TablePanel:** Fix table cells overflowing when there are multiple data links. [#65711](https://github.com/grafana/grafana/pull/65711), [@oscarkilhed](https://github.com/oscarkilhed) +- **TablePanel:** fix footer bug; no footer calculated values after "hidden" column override. [#64269](https://github.com/grafana/grafana/pull/64269), [@baldm0mma](https://github.com/baldm0mma) +- **Team sync:** Fix apply query string instead of param. (Enterprise) +- **Templating:** Allow percent encoding of variable with custom all. [#65266](https://github.com/grafana/grafana/pull/65266), [@dprokop](https://github.com/dprokop) +- **Tempo:** Set default limit if none is provided for traceql queries. [#65039](https://github.com/grafana/grafana/pull/65039), [@domasx2](https://github.com/domasx2) +- **TimeSeries:** Don't extend stepped interpolation to graph edges. [#65657](https://github.com/grafana/grafana/pull/65657), [@leeoniya](https://github.com/leeoniya) +- **TimeSeries:** Improve stacking direction heuristic. [#65499](https://github.com/grafana/grafana/pull/65499), [@leeoniya](https://github.com/leeoniya) +- **Trace View:** Update the queryType to traceql for checking if same trace when clicking span link. [#66645](https://github.com/grafana/grafana/pull/66645), [@ericmustin](https://github.com/ericmustin) +- **TraceView:** Don't require preferredVisualisationType to render. [#64920](https://github.com/grafana/grafana/pull/64920), [@aocenas](https://github.com/aocenas) +- **Utils:** Reimplement util.GetRandomString to avoid modulo bias. [#64481](https://github.com/grafana/grafana/pull/64481), [@DanCech](https://github.com/DanCech) +- **XYChart:** Add all dataset columns in tooltip. [#65027](https://github.com/grafana/grafana/pull/65027), [@mdvictor](https://github.com/mdvictor) + +### Breaking changes + +`default` named retention policies won't be used to query. Users who have a `default` named retention policy in their `influxdb` database, have to rename it to something else. Having `default` named retention policy is not breaking anything. We will make sure to use the actual default retention policy under the hood. To change the hardcoded retention policy in the `dashboard.json`, users must they select the right retention policy from dropdown and save the panel/dashboard. Issue [#66466](https://github.com/grafana/grafana/issues/66466) + +Grafana Alerting rules with `NoDataState` configuration set to `Alerting` will now respect "For" duration. Issue [#65574](https://github.com/grafana/grafana/issues/65574) + +Users who use LDAP role sync to only sync Viewer, Editor and Admin roles, but grant Grafana Server Admin role manually will not be able to do that anymore. After this change, LDAP role sync will override any manual changes to Grafana Server Admin role assignments. If `grafana_admin` is left unset in [LDAP role mapping configuration](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/ldap/#group-mappings), it will default to false. Issue [#58820](https://github.com/grafana/grafana/issues/58820) + +### Plugin development fixes & changes + +- **DateTimePicker:** Can now select time correctly. [#65428](https://github.com/grafana/grafana/pull/65428), [@eledobleefe](https://github.com/eledobleefe) +- **Grafana UI:** Fix tooltip prop of button component. [#64765](https://github.com/grafana/grafana/pull/64765), [@suleymanbariseser](https://github.com/suleymanbariseser) +- **DateTimePicker:** Add min date support to calendar. [#64632](https://github.com/grafana/grafana/pull/64632), [@nevermind89x](https://github.com/nevermind89x) +- **GrafanaUI:** Implement new component Toggletip. [#64459](https://github.com/grafana/grafana/pull/64459), [@yduartep](https://github.com/yduartep) +- **ContextMenu:** Fix padding and show border based on items. [#63948](https://github.com/grafana/grafana/pull/63948), [@aocenas](https://github.com/aocenas) + + + + +# 9.4.9 (2023-04-24) + +### Features and enhancements + +- **Chore:** Upgrade Go to 1.20.3. [#66264](https://github.com/grafana/grafana/pull/66264), [@sakjur](https://github.com/sakjur) + + # 9.4.7 (2023-03-16) diff --git a/Dockerfile b/Dockerfile index 9f23b479ce8..ef8f75a9b0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG BASE_IMAGE=alpine:3.17 ARG JS_IMAGE=node:18-alpine3.17 ARG JS_PLATFORM=linux/amd64 -ARG GO_IMAGE=golang:1.20.1-alpine3.17 +ARG GO_IMAGE=golang:1.20.4-alpine3.17 ARG GO_SRC=go-builder ARG JS_SRC=js-builder @@ -31,6 +31,8 @@ RUN yarn build FROM ${GO_IMAGE} as go-builder +ARG COMMIT_SHA="" +ARG BUILD_BRANCH="" ARG GO_BUILD_TAGS="oss" ARG WIRE_TAGS="oss" ARG BINGO="true" @@ -62,7 +64,9 @@ COPY pkg pkg COPY scripts scripts COPY conf conf COPY .github .github -COPY .git .git + +ENV COMMIT_SHA=${COMMIT_SHA} +ENV BUILD_BRANCH=${BUILD_BRANCH} RUN make build-go GO_BUILD_TAGS=${GO_BUILD_TAGS} WIRE_TAGS=${WIRE_TAGS} diff --git a/Makefile b/Makefile index ad161a619d8..da3b9059179 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,8 @@ GO_BUILD_FLAGS += $(if $(GO_BUILD_TAGS),-build-tags=$(GO_BUILD_TAGS)) targets := $(shell echo '$(sources)' | tr "," " ") +GO_INTEGRATION_TESTS := $(shell find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) + all: deps build ##@ Dependencies @@ -75,7 +77,7 @@ gen-cue: ## Do all CUE/Thema code generation go generate ./public/app/plugins/gen.go go generate ./pkg/kindsys/report.go -gen-go: $(WIRE) gen-cue +gen-go: $(WIRE) @echo "generate go files" $(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server @@ -129,31 +131,32 @@ test-go-unit: ## Run unit tests for backend with flags. .PHONY: test-go-integration test-go-integration: ## Run integration tests for backend with flags. @echo "test backend integration tests" - $(GO) test -run Integration -covermode=atomic -timeout=30m ./pkg/... + $(GO) test -count=1 -run "^TestIntegration" -covermode=atomic -timeout=5m $(GO_INTEGRATION_TESTS) .PHONY: test-go-integration-postgres test-go-integration-postgres: devenv-postgres ## Run integration tests for postgres backend with flags. @echo "test backend integration postgres tests" $(GO) clean -testcache - $(GO) list './pkg/...' | xargs -I {} sh -c 'GRAFANA_TEST_DB=postgres go test -run Integration -covermode=atomic -timeout=2m {}' + GRAFANA_TEST_DB=postgres \ + $(GO) test -p=1 -count=1 -run "^TestIntegration" -covermode=atomic -timeout=10m $(GO_INTEGRATION_TESTS) .PHONY: test-go-integration-mysql test-go-integration-mysql: devenv-mysql ## Run integration tests for mysql backend with flags. @echo "test backend integration mysql tests" - $(GO) clean -testcache - $(GO) list './pkg/...' | xargs -I {} sh -c 'GRAFANA_TEST_DB=mysql go test -run Integration -covermode=atomic -timeout=2m {}' + GRAFANA_TEST_DB=mysql \ + $(GO) test -p=1 -count=1 -run "^TestIntegration" -covermode=atomic -timeout=10m $(GO_INTEGRATION_TESTS) .PHONY: test-go-integration-redis test-go-integration-redis: ## Run integration tests for redis cache. @echo "test backend integration redis tests" $(GO) clean -testcache - REDIS_URL=localhost:6379 $(GO) test -run IntegrationRedis -covermode=atomic -timeout=2m ./pkg/... + REDIS_URL=localhost:6379 $(GO) test -run IntegrationRedis -covermode=atomic -timeout=2m $(GO_INTEGRATION_TESTS) .PHONY: test-go-integration-memcached test-go-integration-memcached: ## Run integration tests for memcached cache. @echo "test backend integration memcached tests" $(GO) clean -testcache - MEMCACHED_HOSTS=localhost:11211 $(GO) test -run IntegrationMemcached -covermode=atomic -timeout=2m ./pkg/... + MEMCACHED_HOSTS=localhost:11211 $(GO) test -run IntegrationMemcached -covermode=atomic -timeout=2m $(GO_INTEGRATION_TESTS) test-js: ## Run tests for frontend. @echo "test frontend" @@ -188,6 +191,8 @@ build-docker-full: ## Build Docker image for development. --build-arg BINGO=false \ --build-arg GO_BUILD_TAGS=$(GO_BUILD_TAGS) \ --build-arg WIRE_TAGS=$(WIRE_TAGS) \ + --build-arg COMMIT_SHA=$$(git rev-parse --short HEAD) \ + --build-arg BUILD_BRANCH=$$(git rev-parse --abbrev-ref HEAD) \ --tag grafana/grafana$(TAG_SUFFIX):dev \ $(DOCKER_BUILD_ARGS) @@ -199,8 +204,10 @@ build-docker-full-ubuntu: ## Build Docker image based on Ubuntu for development. --build-arg BINGO=false \ --build-arg GO_BUILD_TAGS=$(GO_BUILD_TAGS) \ --build-arg WIRE_TAGS=$(WIRE_TAGS) \ + --build-arg COMMIT_SHA=$$(git rev-parse --short HEAD) \ + --build-arg BUILD_BRANCH=$$(git rev-parse --abbrev-ref HEAD) \ --build-arg BASE_IMAGE=ubuntu:20.04 \ - --build-arg GO_IMAGE=golang:1.20.1 \ + --build-arg GO_IMAGE=golang:1.20.4 \ --tag grafana/grafana$(TAG_SUFFIX):dev-ubuntu \ $(DOCKER_BUILD_ARGS) diff --git a/conf/defaults.ini b/conf/defaults.ini index abcfda646ba..30bd9c64886 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -34,6 +34,9 @@ provisioning = conf/provisioning # Protocol (http, https, h2, socket) protocol = http +# Minimum TLS version allowed. By default, this value is empty. Accepted values are: TLS1.2, TLS1.3. If nothing is set TLS1.2 would be taken +min_tls_version = "" + # The ip address to bind to, empty will bind to all interfaces http_addr = @@ -90,6 +93,14 @@ read_timeout = 0 #exampleHeader1 = exampleValue1 #exampleHeader2 = exampleValue2 +#################################### GRPC Server ######################### +[grpc_server] +network = "tcp" +address = "127.0.0.1:10000" +use_tls = false +cert_file = +key_file = + #################################### Database ############################ [database] # You can configure the database connection by specifying type, host, name, user and password @@ -151,6 +162,9 @@ query_retries = 0 # For "sqlite" only. How many times to retry transaction in case of database is locked failures. Default is 5. transaction_retries = 5 +# Set to true to add metrics and tracing for database queries. +instrument_queries = false + #################################### Cache server ############################# [remote_cache] # Either "redis", "memcached" or "database" default is "database" @@ -361,6 +375,9 @@ content_security_policy_report_only_template = """script-src 'self' 'unsafe-eval # Controls if old angular plugins are supported or not. This will be disabled by default in future release angular_support_enabled = true +# The CSRF check will be executed even if the request has no login cookie. +csrf_always_check = false + [security.encryption] # Defines the time-to-live (TTL) for decrypted data encryption keys stored in memory (cache). # Please note that small values may cause performance issues due to a high frequency decryption operations. @@ -405,6 +422,21 @@ default_home_dashboard_path = # Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. datasource_limit = 5000 + +################################### SQL Data Sources ##################### +[sql_datasources] +# Default maximum number of open connections maintained in the connection pool +# when connecting to SQL based data sources +max_open_conns_default = 100 + +# Default maximum number of idle connections maintained in the connection pool +# when connecting to SQL based data sources +max_idle_conns_default = 100 + +# Default maximum connection lifetime used when connecting +# to SQL based data sources. +max_conn_lifetime_default = 14400 + #################################### Users ############################### [users] # disable user signup / registration @@ -455,6 +487,22 @@ user_invite_max_lifetime_duration = 24h # Enter a comma-separated list of usernames to hide them in the Grafana UI. These users are shown to Grafana admins and to themselves. hidden_users = +[secretscan] +# Enable secretscan feature +enabled = false + +# Interval to check for token leaks +interval = 5m + +# base URL of the grafana token leak check service +base_url = https://secret-scanning.grafana.net + +# URL to send outgoing webhooks to in case of detection +oncall_url = + +# Whether to revoke the token if a leak is detected or just send a notification +revoke = true + [service_accounts] # When set, Grafana will not allow the creation of tokens with expiry greater than this setting. token_expiration_day_limit = @@ -843,21 +891,12 @@ facility = tag = [log.frontend] -# Should Sentry javascript agent be initialized +# Should Faro javascript agent be initialized enabled = false -# Defines which provider to use sentry or grafana -provider = sentry - -# Sentry DSN if you want to send events to Sentry. -sentry_dsn = - # Custom HTTP endpoint to send events to. Default will log the events to stdout. custom_endpoint = -# Rate of events to be reported to Sentry between 0 (none) and 1 (all), float -sample_rate = 1.0 - # Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint (/log). log_endpoint_requests_per_second_limit = 3 @@ -939,6 +978,26 @@ admin_config_poll_interval = 60s # 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. alertmanager_config_poll_interval = 60s +# The redis server address that should be connected to. +ha_redis_address = + +# The username that should be used to authenticate with the redis server. +ha_redis_username = + +# The password that should be used to authenticate with the redis server. +ha_redis_password = + +# The redis database, by default it's 0. +ha_redis_db = + +# A prefix that is used for every key or channel that is created on the redis server +# as part of HA for alerting. +ha_redis_prefix = + +# The name of the cluster peer that will be used as identifier. If none is +# provided, a random one will be generated. +ha_redis_peer_name = + # Listen address/hostname and port to receive unified alerting messages for other Grafana instances. The port is used for both TCP and UDP. It is assumed other Grafana instances are also running on the same port. ha_listen_address = "0.0.0.0:9094" @@ -954,6 +1013,11 @@ ha_peers = "" # 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. ha_peer_timeout = 15s +# The label is an optional string to include on each packet and stream. +# It uniquely identifies the cluster and prevents cross-communication +# issues when sending gossip messages in an enviromenet with multiple clusters. +ha_label = + # The interval between sending gossip messages. By lowering this value (more frequent) gossip messages are propagated # across cluster more quickly at the expense of increased bandwidth usage. # 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. @@ -1010,6 +1074,54 @@ disabled_labels = # Enable the state history functionality in Unified Alerting. The previous states of alert rules will be visible in panels and in the UI. enabled = true +# Select which pluggable state history backend to use. Either "annotations", "loki", or "multiple" +# "loki" writes state history to an external Loki instance. "multiple" allows history to be written to multiple backends at once. +# Defaults to "annotations". +backend = + +# For "multiple" only. +# Indicates the main backend used to serve state history queries. +# Either "annotations" or "loki" +primary = + +# For "multiple" only. +# Comma-separated list of additional backends to write state history data to. +secondaries = + +# For "loki" only. +# URL of the external Loki instance. +# Either "loki_remote_url", or both of "loki_remote_read_url" and "loki_remote_write_url" is required for the "loki" backend. +loki_remote_url = + +# For "loki" only. +# URL of the external Loki's read path. To be used in configurations where Loki has separated read and write URLs. +# Either "loki_remote_url", or both of "loki_remote_read_url" and "loki_remote_write_url" is required for the "loki" backend. +loki_remote_read_url = + +# For "loki" only. +# URL of the external Loki's write path. To be used in configurations where Loki has separated read and write URLs. +# Either "loki_remote_url", or both of "loki_remote_read_url" and "loki_remote_write_url" is required for the "loki" backend. +loki_remote_write_url = + +# For "loki" only. +# Optional tenant ID to attach to requests sent to Loki. +loki_tenant_id = + +# For "loki" only. +# Optional username for basic authentication on requests sent to Loki. Can be left blank to disable basic auth. +loki_basic_auth_username = + +# For "loki" only. +# Optional password for basic authentication on requests sent to Loki. Can be left blank. +loki_basic_auth_password = + +[unified_alerting.state_history.external_labels] +# Optional extra labels to attach to outbound state history records or log streams. +# Any number of label key-value-pairs can be provided. +# +# ex. +# mylabelkey = mylabelvalue + #################################### Alerting ############################ [alerting] # Enable the legacy alerting sub-system and interface. If Unified Alerting is already enabled and you try to go back to legacy alerting, all data that is part of Unified Alerting will be deleted. When this configuration section and flag are not defined, the state is defined at runtime. See the documentation for more details. @@ -1245,6 +1357,8 @@ plugin_catalog_url = https://grafana.com/grafana/plugins/ plugin_catalog_hidden_plugins = # Log all backend requests for core and external plugins. log_backend_requests = false +# Force download of public key for verifying plugin signature on startup. +enforce_public_key_download = false #################################### Grafana Live ########################################## [live] @@ -1381,25 +1495,6 @@ default_baselayer_config = # Enable or disable loading other base map layers enable_custom_baselayers = true -#################################### Dashboard previews ##################################### - -[dashboard_previews.crawler] -# Number of dashboards rendered in parallel. Default is 6. -thread_count = - -# Timeout passed down to the Image Renderer plugin. It is used in two separate places within a single rendering request: -# First during the initial navigation to the dashboard and then when waiting for all the panels to load. Default is 20s. -# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes). -rendering_timeout = - -# Maximum duration of a single crawl. Default is 1h. -# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes). -max_crawl_duration = - -# Minimum interval between two subsequent scheduler runs. Default is 12h. -# This setting should be expressed as a duration. Examples: 10s (seconds), 1m (minutes). -scheduler_interval = - #################################### Support Bundles ##################################### [support_bundles] # Enable support bundle creation (default: true) @@ -1433,7 +1528,6 @@ index_update_interval = 10s # Move an app plugin referenced by its id (including all its pages) to a specific navigation section -# Dependencies: needs the `topnav` feature to be enabled # Format: =
[navigation.app_sections] diff --git a/conf/sample.ini b/conf/sample.ini index a2d6b597ff5..edef7dc3872 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -34,6 +34,9 @@ # Protocol (http, https, h2, socket) ;protocol = http +# This is the minimum TLS version allowed. By default, this value is empty. Accepted values are: TLS1.2, TLS1.3. If nothing is set TLS1.2 would be taken +;min_tls_version = "" + # The ip address to bind to, empty will bind to all interfaces ;http_addr = @@ -91,6 +94,14 @@ #exampleHeader1 = exampleValue1 #exampleHeader2 = exampleValue2 +#################################### GRPC Server ######################### +;[grpc_server] +;network = "tcp" +;address = "127.0.0.1:10000" +;use_tls = false +;cert_file = +;key_file = + #################################### Database #################################### [database] # You can configure the database connection by specifying type, host, name, user and password @@ -153,6 +164,9 @@ # For "sqlite" only. How many times to retry transaction in case of database is locked failures. Default is 5. ;transaction_retries = 5 +# Set to true to add metrics and tracing for database queries. +;instrument_queries = false + ################################### Data sources ######################### [datasources] # Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. @@ -367,6 +381,9 @@ # List of allowed headers to be set by the user, separated by spaces. Suggested to use for if authentication lives behind reverse proxies. ;csrf_additional_headers = +# The CSRF check will be executed even if the request has no login cookie. +;csrf_always_check = false + [security.encryption] # Defines the time-to-live (TTL) for decrypted data encryption keys stored in memory (cache). # Please note that small values may cause performance issues due to a high frequency decryption operations. @@ -455,6 +472,22 @@ # Enter a comma-separated list of users login to hide them in the Grafana UI. These users are shown to Grafana admins and themselves. ; hidden_users = +[secretscan] +# Enable secretscan feature +;enabled = false + +# Interval to check for token leaks +;interval = 5m + +# base URL of the grafana token leak check service +;base_url = https://secret-scanning.grafana.net + +# URL to send outgoing webhooks to in case of detection +;oncall_url = + +# Whether to revoke the token if a leak is detected or just send a notification +;revoke = true + [service_accounts] # Service account maximum expiration date in days. # When set, Grafana will not allow the creation of tokens with expiry greater than this setting. @@ -565,6 +598,7 @@ ;role_attribute_strict = false ;allow_assign_grafana_admin = false ;skip_org_role_sync = false +;tls_skip_verify_insecure = false #################################### Google Auth ########################## [auth.google] @@ -830,20 +864,11 @@ ;tag = [log.frontend] -# Should Sentry javascript agent be initialized +# Should Faro javascript agent be initialized ;enabled = false -# Defines which provider to use, default is Sentry -;provider = sentry - -# Sentry DSN if you want to send events to Sentry. -;sentry_dsn = - -# Custom HTTP endpoint to send events captured by the Sentry agent to. Default will log the events to stdout. -;custom_endpoint = /log - -# Rate of events to be reported between 0 (none) and 1 (all), float -;sample_rate = 1.0 +# Custom HTTP endpoint to send events to. Default will log the events to stdout. +;custom_endpoint = /log-grafana-javascript-agent # Requests per second limit enforced an extended period, for Grafana backend log ingestion endpoint (/log). ;log_endpoint_requests_per_second_limit = 3 @@ -923,6 +948,26 @@ # 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. ;alertmanager_config_poll_interval = 60s +# The redis server address that should be connected to. +;ha_redis_address = + +# The username that should be used to authenticate with the redis server. +;ha_redis_username = + +# The password that should be used to authenticate with the redis server. +;ha_redis_password = + +# The redis database, by default it's 0. +;ha_redis_db = + +# A prefix that is used for every key or channel that is created on the redis server +# as part of HA for alerting. +;ha_redis_prefix = + +# The name of the cluster peer that will be used as identifier. If none is +# provided, a random one will be generated. +;ha_redis_peer_name = + # Listen address/hostname and port to receive unified alerting messages for other Grafana instances. The port is used for both TCP and UDP. It is assumed other Grafana instances are also running on the same port. The default value is `0.0.0.0:9094`. ;ha_listen_address = "0.0.0.0:9094" @@ -938,6 +983,11 @@ # 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. ;ha_peer_timeout = "15s" +# The label is an optional string to include on each packet and stream. +# It uniquely identifies the cluster and prevents cross-communication +# issues when sending gossip messages in an enviromenet with multiple clusters. +;ha_label = + # The interval between sending gossip messages. By lowering this value (more frequent) gossip messages are propagated # across cluster more quickly at the expense of increased bandwidth usage. # 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. @@ -967,6 +1017,56 @@ # For example: `disabled_labels=grafana_folder` ;disabled_labels = +[unified_alerting.state_history] +# Enable the state history functionality in Unified Alerting. The previous states of alert rules will be visible in panels and in the UI. +; enabled = true + +# Select which pluggable state history backend to use. Either "annotations", "loki", or "multiple" +# "loki" writes state history to an external Loki instance. "multiple" allows history to be written to multiple backends at once. +# Defaults to "annotations". +; backend = "multiple" + +# For "multiple" only. +# Indicates the main backend used to serve state history queries. +# Either "annotations" or "loki" +; primary = "loki" + +# For "multiple" only. +# Comma-separated list of additional backends to write state history data to. +; secondaries = "annotations" + +# For "loki" only. +# URL of the external Loki instance. +# Either "loki_remote_url", or both of "loki_remote_read_url" and "loki_remote_write_url" is required for the "loki" backend. +; loki_remote_url = "http://loki:3100" + +# For "loki" only. +# URL of the external Loki's read path. To be used in configurations where Loki has separated read and write URLs. +# Either "loki_remote_url", or both of "loki_remote_read_url" and "loki_remote_write_url" is required for the "loki" backend. +; loki_remote_read_url = "http://loki-querier:3100" + +# For "loki" only. +# URL of the external Loki's write path. To be used in configurations where Loki has separated read and write URLs. +# Either "loki_remote_url", or both of "loki_remote_read_url" and "loki_remote_write_url" is required for the "loki" backend. +; loki_remote_write_url = "http://loki-distributor:3100" + +# For "loki" only. +# Optional tenant ID to attach to requests sent to Loki. +; loki_tenant_id = 123 + +# For "loki" only. +# Optional username for basic authentication on requests sent to Loki. Can be left blank to disable basic auth. +; loki_basic_auth_username = "myuser" + +# For "loki" only. +# Optional password for basic authentication on requests sent to Loki. Can be left blank. +; loki_basic_auth_password = "mypass" + +[unified_alerting.state_history.external_labels] +# Optional extra labels to attach to outbound state history records or log streams. +# Any number of label key-value-pairs can be provided. +; mylabelkey = mylabelvalue + #################################### Alerting ############################ [alerting] # Disable legacy alerting engine & UI features @@ -1197,6 +1297,8 @@ ;plugin_catalog_hidden_plugins = # Log all backend requests for core and external plugins. ;log_backend_requests = false +# Force download of public key for verifying plugin signature on startup. +;enforce_public_key_download = false #################################### Grafana Live ########################################## [live] @@ -1342,7 +1444,6 @@ ;enable_custom_baselayers = true # Move an app plugin referenced by its id (including all its pages) to a specific navigation section -# Dependencies: needs the `topnav` feature to be enabled [navigation.app_sections] # The following will move an app plugin with the id of `my-app-id` under the `starred` section # my-app-id = admin diff --git a/contribute/merge-pull-request.md b/contribute/merge-pull-request.md index 69e2781fd4b..681f4ffe774 100644 --- a/contribute/merge-pull-request.md +++ b/contribute/merge-pull-request.md @@ -44,6 +44,8 @@ A milestone **should** be added to every pull request. Several things in the Gra This makes it easier to track what changes go into a certain release. Without this information, release managers have to go through git commits which is not an efficient process. +Always assign the milestone for the version that a PR is merged into. PRs targetting `main` should use the next minor (or major) version and backport PRs should use the same value than the target branch. + ### Include in changelog and release notes? At Grafana we generate the [changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md) and [release notes](https://grafana.com/docs/grafana/latest/release-notes/) based on merged pull requests. Including changes in the changelog/release notes is very important to provide a somewhat complete picture of what changes a Grafana release actually includes. diff --git a/contribute/style-guides/styling.md b/contribute/style-guides/styling.md index caf1b137f41..6dd61117c15 100644 --- a/contribute/style-guides/styling.md +++ b/contribute/style-guides/styling.md @@ -34,7 +34,7 @@ const getStyles = (theme: GrafanaTheme2) => ### Styling complex components In more complex cases, especially when you need to style multiple DOM elements in one component, or when using styles that depend on properties and/or state you -can have your getStyles function return an object with many class names and use [Emotion's `cx` function](https://emotion.sh/docs/emotion#cx) to compose them. +can have your getStyles function return an object with many class names and use [Emotion's `cx` function](https://emotion.sh/docs/@emotion/css#cx) to compose them. Let's say you need to style a component that has a different background depending on the `isActive` property : diff --git a/devenv/datasources.yaml b/devenv/datasources.yaml index 9f2fad5e059..be050bbe382 100644 --- a/devenv/datasources.yaml +++ b/devenv/datasources.yaml @@ -126,9 +126,9 @@ datasources: type: elasticsearch uid: gdev-elasticsearch access: proxy - database: "[logs-]YYYY.MM.DD" url: http://localhost:9200 jsonData: + index: "[logs-]YYYY.MM.DD" interval: Daily timeField: "@timestamp" logLevelField: level @@ -137,9 +137,9 @@ datasources: - name: gdev-elasticsearch-filebeat type: elasticsearch access: proxy - database: "[filebeat-]YYYY.MM.DD" url: http://localhost:9200 jsonData: + index: "[filebeat-]YYYY.MM.DD" interval: Daily timeField: "@timestamp" timeInterval: "10s" @@ -149,9 +149,9 @@ datasources: - name: gdev-elasticsearch-metricbeat type: elasticsearch access: proxy - database: "[metricbeat-]YYYY.MM.DD" url: http://localhost:9200 jsonData: + index: "[metricbeat-]YYYY.MM.DD" interval: Daily timeField: "@timestamp" timeInterval: "10s" diff --git a/devenv/datasources_docker.yaml b/devenv/datasources_docker.yaml index 53c710e1054..61c0b3c7ecd 100644 --- a/devenv/datasources_docker.yaml +++ b/devenv/datasources_docker.yaml @@ -71,18 +71,18 @@ datasources: - name: gdev-elasticsearch type: elasticsearch access: proxy - database: "[logs-]YYYY.MM.DD" url: http://elasticsearch:9200 jsonData: + index: "[logs-]YYYY.MM.DD" interval: Daily timeField: "@timestamp" - name: gdev-elasticsearch-filebeat type: elasticsearch access: proxy - database: "[filebeat-]YYYY.MM.DD" url: http://elasticsearch:9200 jsonData: + index: "[filebeat-]YYYY.MM.DD" interval: Daily timeField: "@timestamp" timeInterval: "10s" @@ -92,9 +92,9 @@ datasources: - name: gdev-elasticsearch-metricbeat type: elasticsearch access: proxy - database: "[metricbeat-]YYYY.MM.DD" url: http://elasticsearch:9200 jsonData: + index: "[metricbeat-]YYYY.MM.DD" interval: Daily timeField: "@timestamp" timeInterval: "10s" diff --git a/devenv/dev-dashboards/annotations/annotation-filtering.json b/devenv/dev-dashboards/annotations/annotation-filtering.json new file mode 100644 index 00000000000..f342c42dcd6 --- /dev/null +++ b/devenv/dev-dashboards/annotations/annotation-filtering.json @@ -0,0 +1,433 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + }, + { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "enable": true, + "filter": { + "exclude": false, + "ids": [ + 1 + ] + }, + "iconColor": "red", + "name": "Red, only panel 1", + "target": { + "lines": 4, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "enable": true, + "filter": { + "exclude": true, + "ids": [ + 1 + ] + }, + "iconColor": "yellow", + "name": "Yellow - all except 1", + "target": { + "lines": 5, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "enable": true, + "filter": { + "exclude": false, + "ids": [ + 3, + 4 + ] + }, + "iconColor": "dark-purple", + "name": "Purple only panel 3+4", + "target": { + "lines": 6, + "refId": "Anno", + "scenarioId": "annotations" + } + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 119, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Panel one", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Panel two", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Panel three", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Panel four", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": ["gdev", "annotations"], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Annotation filtering", + "uid": "ed155665", + "version": 17, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json index 5c277b491f2..dc4e4b7810c 100644 --- a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json +++ b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json @@ -5274,18 +5274,520 @@ ], "title": "Logs", "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 97, + "panels": [ + { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 98, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "alias": "", + "bucketAggs": [ + { + "field": "@timestamp", + "id": "2", + "settings": { + "interval": "auto" + }, + "type": "date_histogram" + } + ], + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "metrics": [ + { + "id": "1", + "type": "count" + } + ], + "query": "hostname: $hostname", + "refId": "A", + "timeField": "@timestamp" + } + ], + "title": "$hostname count", + "type": "timeseries" + }, + { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 99, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "alias": "", + "bucketAggs": [ + { + "field": "@timestamp", + "id": "2", + "settings": { + "interval": "auto" + }, + "type": "date_histogram" + } + ], + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "metrics": [ + { + "field": "float", + "id": "1", + "type": "avg" + } + ], + "query": "hostname: $hostname", + "refId": "A", + "timeField": "@timestamp" + } + ], + "title": "$hostname average float", + "type": "timeseries" + }, + { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 100, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "alias": "", + "bucketAggs": [ + { + "field": "@timestamp", + "id": "2", + "settings": { + "interval": "auto" + }, + "type": "date_histogram" + } + ], + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "metrics": [ + { + "id": "1", + "type": "count" + } + ], + "query": "hostname: $hostname AND level: $level_by_hostname", + "refId": "A", + "timeField": "@timestamp" + } + ], + "title": "$hostname and $level_by_hostname count", + "type": "timeseries" + }, + { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 101, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "alias": "", + "bucketAggs": [ + { + "field": "@timestamp", + "id": "2", + "settings": { + "interval": "auto" + }, + "type": "date_histogram" + } + ], + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "metrics": [ + { + "field": "float", + "id": "1", + "type": "avg" + } + ], + "query": "hostname: $hostname AND level: $level_by_hostname", + "refId": "A", + "timeField": "@timestamp" + } + ], + "title": "$hostname and $level_by_hostname average float", + "type": "timeseries" + } + ], + "title": "Using template variables", + "type": "row" } ], "refresh": "", "schemaVersion": 38, "style": "dark", - "tags": [ + "tags": [ "gdev", "elasticsearch", "datasource-test" ], "templating": { - "list": [] + "list": [ + { + "current": { + "selected": true, + "text": "hostname2", + "value": "hostname2" + }, + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "definition": "{\"find\": \"terms\", \"field\": \"hostname\"}", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "hostname", + "options": [], + "query": "{\"find\": \"terms\", \"field\": \"hostname\"}", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "error", + "value": "error" + }, + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "definition": "{\"find\": \"terms\", \"field\": \"level\", \"query\": \"hostname:$hostname\"}", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "level_by_hostname", + "options": [], + "query": "{\"find\": \"terms\", \"field\": \"level\", \"query\": \"hostname:$hostname\"}", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "filters": [], + "hide": 0, + "name": "adhoc", + "skipUrlSync": false, + "type": "adhoc" + } + ] }, "time": { "from": "now-6h", @@ -5293,7 +5795,7 @@ }, "timepicker": {}, "timezone": "", - "title": "Datasource tests - Elasticsearch complex", + "title": "Datasource tests - Elasticsearch complex with template variables", "uid": "es_complex", "version": 1, "weekStart": "" diff --git a/devenv/dev-dashboards/feature-templating/datadata-macros.json b/devenv/dev-dashboards/feature-templating/datadata-macros.json index d662d2f7add..ebf2972f8a6 100644 --- a/devenv/dev-dashboards/feature-templating/datadata-macros.json +++ b/devenv/dev-dashboards/feature-templating/datadata-macros.json @@ -868,4 +868,4 @@ "uid": "e7c29343-6d1e-4167-9c13-803fe5be8c46", "version": 48, "weekStart": "" - } \ No newline at end of file + } diff --git a/devenv/dev-dashboards/migrations/migrations.json b/devenv/dev-dashboards/migrations/migrations.json index 084551745ea..06e358f6434 100644 --- a/devenv/dev-dashboards/migrations/migrations.json +++ b/devenv/dev-dashboards/migrations/migrations.json @@ -119,8 +119,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -582,4 +581,4 @@ "uid": "cdd412c4", "version": 6, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json b/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json new file mode 100644 index 00000000000..19fbe0b3325 --- /dev/null +++ b/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json @@ -0,0 +1,1441 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#6ED0E0", + "value": 30 + }, + { + "color": "#EAB839", + "value": 50 + }, + { + "color": "#EF843C", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 15, + "x": 5, + "y": 0 + }, + "id": 4, + "options": { + "inlineEditing": true, + "root": { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "elements": [ + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "gateway" + }, + "valign": "middle" + }, + "connections": [ + { + "color": { + "field": "gateway", + "fixed": "rgb(204, 204, 220)" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": 0 + }, + "target": { + "x": -1.02020202020202, + "y": 0.041666666666666664 + }, + "targetName": "Element 2" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 1", + "placement": { + "height": 48, + "left": 117, + "top": 168, + "width": 99 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "productpage" + }, + "valign": "middle" + }, + "connections": [ + { + "color": { + "field": "product-details", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": 1 + }, + "target": { + "x": -0.9797979797979798, + "y": 0 + }, + "targetName": "Element 3" + }, + { + "color": { + "field": "product-reviews", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": -1 + }, + "target": { + "x": -1, + "y": 0.041666666666666664 + }, + "targetName": "Element 4" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 2", + "placement": { + "height": 48, + "left": 327, + "top": 168, + "width": 99 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "details" + }, + "valign": "middle" + }, + "connections": [ + { + "color": { + "field": "details-checkout", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": 0 + }, + "target": { + "x": -0.9595959595959596, + "y": -0.041666666666666664 + }, + "targetName": "Element 11" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 3", + "placement": { + "height": 48, + "left": 548, + "top": 69, + "width": 99 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "reviews" + }, + "valign": "middle" + }, + "connections": [ + { + "color": { + "field": "reviews-ratings", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": 0 + }, + "target": { + "x": -0.9797979797979798, + "y": 0.041666666666666664 + }, + "targetName": "Element 5" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 4", + "placement": { + "height": 48, + "left": 549, + "top": 274, + "width": 99 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "ratings" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 5", + "placement": { + "height": 48, + "left": 797, + "top": 275, + "width": 99 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "field": "gateway", + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "size": 20, + "text": { + "field": "gateway", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 6", + "placement": { + "height": 36, + "left": 235, + "top": 148, + "width": 64 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "field": "product-details", + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "size": 20, + "text": { + "field": "product-details", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 7", + "placement": { + "height": 36, + "left": 415, + "top": 89, + "width": 64 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "field": "product-reviews", + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "size": 20, + "text": { + "field": "product-reviews", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 8", + "placement": { + "height": 36, + "left": 411, + "top": 258, + "width": 64 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "field": "reviews-ratings", + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "size": 20, + "text": { + "field": "reviews-ratings", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 9", + "placement": { + "height": 36, + "left": 686, + "top": 250, + "width": 64 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 16, + "text": { + "fixed": "# of requests" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 10", + "placement": { + "height": 50, + "left": 13, + "top": 309, + "width": 100 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "checkout" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 11", + "placement": { + "height": 48, + "left": 797, + "top": 68, + "width": 99 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "field": "details-checkout", + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "size": 20, + "text": { + "field": "details-checkout", + "fixed": "0", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 12", + "placement": { + "height": 36, + "left": 684, + "top": 47, + "width": 63.99609375 + }, + "type": "metric-value" + } + ], + "name": "Element 1672955773575", + "type": "frame" + }, + "showAdvancedTypes": false + }, + "pluginVersion": "10.1.0-pre", + "targets": [ + { + "csvContent": "gateway, product-details, product-reviews, reviews-ratings, details-checkout\n100, 56, 44, 22, 28", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Service Graph", + "type": "canvas" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "links": [ + { + "targetBlank": true, + "title": "Show details", + "url": "https://ops.grafana-ops.net/d/fU-WBSqWz/synthetic-monitoring-summary?orgId=1" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "blue", + "value": 30 + }, + { + "color": "#EAB839", + "value": 50 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 15, + "w": 15, + "x": 5, + "y": 11 + }, + "id": 2, + "options": { + "inlineEditing": false, + "root": { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "elements": [ + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "blinkRate": { + "fixed": 2 + }, + "bulbColor": { + "field": "server_database" + }, + "statusColor": { + "field": "server_region" + }, + "type": "Single" + }, + "connections": [ + { + "color": { + "field": "server_database2", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": -1, + "y": -1 + }, + "target": { + "x": 0.030303030303030304, + "y": 0 + }, + "targetName": "Element 11" + }, + { + "color": { + "field": "server_database", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": -1, + "y": -1 + }, + "target": { + "x": 0.09090909090909091, + "y": 0.16666666666666666 + }, + "targetName": "Element 12" + }, + { + "color": { + "field": "server_region", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": -1 + }, + "target": { + "x": -0.2727272727272727, + "y": 0.4166666666666667 + }, + "targetName": "Element 13" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "server 1", + "placement": { + "height": 114, + "left": 468, + "top": 11, + "width": 120.99609375 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "blinkRate": { + "fixed": 0.25 + }, + "bulbColor": { + "field": "database_server" + }, + "statusColor": { + "field": "database_server" + }, + "type": "Database" + }, + "connections": [ + { + "color": { + "field": "database_server", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": 1 + }, + "target": { + "x": -0.15151515151515152, + "y": -0.16666666666666666 + }, + "targetName": "Element 12" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "database 1", + "placement": { + "height": 114, + "left": 207, + "top": 367, + "width": 121 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "blinkRate": { + "fixed": 0.5 + }, + "bulbColor": { + "field": "region_server" + }, + "statusColor": { + "field": "region_server" + }, + "type": "Terminal" + }, + "connections": [ + { + "color": { + "field": "region_server", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": -1, + "y": 1 + }, + "target": { + "x": -0.21212121212121213, + "y": 0.08333333333333333 + }, + "targetName": "Element 13" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "region 1", + "placement": { + "height": 116, + "left": 699, + "top": 365, + "width": 121 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "database_server", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "database_server", + "fixed": "10%", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 4", + "placement": { + "height": 29, + "left": 318, + "top": 282, + "width": 46 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "blinkRate": { + "fixed": 0.5, + "mode": "mod" + }, + "bulbColor": { + "field": "database2_server" + }, + "statusColor": { + "field": "database2_server" + }, + "type": "Database" + }, + "connections": [ + { + "color": { + "field": "database2_server", + "fixed": "white" + }, + "path": "straight", + "size": { + "fixed": 2, + "max": 10, + "min": 1 + }, + "source": { + "x": 1, + "y": 1 + }, + "target": { + "x": -0.3333333333333333, + "y": 0 + }, + "targetName": "Element 11" + } + ], + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "database 2", + "placement": { + "height": 116, + "left": 55, + "top": 191, + "width": 117 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "server_database", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "server_database", + "fixed": "10%", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 6", + "placement": { + "height": 29, + "left": 382, + "top": 162, + "width": 46 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "database2_server", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "database2_server", + "fixed": "10%", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 7", + "placement": { + "height": 29, + "left": 206, + "top": 138, + "width": 46 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "server_database2", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "server_database2", + "fixed": "10%", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 8", + "placement": { + "height": 29, + "left": 374, + "top": 103, + "width": 46 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "server_region", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "server_region", + "fixed": "10%", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 9", + "placement": { + "height": 29, + "left": 625, + "top": 158, + "width": 46 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "region_server", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "region_server", + "fixed": "10%", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 10", + "placement": { + "height": 29, + "left": 676, + "top": 282, + "width": 46 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 11", + "placement": { + "height": 24, + "left": 268, + "top": 158, + "width": 33 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 12", + "placement": { + "height": 24, + "left": 372, + "top": 254, + "width": 33 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 13", + "placement": { + "height": 24, + "left": 646, + "top": 252, + "width": 33 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "fixed": "", + "mode": "fixed" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "server_region", + "fixed": "#ffffff" + }, + "size": 20, + "text": { + "field": "server_database2", + "fixed": "Server 1", + "mode": "fixed" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 14", + "placement": { + "height": 29, + "left": 483, + "top": 138, + "width": 89 + }, + "type": "text" + } + ], + "name": "Element 1672954602175", + "placement": { + "height": 100, + "left": 0, + "top": 0, + "width": 100 + }, + "type": "frame" + }, + "showAdvancedTypes": false + }, + "pluginVersion": "10.1.0-pre", + "targets": [ + { + "csvContent": "database_server, server_database, server_region, region_server, database2_server, server_database2\n10, 53, 35, 12, 22, 81", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Network Weathermap", + "type": "canvas" + } + ], + "refresh": "", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [ + "gdev", + "panel-tests", + "canvas" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - Canvas Connection Examples", + "uid": "Pu8lwQAVz", + "version": 3, + "weekStart": "" +} \ No newline at end of file diff --git a/devenv/dev-dashboards/panel-common/shared_queries.json b/devenv/dev-dashboards/panel-common/shared_queries.json index a4804559089..9401ab42a45 100644 --- a/devenv/dev-dashboards/panel-common/shared_queries.json +++ b/devenv/dev-dashboards/panel-common/shared_queries.json @@ -453,4 +453,4 @@ "uid": "ZqZnVvFZz", "version": 8, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json b/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json new file mode 100644 index 00000000000..6fb2f433102 --- /dev/null +++ b/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json @@ -0,0 +1,80 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "selectedSeries": 0 + }, + "targets": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,0" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "B", + "scenarioId": "csv_metric_values", + "stringInput": "5,10,20,30,40,50" + } + ], + "title": "Datagrid with CSV metric values", + "type": "datagrid" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Datagrid example", + "version": 0, + "uid": "c01bf42b-b783-4447-a304-8554cee1843b", + "weekStart": "" +} \ No newline at end of file diff --git a/devenv/dev-dashboards/panel-table/table_tests_new.json b/devenv/dev-dashboards/panel-table/table_tests_new.json index 92143922121..64ddd464bd0 100644 --- a/devenv/dev-dashboards/panel-table/table_tests_new.json +++ b/devenv/dev-dashboards/panel-table/table_tests_new.json @@ -1040,4 +1040,4 @@ "uid": "U_bZIMRMk", "version": 7, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json b/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json new file mode 100644 index 00000000000..7a4b9ea0df2 --- /dev/null +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json @@ -0,0 +1,938 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "dim1" + }, + "properties": [ + { + "id": "custom.width", + "value": 80 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 8, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "showRowNums": false, + "sortBy": [] + }, + "pluginVersion": "10.0.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "queryType": "snapshot", + "refId": "A", + "snapshot": [ + { + "data": { + "values": [ + [ + 1677256641358, + 1677257007358, + 1677257373358, + 1677257739358, + 1677258105358, + 1677258471358, + 1677258837358, + 1677259203358, + 1677259569358, + 1677259935358, + 1677260301358, + 1677260667358, + 1677261033358, + 1677261399358, + 1677261765358, + 1677262131358, + 1677262497358, + 1677262863358, + 1677263229358, + 1677263595358, + 1677263961358, + 1677264327358, + 1677264693358, + 1677265059358, + 1677265425358, + 1677265791358, + 1677266157358, + 1677266523358, + 1677266889358, + 1677267255358, + 1677267621358, + 1677267987358, + 1677268353358, + 1677268719358, + 1677269085358, + 1677269451358, + 1677269817358, + 1677270183358, + 1677270549358, + 1677270915358, + 1677271281358, + 1677271647358, + 1677272013358, + 1677272379358, + 1677272745358, + 1677273111358, + 1677273477358, + 1677273843358, + 1677274209358, + 1677274575358, + 1677274941358, + 1677275307358, + 1677275673358, + 1677276039358, + 1677276405358, + 1677276771358, + 1677277137358, + 1677277503358, + 1677277869358, + 1677278235358 + ], + [ + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7, + 4, + 6, + 8, + 10, + 1, + 3, + 5, + 7 + ], + [ + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d", + "a", + "b", + "c", + "d" + ], + [ + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y", + "x", + "y" + ] + ] + }, + "schema": { + "fields": [ + { + "config": {}, + "labels": {}, + "name": "timestamp", + "type": "time", + "typeInfo": { + "frame": "time.Time" + } + }, + { + "config": {}, + "labels": {}, + "name": "numericData", + "type": "number", + "typeInfo": { + "frame": "float64", + "nullable": true + } + }, + { + "config": {}, + "labels": {}, + "name": "dim1", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "config": {}, + "labels": {}, + "name": "dim2", + "type": "string", + "typeInfo": { + "frame": "string" + } + } + ], + "meta": { + "type": "timeseries-long", + "typeVersion": [ + 0, + 0 + ] + }, + "name": "New Frame", + "refId": "A" + } + }, + { + "data": { + "values": [ + [ + 1677256641358, + 1677257007358, + 1677257373358, + 1677257739358, + 1677258105358, + 1677258471358, + 1677258837358, + 1677259203358, + 1677259569358, + 1677259935358, + 1677260301358, + 1677260667358, + 1677261033358, + 1677261399358, + 1677261765358, + 1677262131358, + 1677262497358, + 1677262863358, + 1677263229358, + 1677263595358, + 1677263961358, + 1677264327358, + 1677264693358, + 1677265059358, + 1677265425358, + 1677265791358, + 1677266157358, + 1677266523358, + 1677266889358, + 1677267255358, + 1677267621358, + 1677267987358, + 1677268353358, + 1677268719358, + 1677269085358, + 1677269451358, + 1677269817358, + 1677270183358, + 1677270549358, + 1677270915358, + 1677271281358, + 1677271647358, + 1677272013358, + 1677272379358, + 1677272745358, + 1677273111358, + 1677273477358, + 1677273843358, + 1677274209358, + 1677274575358, + 1677274941358, + 1677275307358, + 1677275673358, + 1677276039358, + 1677276405358, + 1677276771358, + 1677277137358, + 1677277503358, + 1677277869358, + 1677278235358 + ], + [ + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2, + 3, + 5, + 6, + 3, + 1, + 2, + 3, + 2 + ], + [ + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h", + "e", + "f", + "g", + "h" + ], + [ + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r", + "q", + "r" + ] + ] + }, + "schema": { + "fields": [ + { + "config": {}, + "labels": {}, + "name": "timestamp", + "type": "time", + "typeInfo": { + "frame": "time.Time" + } + }, + { + "config": {}, + "labels": {}, + "name": "value", + "type": "number", + "typeInfo": { + "frame": "float64", + "nullable": true + } + }, + { + "config": {}, + "labels": {}, + "name": "dim3", + "type": "string", + "typeInfo": { + "frame": "string" + } + }, + { + "config": {}, + "labels": {}, + "name": "dim4", + "type": "string", + "typeInfo": { + "frame": "string" + } + } + ], + "meta": { + "type": "timeseries-long", + "typeVersion": [ + 0, + 0 + ] + }, + "name": "New Frame", + "refId": "B" + } + } + ] + } + ], + "title": "timeseries-long", + "transformations": [], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 11, + "x": 8, + "y": 0 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 8, + "refId": "A" + } + ], + "title": "Timeseries panel requires a transform to render timeseries-long", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 19, + "y": 0 + }, + "id": 4, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "The timeseries panel can not show timeseries-long directly, it must first be converted to `timeseries-wide` or `timeseries-multi` first.\n\nThe UI should show a button indicating this.", + "mode": "markdown" + }, + "pluginVersion": "10.0.0-pre", + "title": "Timeseries-long info", + "type": "text" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "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, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 8 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 0 + } + ], + "title": "Expected no-data message (empty series", + "type": "timeseries" + } + ], + "refresh": "", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2023-02-24T16:37:21.358Z", + "to": "2023-02-24T22:37:15.358Z" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - Timeseries - Supported input formats", + "uid": "f4ca24309dd4", + "version": 1, + "weekStart": "" +} diff --git a/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md new file mode 100644 index 00000000000..e8af6516516 --- /dev/null +++ b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md @@ -0,0 +1,25 @@ +# Prometheus behind an OAuth2-proxy + +## How to setup OAuth2-proxy + +1. Make a copy of `oauth2-proxy.example.cfg` and rename it to `oauth2-proxy.cfg` +1. Fill in the required information (`azure client id`, `azure client secret`, `azure tenant id`) +1. Start the containers by executing `make devenv sources="prometheus,auth/prometheus_oauth2_proxy_azure"` +> If you would like to test the login flow from the browser then you need to setup TLS or start a tunnel. I usually use a tunnel (`cloudflared tunnel --url http://localhost:4180`). Do not forget to set the Redirect URIs on Azure's App Registration page + +## How to add a new Prometheus datasource with Azure Authentication enabled + +1. Navigate to Grafana and login +1. Add a new Prometheus datasource +1. On the new Prometheus datasource page + 1. Set the URL + 1. Enable Azure Authentication + 1. Fill in the required fields of the `Azure Authentication` section + 1. Click `Save & test` + 1. You should get a "Data source is working" message + +If you check the logs of OAuth2-proxy, you should see similar lines to this: +``` +2023-04-19 11:29:40 172.31.0.1:55602 - d96b832a-170a-41eb-a974-6558c5ce4454 - - [2023/04/19 09:29:40] some-random-tunnel-address.trycloudflare.com GET / "/api/v1/status/buildinfo" HTTP/1.1 "Grafana/10.0.0-pre" 200 187 0.016 +2023-04-19 11:29:41 172.31.0.1:55602 - db27c56a-ccd6-4cdb-a040-318113781abf - 65ac87f4-931f-4e46-9761-f8bf1ad36b48 [2023/04/19 09:29:41] some-random-tunnel-address.trycloudflare.com POST / "/api/v1/query" HTTP/1.1 "Grafana/10.0.0-pre" 200 103 0.003 +``` \ No newline at end of file diff --git a/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/docker-compose.yaml b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/docker-compose.yaml new file mode 100644 index 00000000000..817adf1bb83 --- /dev/null +++ b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/docker-compose.yaml @@ -0,0 +1,27 @@ + oauth2proxy: + container_name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0 + command: --config /oauth2-proxy.cfg + # command: [ + # "--provider=azure", + # "--cookie-secret=SECRETSECRET1234", + # "--client-id=", + # "--client-secret=", + # "--azure-tenant=", + # "--oidc-issuer-url=https://login.microsoftonline.com//v2.0", + # "--email-domain=*", + # "--http-address=0.0.0.0:4180", + # "--ssl-upstream-insecure-skip-verify=true", + # "--upstream=http://local-prometheus:9090/", + # "--skip-jwt-bearer-tokens=true", + # "--extra-jwt-issuers=https://sts.windows.net//=https://prometheus.monitor.azure.com", + # "--skip-auth-route=/api/v1/status/buildinfo", + # ] + ports: + - 4180:4180 + hostname: oauth2-proxy + volumes: + - "./docker/blocks/auth/prometheus_oauth2_proxy_azure/oauth2-proxy.cfg:/oauth2-proxy.cfg" + extra_hosts: + - "local-prometheus:host-gateway" + restart: unless-stopped diff --git a/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/oauth2-proxy.example.cfg b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/oauth2-proxy.example.cfg new file mode 100644 index 00000000000..dc9771bb076 --- /dev/null +++ b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/oauth2-proxy.example.cfg @@ -0,0 +1,13 @@ +provider="azure" +cookie_secret="SECRETSECRET1234" +client_id="" +client_secret="" +azure_tenant="" +oidc_issuer_url="https://login.microsoftonline.com//v2.0" +email_domains=["*"] +http_address="0.0.0.0:4180" +ssl_upstream_insecure_skip_verify="true" +upstreams=[ "http://local-prometheus:9090/" ] +skip_jwt_bearer_tokens="true" +extra_jwt_issuers="https://sts.windows.net//=https://prometheus.monitor.azure.com" +skip_auth_routes=[ "/api/v1/status/buildinfo" ] diff --git a/devenv/docker/blocks/elastic/data/data.js b/devenv/docker/blocks/elastic/data/data.js index efff9370005..080ae9f8f94 100644 --- a/devenv/docker/blocks/elastic/data/data.js +++ b/devenv/docker/blocks/elastic/data/data.js @@ -64,6 +64,18 @@ async function elasticSetupIndexTemplate() { '@timestamp': { type: 'date', }, + '@timestamp_custom': { + type: 'date', + format: 'yyyy_MM_dd_HH_mm_ss' + }, + '@timestamp_unix': { + type: 'date', + format: 'epoch_millis' + }, + '@timestamp_nanos': { + type: 'date_nanos', + format: 'strict_date_optional_time_nanos' + }, counter: { type: 'integer', }, @@ -115,6 +127,9 @@ function getRandomLogItem(counter, timestamp) { const maybeAnsiText = Math.random() < 0.5 ? 'with ANSI \u001b[31mpart of the text\u001b[0m' : ''; return { '@timestamp': timestamp.toISOString(), + '@timestamp_custom': timestamp.toISOString().split('.')[0].replace(/[T:-]/g,'_'), + '@timestamp_unix': timestamp.getTime(), + '@timestamp_nanos': timestamp.toISOString().slice(0,-1) + '123Z', line: `log text ${maybeAnsiText} [${randomText}]`, counter: counter.toString(), float: 100 * Math.random().toString(), diff --git a/devenv/docker/blocks/loki/data/data.js b/devenv/docker/blocks/loki/data/data.js index 8462deed770..bc013954726 100644 --- a/devenv/docker/blocks/loki/data/data.js +++ b/devenv/docker/blocks/loki/data/data.js @@ -63,6 +63,11 @@ async function lokiSendLogLine(timestampNs, line, tags) { await jsonRequest(data, 'POST', url, 204); } +function getSineValue(counter, loopLength) { + // we try to make a sine-wave, where one full "loop" takes loopLength steps + return Math.sin((Math.PI * 2 * counter) / loopLength); +} + function getRandomLogItem(counter) { const randomText = `${Math.trunc(Math.random() * 1000 * 1000 * 1000)}`; const maybeAnsiText = Math.random() < 0.5 ? 'with ANSI \u001b[31mpart of the text\u001b[0m' : ''; @@ -70,6 +75,7 @@ function getRandomLogItem(counter) { _entry: `log text ${maybeAnsiText} [${randomText}]`, counter: counter.toString(), float: Math.random() > 0.2 ? (Math.trunc(100000 * Math.random())/1000).toString() : 'NaN', + wave: getSineValue(counter, 20), // let's loop in 20 steps label: chooseRandomElement(['val1', 'val2', 'val3']), level: chooseRandomElement(['debug','info', 'info', 'info', 'info', 'warning', 'error', 'error']), }; @@ -151,8 +157,16 @@ function getRandomNanosecPart() { return Math.trunc(Math.random()*1000000).toString().padStart(6, '0') } +const sharedLabels = { + source: 'data', + instance: 'server\\1', + re: 'one.two$three^four', // to test regex escaping + job: '"grafana/data"' +}; -async function main() { +let globalCounter = 0; + +async function sendOldLogs() { const delays = calculateDelays(DAYS * POINTS_PER_DAY); const timeRange = DAYS * 24 * 60 * 60 * 1000; let timestampMs = new Date().getTime() - timeRange; @@ -160,12 +174,33 @@ async function main() { const delay = delays[i]; timestampMs += Math.trunc(delay * timeRange); const timestampNs = `${timestampMs}${getRandomNanosecPart()}`; - const item = getRandomLogItem(i + 1) - await lokiSendLogLine(timestampNs, JSON.stringify(item), {place:'moon', source: 'data', instance: 'server\\1', job: '"grafana/data"'}); - await lokiSendLogLine(timestampNs, logFmtLine(item), {place:'luna', source: 'data', instance: 'server\\2', job: '"grafana/data"'}); + globalCounter += 1; + const item = getRandomLogItem(globalCounter) + await lokiSendLogLine(timestampNs, JSON.stringify(item), {place:'moon', ...sharedLabels}); + await lokiSendLogLine(timestampNs, logFmtLine(item), {place:'luna', ...sharedLabels}); }; } +async function sendNewLogs() { + while(true) { + globalCounter += 1; + const nowMs = new Date().getTime(); + const timestampNs = `${nowMs}${getRandomNanosecPart()}`; + const item = getRandomLogItem(globalCounter) + await lokiSendLogLine(timestampNs, JSON.stringify(item), {place:'moon', ...sharedLabels}); + await lokiSendLogLine(timestampNs, logFmtLine(item), {place:'luna', ...sharedLabels}); + const sleepDuration = 200 + Math.random() * 800; // between 0.2 and 1 seconds + await sleep(sleepDuration); + } +} + +async function main() { + // first we send logs to build a last-7-days log-data + await sendOldLogs(); + // then keep sending new data forever + await sendNewLogs(); +} + // when running in docker, we catch the needed stop-signal, to shutdown fast process.on('SIGTERM', () => { console.log('shutdown requested'); diff --git a/devenv/docker/blocks/mysql/.env b/devenv/docker/blocks/mysql/.env index 149162e38c2..3a9117f5ede 100644 --- a/devenv/docker/blocks/mysql/.env +++ b/devenv/docker/blocks/mysql/.env @@ -1 +1 @@ -mysql_version=5.7.39 +mysql_version=8.0.32 diff --git a/devenv/docker/blocks/mysql_tests/.env b/devenv/docker/blocks/mysql_tests/.env index 149162e38c2..3a9117f5ede 100644 --- a/devenv/docker/blocks/mysql_tests/.env +++ b/devenv/docker/blocks/mysql_tests/.env @@ -1 +1 @@ -mysql_version=5.7.39 +mysql_version=8.0.32 diff --git a/devenv/docker/blocks/mysql_tests/Dockerfile b/devenv/docker/blocks/mysql_tests/Dockerfile index bc7b499ecf0..8021ec4dee8 100644 --- a/devenv/docker/blocks/mysql_tests/Dockerfile +++ b/devenv/docker/blocks/mysql_tests/Dockerfile @@ -1,4 +1,4 @@ -ARG mysql_version=5.7.39 +ARG mysql_version=8.0.32 FROM mysql:${mysql_version} ADD setup.sql /docker-entrypoint-initdb.d RUN chown -R mysql:mysql /docker-entrypoint-initdb.d/ diff --git a/devenv/docker/blocks/pyroscope/docker-compose.yaml b/devenv/docker/blocks/pyroscope/docker-compose.yaml new file mode 100644 index 00000000000..8a8a31a3219 --- /dev/null +++ b/devenv/docker/blocks/pyroscope/docker-compose.yaml @@ -0,0 +1,6 @@ + pyroscope: + image: "pyroscope/pyroscope:latest" + command: + - "server" + ports: + - "4040:4040" diff --git a/devenv/docker/ha-test-unified-alerting/docker-compose.yaml b/devenv/docker/ha-test-unified-alerting/docker-compose.yaml index 12ec330b1f2..bca8412b913 100644 --- a/devenv/docker/ha-test-unified-alerting/docker-compose.yaml +++ b/devenv/docker/ha-test-unified-alerting/docker-compose.yaml @@ -2,7 +2,7 @@ version: "2.1" services: db: - image: mysql:5.7.39 + image: mysql:8.0.32 platform: linux/x86_64 environment: MYSQL_ROOT_PASSWORD: rootpass diff --git a/devenv/docker/ha_test/docker-compose.yaml b/devenv/docker/ha_test/docker-compose.yaml index 52cbe8634c1..812600f95e5 100644 --- a/devenv/docker/ha_test/docker-compose.yaml +++ b/devenv/docker/ha_test/docker-compose.yaml @@ -16,7 +16,7 @@ services: tag: nginx db: - image: mysql:5.7.39 + image: mysql:8.0.32 environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: grafana diff --git a/devenv/jsonnet/dev-dashboards.go b/devenv/jsonnet/dev-dashboards.go index b1d926a25ff..92079311756 100644 --- a/devenv/jsonnet/dev-dashboards.go +++ b/devenv/jsonnet/dev-dashboards.go @@ -5,6 +5,7 @@ package main import ( "bytes" + "context" "embed" "fmt" "os" @@ -13,8 +14,8 @@ import ( "strings" "text/template" + "github.com/grafana/codejen" dev_dashboards "github.com/grafana/grafana/devenv/dev-dashboards" - "github.com/grafana/grafana/pkg/codegen" ) var ( @@ -35,17 +36,21 @@ func main() { if err != nil { panic(err) } - wd := codegen.NewWriteDiffer() - wd[OUTPUT_PATH] = []byte(out) + + f := codejen.NewFile(OUTPUT_PATH, []byte(out), dummyJenny{}) + fs := codejen.NewFS() + if err = fs.Add(*f); err != nil { + panic(err) + } if _, set := os.LookupEnv("CODEGEN_VERIFY"); set { - err = wd.Verify() + err = fs.Verify(context.Background(), "") if err != nil { fmt.Fprintf(os.Stderr, "generated code is not up to date:\n%s\nrun `make gen-jsonnet` to regenerate\n\n", err) os.Exit(1) } } else { - err = wd.Write() + err = fs.Verify(context.Background(), "") if err != nil { fmt.Fprintf(os.Stderr, "error while writing generated code to disk:\n%s\n", err) os.Exit(1) @@ -121,3 +126,9 @@ func (g *libjsonnetGen) readDir(dir string) error { } return nil } + +type dummyJenny struct{} + +func (dummyJenny) JennyName() string { + return "dummyJenny" +} diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet index 78eb25255c1..3dbd57652c7 100644 --- a/devenv/jsonnet/dev-dashboards.libsonnet +++ b/devenv/jsonnet/dev-dashboards.libsonnet @@ -79,6 +79,13 @@ local dashboard = grafana.dashboard; id: 0, } }, + dashboard.new('annotation-filtering', import '../dev-dashboards/annotations/annotation-filtering.json') + + resource.addMetadata('folder', 'dev-dashboards') + + { + spec+: { + id: 0, + } + }, dashboard.new('auto_decimals', import '../dev-dashboards/panel-common/auto_decimals.json') + resource.addMetadata('folder', 'dev-dashboards') + { @@ -128,6 +135,13 @@ local dashboard = grafana.dashboard; id: 0, } }, + dashboard.new('canvas-connection-examples', import '../dev-dashboards/panel-canvas/canvas-connection-examples.json') + + resource.addMetadata('folder', 'dev-dashboards') + + { + spec+: { + id: 0, + } + }, dashboard.new('canvas-examples', import '../dev-dashboards/panel-canvas/canvas-examples.json') + resource.addMetadata('folder', 'dev-dashboards') + { @@ -156,6 +170,13 @@ local dashboard = grafana.dashboard; id: 0, } }, + dashboard.new('datagrid_metric_values', import '../dev-dashboards/panel-datagrid/datagrid_metric_values.json') + + resource.addMetadata('folder', 'dev-dashboards') + + { + spec+: { + id: 0, + } + }, dashboard.new('demo1', import '../dev-dashboards/datasource-testdata/demo1.json') + resource.addMetadata('folder', 'dev-dashboards') + { @@ -681,6 +702,13 @@ local dashboard = grafana.dashboard; id: 0, } }, + dashboard.new('timeseries-formats', import '../dev-dashboards/panel-timeseries/timeseries-formats.json') + + resource.addMetadata('folder', 'dev-dashboards') + + { + spec+: { + id: 0, + } + }, dashboard.new('timeseries-gradient-area', import '../dev-dashboards/panel-timeseries/timeseries-gradient-area.json') + resource.addMetadata('folder', 'dev-dashboards') + { diff --git a/devenv/local-npm/conf/config.yaml b/devenv/local-npm/conf/config.yaml new file mode 100644 index 00000000000..26686016f46 --- /dev/null +++ b/devenv/local-npm/conf/config.yaml @@ -0,0 +1,202 @@ +# +# This is the default configuration file. It allows all users to do anything, +# please read carefully the documentation and best practices to +# improve security. +# +# Do not configure host and port under `listen` in this file +# as it will be ignored when using docker. +# see https://verdaccio.org/docs/en/docker#docker-and-custom-port-configuration +# +# Look here for more config file examples: +# https://github.com/verdaccio/verdaccio/tree/5.x/conf +# +# Read about the best practices +# https://verdaccio.org/docs/best + +# path to a directory with all packages +storage: /verdaccio/storage/data +# path to a directory with plugins to include +plugins: /verdaccio/plugins + +# https://verdaccio.org/docs/webui +web: + title: Verdaccio + # comment out to disable gravatar support + # gravatar: false + # by default packages are ordercer ascendant (asc|desc) + # sort_packages: asc + # convert your UI to the dark side + # darkMode: true + # html_cache: true + # by default all features are displayed + # login: true + # showInfo: true + # showSettings: true + # In combination with darkMode you can force specific theme + # showThemeSwitch: true + # showFooter: true + # showSearch: true + # showRaw: true + # showDownloadTarball: true + # HTML tags injected after manifest + # scriptsBodyAfter: + # - '' + # HTML tags injected before ends + # metaScripts: + # - '' + # - '' + # - '' + # HTML tags injected first child at + # bodyBefore: + # - '
html before webpack scripts
' + # Public path for template manifest scripts (only manifest) + # publicPath: http://somedomain.org/ + +# https://verdaccio.org/docs/configuration#authentication +auth: + htpasswd: + file: /verdaccio/storage/htpasswd + # Maximum amount of users allowed to register, defaults to "+infinity". + # You can set this to -1 to disable registration. + # max_users: 1000 + # Hash algorithm, possible options are: "bcrypt", "md5", "sha1", "crypt". + # algorithm: bcrypt # by default is crypt, but is recommended use bcrypt for new installations + # Rounds number for "bcrypt", will be ignored for other algorithms. + # rounds: 10 + +# https://verdaccio.org/docs/configuration#uplinks +# a list of other known repositories we can talk to +uplinks: + npmjs: + url: https://registry.npmjs.org/ + +# Learn how to protect your packages +# https://verdaccio.org/docs/protect-your-dependencies/ +# https://verdaccio.org/docs/configuration#packages +packages: + '@*/*': + # allow all users (including non-authenticated users) to read and + # publish scoped packages + access: $anonymous + publish: $anonymous + unpublish: $anonymous + proxy: npmjs + + '**': + # allow all users (including non-authenticated users) to read and + # publish all packages + # + # you can specify usernames/groupnames (depending on your auth plugin) + # and three keywords: "$all", "$anonymous", "$authenticated" + access: $anonymous + + # allow all known users to publish/publish packages + # (anyone can register by default, remember?) + publish: $anonymous + unpublish: $anonymous + + # if package is not available locally, proxy requests to 'npmjs' registry + proxy: npmjs + +# To improve your security configuration and avoid dependency confusion +# consider removing the proxy property for private packages +# https://verdaccio.org/docs/best#remove-proxy-to-increase-security-at-private-packages + +# https://verdaccio.org/docs/configuration#server +# You can specify HTTP/1.1 server keep alive timeout in seconds for incoming connections. +# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout. +# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enough. +server: + keepAliveTimeout: 60 + # Allow `req.ip` to resolve properly when Verdaccio is behind a proxy or load-balancer + # See: https://expressjs.com/en/guide/behind-proxies.html + # trustProxy: '127.0.0.1' + +# https://verdaccio.org/docs/configuration#offline-publish +# publish: +# allow_offline: false + +# https://verdaccio.org/docs/configuration#url-prefix +# url_prefix: /verdaccio/ +# VERDACCIO_PUBLIC_URL='https://somedomain.org'; +# url_prefix: '/my_prefix' +# // url -> https://somedomain.org/my_prefix/ +# VERDACCIO_PUBLIC_URL='https://somedomain.org'; +# url_prefix: '/' +# // url -> https://somedomain.org/ +# VERDACCIO_PUBLIC_URL='https://somedomain.org/first_prefix'; +# url_prefix: '/second_prefix' +# // url -> https://somedomain.org/second_prefix/' + +# https://verdaccio.org/docs/configuration#security +# security: +# api: +# legacy: true +# jwt: +# sign: +# expiresIn: 29d +# verify: +# someProp: [value] +# web: +# sign: +# expiresIn: 1h # 1 hour by default +# verify: +# someProp: [value] + +# https://verdaccio.org/docs/configuration#user-rate-limit +# userRateLimit: +# windowMs: 50000 +# max: 1000 + +# https://verdaccio.org/docs/configuration#max-body-size +# max_body_size: 10mb + +# https://verdaccio.org/docs/configuration#listen-port +# listen: +# - localhost:4873 # default value +# - http://localhost:4873 # same thing +# - 0.0.0.0:4873 # listen on all addresses (INADDR_ANY) +# - https://example.org:4873 # if you want to use https +# - "[::1]:4873" # ipv6 +# - unix:/tmp/verdaccio.sock # unix socket + +# The HTTPS configuration is useful if you do not consider use a HTTP Proxy +# https://verdaccio.org/docs/configuration#https +# https: +# key: ./path/verdaccio-key.pem +# cert: ./path/verdaccio-cert.pem +# ca: ./path/verdaccio-csr.pem + +# https://verdaccio.org/docs/configuration#proxy +# http_proxy: http://something.local/ +# https_proxy: https://something.local/ + +# https://verdaccio.org/docs/configuration#notifications +# notify: +# method: POST +# headers: [{ "Content-Type": "application/json" }] +# endpoint: https://usagge.hipchat.com/v2/room/3729485/notification?auth_token=mySecretToken +# content: '{"color":"green","message":"New package published: * {{ name }}*","notify":true,"message_format":"text"}' + +middlewares: + audit: + enabled: true + +# https://verdaccio.org/docs/logger +# log settings +logs: { type: stdout, format: pretty, level: http } +#experiments: +# # support for npm token command +# token: false +# # enable tarball URL redirect for hosting tarball with a different server, the tarball_url_redirect can be a template string +# tarball_url_redirect: 'https://mycdn.com/verdaccio/${packageName}/${filename}' +# # the tarball_url_redirect can be a function, takes packageName and filename and returns the url, when working with a js configuration file +# tarball_url_redirect(packageName, filename) { +# const signedUrl = // generate a signed url +# return signedUrl; +# } + +# translate your registry, api i18n not available yet +# i18n: +# list of the available translations https://github.com/verdaccio/verdaccio/blob/master/packages/plugins/ui-theme/src/i18n/ABOUT_TRANSLATIONS.md +# web: en-US \ No newline at end of file diff --git a/devenv/local-npm/docker-compose.yaml b/devenv/local-npm/docker-compose.yaml index 1a0acdc8a25..be0ea5220ec 100644 --- a/devenv/local-npm/docker-compose.yaml +++ b/devenv/local-npm/docker-compose.yaml @@ -7,7 +7,7 @@ services: ports: - "4873:4873" volumes: - - verdaccio:/verdaccio + - ./conf:/verdaccio/conf volumes: verdaccio: diff --git a/docs/Makefile b/docs/Makefile index 1d069a90692..2eaab546cf3 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,33 +1,8 @@ -.PHONY: pull docs docs-quick docs-no-pull docs-test docs-local-static +.ONESHELL: +.DELETE_ON_ERROR: +export SHELL := bash +export SHELLOPTS := pipefail:errexit +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rule -PODMAN = $(shell if command -v podman >/dev/null 2>&1; then echo podman; else echo docker; fi) -IMAGE = grafana/docs-base:latest -CONTENT_PATH = /hugo/content/docs/grafana/latest -LOCAL_STATIC_PATH = ../../website/static -PORT = 3002:3002 - -pull: - $(PODMAN) pull $(IMAGE) - -docs: pull - $(PODMAN) run --init -v $(shell pwd)/sources:$(CONTENT_PATH):Z -p $(PORT) --rm -it $(IMAGE) make server - -docs-preview: pull - $(PODMAN) run --init -v $(shell pwd)/sources:$(CONTENT_PATH):Z -p $(PORT) --rm -it $(IMAGE) make server BUILD_DRAFTS=true - -docs-no-pull: - $(PODMAN) run --init -v $(shell pwd)/sources:$(CONTENT_PATH):Z -p $(PORT) --rm -it $(IMAGE) make server - -docs-test: pull - $(PODMAN) run --init -v $(shell pwd)/sources:$(CONTENT_PATH):Z --rm -it $(IMAGE) make prod - -# expects that you have grafana/website checked out in same path as the grafana repo. -docs-local-static: pull - if [ ! -d "$(LOCAL_STATIC_PATH)" ]; then echo "local path (website project) $(LOCAL_STATIC_PATH) not found"]; exit 1; fi - $(PODMAN) run --init -v $(shell pwd)/sources:$(CONTENT_PATH):Z \ - -v $(shell pwd)/$(LOCAL_STATIC_PATH):/hugo/static:Z -p $(PORT) --rm -it $(IMAGE) - -.PHONY: doc-validator/% -doc-validator/%: ## Run doc-validator on a specific path. To lint the path /docs/sources/administration, run 'make doc-validator/administration'. -doc-validator/%: - $(PODMAN) run --init -v "$(shell pwd)/sources:/sources" grafana/doc-validator:latest --skip-image-validation --include=$(subst doc-validator/,,$@) ./sources /docs/grafana/latest +include docs.mk diff --git a/docs/README.md b/docs/README.md index 22fa25f6f78..006bb41449b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,14 +1,19 @@ # Building the docs locally -When you contribute to documentation, it is a good practice to build the docs on your local machine to make sure your changes appear as you expect. This README explains the process for doing that. +When you contribute to documentation, it's a good practice to build the docs on your local machine to make sure your changes appear as you expect. This README explains the process for doing that. + +To build a local version, you need to run a process in a Docker container. +Grafana periodically updates the Docker image, [`docs-base`](https://hub.docker.com/r/grafana/docs-base), to update the styling of the Docs. ## Requirements -Docker >= 2.1.0.3 -Yarn >= 1.22.4 +- Docker >= 2.1.0.3 +- Yarn >= 1.22.4 ## Build the doc site +First, make sure the Docker daemon is running on your machine. Then, follow these steps: + 1. On the command line, first change to the docs folder: `cd docs`. 1. Run `make docs`. This launches a preview of the website with the current grafana docs at `http://localhost:3002/docs/grafana/latest/` which will refresh automatically when changes are made to content in the `sources` directory. diff --git a/docs/docs.mk b/docs/docs.mk new file mode 100644 index 00000000000..7c7072085be --- /dev/null +++ b/docs/docs.mk @@ -0,0 +1,110 @@ +include variables.mk +-include variables.mk.local + +.ONESHELL: +.DELETE_ON_ERROR: +export SHELL := bash +export SHELLOPTS := pipefail:errexit +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rule + +.DEFAULT_GOAL: help + +# Adapted from https://www.thapaliya.com/en/writings/well-documented-makefiles/ +.PHONY: help +help: ## Display this help. +help: + @awk 'BEGIN { \ + FS = ": ##"; \ + printf "Usage:\n make \n\nTargets:\n" \ + } \ + /^[a-zA-Z0-9_\.\-\/%]+: ##/ { printf " %-15s %s\n", $$1, $$2 }' \ + $(MAKEFILE_LIST) + +GIT_ROOT := $(shell git rev-parse --show-toplevel) + +PODMAN := $(shell if command -v podman >/dev/null 2>&1; then echo podman; else echo docker; fi) + +ifeq ($(PROJECTS),) +$(error "PROJECTS variable must be defined in variables.mk") +endif + +# First project is considered the primary one used for doc-validator. +PRIMARY_PROJECT := $(subst /,-,$(firstword $(subst :, ,$(firstword $(PROJECTS))))) + +# Name for the container. +ifeq ($(origin DOCS_CONTAINER), undefined) +export DOCS_CONTAINER := $(PRIMARY_PROJECT)-docs +endif + +# Host port to publish container port to. +ifeq ($(origin DOCS_HOST_PORT), undefined) +export DOCS_HOST_PORT := 3002 +endif + +# Container image used to perform Hugo build. +ifeq ($(origin DOCS_IMAGE), undefined) +export DOCS_IMAGE := grafana/docs-base:latest +endif + +# Container image used for doc-validator linting. +ifeq ($(origin DOC_VALIDATOR_IMAGE), undefined) +export DOC_VALIDATOR_IMAGE := grafana/doc-validator:latest +endif + +# PATH-like list of directories within which to find projects. +# If all projects are checked out into the same directory, ~/repos/ for example, then the default should work. +ifeq ($(origin REPOS_PATH), undefined) +export REPOS_PATH := $(realpath $(GIT_ROOT)/..) +endif + +# How to treat Hugo relref errors. +ifeq ($(origin HUGO_REFLINKSERRORLEVEL), undefined) +export HUGO_REFLINKSERRORLEVEL := WARNING +endif + +.PHONY: docs-rm +docs-rm: ## Remove the docs container. + $(PODMAN) rm -f $(DOCS_CONTAINER) + +.PHONY: docs-pull +docs-pull: ## Pull documentation base image. + $(PODMAN) pull $(DOCS_IMAGE) + +make-docs: ## Fetch the latest make-docs script. +make-docs: + if [[ ! -f "$(PWD)/make-docs" ]]; then + echo 'WARN: No make-docs script found in the working directory. Run `make update` to download it.' >&2 + exit 1 + fi + +.PHONY: docs +docs: ## Serve documentation locally, which includes pulling the latest `DOCS_IMAGE` (default: `grafana/docs-base:latest`) container image. See also `docs-no-pull`. +docs: docs-pull make-docs + $(PWD)/make-docs $(PROJECTS) + +.PHONY: docs-no-pull +docs-no-pull: ## Serve documentation locally without pulling the `DOCS_IMAGE` (default: `grafana/docs-base:latest`) container image. +docs-no-pull: make-docs + $(PWD)/make-docs $(PROJECTS) + +.PHONY: docs-debug +docs-debug: ## Run Hugo web server with debugging enabled. TODO: support all SERVER_FLAGS defined in website Makefile. +docs-debug: make-docs + WEBSITE_EXEC='hugo server --debug' $(PWD)/make-docs $(PROJECTS) + +.PHONY: doc-validator +doc-validator: ## Run docs-validator on the entire docs folder. +doc-validator: make-docs + DOCS_IMAGE=$(DOC_VALIDATOR_IMAGE) $(PWD)/make-docs $(PROJECTS) + +.PHONY: doc-validator/% +doc-validator/%: ## Run doc-validator on a specific path. To lint the path /docs/sources/administration, run 'make doc-validator/administration'. +doc-validator/%: make-docs + DOCS_IMAGE=$(DOC_VALIDATOR_IMAGE) DOC_VALIDATOR_INCLUDE=$(subst doc-validator/,,$@) $(PWD)/make-docs $(PROJECTS) + +.PHONY: update +update: ## Fetch the latest version of this Makefile and the `make-docs` script from Writers' Toolkit. + curl -s -LO https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/docs.mk + curl -s -LO https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs + chmod +x make-docs diff --git a/docs/make-docs b/docs/make-docs new file mode 100755 index 00000000000..b8350109abc --- /dev/null +++ b/docs/make-docs @@ -0,0 +1,359 @@ +#!/bin/sh +# The source of this file is https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs. + +set -ef + +readonly DOCS_CONTAINER="${DOCS_CONTAINER:-make-docs}" +readonly DOCS_HOST_PORT="${DOCS_HOST_PORT:-3002}" +readonly DOCS_IMAGE="${DOCS_IMAGE:-grafana/docs-base:latest}" + +readonly DOC_VALIDATOR_INCLUDE="${DOC_VALIDATOR_INCLUDE:-.+\.md$}" + +readonly HUGO_REFLINKSERRORLEVEL="${HUGO_REFLINKSERRORLEVEL:-WARNING}" +readonly WEBSITE_EXEC="${WEBSITE_EXEC:-make server}" +# If set, the docs-base image will run a prebuild script that sets up Hugo mounts. +readonly WEBSITE_MOUNTS="${WEBSITE_MOUNTS:-}" + +PODMAN="$(if command -v podman >/dev/null 2>&1; then echo podman; else echo docker; fi)" + +about() { + cat <...]> $0 [[:[:[:]]]...] + +Examples: + REPOS_PATH=~/ext/grafana/ $0 writers-toolkit tempo:latest helm-charts/mimir-distributed:latest:mimir:docs/sources/mimir-distributed +EOF +} + +if [ $# -lt 1 ]; then + cat <&2 +ERRR: arguments required but not supplied. + +$(about) + +$(usage) +EOF + exit 1 +fi + +readonly REPOS_PATH="${REPOS_PATH:-$(realpath "$(git rev-parse --show-toplevel)/..")}" + +if [ -z "${REPOS_PATH}" ]; then + cat <&2 +ERRR: REPOS_PATH environment variable is required but has not been provided. + +$(usage) +EOF + exit 1 +fi + +SOURCES_as_code='as-code-docs' +SOURCES_enterprise_metrics='backend-enterprise' +SOURCES_enterprise_metrics_='backend-enterprise' +SOURCES_grafana_cloud='cloud-docs' +SOURCES_grafana_cloud_k6='k6-docs' +SOURCES_helm_charts_mimir_distributed='mimir' +SOURCES_helm_charts_tempo_distributed='tempo' +SOURCES_opentelemetry='opentelemetry-docs' + +VERSIONS_as_code='UNVERSIONED' +VERSIONS_grafana_cloud='UNVERSIONED' +VERSIONS_grafana_cloud_k6='UNVERSIONED' +VERSIONS_opentelemetry='UNVERSIONED' +VERSIONS_technical_documentation='UNVERSIONED' +VERSIONS_writers_toolkit='UNVERSIONED' + +PATHS_helm_charts_mimir_distributed='docs/sources/helm-charts/mimir-distributed' +PATHS_helm_charts_tempo_distributed='docs/sources/helm-charts/tempo-distributed' +PATHS_mimir='docs/sources/mimir' +PATHS_tempo='docs/sources/tempo' + +# identifier STR +# Replace characters that are not valid in an identifier with underscores. +identifier() { + echo "$1" | tr -C '[:alnum:]_\n' '_' +} + +# aget ARRAY KEY +# Get the value of KEY from associative array ARRAY. +# Characters that are not valid in an identifier are replaced with underscores. +aget() { + eval echo '$'"$(identifier "$1")_$(identifier "$2")" +} + +# new_proj populates a new project structure. +new_proj() { + _project="$1" + _version="$2" + _repo="$3" + _path="$4" + + # If version is not set, use the script mapping of project to default versions if it exists. + # Fallback to 'latest'. + if [ -z "${_version}" ]; then + if [ -z "$(aget VERSIONS "${_project}")" ]; then + _version=latest + else + _version="$(aget VERSIONS "${_project}")" + fi + fi + + # If repo is not set, use the script mapping of project to repo name if it exists. + # Fallback to using the project name. + if [ -z "${_repo}" ]; then + if [ -z "$(aget SOURCES "${_project}")" ]; then + _repo="${_project}" + else + _repo="$(aget SOURCES "${_project}")" + fi + fi + + # If path is not set, use the script mapping of project to docs sources path if it exists. + # Fallback to using 'docs/sources'. + if [ -z "${_path}" ]; then + if [ -z "$(aget PATHS "${_project}")" ]; then + _path="docs/sources" + else + _path="$(aget PATHS "${_project}")" + fi + fi + + echo "${_project}:${_version}:${_repo}:${_path}" + unset _project _version _repo _path +} + +# proj_url returns the webserver URL for a project. +# It expects a complete project structure as input. +proj_url() { + IFS=: read -r _project _version _ _ <&2 + echo "NOTE: you must have a checkout of the project '${_repo}' at '${REPOS_PATH##:*}/${_repo}'." >&2 + echo "NOTE: if you have cloned the repository into a directory with a different name, consider changing it to ${_repo}." >&2 + unset _path _repo + exit 1 +} + +# proj_canonical returns the canonical absolute path partial URI for a project. +# It expects a complete project structure as input. +proj_canonical() { + IFS=: read -r _project _version _ _ <&2 + echo "Is '${_src}' the correct source directory?" >&2 + exit 1 + fi + fi + + echo "DEBG: Mounting '${_src}' at container path '${_dst}'" >&2 + if [ -z "${volumes}" ]; then + volumes="--volume=${_src}:${_dst}" + else + volumes="${volumes} --volume=${_src}:${_dst}" + fi + + if [ -n "${_ver}" ] && [ "${_ver}" != 'UNVERSIONED' ]; then + if [ -z "${redirects}" ]; then + redirects="${_dst}^${_ver}" + else + redirects="${redirects} ${_dst}^${_ver}" + fi + fi + unset _url _src _dst _ver +done + +IFS=':' read -r image _ </tmp/make-docs-entrypoint +#!/usr/bin/env bash +for redirect in ${redirects}; do + IFS='^' read -r path ver <<<"\${redirect}" + echo -e "---\\nredirectURL: \"\${path/\/hugo\/content/}\"\\ntype: redirect\\n---\\n" > "\${path/\${ver}/_index.md}" + + if [[ -n "${WEBSITE_MOUNTS}" ]]; then + unset WEBSITE_SKIP_MOUNTS + fi +done + +${WEBSITE_EXEC} +EOF + chmod +x /tmp/make-docs-entrypoint + volumes="${volumes} --volume=/tmp/make-docs-entrypoint:/entrypoint" + readonly volumes + + echo + echo "Documentation will be served at the following URLs:" + for x in ${url_src_dst_vers}; do + IFS='^' read -r url _ _ <Provisioning

Learn how to automate your Grafana configuration.

- }}" class="nav-cards__item nav-cards__item--guide"> -

What's new in v9.4

+
}}" class="nav-cards__item nav-cards__item--guide"> +

What's new in v9.5

Explore the features and enhancements in the latest release.

diff --git a/docs/sources/administration/api-keys/index.md b/docs/sources/administration/api-keys/index.md index d9f4fdf8653..1116227f743 100644 --- a/docs/sources/administration/api-keys/index.md +++ b/docs/sources/administration/api-keys/index.md @@ -17,7 +17,7 @@ An API key is a randomly generated string that external systems use to interact When you create an API key, you specify a **Role** that determines the permissions associated with the API key. Role permissions control that actions the API key can perform on Grafana resources. -> **Note:** If you use Grafana v8.5 or newer, use service accounts instead of API keys. For more information, refer to [Grafana service accounts]({{< relref "../service-accounts/" >}}). +> **Note:** If you use Grafana v9.1 or newer, use service accounts instead of API keys. For more information, refer to [Grafana service accounts]({{< relref "../service-accounts/" >}}). {{< section >}} @@ -61,7 +61,7 @@ For more information about the benefits of service accounts, refer to [Grafana s The service account endpoints generate a machine user for authentication instead of using API keys. When you migrate an API key to a service account, a service account will be created with a service account token. -> **Note:** If you are using API keys for authentication, we recommend that you migrate your integration to the service account authentication method. The API key will continue to work. You can locate the API key in the [Grafana service account tokens]({{< relref "../service-accounts/#service-account-tokens" >}}) details. +> **Note:** If you're currently using API keys for authentication, we strongly recommend to use Grafana Service Accounts instead. Rest assured, when migrating to Service Accounts, your existing API keys will continue working as before. To find the migrated API keys, navigate to the Service Accounts section and select the Service Account Tokens tab. For more information, please refer to the [Grafana service account tokens]({{< relref "../service-accounts/#service-account-tokens" >}}) details. ## Ways of migrating API keys to service accounts @@ -106,9 +106,9 @@ To migrate a single API key to a service account, complete the following steps: 1. Find the API Key you want to migrate. 1. Click **Migrate to service account**. -### Migrate API keys to Grafana service accounts using the API +### Migrate API keys to Grafana service accounts for API calls -This section shows you how to migrate API keys to Grafana service accounts using the Grafana API. +This section shows you how to migrate API keys to Grafana service accounts for Grafana API workflows. For references see: [Grafana Service Accounts for the Grafana API]({{< relref "../../developers/http_api/serviceaccount/#create-service-account" >}}). #### Before you begin @@ -120,7 +120,7 @@ To follow these instructions, you need one of the following: #### Steps -Complete the following steps to migrate from API keys to service accounts using the API: +Complete the following steps to migrate from API keys to service accounts for API: 1. Call the `POST /api/serviceaccounts` endpoint and the `POST /api/serviceaccounts//tokens`. @@ -135,9 +135,44 @@ Complete the following steps to migrate from API keys to service accounts using 1. Remove code that handles the old `/api/auth/keys` endpoint. 1. Track the [API keys](http://localhost:3000/org/apikeys) in use and migrate them to SATs. -### Migrate API keys to Grafana service accounts using Terraform +#### Example -This section shows you how to migrate API keys to Grafana service accounts using Terraform. +Your current setup + +```sh +curl -X POST -H "Content-Type: application/json" -d '{"name": "my-api-key", "role": "Viewer"}' http://admin:admin@localhost:3000/api/auth/keys + +# response from the api +{"id":2,"name":"my-api-key","key":"eyJrIjoiTFRSN1RBOVc3SGhjblc0bWZodXZ3MnNDcU92Um5VZUIiLKJuIjoibXktYXBpLWtleSIsImlkIjoxfQ=="}% +``` + +New setup + +```sh +# create a service account +curl -X POST -H "Content-Type: application/json" -d '{"name": "my-service-account", "role": "Viewer"}' http://admin:admin@localhost:3000/api/serviceaccounts + +# response with the created service account id,name, login +{"id":1,"name":"my-service-account","login":"sa-my-service-account","orgId":1,"isDisabled":false,"role":"Viewer","tokens":0,"avatarUrl":""}% + +# create the service account token with the service account id 1 - /serviceaccounts/{id} returned from the previous step +curl -X POST -H "Content-Type: application/json" -d '{"name": "my-service-account-token"}' http://admin:admin@localhost:3000/api/serviceaccounts/1/tokens + +# response with the created SAT id,name and key. +{"id":2,"name":"my-service-account-token","key":"glsa_9244xlVFZK0j8Lh4fU8Cz6Z5tO664zIi_7a762939"}% + +# now you can authenticate the same way as you did with the API key +curl --request GET --url http://localhost:3000/api/folders --header 'Authorization: Bearer glsa_9244xlVFZK0j8Lh4fU8Cz6Z5tO664zIi_7a762939' + +# response +[{"id":1,"uid":"a5261a84-eebc-4733-83a9-61f4713561d1","title":"gdev dashboards"}]% +``` + +### Migrate API keys to Grafana service accounts in Terraform + +This section shows you how to migrate your Terraform configuration for API keys to Grafana service accounts. For resources, see [Grafana Service Accounts in Terraform](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/service_account_token). + +For migration your cloud stack api keys, use the `grafana_cloud_stack_service_account` and `gafana_cloud_stack_service_account_token` resources see [Grafana Cloud Stack Service Accounts in Terraform](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/cloud_stack_service_account). #### Steps @@ -147,3 +182,168 @@ Complete the following steps to migrate from API keys to service accounts using 1. Specify the desired scopes and expiration date when creating the service account. 1. Use the token returned from `grafana_service_account_token` to authenticate the API requests. 1. Remove the terraform configuration for creating your `grafana_api_key` resources. + +**Example: your current Terraform configuration** + +```tf +terraform { + required_providers { + grafana = { + source = "grafana/grafana" + } + } +} + +# configure the provider with basic auth +provider "grafana" { + url = "http://localhost:3000" + auth = "admin:admin" +} + +resource "grafana_api_key" "foo" { + name = "key_foo" + role = "Viewer" +} + +resource "grafana_api_key" "bar" { + name = "key_bar" + role = "Admin" + seconds_to_live = 30 +} +``` + +**Your new Terraform configuration** + +_Note:_ you can create multiple tokens using one service account. + +```tf +terraform { + required_providers { + grafana = { + source = "grafana/grafana" + } + } +} + +# configure the provider with basic auth +provider "grafana" { + url = "http://localhost:3000" + auth = "admin:admin" +} + +# Creating a service account in Grafana instance to be used as auth and attach tokens +# notice we can attach multiple tokens to one service account +resource "grafana_service_account" "sa-admin" { + name = "sa-admin" + role = "Admin" +} + +# Creating a service account token in Grafana instance to be used for creating resources in Grafana instance +resource "grafana_service_account_token" "sat-bar" { + name = "sat-bar" + service_account_id = grafana_service_account.sa-admin.id +} + +# Creating a service account token in Grafana instance to be used for creating resources in Grafana instance +resource "grafana_service_account_token" "sat-foo" { + name = "sat-foo" + service_account_id = grafana_service_account.sa-admin.id + seconds_to_live = 30 +} +``` + +### Migrate Cloud **Stack** API keys to Grafana cloud stack service accounts in Terraform + +This section shows you how to migrate your Terraform configuration for Grafana cloud stack API keys to Grafana cloud stack service accounts. For migration your cloud stack api keys, use the `grafana_cloud_stack_service_account` and `gafana_cloud_stack_service_account_token` resources see [Grafana Cloud Stack Service Accounts in Terraform](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/cloud_stack_service_account). + +> **Note:** This is only relevant for Grafana Cloud **Stack** API keys `grafana_cloud_stack_api_key`. Grafana Cloud API keys resource `grafana_cloud_api_key` are not deprecated and should be used for authentication for managing your Grafana cloud. + +#### Steps + +Complete the following steps to migrate from cloud stack API keys to cloud stack service accounts using Terraform: + +1. Generate `grafana_cloud_stack_service_account` and `grafana_cloud_stack_service_account_token` resources. +1. Specify the desired scopes and expiration date when creating the service account. +1. Use the token returned from `grafana_cloud_stack_service_account_token` to authenticate the API requests. +1. Remove the Terraform configuration for creating your `grafana_cloud_stack_api_key` resources. + +**Example: Your current Terraform configuration** + +```tf +terraform { + required_providers { + grafana = { + source = "grafana/grafana" + } + } +} + +# Declaring the first provider to be only used for creating the cloud-stack +provider "grafana" { + alias = "cloud" + + cloud_api_key = "" +} + +resource "grafana_cloud_stack" "my_stack" { + provider = grafana.cloud + + name = "my_stack" + slug = "my_stack" + region_slug = "eu" # Example “us”,”eu” etc +} + +# Creating a Grafana API key to be used as auth +resource "grafana_cloud_stack_api_key" "management" { + provider = grafana.cloud + + stack_slug = grafana_cloud_stack.my_stack.slug + name = "management-key" + role = "Admin" +} +``` + +**Your new Terraform configuration** + +```tf +terraform { + required_providers { + grafana = { + source = "grafana/grafana" + } + } +} + +# Declaring the first provider to be only used for creating the cloud-stack +provider "grafana" { + alias = "cloud" + + cloud_api_key = "" +} + +resource "grafana_cloud_stack" "my_stack" { + provider = grafana.cloud + + name = "my_stack" + slug = "my_stack" + region_slug = "eu" # Example “us”,”eu” etc +} + +# Creating a grafana cloud stack service account +resource "grafana_cloud_stack_service_account" "mystack_cloud-stack_service_account" { + provider = grafana.cloud + stack_slug = grafana_cloud_stack.my_stack.slug + + name = "mystack-cloud-stack-sa" + role = "Admin" +} + +# Creating a grafana cloud stack service account token +resource "grafana_cloud_stack_service_account_token" "mystack_cloud-stack_service-account_token" { + provider = grafana.cloud + stack_slug = grafana_cloud_stack.my_stack.slug + + name = "mystack-cloud-stack-sa-token" + service_account_id = grafana_cloud_stack_service_account.mystack_cloud-stack_service_account.id +} +``` diff --git a/docs/sources/administration/correlations/_index.md b/docs/sources/administration/correlations/_index.md new file mode 100644 index 00000000000..90381514d69 --- /dev/null +++ b/docs/sources/administration/correlations/_index.md @@ -0,0 +1,35 @@ +--- +title: Correlations +weight: 900 +keywords: + - correlations + - Explore +--- + +# Correlations + +You can create interactive links for Explore visualizations to run queries related to presented data by setting up Correlations. + +A correlation defines how data in one [data source]({{< relref "/docs/grafana/latest/datasources/" >}}) is used to query data in another data source. Some examples: + +- an application name returned in a logs data source can be used to query metrics related to that application in a metrics data source, or +- a user name returned by an SQL data source can be used to query logs related to that particular user in a logs data source + +[Explore]({{< relref "/docs/grafana/latest/explore/" >}}) takes user-defined correlations to display links inside the visualizations. You can click on a link to run the related query and see results in [Explore Split View]({{< relref "/docs/grafana/latest/explore/#split-and-compare" >}}). + +Explore visualizations that currently support showing links based on correlations: + +- [Logs Panel]({{< relref "./use-correlations-in-visualizations#correlations-in-logs-panel">}}) +- [Table]({{< relref "./use-correlations-in-visualizations#correlations-in-table">}}) + +You can configure correlations using [Administration > Correlation page]({{< relref "/docs/grafana/latest/administration/" >}}) or with [provisioning]({{< relref "/docs/grafana/latest/administration/provisioning" >}}). + +> **Note:** Correlations are available in Grafana 10.0+ as an opt-in beta feature. Modify Grafana [configuration file]({{< relref "/docs/grafana/latest/setup-grafana/configure-grafana/#configuration-file-location" >}}) to enable the `correlations` [feature toggle]({{< relref "/docs/grafana/latest/setup-grafana/configure-grafana/#feature_toggles" >}}) to use it. + +## Example of how links work in Explore once set up + +{{< figure src="/static/img/docs/correlations/correlations-in-explore-10-0.gif" caption="Correlations links in Explore" >}} + +See also: + +{{< section >}} diff --git a/docs/sources/administration/correlations/add-permissions-to-create-new-correlations/index.md b/docs/sources/administration/correlations/add-permissions-to-create-new-correlations/index.md new file mode 100644 index 00000000000..bb644be513c --- /dev/null +++ b/docs/sources/administration/correlations/add-permissions-to-create-new-correlations/index.md @@ -0,0 +1,18 @@ +--- +title: Add permissions to create new correlations +menuTitle: Add permissions +weight: 30 +--- + +# Add permissions to create new correlations + +## Before you begin + +Adding access to create correlations for [Viewers and Editors]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/" >}}) is available with [Role-based access control]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/" >}}). + +## Add permissions to create correlations + +1. Go to the Administration section in Grafana. +1. Open Users page. +1. Select the user to be granted access to create correlations. +1. Add _Data sources writer_ role. diff --git a/docs/sources/administration/correlations/correlation-configuration/index.md b/docs/sources/administration/correlations/correlation-configuration/index.md new file mode 100644 index 00000000000..b83a8836552 --- /dev/null +++ b/docs/sources/administration/correlations/correlation-configuration/index.md @@ -0,0 +1,101 @@ +--- +title: Correlation +weight: 10 +--- + +# Correlation + +Each correlation is configured with the following options: + +**Label** +: Link label, shown in the visualization + +**Description** +: Optional description + +**Source data source** +: The source of results that have links displayed + +**Results field** +: Defines where the link is shown in a visualization + +**Target query** +: The target query run when a link is clicked + +**Transformations** +: Optional manipulations to the source data included passed to the target query + +Learn how to create correlations using the [Administration page]({{< relref "./create-a-new-correlation#create-a-correlation-in-administration-page" >}}) or with [provisioning]({{< relref "./create-a-new-correlation#create-a-correlation-with-provisioning" >}}). + +## Source data source and result field + +Links are shown in Explore visualizations for the results from the correlation’s source data source. A link is assigned to one of the fields from the result provided in the correlation configuration (the results field). Each visualization displays fields with links in a different way ([Correlations in Logs Panel]({{< relref "./use-correlations-in-visualizations#correlations-in-logs-panel">}}) and see [Correlations in Table]({{< relref "./use-correlations-in-visualizations#correlations-in-table">}})) + +## Target query + +The target query is run when a link is clicked in the visualization. You can use the query editor of the selected target data source to specify the target query. Source data results can be accessed inside the target query with variables. + +### Correlation Variables + +You can use variables inside the target query to access the source data related to the query. Correlations use [Grafana variable syntax]({{< relref "/docs/grafana/latest/dashboards/variables/variable-syntax" >}}). Variables are filled with values from the source results when the link is clicked. There are two types of variables you can use: + +- [field variables]({{< relref "/docs/grafana/latest/panels-visualizations/configure-data-links#field-variables" >}}) (allows to access field values and labels) +- correlation variables (allows to access field values and transformations) + +Example: If source results contain a field called “employee”, the value of the field can be accessed with: + +- A field variable ${\_\_data.fields.employee} +- A correlation variable that maps the field value above to ${employee} + +In addition to mapping field values to shorter variable names, more correlation variables can be created by applying transformations to existing fields + +For more details, please see the example in [Use variables and transformations in a correlation]({{< relref "./use-variables-and-transformations" >}}). + +### Correlation Transformations + +Correlations provide a way to extract more variables out of field values. The output of transformations is a set of new variables that can be accessed as any other variable. + +There are two types of transformations: logfmt and regular expression. + +Each transformation uses a selected field value as the input. The output of a transformation is a set of new variables based on the type and options of the transformation. + +For more details, please see the example in [Use variables and transformations in a correlation]({{< relref "./use-variables-and-transformations" >}}) for more details. + +### Logfmt transformation + +The logfmt transformation deconstructs a field value containing text formatted with [logfmt key/value pairs](https://brandur.org/logfmt). Each pair becomes a variable with the key being the name of the variable. + +The logfmt transformation only requires specifying the input field name if you would like the transformation to apply to a different field than the results field. +Example output variables for field = “host=srv001 endpoint=/test app=foo”: + +| name | value | +| :------- | :----- | +| host | srv001 | +| endpoint | /test | +| app | foo | + +### Regular expression transformation + +The regular expression transformation deconstructs a field value based on the provided regular expression. + +Regular expression transformation options: + +**field** +: Input field name + +**expression** +: Regular expression. Named capture groups are mapped to variables matching the group name. If non-named matching groups are used a variable is created out of the first match. The value overrides the variable matching the input field or a new variable is created if mapValue is provided (check the example below) + +**mapValue** +: Used with simple regex groups without named matching groups. By default, the first match overrides the variable with the name of the field that is used as the input. To change that default behavior you can specify the mapValue property. The provided name is used to create a new variable. This can be useful if your target query requires both the exact value and a part of the value extracted with the transformation. + +Example: Assuming the selected field name is “employee” and the field value is “John Doe”. + +Various output variables based on expression and mapValue options: + +| expression | mapValue | output variables | comment | +| :---------------- | :------- | :--------------------------- | :------------------------------------------------------------------------------------------------ | +| /\\w+ (\\w+)/ | - | employee=Doe | No mapValue provided. The first matching is mapped to the existing field name variable (employee) | +| /(\\w+) (\\w+)/ | name | name=John | The first matching is mapped to a new variable called “name” | +| /(?\\w+) (?\\w+)/ | - | firstName=John, lastName=Doe | When named groups are used they are the names of the output variables and mapValue is ignored. | +| /(?\\w+) (?\\w+)/ | name | firstName=John, lastName=Doe | Same as above | diff --git a/docs/sources/administration/correlations/correlation-permissions/index.md b/docs/sources/administration/correlations/correlation-permissions/index.md new file mode 100644 index 00000000000..5e2391ca841 --- /dev/null +++ b/docs/sources/administration/correlations/correlation-permissions/index.md @@ -0,0 +1,15 @@ +--- +title: Permissions +weight: 20 +--- + +# Permissions + +Users with [Viewer base role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/" >}}) or with [datasources:query RBAC role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/" >}}) can: + +- Use correlations in Explore’s visualizations +- List all available correlations in read-only mode + +Users with [Admin base role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/" >}}) or with [datasources:write RBAC role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/" >}}) can: + +- Add, edit and delete correlations diff --git a/docs/sources/administration/correlations/create-a-new-correlation/index.md b/docs/sources/administration/correlations/create-a-new-correlation/index.md new file mode 100644 index 00000000000..c67a4af9c1d --- /dev/null +++ b/docs/sources/administration/correlations/create-a-new-correlation/index.md @@ -0,0 +1,120 @@ +--- +title: Create a new correlation +weight: 40 +--- + +# Create a new correlation + +## Before you begin + +Make sure you have permission to add new correlations. Only users with write permissions to data sources can define new correlations. + +## Create a correlation in Administration page + +1. Go to the Administration section in Grafana. +1. Open Correlations page. +1. Click the “Add” button in the top right corner. +1. Provide a **label** for the correlation. +1. Provide an optional **description**. +1. Go to the next page. +1. Provide **target data source**. +1. Provide **target query** using variables. +1. Go to the next page. +1. Provide **source data source**. +1. Provide **results field**. +1. Add transformations if you need variables that are not fields in the source data source. +1. Click “Add” to add a new transformation. +1. Select the type of a transformation. +1. Configure transformation depending on the selected type. +1. Save correlation. + +You can edit correlation in the same way, but you cannot change the selected data sources. + +## Create a correlation with provisioning + +Provision correlations by extending provisioned data sources. Correlations are defined as a subsection of the source data source configuration: + +```yaml +datasources: + - name: Data source name # source data source + ... + jsonData: + ... + correlations: + - targetUID: uid + label: "test" + description: "..." + config: + type: "query" + target: + expr: "..." + field: "name" + transformations: + - type: regex + field: "test" + expression: /\w+/ + mapValue: "other" + - type: logfmt + field: "test" +``` + +Description of provisioning properties: + +**targetUID** +: Target data source UID + +**label** +: Link label + +**description** +: Optional description + +**config** +: Config object + +**config.type** +: Correlation type. “query” is the only supported type at the moment + +**config.target** +: [Target query model]({{< relref "#determine-target-query-model-structure" >}}) + +**config.field** +: Name of the field where link is shown + +**config.transformations (list)** +: List of transformation objects + +**transformation.type** +: regex, or logfmt + +**transformation.field** +: The field that will be transformed. If this is not defined, it will apply the transformation to the data from the correlation's config.field. + +**transformation.expression** +: Regex expression (regex transformation only) + +**transformation.mapValue** +: New name of the variable from the first regex match (regex transformation only) + +### Determine target query model structure + +When you set up a correlation with admin page you can use the target query editor. When you use provisioning you may need to know the structure of the target query which may not be well documented depending on the plugin. Here is a quick step-by-step guide on how to determine the target query model: + +1. Open Explore. +1. Select the data source you want to use as the target of the correlation. +1. Open the inspector tab and select “Query”. +1. Run a sample query. +1. Inspect results. +1. Look for the “queries” list object. Each object is created using the query model structure defined by the data source. You can use the same structure in your provisioning file. + + {{< figure src="/static/img/docs/correlations/determine-target-query-structure-inspector-10-0.png" max-width="600px" caption="Query inspector with target query structure" >}} + + The query model in this example is represented by the first entry in the queries list. Properties “refId” and “datasource” are added to all queries in runtime and can be omitted: + + ```json + { + "scenario_id": "random_walk", + "alias": "app", + "seriesCount: 2 + } + ``` diff --git a/docs/sources/administration/correlations/use-correlations-in-visualizations/index.md b/docs/sources/administration/correlations/use-correlations-in-visualizations/index.md new file mode 100644 index 00000000000..a1ea800f08d --- /dev/null +++ b/docs/sources/administration/correlations/use-correlations-in-visualizations/index.md @@ -0,0 +1,29 @@ +--- +title: Use correlations in visualizations +weight: 70 +--- + +# Use correlations in visualizations + +## Correlations in Logs Panel + +1. Setup a correlation. +1. Open Explore. +1. Select a data source that you chose as the source data source of the correlation. +1. Run a query that results in data containing fields required to build variables in the target query. +1. Expand log row details. +1. If the selected row contains all the information required to build the target query a link appears in the “Links” section at the bottom. +1. Additional information about used variables and their values is shown next to each link. + + {{< figure src="/static/img/docs/correlations/correlations-in-logs-panel-10-0.png" max-width="600px" caption="Correlation links in Logs panel" >}} + +## Correlations in Table + +1. Setup a correlation. +1. Open Explore. +1. Select a data source that you chose as the source data source of the correlation. +1. Run a query that results in data containing fields required to build variables in the target query. +1. Links are added to cell rows in the column representing the field with the assigned link ([the results field]({{< relref "/docs/grafana/latest/administration/correlations/correlation-configuration#source-data-source-and-result-field" >}}). +1. Cells containing multiple links accessible with a context menu. + + {{< figure src="/static/img/docs/correlations/correlations-in-table-10-0.png" max-width="600px" caption="Correlations links in table" >}} diff --git a/docs/sources/administration/correlations/use-variables-and-transformations/index.md b/docs/sources/administration/correlations/use-variables-and-transformations/index.md new file mode 100644 index 00000000000..d966506ab28 --- /dev/null +++ b/docs/sources/administration/correlations/use-variables-and-transformations/index.md @@ -0,0 +1,135 @@ +--- +title: Use variables and transformations in a correlation +weight: 60 +--- + +# Use variables and transformations in a correlation + +## Before you begin + +This example walks through creating a link in a test data source but the same principles apply to any data source. + +The example emulates a scenario with two data sources: + +- Logs containing lines in the format: “2020-01-01 10:00 level=error message=error service=app1.loginService” stored in a field named “msg” +- Metrics for application included in the service name of a log line (e.g. app1) + +Instructions below show how to set up a link that can run metrics query for the host included in each log line with provisioning and regex transformation. Additionally, a link with a query containing the full name of the service is set up to demonstrate the logfmt transformation. + +## Use variables and transformations in provisioning + +1. Add the following provisioning configuration to your Grafana: + + ```yaml + datasources: + - name: Target + uid: test-target + type: testdata + + - name: Source + uid: test-source + type: testdata + - name: Source + uid: test-source + type: testdata + correlations: + - targetUID: test-target + label: App metrics + description: Application HTTP request metrics + config: + type: query + target: + scenario_id: random_walk + alias: $${application} + field: msg + transformations: + - type: regex + field: msg + expression: service=(\w+)\.\w+ + mapValue: application + - targetUID: test-target + label: Service metrics + description: Service metrics + config: + type: query + target: + scenario_id: random_walk + alias: $${service} + field: msg + transformations: + - type: logfmt + field: msg + ``` + + Two data sources are created: Source (emulating logs data source) and Target (emulating metrics data source): + + - A correlation called “App metrics” is created targeting the Target data source with its UID. + - The label and description are provided as text + - Each correlation contains the following configuration: + - Required correlation type (query) + - Target query matching test data source model + - “App metrics” correlation contains the following configuration: + - Alias is set to ${application} variable (note that in provisioning files $ is used to access environment variables so it has to be [escaped]({{< relref "/docs/grafana/latest/administration/provisioning#using-environment-variables" >}})). + - Regular expression transformation is created to extract values from “msg” field + - Regular expression transformation is used to capture the application name from the full name of the service stored in the log line. + - The output of the transformation is mapped to a variable called “application”. + - “Service metrics” correlation is created in a similar way but with logfmt transformation to break down log message and access full name of the service (e.g. “app1.loginService”). + - For example, when a logline “2020-01-01 10:00 level=error message=error service=app1.loginService” is provided as the input, the transformation produces new variables: level, message, and service. + - “service” variable is used as the alias in the target query. + +1. Navigate to Explore and open “Source” data source. +1. Select the “Raw Frames” scenario and provide the following data frames to emulate returning log lines: + ```json + [ + { + "meta": { + "preferredVisualisationType": "logs" + }, + "fields": [ + { + "name": "time", + "values": [1, 2] + }, + { + "name": "msg", + "values": [ + "level=error msg=error service=app1.loginService", + "level=debug msg=info service=app2.userProfileService" + ] + } + ] + } + ] + ``` +1. Run the query and open log details by clicking on the log line. + + {{< figure src="/static/img/docs/correlations/correlations-in-logs-panel-10-0.png" max-width="600px" caption="Correlation links in Logs panel" >}} + + A link “App metrics” and “Service metrics” show next to variables extracted out of the log line with transformations + +1. Click on the “App metrics” link. +1. A split view is opened and the target query is run. +1. Notice how the application name from the log line is filled in as the alias property in the target query. + + {{< figure src="/static/img/docs/correlations/interpolated-target-query-10-0.png" max-width="600px" caption="Interpolated target query" >}} + + This allows you to run a specific query based on the source results: + + {{< figure src="/static/img/docs/correlations/target-query-results-10-0.png" max-width="600px" caption="Interpolated target query results" >}} + +1. Go back to the source query and change raw frames’ preferred visualization type to “table” to see how links are displayed in a Table visualization. + + ```json + [ + { + "meta": { + "preferredVisualisationType": "table" + }, + "fields": [...] + } + ] + ``` + +1. Run the query and notice how links are created in the Table cell: + + {{< figure src="/static/img/docs/correlations/correlations-in-table-10-0.png" max-width="600px" caption="Correlations links in table" >}} diff --git a/docs/sources/administration/enterprise-licensing/_index.md b/docs/sources/administration/enterprise-licensing/_index.md index 1bf0e64d8c1..bee4a0d4f92 100644 --- a/docs/sources/administration/enterprise-licensing/_index.md +++ b/docs/sources/administration/enterprise-licensing/_index.md @@ -52,8 +52,8 @@ There is more than one way to add the license to a Grafana instance: This is the preferred option for single instance installations of Grafana Enterprise. 1. Sign in as a Grafana server administrator. -1. Navigate to **Server Admin > Upgrade** within Grafana. -1. Click **Upload license token file**. +1. Click **Administration > Stats and license** in the side navigation menu. +1. Click **Upload a new token**. 1. Select your license file, and upload it. #### Put the `license.jwt` file into the data directory of Grafana @@ -205,7 +205,7 @@ To determine the number of active users: 1. Sign in to Grafana Enterprise as a System Administrator. -1. Click **Administration** in the left-side menu. +1. Click **Administration** in the side navigation menu. 1. Click **Stats and license**. diff --git a/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/manage-license-in-aws-marketplace/index.md b/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/manage-license-in-aws-marketplace/index.md index d142d5fc209..1b0eeebf7da 100644 --- a/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/manage-license-in-aws-marketplace/index.md +++ b/docs/sources/administration/enterprise-licensing/activate-aws-marketplace-license/manage-license-in-aws-marketplace/index.md @@ -32,9 +32,9 @@ You can use AWS Marketplace to make the following modifications to your Grafana 1. Sign in to Grafana as a Server Administrator. -1. Click **Administration** in the left-side menu, and then **Stats and license**. +1. Click **Administration** in the side navigation menu, and then **Stats and license**. -1. In the **Token** section under **Enterprise License**, click **Renew License**. +1. In the **Token** section under **Enterprise License**, click **Renew token**. This action retrieves updated license information from AWS. diff --git a/docs/sources/administration/plugin-management/index.md b/docs/sources/administration/plugin-management/index.md index 6e3eff53e43..bde0cf5d17e 100644 --- a/docs/sources/administration/plugin-management/index.md +++ b/docs/sources/administration/plugin-management/index.md @@ -72,18 +72,20 @@ The Plugin catalog allows you to browse and manage plugins from within Grafana. +_Video shows the Plugin catalog in a previous version of Grafana._ + In order to be able to install / uninstall / update plugins using plugin catalog, you must enable it via the `plugin_admin_enabled` flag in the [configuration]({{< relref "../../setup-grafana/configure-grafana/#plugin_admin_enabled" >}}) file. Before following the steps below, make sure you are logged in as a Grafana administrator. -- Administrators can find the Plugin catalog at **Administration > Plugins**. +Administrators can find the Plugin catalog at **Administration > Plugins**. ### Browse plugins To browse for available plugins: -1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins. +1. In Grafana, click **Administration > Plugins** in the side navigation menu to view installed plugins. 1. Click the **All** filter to browse all available plugins. 1. Click the **Data sources**, **Panels**, or **Applications** buttons to filter by plugin type. @@ -91,7 +93,7 @@ To browse for available plugins: To install a plugin: -1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins. +1. In Grafana, click **Administration > Plugins** in the side navigation menu to view installed plugins. 1. Browse and find a plugin. 1. Click on the plugin logo. 1. Click **Install**. @@ -102,7 +104,7 @@ When the update is complete, you see a confirmation message that the installatio To update a plugin: -1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins. +1. In Grafana, click **Administration > Plugins** in the side navigation menu to view installed plugins. 1. Click on the plugin logo. 1. Click **Update**. @@ -112,7 +114,7 @@ When the update is complete, you see a confirmation message that the update was To uninstall a plugin: -1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins. +1. In Grafana, click **Administration > Plugins** in the side navigation menu to view installed plugins. 1. Click on the plugin logo. 1. Click **Uninstall**. diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md index f1c59989121..d93ddc329ab 100644 --- a/docs/sources/administration/provisioning/index.md +++ b/docs/sources/administration/provisioning/index.md @@ -169,57 +169,59 @@ Common settings in the [built-in core data sources]({{< relref "../../datasource > **Note:** Data sources tagged with _HTTP\*_ communicate using the HTTP protocol, which includes all core data source plugins except MySQL, PostgreSQL, and MSSQL. -| Name | Type | Data source | Description | -| -------------------------- | ------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| tlsAuth | boolean | _HTTP\*_, MySQL | Enable TLS authentication using client cert configured in secure json data | -| tlsAuthWithCACert | boolean | _HTTP\*_, MySQL, PostgreSQL | Enable TLS authentication using CA cert | -| tlsSkipVerify | boolean | _HTTP\*_, MySQL, PostgreSQL, MSSQL | Controls whether a client verifies the server's certificate chain and host name. | -| serverName | string | _HTTP\*_, MSSQL | Optional. Controls the server name used for certificate common name/subject alternative name verification. Defaults to using the data source URL. | -| timeout | string | _HTTP\*_ | Request timeout in seconds. Overrides dataproxy.timeout option | -| graphiteVersion | string | Graphite | Graphite version | -| timeInterval | string | Prometheus, Elasticsearch, InfluxDB, MySQL, PostgreSQL and MSSQL | Lowest interval/step value that should be used for this data source. | -| httpMode | string | Influxdb | HTTP Method. 'GET', 'POST', defaults to GET | -| maxSeries | number | Influxdb | Max number of series/tables that Grafana processes | -| httpMethod | string | Prometheus | HTTP Method. 'GET', 'POST', defaults to POST | -| customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. | -| manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI | -| alertmanagerUid | string | Prometheus and Loki | UID of Alert Manager that manages Alert for this data source. | -| timeField | string | Elasticsearch | Which field that should be used as timestamp | -| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' | -| logMessageField | string | Elasticsearch | Which field should be used as the log message | -| logLevelField | string | Elasticsearch | Which field should be used to indicate the priority of the log message | -| maxConcurrentShardRequests | number | Elasticsearch | Maximum number of concurrent shard requests that each sub-search request executes per node | -| sigV4Auth | boolean | Elasticsearch and Prometheus | Enable usage of SigV4 | -| sigV4AuthType | string | Elasticsearch and Prometheus | SigV4 auth provider. default/credentials/keys | -| sigV4ExternalId | string | Elasticsearch and Prometheus | Optional SigV4 External ID | -| sigV4AssumeRoleArn | string | Elasticsearch and Prometheus | Optional SigV4 ARN role to assume | -| sigV4Region | string | Elasticsearch and Prometheus | SigV4 AWS region | -| sigV4Profile | string | Elasticsearch and Prometheus | Optional SigV4 credentials profile | -| authType | string | Cloudwatch | Auth provider. default/credentials/keys | -| externalId | string | Cloudwatch | Optional External ID | -| assumeRoleArn | string | Cloudwatch | Optional ARN role to assume | -| defaultRegion | string | Cloudwatch | Optional default AWS region | -| customMetricsNamespaces | string | Cloudwatch | Namespaces of Custom Metrics | -| profile | string | Cloudwatch | Optional credentials profile | -| tsdbVersion | string | OpenTSDB | Version | -| tsdbResolution | string | OpenTSDB | Resolution | -| sslmode | string | PostgreSQL | SSLmode. 'disable', 'require', 'verify-ca' or 'verify-full' | -| tlsConfigurationMethod | string | PostgreSQL | SSL Certificate configuration, either by 'file-path' or 'file-content' | -| sslRootCertFile | string | PostgreSQL, MSSQL | SSL server root certificate file, must be readable by the Grafana user | -| sslCertFile | string | PostgreSQL | SSL client certificate file, must be readable by the Grafana user | -| sslKeyFile | string | PostgreSQL | SSL client key file, must be readable by _only_ the Grafana user | -| encrypt | string | MSSQL | Connection SSL encryption handling. 'disable', 'false' or 'true' | -| 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+) | -| 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 | The type of the Prometheus data sources. such as `Prometheus`, `Cortex`, `Thanos`, `Mimir` | -| cacheLevel | string | Prometheus | This determines the duration of the browser cache. Valid values include: `Low`, `Medium`, `High`, and `None`. This field is configurable when you enable the `prometheusResourceBrowserCache` feature flag. | -| implementation | string | AlertManager | The implementation of the AlertManager data source, such as `prometheus`, `cortex` or `mimir` | -| handleGrafanaManagedAlerts | boolean | AlertManager | When enabled, Grafana-managed alerts are sent to this Alertmanager | +| Name | Type | Data source | Description | +| ----------------------------- | ------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| tlsAuth | boolean | _HTTP\*_, MySQL | Enable TLS authentication using client cert configured in secure json data | +| tlsAuthWithCACert | boolean | _HTTP\*_, MySQL, PostgreSQL | Enable TLS authentication using CA cert | +| tlsSkipVerify | boolean | _HTTP\*_, MySQL, PostgreSQL, MSSQL | Controls whether a client verifies the server's certificate chain and host name. | +| serverName | string | _HTTP\*_, MSSQL | Optional. Controls the server name used for certificate common name/subject alternative name verification. Defaults to using the data source URL. | +| timeout | string | _HTTP\*_ | Request timeout in seconds. Overrides dataproxy.timeout option | +| graphiteVersion | string | Graphite | Graphite version | +| timeInterval | string | Prometheus, Elasticsearch, InfluxDB, MySQL, PostgreSQL and MSSQL | Lowest interval/step value that should be used for this data source. | +| httpMode | string | Influxdb | HTTP Method. 'GET', 'POST', defaults to GET | +| maxSeries | number | Influxdb | Max number of series/tables that Grafana processes | +| httpMethod | string | Prometheus | HTTP Method. 'GET', 'POST', defaults to POST | +| customQueryParameters | string | Prometheus | Query parameters to add, as a URL-encoded string. | +| manageAlerts | boolean | Prometheus and Loki | Manage alerts via Alerting UI | +| alertmanagerUid | string | Prometheus and Loki | UID of Alert Manager that manages Alert for this data source. | +| timeField | string | Elasticsearch | Which field that should be used as timestamp | +| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' | +| logMessageField | string | Elasticsearch | Which field should be used as the log message | +| logLevelField | string | Elasticsearch | Which field should be used to indicate the priority of the log message | +| maxConcurrentShardRequests | number | Elasticsearch | Maximum number of concurrent shard requests that each sub-search request executes per node | +| sigV4Auth | boolean | Elasticsearch and Prometheus | Enable usage of SigV4 | +| sigV4AuthType | string | Elasticsearch and Prometheus | SigV4 auth provider. default/credentials/keys | +| sigV4ExternalId | string | Elasticsearch and Prometheus | Optional SigV4 External ID | +| sigV4AssumeRoleArn | string | Elasticsearch and Prometheus | Optional SigV4 ARN role to assume | +| sigV4Region | string | Elasticsearch and Prometheus | SigV4 AWS region | +| sigV4Profile | string | Elasticsearch and Prometheus | Optional SigV4 credentials profile | +| authType | string | Cloudwatch | Auth provider. default/credentials/keys | +| externalId | string | Cloudwatch | Optional External ID | +| assumeRoleArn | string | Cloudwatch | Optional ARN role to assume | +| defaultRegion | string | Cloudwatch | Optional default AWS region | +| customMetricsNamespaces | string | Cloudwatch | Namespaces of Custom Metrics | +| profile | string | Cloudwatch | Optional credentials profile | +| tsdbVersion | string | OpenTSDB | Version | +| tsdbResolution | string | OpenTSDB | Resolution | +| sslmode | string | PostgreSQL | SSLmode. 'disable', 'require', 'verify-ca' or 'verify-full' | +| tlsConfigurationMethod | string | PostgreSQL | SSL Certificate configuration, either by 'file-path' or 'file-content' | +| sslRootCertFile | string | PostgreSQL, MSSQL | SSL server root certificate file, must be readable by the Grafana user | +| sslCertFile | string | PostgreSQL | SSL client certificate file, must be readable by the Grafana user | +| sslKeyFile | string | PostgreSQL | SSL client key file, must be readable by _only_ the Grafana user | +| encrypt | string | MSSQL | Connection SSL encryption handling. 'disable', 'false' or 'true' | +| 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+) | +| 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 | The type of the Prometheus data sources. such as `Prometheus`, `Cortex`, `Thanos`, `Mimir` | +| cacheLevel | string | Prometheus | This determines the duration of the browser cache. Valid values include: `Low`, `Medium`, `High`, and `None`. This field is configurable when you enable the `prometheusResourceBrowserCache` feature flag. | +| incrementalQuerying | string | Prometheus | Experimental: Turn on incremental querying to enhance dashboard reload performance with slow data sources | +| incrementalQueryOverlapWindow | string | Prometheus | Experimental: Configure incremental query overlap window. Requires a valid duration string, i.e. `180s` or `15m` Default value is `10m` (10 minutes). | +| implementation | string | AlertManager | The implementation of the AlertManager data source, such as `prometheus`, `cortex` or `mimir` | +| handleGrafanaManagedAlerts | boolean | AlertManager | When enabled, Grafana-managed alerts are sent to this Alertmanager | For examples of specific data sources' JSON data, refer to that [data source's documentation]({{< relref "../../datasources" >}}). diff --git a/docs/sources/administration/recorded-queries/index.md b/docs/sources/administration/recorded-queries/index.md index cc380b234ca..81475ae48c3 100644 --- a/docs/sources/administration/recorded-queries/index.md +++ b/docs/sources/administration/recorded-queries/index.md @@ -7,6 +7,10 @@ keywords: - query - queries - recorded +labels: + products: + - cloud + - enterprise title: Recorded queries weight: 300 --- diff --git a/docs/sources/administration/roles-and-permissions/_index.md b/docs/sources/administration/roles-and-permissions/_index.md index f4d3199881b..ac87f3a608b 100644 --- a/docs/sources/administration/roles-and-permissions/_index.md +++ b/docs/sources/administration/roles-and-permissions/_index.md @@ -92,14 +92,14 @@ The following table lists permissions for each role. ## Dashboard permissions -When you want to extend a viewer's ability to edit and save dashboard changes or limit an editor's permission to modify a dashboard, you can assign permissions to dashboards and dashboard folders. For example, you might want a certain viewer to be able to edit a dashboard. While that user can _see_ all dashboards, you can grant them access to _update_ only one of them. +When you want to extend a viewer's ability to edit and save dashboard changes or limit an editor's permission to modify a dashboard, you can assign permissions to dashboards and folders. For example, you might want a certain viewer to be able to edit a dashboard. While that user can _see_ all dashboards, you can grant them access to _update_ only one of them. > Important: The dashboard permissions you specify override the organization permissions you assign to the user for the selected entity. You can specify the following permissions to dashboards and folders. -- **Admin**: Can create, edit, or delete a dashboard. Can edit or delete a folder. Administrators can also change dashboard and folder permissions. -- **Edit**: Can create, edit, or delete a dashboard. Can edit or delete a folder. Editors _cannot_ change folder or dashboard permissions. +- **Admin**: Can create, edit, or delete a dashboard. Can edit or delete a folder, and create dashboards and subfolders in a folder. Administrators can also change dashboard and folder permissions. +- **Edit**: Can create, edit, or delete a dashboard. Can edit or delete a folder, and create dashboards and subfolders in a folder. Editors _cannot_ change folder or dashboard permissions. - **View**: Can only view dashboards and folders. > Important: When a user creates a dashboard or a folder, he is set as **Admin** of it. diff --git a/docs/sources/administration/roles-and-permissions/access-control/_index.md b/docs/sources/administration/roles-and-permissions/access-control/_index.md index c4e96848c3c..d95e7aa214d 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/_index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/_index.md @@ -3,6 +3,11 @@ aliases: - ../../enterprise/access-control/ - ../../enterprise/access-control/about-rbac/ - ../../enterprise/access-control/roles/ +cascade: + labels: + products: + - cloud + - enterprise description: Role-based access control (RBAC) provides a standardized way of granting, changing, and revoking access so that users can view and modify Grafana resources, such as users and reports. 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 5176af499f4..373075b078c 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 @@ -23,128 +23,129 @@ To learn more about the Grafana resources to which you can apply RBAC, refer to The following list contains role-based access control actions. -| Action | Applicable scope | Description | -| ------------------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `alert.instances.external:read` | `datasources:*`
`datasources:uid:*` | Read alerts and silences in data sources that support alerting. | -| `alert.instances.external:write` | `datasources:*`
`datasources:uid:*` | Manage alerts and silences in data sources that support alerting. | -| `alert.instances:create` | n/a | Create silences in the current organization. | -| `alert.instances:read` | n/a | Read alerts and silences in the current organization. | -| `alert.instances:write` | n/a | Update and expire silences in the current organization. | -| `alert.notifications.external:read` | `datasources:*`
`datasources:uid:*` | Read templates, contact points, notification policies, and mute timings in data sources that support alerting. | -| `alert.notifications.external:write` | `datasources:*`
`datasources:uid:*` | Manage templates, contact points, notification policies, and mute timings in data sources that support alerting. | -| `alert.notifications:write` | n/a | Manage templates, contact points, notification policies, and mute timings in the current organization. | -| `alert.notifications:read` | n/a | Read all templates, contact points, notification policies, and mute timings in the current organization. | -| `alert.rules.external:read` | `datasources:*`
`datasources:uid:*` | Read alert rules in data sources that support alerting (Prometheus, Mimir, and Loki) | -| `alert.rules.external:write` | `datasources:*`
`datasources:uid:*` | Create, update, and delete alert rules in data sources that support alerting (Mimir and Loki). | -| `alert.rules:create` | `folders:*`
`folders:uid:*` | Create Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | -| `alert.rules:delete` | `folders:*`
`folders:uid:*` | Delete Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | -| `alert.rules:read` | `folders:*`
`folders:uid:*` | Read Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | -| `alert.rules:write` | `folders:*`
`folders:uid:*` | Update Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | -| `alert.provisioning:read` | n/a | Read all Grafana alert rules, notification policies, etc via provisioning API. Permissions to folders and datasource are not required. | -| `alert.provisioning:write` | n/a | Update all Grafana alert rules, notification policies, etc via provisioning API. Permissions to folders and datasource are not required. | -| `annotations:create` | `annotations:*`
`annotations:type:*` | Create annotations. | -| `annotations:delete` | `annotations:*`
`annotations:type:*` | Delete annotations. | -| `annotations:read` | `annotations:*`
`annotations:type:*` | Read annotations and annotation tags. | -| `annotations:write` | `annotations:*`
`annotations:type:*` | Update annotations. | -| `apikeys:create` | n/a | Create API keys. | -| `apikeys:read` | `apikeys:*`
`apikeys:id:*` | Read API keys. | -| `apikeys:delete` | `apikeys:*`
`apikeys:id:*` | Delete API keys. | -| `dashboards:create` | `folders:*`
`folders:uid:*` | Create dashboards in one or more folders. | -| `dashboards:delete` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Delete one or more dashboards. | -| `dashboards.insights:read` | n/a | Read dashboard insights data and see presence indicators. | -| `dashboards.permissions:read` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Read permissions for one or more dashboards. | -| `dashboards.permissions:write` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Update permissions for one or more dashboards. | -| `dashboards:read` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Read one or more dashboards. | -| `dashboards:write` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Update one or more dashboards. | -| `datasources.caching:read` | `datasources:*`
`datasources:uid:*` | Read data source query caching settings. | -| `datasources.caching:write` | `datasources:*`
`datasources:uid:*` | Update data source query caching settings. | -| `datasources:create` | n/a | Create data sources. | -| `datasources:delete` | `datasources:*`
`datasources:uid:*` | Delete data sources. | -| `datasources:explore` | n/a | Enable access to the **Explore** tab. | -| `datasources.id:read` | `datasources:*`
`datasources:uid:*` | Read data source IDs. | -| `datasources.insights:read` | n/a | Read data sources insights data. | -| `datasources.permissions:read` | `datasources:*`
`datasources:uid:*` | List data source permissions. | -| `datasources.permissions:write` | `datasources:*`
`datasources:uid:*` | Update data source permissions. | -| `datasources:query` | `datasources:*`
`datasources:uid:*` | Query data sources. | -| `datasources:read` | `datasources:*`
`datasources:uid:*` | List data sources. | -| `datasources:write` | `datasources:*`
`datasources:uid:*` | Update data sources. | -| `folders.permissions:read` | `folders:*`
`folders:uid:*` | Read permissions for one or more folders. | -| `folders.permissions:write` | `folders:*`
`folders:uid:*` | Update permissions for one or more folders. | -| `folders:create` | n/a | Create folders. | -| `folders:delete` | `folders:*`
`folders:uid:*` | Delete one or more folders. | -| `folders:read` | `folders:*`
`folders:uid:*` | Read one or more folders. | -| `folders:write` | `folders:*`
`folders:uid:*` | Update one or more folders. | -| `ldap.config:reload` | n/a | Reload the LDAP configuration. | -| `ldap.status:read` | n/a | Verify the availability of the LDAP server or servers. | -| `ldap.user:read` | n/a | Read users via LDAP. | -| `ldap.user:sync` | n/a | Sync users via LDAP. | -| `licensing.reports:read` | n/a | Get custom permission reports. | -| `licensing:delete` | n/a | Delete the license token. | -| `licensing:read` | n/a | Read licensing information. | -| `licensing:write` | n/a | Update the license token. | -| `org.users:write` | `users:*`
`users:id:*` | Update the organization role (`Viewer`, `Editor`, or `Admin`) of a user. | -| `org.users:add` | `users:*` | Add a user to an organization or invite a new user to an organization. | -| `org.users:read` | `users:*`
`users:id:*` | Get user profiles within an organization. | -| `org.users:remove` | `users:*`
`users:id:*` | Remove a user from an organization. | -| `org:create` | n/a | Create an organization. | -| `orgs.preferences:read` | `orgs:*`
`orgs:id:*` | Read organization preferences. | -| `orgs.preferences:write` | `orgs:*`
`orgs:id:*` | Update organization preferences. | -| `orgs.quotas:read` | `orgs:*`
`orgs:id:*` | Read organization quotas. | -| `orgs.quotas:write` | `orgs:*`
`orgs:id:*` | Update organization quotas. | -| `orgs:delete` | `orgs:*`
`orgs:id:*` | Delete one or more organizations. | -| `orgs:read` | `orgs:*`
`orgs:id:*` | Read one or more organizations. | -| `orgs:write` | `orgs:*`
`orgs:id:*` | Update one or more organizations. | -| `plugins.app:access` | `plugins:*`
`plugins:id:*` | Access one or more application plugins (still enforcing the organization role) | -| `plugins:install` | n/a | Install and uninstall plugins. | -| `plugins:write` | `plugins:*`
`plugins:id:*` | Edit settings for one or more plugins. | -| `provisioning:reload` | `provisioners:*` | Reload provisioning files. To find the exact scope for specific provisioner, see [Scope definitions]({{< relref "#scope-definitions" >}}). | -| `reports:create` | n/a | Create reports. | -| `reports:write` | `reports:*`
`reports:id:*` | Update reports. | -| `reports.settings:read` | n/a | Read report settings. | -| `reports.settings:write` | n/a | Update report settings. | -| `reports:delete` | `reports:*`
`reports:id:*` | Delete reports. | -| `reports:read` | `reports:*` | List all available reports or get a specific report. | -| `reports:send` | `reports:*` | Send a report email. | -| `roles:delete` | `permissions:type:delegate` | Delete a custom role. | -| `roles:read` | `roles:*`
`roles:uid:*` | List roles and read a specific with its permissions. | -| `roles:write` | `permissions:type:delegate` | Create or update a custom role. | -| `roles:write` | `permissions:type:escalate` | Reset basic roles to their default permissions. | -| `server.stats:read` | n/a | Read Grafana instance statistics. | -| `serviceaccounts:write` | `serviceaccounts:*` | Create Grafana service accounts. | -| `serviceaccounts:create` | n/a | Update Grafana service accounts. | -| `serviceaccounts:delete` | `serviceaccounts:*` | Delete Grafana service accounts. | -| `serviceaccounts:read` | `serviceaccounts:*` | Read Grafana service accounts. | -| `serviceaccounts.permissions:write` | `serviceaccounts:*` | Update Grafana service account permissions to control who can do what with the service account. | -| `serviceaccounts.permissions:read` | `serviceaccounts:*` | Read Grafana service account permissions to see who can do what with the service account. | -| `settings:read` | `settings:*`
`settings:auth.saml:*`
`settings:auth.saml:enabled` (property level) | Read the [Grafana configuration settings]({{< relref "../../../../setup-grafana/configure-grafana/" >}}) | -| `settings:write` | `settings:*`
`settings:auth.saml:*`
`settings:auth.saml:enabled` (property level) | Update any Grafana configuration settings that can be [updated at runtime]({{< relref "../../../../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}). | -| `status:accesscontrol` | `services:accesscontrol` | Get access-control enabled status. | -| `teams.permissions:read` | `teams:*`
`teams:id:*` | Read members and External Group Synchronization setup for teams. | -| `teams.permissions:write` | `teams:*`
`teams:id:*` | Add, remove and update members and manage External Group Synchronization setup for teams. | -| `teams.roles:add` | `permissions:type:delegate` | Assign a role to a team. | -| `teams.roles:read` | `teams:*` | List roles assigned directly to a team. | -| `teams.roles:remove` | `permissions:type:delegate` | Unassign a role from a team. | -| `teams:create` | n/a | Create teams. | -| `teams:delete` | `teams:*`
`teams:id:*` | Delete one or more teams. | -| `teams:read` | `teams:*`
`teams:id:*` | Read one or more teams and team preferences. | -| `teams:write` | `teams:*`
`teams:id:*` | Update one or more teams and team preferences. | -| `users.authtoken:read` | `global.users:*`
`global.users:id:*` | List authentication tokens that are assigned to a user. | -| `users.authtoken:write` | `global.users:*`
`global.users:id:*` | Update authentication tokens that are assigned to a user. | -| `users.password:write` | `global.users:*`
`global.users:id:*` | Update a user’s password. | -| `users.permissions:read` | `users:*` | List permissions of a user. | -| `users.permissions:write` | `global.users:*`
`global.users:id:*` | Update a user’s organization-level permissions. | -| `users.quotas:read` | `global.users:*`
`global.users:id:*` | List a user’s quotas. | -| `users.quotas:write` | `global.users:*`
`global.users:id:*` | Update a user’s quotas. | -| `users.roles:add` | `permissions:type:delegate` | Assign a role to a user or a service account. | -| `users.roles:read` | `users:*` | List roles assigned directly to a user or a service account. | -| `users.roles:remove` | `permissions:type:delegate` | Unassign a role from a user or a service account. | -| `users:create` | n/a | Create a user. | -| `users:delete` | `global.users:*`
`global.users:id:*` | Delete a user. | -| `users:disable` | `global.users:*`
`global.users:id:*` | Disable a user. | -| `users:enable` | `global.users:*`
`global.users:id:*` | Enable a user. | -| `users:logout` | `global.users:*`
`global.users:id:*` | Sign out a user. | -| `users:read` | `global.users:*` | Read or search user profiles. | -| `users:write` | `global.users:*`
`global.users:id:*` | Update a user’s profile. | +| Action | Applicable scope | Description | +| ------------------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `alert.instances.external:read` | `datasources:*`
`datasources:uid:*` | Read alerts and silences in data sources that support alerting. | +| `alert.instances.external:write` | `datasources:*`
`datasources:uid:*` | Manage alerts and silences in data sources that support alerting. | +| `alert.instances:create` | n/a | Create silences in the current organization. | +| `alert.instances:read` | n/a | Read alerts and silences in the current organization. | +| `alert.instances:write` | n/a | Update and expire silences in the current organization. | +| `alert.notifications.external:read` | `datasources:*`
`datasources:uid:*` | Read templates, contact points, notification policies, and mute timings in data sources that support alerting. | +| `alert.notifications.external:write` | `datasources:*`
`datasources:uid:*` | Manage templates, contact points, notification policies, and mute timings in data sources that support alerting. | +| `alert.notifications:write` | n/a | Manage templates, contact points, notification policies, and mute timings in the current organization. | +| `alert.notifications:read` | n/a | Read all templates, contact points, notification policies, and mute timings in the current organization. | +| `alert.rules.external:read` | `datasources:*`
`datasources:uid:*` | Read alert rules in data sources that support alerting (Prometheus, Mimir, and Loki) | +| `alert.rules.external:write` | `datasources:*`
`datasources:uid:*` | Create, update, and delete alert rules in data sources that support alerting (Mimir and Loki). | +| `alert.rules:create` | `folders:*`
`folders:uid:*` | Create Grafana alert rules in a folder and its subfolders. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | +| `alert.rules:delete` | `folders:*`
`folders:uid:*` | Delete Grafana alert rules in a folder and its subfolders. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | +| `alert.rules:read` | `folders:*`
`folders:uid:*` | Read Grafana alert rules in a folder and its subfolders. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | +| `alert.rules:write` | `folders:*`
`folders:uid:*` | Update Grafana alert rules in a folder and its subfolders. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. | +| `alert.provisioning:read` | n/a | Read all Grafana alert rules, notification policies, etc via provisioning API. Permissions to folders and datasource are not required. | +| `alert.provisioning:write` | n/a | Update all Grafana alert rules, notification policies, etc via provisioning API. Permissions to folders and datasource are not required. | +| `annotations:create` | `annotations:*`
`annotations:type:*` | Create annotations. | +| `annotations:delete` | `annotations:*`
`annotations:type:*` | Delete annotations. | +| `annotations:read` | `annotations:*`
`annotations:type:*` | Read annotations and annotation tags. | +| `annotations:write` | `annotations:*`
`annotations:type:*` | Update annotations. | +| `apikeys:create` | n/a | Create API keys. | +| `apikeys:read` | `apikeys:*`
`apikeys:id:*` | Read API keys. | +| `apikeys:delete` | `apikeys:*`
`apikeys:id:*` | Delete API keys. | +| `dashboards:create` | `folders:*`
`folders:uid:*` | Create dashboards in one or more folders and their subfolders. | +| `dashboards:delete` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Delete one or more dashboards. | +| `dashboards.insights:read` | n/a | Read dashboard insights data and see presence indicators. | +| `dashboards.permissions:read` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Read permissions for one or more dashboards. | +| `dashboards.permissions:write` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Update permissions for one or more dashboards. | +| `dashboards:read` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Read one or more dashboards. | +| `dashboards:write` | `dashboards:*`
`dashboards:uid:*`
`folders:*`
`folders:uid:*` | Update one or more dashboards. | +| `dashboards.public:write` | `dashboards:*`
`dashboards:uid:*` | Write public dashboard configuration. | +| `datasources.caching:read` | `datasources:*`
`datasources:uid:*` | Read data source query caching settings. | +| `datasources.caching:write` | `datasources:*`
`datasources:uid:*` | Update data source query caching settings. | +| `datasources:create` | n/a | Create data sources. | +| `datasources:delete` | `datasources:*`
`datasources:uid:*` | Delete data sources. | +| `datasources:explore` | n/a | Enable access to the **Explore** tab. | +| `datasources.id:read` | `datasources:*`
`datasources:uid:*` | Read data source IDs. | +| `datasources.insights:read` | n/a | Read data sources insights data. | +| `datasources.permissions:read` | `datasources:*`
`datasources:uid:*` | List data source permissions. | +| `datasources.permissions:write` | `datasources:*`
`datasources:uid:*` | Update data source permissions. | +| `datasources:query` | `datasources:*`
`datasources:uid:*` | Query data sources. | +| `datasources:read` | `datasources:*`
`datasources:uid:*` | List data sources. | +| `datasources:write` | `datasources:*`
`datasources:uid:*` | Update data sources. | +| `folders.permissions:read` | `folders:*`
`folders:uid:*` | Read permissions for one or more folders and their subfolders. | +| `folders.permissions:write` | `folders:*`
`folders:uid:*` | Update permissions for one or more folders and their subfolders. | +| `folders:create` | n/a | Create folders in the root level. If granted together with `folders:write`, also allows creating subfolders under all folders that the user can update. | +| `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. If granted together with `folders:create` permission, also allows creating subfolders under these folders. | +| `ldap.config:reload` | n/a | Reload the LDAP configuration. | +| `ldap.status:read` | n/a | Verify the availability of the LDAP server or servers. | +| `ldap.user:read` | n/a | Read users via LDAP. | +| `ldap.user:sync` | n/a | Sync users via LDAP. | +| `licensing.reports:read` | n/a | Get custom permission reports. | +| `licensing:delete` | n/a | Delete the license token. | +| `licensing:read` | n/a | Read licensing information. | +| `licensing:write` | n/a | Update the license token. | +| `org.users:write` | `users:*`
`users:id:*` | Update the organization role (`Viewer`, `Editor`, or `Admin`) of a user. | +| `org.users:add` | `users:*` | Add a user to an organization or invite a new user to an organization. | +| `org.users:read` | `users:*`
`users:id:*` | Get user profiles within an organization. | +| `org.users:remove` | `users:*`
`users:id:*` | Remove a user from an organization. | +| `org:create` | n/a | Create an organization. | +| `orgs.preferences:read` | `orgs:*`
`orgs:id:*` | Read organization preferences. | +| `orgs.preferences:write` | `orgs:*`
`orgs:id:*` | Update organization preferences. | +| `orgs.quotas:read` | `orgs:*`
`orgs:id:*` | Read organization quotas. | +| `orgs.quotas:write` | `orgs:*`
`orgs:id:*` | Update organization quotas. | +| `orgs:delete` | `orgs:*`
`orgs:id:*` | Delete one or more organizations. | +| `orgs:read` | `orgs:*`
`orgs:id:*` | Read one or more organizations. | +| `orgs:write` | `orgs:*`
`orgs:id:*` | Update one or more organizations. | +| `plugins.app:access` | `plugins:*`
`plugins:id:*` | Access one or more application plugins (still enforcing the organization role) | +| `plugins:install` | n/a | Install and uninstall plugins. | +| `plugins:write` | `plugins:*`
`plugins:id:*` | Edit settings for one or more plugins. | +| `provisioning:reload` | `provisioners:*` | Reload provisioning files. To find the exact scope for specific provisioner, see [Scope definitions]({{< relref "#scope-definitions" >}}). | +| `reports:create` | n/a | Create reports. | +| `reports:write` | `reports:*`
`reports:id:*` | Update reports. | +| `reports.settings:read` | n/a | Read report settings. | +| `reports.settings:write` | n/a | Update report settings. | +| `reports:delete` | `reports:*`
`reports:id:*` | Delete reports. | +| `reports:read` | `reports:*` | List all available reports or get a specific report. | +| `reports:send` | `reports:*` | Send a report email. | +| `roles:delete` | `permissions:type:delegate` | Delete a custom role. | +| `roles:read` | `roles:*`
`roles:uid:*` | List roles and read a specific with its permissions. | +| `roles:write` | `permissions:type:delegate` | Create or update a custom role. | +| `roles:write` | `permissions:type:escalate` | Reset basic roles to their default permissions. | +| `server.stats:read` | n/a | Read Grafana instance statistics. | +| `serviceaccounts:write` | `serviceaccounts:*` | Create Grafana service accounts. | +| `serviceaccounts:create` | n/a | Update Grafana service accounts. | +| `serviceaccounts:delete` | `serviceaccounts:*` | Delete Grafana service accounts. | +| `serviceaccounts:read` | `serviceaccounts:*` | Read Grafana service accounts. | +| `serviceaccounts.permissions:write` | `serviceaccounts:*` | Update Grafana service account permissions to control who can do what with the service account. | +| `serviceaccounts.permissions:read` | `serviceaccounts:*` | Read Grafana service account permissions to see who can do what with the service account. | +| `settings:read` | `settings:*`
`settings:auth.saml:*`
`settings:auth.saml:enabled` (property level) | Read the [Grafana configuration settings]({{< relref "../../../../setup-grafana/configure-grafana/" >}}) | +| `settings:write` | `settings:*`
`settings:auth.saml:*`
`settings:auth.saml:enabled` (property level) | Update any Grafana configuration settings that can be [updated at runtime]({{< relref "../../../../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}). | +| `status:accesscontrol` | `services:accesscontrol` | Get access-control enabled status. | +| `teams.permissions:read` | `teams:*`
`teams:id:*` | Read members and External Group Synchronization setup for teams. | +| `teams.permissions:write` | `teams:*`
`teams:id:*` | Add, remove and update members and manage External Group Synchronization setup for teams. | +| `teams.roles:add` | `permissions:type:delegate` | Assign a role to a team. | +| `teams.roles:read` | `teams:*` | List roles assigned directly to a team. | +| `teams.roles:remove` | `permissions:type:delegate` | Unassign a role from a team. | +| `teams:create` | n/a | Create teams. | +| `teams:delete` | `teams:*`
`teams:id:*` | Delete one or more teams. | +| `teams:read` | `teams:*`
`teams:id:*` | Read one or more teams and team preferences. | +| `teams:write` | `teams:*`
`teams:id:*` | Update one or more teams and team preferences. | +| `users.authtoken:read` | `global.users:*`
`global.users:id:*` | List authentication tokens that are assigned to a user. | +| `users.authtoken:write` | `global.users:*`
`global.users:id:*` | Update authentication tokens that are assigned to a user. | +| `users.password:write` | `global.users:*`
`global.users:id:*` | Update a user’s password. | +| `users.permissions:read` | `users:*` | List permissions of a user. | +| `users.permissions:write` | `global.users:*`
`global.users:id:*` | Update a user’s organization-level permissions. | +| `users.quotas:read` | `global.users:*`
`global.users:id:*` | List a user’s quotas. | +| `users.quotas:write` | `global.users:*`
`global.users:id:*` | Update a user’s quotas. | +| `users.roles:add` | `permissions:type:delegate` | Assign a role to a user or a service account. | +| `users.roles:read` | `users:*` | List roles assigned directly to a user or a service account. | +| `users.roles:remove` | `permissions:type:delegate` | Unassign a role from a user or a service account. | +| `users:create` | n/a | Create a user. | +| `users:delete` | `global.users:*`
`global.users:id:*` | Delete a user. | +| `users:disable` | `global.users:*`
`global.users:id:*` | Disable a user. | +| `users:enable` | `global.users:*`
`global.users:id:*` | Enable a user. | +| `users:logout` | `global.users:*`
`global.users:id:*` | Sign out a user. | +| `users:read` | `global.users:*` | Read or search user profiles. | +| `users:write` | `global.users:*`
`global.users:id:*` | Update a user’s profile. | ### Grafana OnCall action definitions (beta) @@ -193,7 +194,7 @@ The following list contains role-based access control scopes. | `apikeys:*`
`apikeys:id:*` | Restrict an action to a set of API keys. For example, `apikeys:*` matches any API key, `apikey:id:1` matches the API key whose id is `1`. | | `dashboards:*`
`dashboards:uid:*` | Restrict an action to a set of dashboards. For example, `dashboards:*` matches any dashboard, and `dashboards:uid:1` matches the dashboard whose UID is `1`. | | `datasources:*`
`datasources:uid:*` | Restrict an action to a set of data sources. For example, `datasources:*` matches any data source, and `datasources:uid:1` matches the data source whose UID is `1`. | -| `folders:*`
`folders:uid:*` | Restrict an action to a set of folders. For example, `folders:*` matches any folder, and `folders:uid:1` matches the folder whose UID is `1`. | +| `folders:*`
`folders:uid:*` | Restrict an action to a set of folders. For example, `folders:*` matches any folder, and `folders:uid:1` matches the folder whose UID is `1`. Note that permissions granted to a folder cascade down to subfolders located under it | | `global.users:*`
`global.users:id:*` | Restrict an action to a set of global users. For example, `global.users:*` matches any user and `global.users:id:1` matches the user whose ID is `1`. | | `orgs:*`
`orgs:id:*` | Restrict an action to a set of organizations. For example, `orgs:*` matches any organization and `orgs:id:1` matches the organization whose ID is `1`. | | `permissions:type:delegate` | The scope is only applicable for roles associated with the Access Control itself and indicates that you can delegate your permissions only, or a subset of it, by creating a new role or making an assignment. | 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 907ac6a92f8..804ec775c0c 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 @@ -17,12 +17,12 @@ The following tables list permissions associated with basic and fixed roles. ## Basic role assignments -| Basic role | Associated fixed roles | Description | -| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| 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` | Default [Grafana server administrator]({{< relref "../#grafana-server-administrators" >}}) assignments. | -| 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: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:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:writer` | Default [Grafana organization administrator]({{< relref "../#organization-users-and-permissions" >}}) assignments. | -| 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` | Default [Editor]({{< relref "../#organization-users-and-permissions" >}}) assignments. | -| 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` | Default [Viewer]({{< relref "../#organization-users-and-permissions" >}}) assignments. | +| Basic role | Associated fixed roles | Description | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| 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` | Default [Grafana server administrator]({{< relref "../#grafana-server-administrators" >}}) assignments. | +| 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:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:writer` | Default [Grafana organization administrator]({{< relref "../#organization-users-and-permissions" >}}) assignments. | +| 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` | Default [Editor]({{< relref "../#organization-users-and-permissions" >}}) assignments. | +| 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` | Default [Viewer]({{< relref "../#organization-users-and-permissions" >}}) assignments. | ## Fixed role definitions @@ -42,10 +42,12 @@ The following tables list permissions associated with basic and fixed roles. | `fixed:annotations:writer` | All permissions from `fixed:annotations:reader`
`annotations:write`
`annotations.create`
`annotations:delete` for scope `annotations:type:*` | Read, create, update and delete all annotations and annotation tags. | | `fixed:apikeys:reader` | `apikeys:read` for scope `apikeys:*` | Read all api keys. | | `fixed:apikeys:writer` | All permissions from `fixed:apikeys:reader` and
`apikeys:create`
`apikeys:delete` for scope `apikeys:*` | Read, create, delete all api keys. | +| `fixed:authentication.config:writer` | `settings:read` for scope `settings:auth.saml:*`
`settings:write` for scope `settings:auth.saml:*` | Read and update authentication and SAML settings. | | `fixed:dashboards:creator` | `dashboards:create`
`folders:read` | Create dashboards. | | `fixed:dashboards.insights:reader` | `dashboards.insights:read` | Read dashboard insights data and see presence indicators. | | `fixed:dashboards.permissions:reader` | `dashboards.permissions:read` | Read all dashboard permissions. | | `fixed:dashboards.permissions:writer` | All permissions from `fixed:dashboards.permissions:reader` and
`dashboards.permissions:write` | Read and update all dashboard permissions. | +| `fixed:dashboards.public:writer` | `dashboards.public:write` | Create, update, delete or pause a public dashboard. | | `fixed:dashboards:reader` | `dashboards:read` | Read all dashboards. | | `fixed:dashboards:writer` | All permissions from `fixed:dashboards:reader` and
`dashboards:write`
`dashboards:edit`
`dashboards:delete`
`dashboards:create`
`dashboards.permissions:read`
`dashboards.permissions:write` | Read, create, update, and delete all dashboards. | | `fixed:datasources.caching:reader` | `datasources.caching:read` | Read data source query caching settings. | @@ -59,9 +61,9 @@ The following tables list permissions associated with basic and fixed roles. | `fixed:datasources:writer` | All permissions from `fixed:datasources:reader` and
`datasources:create`
`datasources:write`
`datasources:delete` | Read, query, create, delete, or update a data source. | | `fixed:folders.permissions:reader` | `folders.permissions:read` | Read all folder permissions. | | `fixed:folders.permissions:writer` | All permissions from `fixed:folders.permissions:reader` and
`folders.permissions:write` | Read and update all folder permissions. | -| `fixed:folders:creator` | `folders:create` | Create folders. | +| `fixed:folders:creator` | `folders:create` | Create folders in the root level. If granted together with `folders:write` permission, also allows creating subfolders under all folders. | | `fixed:folders:reader` | `folders:read`
`dashboards:read` | Read all folders and dashboards. | -| `fixed:folders:writer` | All permissions from `fixed:dashboards:writer` and
`folders:read`
`folders:write`
`folders:create`
`folders:delete`
`folders.permissions:read`
`folders.permissions:write` | Read, create, update, and delete all folders and dashboards. | +| `fixed:folders:writer` | All permissions from `fixed:dashboards:writer` and
`folders:read`
`folders:write`
`folders:create`
`folders:delete`
`folders.permissions:read`
`folders.permissions:write` | Read, create, update, and delete all folders and dashboards. If granted together with `fixed:folders:creator`, allows creating subfolders under all folders. | | `fixed:ldap:reader` | `ldap.user:read`
`ldap.status:read` | Read the LDAP configuration and LDAP status information. | | `fixed:ldap:writer` | 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:licensing:reader` | `licensing:read`
`licensing.reports:read` | Read licensing information and licensing reports. | 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 new file mode 100644 index 00000000000..ecae3532a2f --- /dev/null +++ b/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md @@ -0,0 +1,90 @@ +--- +aliases: + - ../../../enterprise/access-control/troubleshooting/ +labels: + products: + - cloud + - enterprise + - oss +description: RBAC troubleshooting guide. +menuTitle: Troubleshooting RBAC +title: Troubleshooting RBAC +weight: 80 +--- + +# Troubleshooting RBAC + +In this section, you’ll learn about logs that are available for RBAC and you’ll find the most common RBAC issues. + +## Enable debug logging + +You can enable debug log messages for RBAC in the Grafana configuration file. Debug logs are added to the Grafana server logs. + +```bash +[log] +filters = accesscontrol:debug accesscontrol.evaluator:debug dashboard.permissions:debug +``` + +## Enable audit logging + +> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud Advanced](/docs/grafana-cloud). + +You can enable auditing in the Grafana configuration file. + +```bash +[auditing] +enabled = true +``` + +All permission and role updates, and role assignments are added to audit logs. +Learn more about [access control audit logs]({{< relref "../../../../setup-grafana/configure-security/audit-grafana/#access-control" >}}). + +## Missing dashboard, folder or data source permissions + +[Dashboard and folder permissions]({{< relref "../../#dashboard-permissions" >}}) and [data source permissions]({{< relref "../../#data-source-permissions" >}}) can go out of sync if a Grafana instance version is upgraded, downgraded and then upgraded again. +This happens when an instance is downgraded from a version that uses RBAC to a version that uses the legacy access control, and dashboard, folder or data source permissions are updated. +These permission updates will not be applied to RBAC, so permissions will be out of sync when the instance is next upgraded to a version with RBAC. + +> **Note:** the steps provided below will set all dashboard, folder and data source permissions to what they are set to with the legacy access control. +> If you have made dashboard, folder or data source permission updates with RBAC enabled, these updates will be wiped. + +To resynchronize the permissions: + +1. make a backup of your database +1. run the following SQL queries + ```sql + DELETE + FROM builtin_role + where role_id IN (SELECT id + FROM role + WHERE name LIKE 'managed:%'); + DELETE + FROM team_role + where role_id IN (SELECT id + FROM role + WHERE name LIKE 'managed:%'); + DELETE + FROM user_role + where role_id IN (SELECT id + FROM role + WHERE name LIKE 'managed:%'); + DELETE + FROM permission + where role_id IN (SELECT id + FROM role + WHERE name LIKE 'managed:%'); + DELETE + FROM role + WHERE name LIKE 'managed:%'; + DELETE + FROM migration_log + WHERE migration_id IN ('teams permissions migration', + 'dashboard permissions', + 'dashboard permissions uid scopes', + 'data source permissions', + 'data source uid permissions', + 'managed permissions migration', + 'managed folder permissions alert actions repeated migration', + 'managed permissions migration enterprise'); + ``` +1. restart your Grafana instance diff --git a/docs/sources/administration/service-accounts/index.md b/docs/sources/administration/service-accounts/index.md index 21ca111affd..900d87ae63a 100644 --- a/docs/sources/administration/service-accounts/index.md +++ b/docs/sources/administration/service-accounts/index.md @@ -31,7 +31,8 @@ In [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) > **Note:** Service accounts can only act in the organization they are created for. If you have the same task that is needed for multiple organizations, we recommend creating service accounts in each organization. {{< vimeo 742056367 >}} -
+ +_Video shows service accounts in Grafana v9.1. Refer to [Create a service account in Grafana]({{< relref "#create-a-service-account-in-grafana" >}}) for current instructions._ ## Service account tokens diff --git a/docs/sources/administration/user-management/manage-dashboard-permissions/index.md b/docs/sources/administration/user-management/manage-dashboard-permissions/index.md index 47b17d4f9ac..fe2458c0c0c 100644 --- a/docs/sources/administration/user-management/manage-dashboard-permissions/index.md +++ b/docs/sources/administration/user-management/manage-dashboard-permissions/index.md @@ -8,13 +8,13 @@ weight: 500 # Manage dashboard permissions -Dashboard and dasboard folder permissions enable you to grant a viewer the ability to edit and save dashboard changes, or limit an editor's permission to modify a dashboard. +Dashboard and folder permissions enable you to grant a viewer the ability to edit and save dashboard changes, or limit an editor's permission to modify a dashboard. For more information about dashboard permissions, refer to [Dashboard permissions]({{< relref "../../roles-and-permissions/#dashboard-permissions" >}}). -## Grant dashboard folder permissions +## Grant folder permissions -When you grant user permissions for folders, that setting applies to all dashboards contained in the folder. Consider using this approach to assigning dashboard permissions when you have users or teams who require access to groups of related dashboards. +When you grant user permissions for folders, that setting applies to all dashboards and subfolders contained in the folder. Consider using this approach to assigning dashboard and folder permissions when you have users or teams who require access to groups of related dashboards or folders. ### Before you begin @@ -33,13 +33,13 @@ When you grant user permissions for folders, that setting applies to all dashboa ## Grant dashboard permissions -When you grant dashboard folder permissions, that setting applies to all dashboards in the folder. For a more granular approach to assigning permissions, you can also assign user permissions to individual dashboards. +When you grant folder permissions, that setting applies to all dashboards and subfolders in the folder. For a more granular approach to assigning permissions, you can also assign user permissions to individual dashboards. For example, if a user with the viewer organization role requires editor (or admin) access to a dashboard, you can assign those elevated permissions on an individual basis. > **Note**: If you have assigned a user dashboard folder permissions, you cannot also assign the user permission to dashboards contained in the folder. -Grant dashboard permissions when you want to restrict or enhance dashboard access for users who do not have permissions defined in the associated dashboard folder. +Grant dashboard permissions when you want to restrict or enhance dashboard access for users who do not have permissions defined in the associated folder. ### Before you begin @@ -102,7 +102,7 @@ Edit dashboard permissions when you are want to enhance or restrict a user's acc Grafana applies the highest permission a given user has to access a resource like a dashboard, so if you want to prevent a user from accessing a folder or dashboard you need to consider the user's organization role, folder permissions, and dashboard permissions. - You cannot override organization administrator permissions. Organization administrators have access to all organization resources. -- User permissions set for a dashboard folder propagate to dashboards contained in the folder. +- User permissions set for a folder propagate to all dashboards and subfolders contained in a folder. Permissions also cascade down to all dashboards and folders under the subfolders, and so on. - A lower permission level does not affect access if a more general rule exists with a higher permission. Refer to the following examples to understand how organization and dashboard permissions impact a user's access to dashboards. @@ -141,4 +141,14 @@ Dashboard permissions settings: Result: You receive an error message that cannot override a higher permission with a lower permission in the same dashboard. User1 has administrator permissions. +### Example 4 + +In this example, user1 has the viewer organization role. + +Folder permissions settings: + +- user1 is set to `edit`, which is inherited from the permissions set in grandparent folder + +Result: User1 has editor permissions for the folder because user1 has permissions on a folder that contains this folder. + > Refer to [Role-based access Control]({{< relref "../../roles-and-permissions/access-control/" >}}) in Grafana Enterprise to understand how to use RBAC permissions to restrict access to dashboards, folders, administrative functions, and other resources. diff --git a/docs/sources/alerting/_index.md b/docs/sources/alerting/_index.md index c7747d9dea4..a8d24318f7e 100644 --- a/docs/sources/alerting/_index.md +++ b/docs/sources/alerting/_index.md @@ -13,39 +13,21 @@ weight: 114 # Alerting -Grafana Alerting allows you to learn about problems in your systems moments after they occur. Create, manage, and take action on your alerts in a single, consolidated view, and improve your team’s ability to identify and resolve issues quickly. +Grafana Alerting allows you to learn about problems in your systems moments after they occur. + +Monitor your incoming metrics data or log entries and set up your Alerting system to watch for specific events or circumstances and then send notifications when those things are found. + +In this way, you eliminate the need for manual monitoring and provide a first line of defense against system outages or changes that could turn into major incidents. + +Using Grafana Alerting, you create queries and expressions from multiple data sources — no matter where your data is stored — giving you the flexibility to combine your data and alert on your metrics and logs in new and unique ways. You can then create, manage, and take action on your alerts from a single, consolidated view, and improve your team’s ability to identify and resolve issues quickly. Grafana Alerting is available for Grafana OSS, Grafana Enterprise, or Grafana Cloud. With Mimir and Loki alert rules you can run alert expressions closer to your data and at massive scale, all managed by the Grafana UI you are already familiar with. Watch this video to learn more about Grafana Alerting: {{< vimeo 720001629 >}} -## Overview +_Refer to [Manage your alert rules]({{< relref "../alerting/alerting-rules/" >}}) for current instructions._ -The following diagram gives you an overview of how Grafana Alerting works and introduces you to some of the key concepts that work together and form the core of our flexible and powerful alerting engine. - -{{< figure src="/static/img/docs/alerting/unified/about-alerting-flow-diagram-latest.png" caption="Grafana Alerting overview" >}} - -1. Alert rules - - Set evaluation criteria that determines whether an alert instance will fire. An alert rule consists of one or more queries and expressions, a condition, the frequency of evaluation, and optionally, the duration over which the condition is met. - - Grafana managed alerts support multi-dimensional alerting, which means that each alert rule can create multiple alert instances. This is exceptionally powerful if you are observing multiple series in a single expression. - - Once an alert rule has been created, they go through various states and transitions. The state and health of alert rules help you understand several key status indicators about your alerts. - -1. Labels - - Match an alert rule and its instances to notification policies and silences. They can also be used to group your alerts by severity. - -1. Notification policies - - Set where, when, and how the alerts get routed. Each notification policy specifies a set of label matchers to indicate which alerts they are responsible for. A notification policy has a contact point assigned to it that consists of one or more notifiers. - -1. Contact points - - Define how your contacts are notified when an alert fires. We support a multitude of ChatOps tools to ensure the alerts come to your team. - -## Features +## Key features and benefits **One page for all alerts** @@ -53,24 +35,55 @@ A single Grafana Alerting page consolidates both Grafana-managed alerts and aler **Multi-dimensional alerts** -Alert rules can create multiple individual alert instances per alert rule, known as multi-dimensional alerts, giving you the power and flexibility to gain visibility into your entire system with just a single alert. +Alert rules can create multiple individual alert instances per alert rule, known as multi-dimensional alerts, giving you the power and flexibility to gain visibility into your entire system with just a single alert rule. You do this by adding labels to your query to specify which component is being monitored and generate multiple alert instances for a single alert rule. For example, if you want to monitor each server in a cluster, a multi-dimensional alert will alert on each CPU, whereas a standard alert will alert on the overall server. -**Routing alerts** +**Route alerts** Route each alert instance to a specific contact point based on labels you define. Notification policies are the set of rules for where, when, and how the alerts are routed to contact points. -**Silencing alerts** +**Silence alerts** -Silences allow you to stop receiving persistent notifications from one or more alerting rules. You can also partially pause an alert based on certain criteria. Silences have their own dedicated section for better organization and visibility, so that you can scan your paused alert rules without cluttering the main alerting view. +Silences stop notifications from getting created and last for only a specified window of time. +Silences allow you to stop receiving persistent notifications from one or more alert rules. You can also partially pause an alert based on certain criteria. Silences have their own dedicated section for better organization and visibility, so that you can scan your paused alert rules without cluttering the main alerting view. **Mute timings** -With mute timings, you can specify a time interval when you don’t want new notifications to be generated or sent. You can also freeze alert notifications for recurring periods of time, such as during a maintenance period. +A mute timing is a recurring interval of time when no new notifications for a policy are generated or sent. Use them to prevent alerts from firing a specific and reoccurring period, for example, a regular maintenance period. + +Similar to silences, mute timings do not prevent alert rules from being evaluated, nor do they stop alert instances from being shown in the user interface. They only prevent notifications from being created. + +## Design your Alerting system + +Monitoring complex IT systems and understanding whether everything is up and running correctly is a difficult task. Setting up an effective alert management system is therefore essential to inform you when things are going wrong before they start to impact your business outcomes. + +Designing and configuring an alert management set up that works takes time. + +Here are some tips on how to create an effective alert management set up for your business: + +**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). + +**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. ## Useful links -- [Fundamental concepts]({{< relref "/docs/grafana/latest/alerting/fundamentals" >}}) of Grafana Alerting. - -- [Role-based access control]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control" >}}) in Grafana Enterprise. - -- [High availability]({{< relref "/docs/grafana/latest/alerting/fundamentals/high-availability" >}}) +- [Introduction to Alerting]({{< relref "/docs/grafana/latest/alerting/fundamentals" >}}) 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 5585cfb1c58..f789ff663eb 100644 --- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md @@ -8,53 +8,68 @@ keywords: - guide - rules - create -title: Create Grafana managed alert rules +title: Create Grafana-managed alert rules weight: 400 --- -# Create a Grafana managed alerting rule +# Create Grafana-managed alert rules -Grafana allows you to create alerting rules that query one or more data sources, reduce or transform the results and compare them to each other or to fixed thresholds. When these are executed, Grafana sends notifications to the contact point. For information on Grafana Alerting, see [About Grafana Alerting]({{< relref "../" >}}) which explains the various components of Grafana Alerting. We also recommend that you familiarize yourself with some of the [fundamental concepts]({{< relref "../fundamentals/" >}}) of Grafana Alerting. +Grafana-managed rules are the most flexible alert rule type. They allow you to create alerts that can act on data from any of our supported data sources. In addition to supporting multiple data sources, you can also add expressions to transform your data and set alert conditions. Using images in alert notifications is also supported. This is the only type of rule that allows alerting from multiple data sources in a single rule definition. + +Multiple alert instances can be created as a result of one alert rule (also known as a multi-dimensional alerting). + +For information on Grafana Alerting, see [Introduction to Grafana Alerting](/docs/grafana/next/alerting/fundamentals/), which explains the key concepts and features of Grafana Alerting. Watch this video to learn more about creating alerts: {{< vimeo 720001934 >}} -## Add Grafana managed rule +_Refer to [Add Grafana managed rule]({{< relref "#add-grafana-managed-rule" >}}) (following) for current instructions._ + +To create a Grafana-managed alert rule, complete the following steps. 1. In the left-side menu, click **Alerts & IRM** and then **Alerting**. -1. Click **Alert rules**. -1. Click **+ Create alert rule**. The new alerting rule page opens where the **Grafana managed alerts** option is selected by default. -1. In Step 1, add the rule name. +2. Click **Alert rules**. +3. Click **+ Create alert rule**. The new alert rule page opens where the **Grafana managed alerts** option is selected by default. +4. In Step 1, add the rule name. - In **Rule name**, add a descriptive name. This name is displayed in the alert rule list. It is also the `alertname` label for every alert instance that is created from this rule. -1. In Step 2, add queries and expressions to evaluate, and then select the alert condition. +5. In Step 2, add queries and expressions to evaluate, and then select the alert condition. + - For queries, select a data source from the dropdown. + - Specify a [time range](/docs/grafana/latest/dashboards/use-dashboards/?pg=blog&plcmt=body-txt#time-units-and-relative-ranges). + + **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`. + - Add one or more [queries]({{< relref "/docs/grafana/latest/panels-visualizations/query-transform-data#add-a-query" >}}) or [expressions]({{< relref "/docs/grafana/latest/panels-visualizations/query-transform-data/expression-queries" >}}). - For each expression, select either **Classic condition** to create a single alert rule, or choose from the **Math**, **Reduce**, and **Resample** options to generate separate alert for each series. For details on these options, see [Single and multi dimensional rule](#single-and-multi-dimensional-rule). - Click **Run queries** to verify that the query is successful. - Next, select the query or expression for your alert condition. -1. In Step 3, specify the alert evaluation interval. + +6. In Step 3, specify the alert evaluation interval. - From the **Condition** dropdown, select the query or expression to trigger the alert rule. - For **Evaluate every**, specify the frequency of evaluation. Must be a multiple of 10 seconds. For examples, `1m`, `30s`. - For **Evaluate for**, specify the duration for which the condition must be true before an alert fires. > **Note:** Once a condition is breached, the alert goes into the Pending state. If the condition remains breached for the duration specified, the alert transitions to the `Firing` state, otherwise it reverts back to the `Normal` state. - - In **Configure no data and error handling**, configure alerting behavior in the absence of data. Use the guidelines in [No data and error handling](#no-data-and-error-handling). + - In **Configure no data and error handling**, configure alerting behavior in the absence of data. Use the guidelines in [No data and error handling](#configure-no-data-and-error-handling). - Click **Preview** to check the result of running the query at this moment. Preview excludes no data and error handling. **Note:** You can pause alert rule evaluation to prevent noisy alerting while tuning your alerts. Pausing stops alert rule evaluation and does not create any alert instances. This is different to mute timings, which stop notifications from being delivered, but still allow for alert rule evaluation and the creation of alert instances. -1. In Step 4, add the storage location, rule group, as well as additional metadata associated with the rule. +7. In Step 4, add the storage location, rule group, as well as additional metadata associated with the rule. - From the **Folder** dropdown, select the folder where you want to store the rule. - For **Group**, specify a pre-defined group. Newly created rules are appended to the end of the group. Rules within a group are run sequentially at a regular interval, with the same evaluation time. - Add a description and summary to customize alert messages. Use the guidelines in [Annotations and labels for alerting]({{< relref "../fundamentals/annotation-label/" >}}). - Add Runbook URL, panel, dashboard, and alert IDs. -1. In Step 5, add custom labels. +8. In Step 5, add custom labels. - Add custom labels selecting existing key-value pairs from the drop down, or add new labels by entering the new key or value . -1. Click **Save** to save the rule or **Save and exit** to save the rule and go back to the Alerting page. -1. Next, create a for the rule. +9. Click **Save** to save the rule or **Save and exit** to save the rule and go back to the Alerting page. +10. Next, create a for the rule. -### Single and multi dimensional rule +### Single and multi-dimensional rule For Grafana managed alerts, you can create a rule with a classic condition or you can create a multi-dimensional rule. @@ -62,7 +77,9 @@ For Grafana managed alerts, you can create a rule with a classic condition or yo Use the classic condition expression to create a rule that triggers a single alert when its condition is met. For a query that returns multiple series, Grafana does not track the alert state of each series. As a result, Grafana sends only a single alert even when alert conditions are met for multiple series. -**Multi dimensional rule** +For more information, see [expressions documentation]({{< relref "/docs/grafana/latest/panels-visualizations/query-transform-data/expression-queries" >}}). + +**Multi-dimensional rule** To generate a separate alert for each series, create a multi-dimensional rule. Use `Math`, `Reduce`, or `Resample` expressions to create a multi-dimensional rule. For example: @@ -73,22 +90,30 @@ To generate a separate alert for each series, create a multi-dimensional rule. U > **Note:** Grafana does not support alert queries with template variables. More information is available at . -#### Rule with classic condition +### Configure no data and error handling -For more information, see [expressions documentation]({{< relref "/docs/grafana/latest/panels-visualizations/query-transform-data/expression-queries" >}}). +Configure alerting behavior when your alert rule evaluation returns no data or an error. -### No data and error handling +**Note:** Alert rules that are configured to fire when an evaluation returns no data or error only fire when the entire duration of the evaluation period has finished. This means that rather than immediately firing when the alert rule condition is breached, the alert rule waits until the time set as the **For** field has finished and then fires, reducing alert noise and allowing for temporary data availability issues. -Configure alerting behavior in the absence of data using information in the following tables. +If your alert rule evaluation returns no data, you can set the state on your alert rule to appear as follows: -| No Data Option | Description | -| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -| No Data | Create a new alert `DatasourceNoData` with the name and UID of the alert rule, and UID of the datasource that returned no data as labels. | -| Alerting | Set alert rule state to `Alerting`. This option will respect the configured **Evaluate for** pending period. | -| Ok | Set alert rule state to `Normal`. | +| No Data | Description | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| No Data | Creates a new alert `DatasourceNoData` with the name and UID of the alert rule, and UID of the datasource that returned no data as labels. | +| Alerting | Sets alert rule state to `Alerting`. The alert rule waits until the time set in the **For** field has finished before firing. | +| Ok | Sets alert rule state to `Normal`. | -| Error or timeout option | Description | -| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| Error | Create a new alert `DatasourceError` with the name and UID of the alert rule, and UID of the datasource that returned no data as labels. | -| Alerting | Set alert rule state to `Alerting`. This option will respect the configured **Evaluate for** pending period. | -| OK | Set alert rule state to `Normal` | +If your evaluation returns an error, you can set the state on your alert rule to appear as follows: + +| Error | Description | +| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| Error | Creates an alert instance `DatasourceError` with the name and UID of the alert rule, and UID of the datasource that returned no data as labels. | +| Alerting | Sets alert rule state to `Alerting`. The alert rule waits until the time set in the **For** field has finished before firing. | +| Ok | Sets alert rule state to `Normal`. | + +### Resolve stale alert instances + +An alert instance is considered stale if its dimension or series has disappeared from the query results entirely for two evaluation intervals. + +Stale alert instances that are in the **Alerting**/**NoData**/**Error** states are automatically marked as **Resolved** and the grafana_state_reason annotation is added to the alert instance with the reason **MissingSeries**. 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 5386189bb10..7a5943e28d8 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 @@ -30,6 +30,8 @@ Grafana allows you to create alerting rules for an external Grafana Mimir or Lok Watch this video to learn more about how to create a Mimir managed alert rule: {{< vimeo 720001865 >}} +_Refer to [Add a Grafana Mimir or Loki managed alerting rule]({{< relref "#add-a-grafana-mimir-or-loki-managed-alerting-rule" >}}) (following) for current instructions._ + > **Note:** If you do not want to manage alerting rules for a particular Loki or Prometheus data source, go to its settings and clear the **Manage alerts via Alerting UI** checkbox. ## Add a Grafana Mimir or Loki managed alerting rule diff --git a/docs/sources/alerting/alerting-rules/view-state-health.md b/docs/sources/alerting/alerting-rules/view-state-health.md index 44933c8b711..1368946c2b1 100644 --- a/docs/sources/alerting/alerting-rules/view-state-health.md +++ b/docs/sources/alerting/alerting-rules/view-state-health.md @@ -70,3 +70,56 @@ When evaluation of an alerting rule produces state `NoData` or `Error`, Grafana | **datasource_uid** | The UID of the data source that caused the state. | You can handle these alerts the same way as regular alerts by adding a silence, route to a contact point, and so on. + +## State history view + +Use the State history view to get insight into how your alert instances behave over time. View information on when a state change occurred, what the previous state was, the current state, any other alert instances that changed their state at the same time as well as what the query value was that triggered the change. + +### Configure the state history view + +**Note:** This applies to Open Source only. There is no configuration required if you are using Grafana Cloud. + +To enable the state history view, complete the following steps. + +1. Ensure you have a Loki instance running to save your history to. +1. Configure the following settings in your Grafana configuration: + + a. Enable the Loki backend and Loki remote URL. + + b. Enable the three feature toggles for alert state history. + +**Example:** + +``` +[unified_alerting.state_history] +enabled = true +backend = loki +loki_remote_url = http://localhost:3100 + +[feature_toggles] +enable = alertStateHistoryLokiSecondary, alertStateHistoryLokiPrimary, alertStateHistoryLokiOnly +``` + +### View state history + +To use the State history view, complete the following steps. + +1. Navigate to **Alerts&IRM** -> **Alerting** -> **Alert rules**. +1. Click an alert rule. +1. Select **Show state history**. + + The State history view opens. + + The timeline view at the top displays a timeline of changes for the past hour, so you can track how your alert instances are behaving over time. + + The bottom part shows the alert instances, their previous and current state, the value of each part of the expression and a unique set of labels. + + Common labels are displayed at the top to make it easier to identify different alert instances. + +1. From the timeline view, hover over a time to get an automatic display of all the changes that happened at that particular moment. + + These changes are displayed in real time in the timestamp view at the bottom of the page. The timestamp view is a list of all the alert instances that changed state at that point in time. The visualization only displays 12 instances by default. + + The value shown for each instance is for each part of the expression that was evaluated. + +1. Click the labels to filter and narrow down the results. diff --git a/docs/sources/alerting/fundamentals/_index.md b/docs/sources/alerting/fundamentals/_index.md index e2015a7d441..0fd28e28610 100644 --- a/docs/sources/alerting/fundamentals/_index.md +++ b/docs/sources/alerting/fundamentals/_index.md @@ -10,42 +10,40 @@ weight: 105 Whether you’re starting or expanding your implementation of Grafana Alerting, learn more about the key concepts and available features that help you create, manage, and take action on your alerts and improve your team’s ability to resolve issues quickly. -First of all, let’s look at the different alert rule types that Grafana Alerting offers. +The following diagram gives you an overview of how Grafana Alerting works and introduces you to some of the key concepts that work together and form the core of our flexible and powerful alerting engine. -## Alert rule types +{{< figure src="/media/docs/alerting/how-alerting-works.png" max-width="750px" caption="How Alerting works" >}} -### Grafana-managed rules +You can either create your alerting resources (alert rules, notification policies, and so on) directly in the Grafana UI, using provisioning, or in your Grafana Mimir or Loki instances. -Grafana-managed rules are the most flexible alert rule type. They allow you to create alerts that can act on data from any of our supported data sources. -In addition to supporting multiple data sources, you can also add expressions to transform your data and set alert conditions. -This is the only type of rule that allows alerting from multiple data sources in a single rule definition. +**Alert rules** -### Mimir and Loki rules +An alert rule is a set of evaluation criteria for when an alert rule should fire. An alert rule consists of one or more queries and expressions, a condition, and the duration over which the condition needs to be met to start firing. -To create Mimir or Loki alerts you must have a compatible Prometheus or Loki data source. You can check if your data source supports rule creation via Grafana by testing the data source and observing if the ruler API is supported. +Add annotations to your alert rule to provide additional information about the alert rule and add labels to uniquely identify your alert rule and configure alert routing. Labels link alert rules to notification policies, so you can easily manage which policy should handle which alerts and who gets notified. -### Recording rules +Once alert rules are created, they go through various states and transitions. An alert rule can produce multiple alert instances - one alert instance for each time series. -Recording rules are only available for compatible Prometheus or Loki data sources. -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. -Grafana Enterprise offers an alternative to recorded rules in the form of [recorded queries](https://grafana.com/docs/grafana/v9.0/enterprise/recorded-queries/) that can be executed against any data source. +The alert rule state is determined by the “worst case” state of the alert instances produced and the states can be Normal, Pending, or Firing. For example, if one alert instance is firing, the alert rule state will also be firing. -## Key concepts and features +The alert rule health is determined by the status of the evaluation of the alert rule, which can be Ok, Error, and NoData. -The following table includes a list of key concepts, features and their definitions, designed to help you make the most of Grafana Alerting. +**Alert instances** -| Key concept or feature | Definition | -| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Data sources for Alerting | Select data sources you want to query and visualize metrics, logs and traces from. | -| Provisioning for Alerting | Manage your alerting resources and provision them into your Grafana system using file provisioning or Terraform. | -| Scheduler | Evaluates your alert rules; think of it as the component that periodically runs your query against data sources. It is only applicable to Grafana-managed rules. | -| Alertmanager | Manages the routing and grouping of alert instances. | -| Alert rule | A set of evaluation criteria for when an alert rule should fire. An alert rule consists of one or more queries and expressions, a condition, the frequency of evaluation, and the duration over which the condition is met. An alert rule can produce multiple alert instances. | -| Alert instance | An alert instance is created when an alert rule fires. An alert rule can create one or more alert instances. When multiple instances are created as a result of one alert rule, this is referred to as a multi-dimensional alert. | -| Alert group | The Alertmanager groups alert instances by default using the labels for the root notification policy. This controls de-duplication and groups of alert instances which are sent to contact points. | -| Contact point | Define how your contacts are notified when an alert rule fires. | -| Message templating | Create reusable custom templates and use them in contact points. | -| Notification policy | Set of rules for where, when, and how the alerts are grouped and routed to contact points. | -| Labels and label matchers | Labels uniquely identify alert rules. They link alert rules to notification policies and silences, determining which policy should handle them and which alert rules should be silenced. | -| Silences | Stop notifications from one or more alert instances. The difference between a silence and a mute timing is that a silence only lasts for only a specified window of time whereas a mute timing is meant to be recurring on a schedule. Uses label matchers to silence alert instances. | -| Mute timings | Specify a time interval when you don’t want new notifications to be generated or sent. You can also freeze alert notifications for recurring periods of time, such as during a maintenance period. Must be linked to an existing notification policy. | +For Grafana-managed alert rules, multiple alert instances can be created as a result of one alert rule (also known as a multi-dimensional alerting). + +Both Grafana-managed alert and Mimir or Loki-managed alert instances can be in Normal, Pending, Alerting, No Data, Error states. + +**Note:** For Mimir or Loki-managed alert rules, alert instances are only created when the threshold condition defined in an alert rule is breached. + +Alerting alert instances are grouped by labels according to the notification policy. This controls de-duplication and groups alert instances to send to your contact points. + +**Notification policy** + +Set where, when, and how firing alert instances get routed. + +Each notification policy contains a set of label matchers to indicate which alerts rules or instances it is responsible for. It also has a contact point assigned to it that consists of one or more contact point types, such as Slack or email. Contact points define how your contacts are notified when an alert instance fires. + +Use message templates for your notifications to create reusable custom templates and use them in contact points. + +Add silences to stop notifications from one or more alert instances or use mute timings to specify time intervals when you don’t want new notifications to be generated or sent out. The difference between the two being that a silence only lasts for only a specified window of time whereas a mute timing recurs on a schedule, for example, during a maintenance period. diff --git a/docs/sources/alerting/fundamentals/alert-rules/_index.md b/docs/sources/alerting/fundamentals/alert-rules/_index.md index 79271341ddf..a9739504407 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rules/_index.md @@ -8,9 +8,9 @@ title: Alert rules weight: 101 --- -# About alert rules +# Alert rules -An alerting rule is a set of evaluation criteria that determines whether an alert instance will fire. The rule consists of one or more queries and expressions, a condition, the frequency of evaluation, and optionally, the duration over which the condition is met. +An alert rule is a set of evaluation criteria for when an alert rule should fire. An alert rule consists of one or more queries and expressions, a condition, and the duration over which the condition needs to be met to start firing. While queries and expressions select the data set to evaluate, a condition sets the threshold that an alert must meet or exceed to create an alert. diff --git a/docs/sources/alerting/fundamentals/alert-rules/alert-rule-types.md b/docs/sources/alerting/fundamentals/alert-rules/alert-rule-types.md index 4d5e94d2e11..92e09d34ab7 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/alert-rule-types.md +++ b/docs/sources/alerting/fundamentals/alert-rules/alert-rule-types.md @@ -10,24 +10,72 @@ weight: 102 # Alert rule types -Grafana supports several alert rule types, the following sections will explain their merits and demerits and help you choose the right alert type for your use case. +Grafana supports several different alert rule types. Learn more about each of the alert rule types, how they work, and decide which one is best for your use case. -## Grafana managed rules +## Grafana-managed alert rules -Grafana-managed rules are the most flexible alert rule type. They allow you to create alerts that can act on data from any of your existing data sources. +Grafana-managed alert rules are the most flexible alert rule type. They allow you to create alerts that can act on data from any of our supported data sources. -In addition to supporting any data source, you can add [expressions]({{< relref "/docs/grafana/latest/panels-visualizations/query-transform-data/expression-queries" >}}) to transform your data and express alert conditions. +In addition to supporting multiple data sources, you can also add expressions to transform your data and set alert conditions. Using images in alert notifications is also supported. This is the only type of rule that allows alerting from multiple data sources in a single rule definition. -## Mimir, Loki and Cortex rules +The following diagram shows how Grafana-managed alerting works. -To create Mimir, Loki or Cortex alerts you must have a compatible Prometheus data source. You can check if your data source is compatible by testing the data source and checking the details if the ruler API is supported. +{{< figure src="/media/docs/alerting/grafana-managed-rule.png" max-width="750px" caption="Grafana-managed alerting" >}} -{{< figure src="/static/img/docs/alerting/unified/mimir-datasource-check.png" caption="Successfully connected to a Mimir Prometheus datasource" max-width="40%" >}} +1. Alert rules are created within Grafana based on one or more data sources. + +1. Alert rules are evaluated by the Alert Rule Evaluation Engine from within Grafana. + +1. Alerts are delivered using the internal Grafana Alertmanager. + +**Note:** + +You can also configure alerts to be delivered using an external Alertmanager; or use both internal and external alertmanagers. +For more information, see Add an external Alertmanager. + +## Grafana Mimir or Loki-managed alert rules + +To create Grafana Mimir or Grafana Loki-managed alert rules, you must have a compatible Prometheus or Loki data source. + +You can check if your data source supports rule creation via Grafana by testing the data source and observing if the Ruler API is supported. + +For more information on the Ruler API, refer to [Ruler API](docs/loki/latest/api/#ruler). + +The following diagram shows how Grafana Mimir or Grafana Loki-managed alerting works. + +{{< figure src="/media/docs/alerting/loki-mimir-rule.png" max-width="750px" caption="Grafana Mimir/Loki-managed alerting" >}} + +1. Alert rules are created and stored within the data source itself. +1. Alert rules can only be created based on Prometheus data. +1. Alert rule evaluation and delivery is distributed across multiple nodes for high availability and fault tolerance. ## Recording rules -Recording rules are only available for compatible Prometheus data sources like Mimir, Loki and Cortex. +Recording rules are only available for compatible Prometheus or Loki data sources. -A recording rule allows you to save an expression's result to 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 the same expression repeatedly. +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. -Read more about [recording rules](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/) in Prometheus. +Grafana Enterprise offers an alternative to recorded rules in the form of recorded queries that can be executed against any data source. + +For more information on recording rules in Prometheus, refer to [recording rules](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/). + +## Choose an alert rule type + +When choosing which alert rule type to use, consider the following comparison between Grafana-managed alert rules and Grafana Mimir or Loki alert rules. + +{{< responsive-table >}} +|
Feature
|
Grafana-managed alert rule
|
Loki/Mimir-managed alert rule | +| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Create alert rules based on data from any of our supported data sources | Yes | No: You can only create alert rules that are based on Prometheus data. The data source must have the Ruler API enabled. | +| Mix and match data sources | Yes | No | +| Includes support for recording rules | No | Yes | +| Add expressions to transform your data and set alert conditions | Yes | No | +| Use images in alert notifications | Yes | No | +| 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. | + +{{< /responsive-table >}} + +**Note:** + +If you are using non-Prometheus data, we recommend choosing Grafana-managed alert rules. Otherwise, choose Grafana Mimir or Grafana Loki alert rules where possible. diff --git a/docs/sources/alerting/fundamentals/annotation-label/variables-label-annotation.md b/docs/sources/alerting/fundamentals/annotation-label/variables-label-annotation.md index 7044712eb47..8d935e91865 100644 --- a/docs/sources/alerting/fundamentals/annotation-label/variables-label-annotation.md +++ b/docs/sources/alerting/fundamentals/annotation-label/variables-label-annotation.md @@ -122,6 +122,14 @@ If the rule uses Classic Conditions instead of Threshold, Reduce and Math expres The first condition is {{ $values.B0 }}, and the second condition is {{ $values.B1 }} ``` +With classic conditions, labels from the query are not available in `$labels` variable, because single alert instance are generated. Instead, you can retrieve the labels from the `$values` variable. + +``` +{{ range $k, $v := $values }} +The value is {{ $v }} and the labels are {{ $v.Labels }} +{{ end }} +``` + ## Functions The following functions are also available when expanding labels and annotations: diff --git a/docs/sources/alerting/fundamentals/contact-points/index.md b/docs/sources/alerting/fundamentals/contact-points/index.md index 63a8a8f858c..4d8a6e955c0 100644 --- a/docs/sources/alerting/fundamentals/contact-points/index.md +++ b/docs/sources/alerting/fundamentals/contact-points/index.md @@ -54,6 +54,6 @@ The following table lists the contact point integrations supported by Grafana. ## Useful links -[Manage contact points](/docs/grafana/next/alerting/manage-notifications/create-contact-point/) +[Manage contact points](/docs/grafana/latest/alerting/manage-notifications/manage-contact-points/) -[Create and edit notification templates](/docs/grafana/next/alerting/manage-notifications/create-message-template/) +[Create and edit notification templates](/docs/grafana/latest/alerting/manage-notifications/template-notifications/create-notification-templates/) diff --git a/docs/sources/alerting/manage-notifications/manage-contact-points/configure-integrations.md b/docs/sources/alerting/manage-notifications/manage-contact-points/configure-integrations.md index e462c5d20c9..8c28bfda42e 100644 --- a/docs/sources/alerting/manage-notifications/manage-contact-points/configure-integrations.md +++ b/docs/sources/alerting/manage-notifications/manage-contact-points/configure-integrations.md @@ -1,6 +1,5 @@ --- description: Configure integrations -draft: true keywords: - Grafana - alerting @@ -14,7 +13,7 @@ weight: 100 # Configure integrations -Configure integrations in Grafana to select your preferred communication channel for receiving notifications when your alert rules are fire. Each integration has its own configuration options and setup process. In most cases, this involves providing an API key or a Webhook URL. +Configure integrations in Grafana to select your preferred communication channel for receiving notifications when your alert rules are firing. Each integration has its own configuration options and setup process. In most cases, this involves providing an API key or a Webhook URL. Once configured, you can use integrations as part of your contact points to receive notifications whenever your alert changes its state. In this section, we'll cover the basic steps to configure your integrations, so you can start receiving real-time alerts and stay on top of your monitoring data. diff --git a/docs/sources/alerting/manage-notifications/template-notifications/create-notification-templates.md b/docs/sources/alerting/manage-notifications/template-notifications/create-notification-templates.md index b4aee1d832f..6df29633643 100644 --- a/docs/sources/alerting/manage-notifications/template-notifications/create-notification-templates.md +++ b/docs/sources/alerting/manage-notifications/template-notifications/create-notification-templates.md @@ -32,7 +32,7 @@ To create a template, complete the following steps. {{< figure max-width="940px" src="/static/img/docs/alerting/unified/new-notification-template-email-subject-9-4.png" caption="New notification template email.subject" >}} -4. Click Save. +4. Click **Save**. `{{ define "email.subject" }}` and `{{ end }}` is automatically added to the start and end of the content: @@ -48,7 +48,43 @@ To create a notification template that contains more than one template: {{< figure max-width="940px" src="/static/img/docs/alerting/unified/new-notification-template-email-9-4.png" caption="New notification template" >}} -4. Click Save. +4. Click **Save**. + +## Preview notification templates + +Preview how your notification templates will 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**. +1. Click **+Add 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 **Choose alert instances** or **Add alert data**. + +1. [Optional] To add alert data from existing alert instances: + + a. Click **Choose 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 alert 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 to payload**. + + d. Click **Refresh preview** to see what your template content will 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. Click **Save.** ## Template the subject of an email diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/_index.md b/docs/sources/alerting/set-up/provision-alerting-resources/_index.md index 1c2e5fd6787..854c4094dd1 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/_index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/_index.md @@ -24,22 +24,21 @@ There are three options to choose from: For more information on the Alerting Provisioning HTTP API, refer to [Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/). - **Note:** - - Typically, you cannot edit API-provisioned alert rules from the Grafana UI. - - In order to enable editing, add the x-disable-provenance header to the following requests when creating or editing your alert rules in the API: - - POST /api/v1/provisioning/alert-rules - - PUT /api/v1/provisioning/alert-rules/{UID} - 1. Provision your alerting resources using Terraform. **Note:** Currently, provisioning for Grafana Alerting supports alert rules, contact points, mute timings, and templates. Provisioned alerting resources using file provisioning or Terraform can only be edited in the source that created them and not from within Grafana or any other source. For example, if you provision your alerting resources using files from disk, you cannot edit the data in Terraform or from within Grafana. +To allow editing of provisioned resources in the Grafana UI, add the `X-Disable-Provenance` header to the following requests in the API: + +- `POST /api/v1/provisioning/alert-rules` +- `PUT /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}` (calling this endpoint will change provenance for all alert rules within the alert group) +- `POST /api/v1/provisioning/contact-points` +- `POST /api/v1/provisioning/mute-timings` +- `PUT /api/v1/provisioning/policies` +- `PUT /api/v1/provisioning/templates/{name}` + **Useful Links:** [Grafana provisioning](/docs/grafana/latest/administration/provisioning/) 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 9abfaf4cc94..d5234e6bc3f 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 @@ -548,7 +548,8 @@ policies: repeat_interval: 4h # Zero or more child routes # routes: - # ... + # - Another recursively nested policy... + # ... ``` Here is an example of a configuration file for resetting notification policies. diff --git a/docs/sources/cli.md b/docs/sources/cli.md index 3e030da5fc1..622b72906cd 100644 --- a/docs/sources/cli.md +++ b/docs/sources/cli.md @@ -1,11 +1,11 @@ --- aliases: - administration/cli/ -description: Guide to using grafana-cli +description: Guide to using grafana cli keywords: - grafana - cli - - grafana-cli + - grafana cli - command line interface title: Grafana CLI weight: 400 @@ -18,12 +18,12 @@ Grafana CLI is a small executable that is bundled with Grafana server. It can be To list all commands and options: ``` -grafana-cli -h +grafana cli -h ``` ## Invoking Grafana CLI -To invoke Grafana CLI, add the path to the grafana binaries in your `PATH` environment variable. Alternately, if your current directory is the `bin` directory, use `./grafana-cli`. Otherwise, you can specify full path to the CLI. For example, on Linux `/usr/share/grafana/bin/grafana-cli` and on Windows `C:\Program Files\GrafanaLabs\grafana\bin\grafana-cli.exe`. +To invoke Grafana CLI, add the path to the grafana binaries in your `PATH` environment variable. Alternately, if your current directory is the `bin` directory, use `./grafana cli`. Otherwise, you can specify full path to the CLI. For example, on Linux `/usr/share/grafana/bin/grafana` and on Windows `C:\Program Files\GrafanaLabs\grafana\bin\grafana.exe`, and invoke it with `grafana cli`. > **Note:** Some commands, such as installing or removing plugins, require `sudo` on Linux. If you are on Windows, run Windows PowerShell as Administrator. @@ -32,7 +32,7 @@ To invoke Grafana CLI, add the path to the grafana binaries in your `PATH` envir The general syntax for commands in Grafana CLI is: ```bash -grafana-cli [global options] command [command options] [arguments...] +grafana cli [global options] command [command options] [arguments...] ``` ## Global options @@ -48,7 +48,7 @@ Each global option applies only to the command in which it is used. For example, **Example:** ```bash -grafana-cli -h +grafana cli -h ``` ### Display Grafana CLI version @@ -58,7 +58,7 @@ grafana-cli -h **Example:** ```bash -grafana-cli -v +grafana cli -v ``` ### Override default plugin directory @@ -68,7 +68,7 @@ grafana-cli -v **Example:** ```bash -grafana-cli --pluginsDir "/var/lib/grafana/devplugins" plugins install +grafana cli --pluginsDir "/var/lib/grafana/devplugins" plugins install ``` ### Override default plugin repo URL @@ -78,7 +78,7 @@ grafana-cli --pluginsDir "/var/lib/grafana/devplugins" plugins install +grafana cli --repo "https://example.com/plugins" plugins install ``` ### Override default plugin .zip URL @@ -88,7 +88,7 @@ grafana-cli --repo "https://example.com/plugins" plugins install **Example:** ```bash -grafana-cli --pluginUrl https://company.com/grafana/plugins/-.zip plugins install +grafana cli --pluginUrl https://company.com/grafana/plugins/-.zip plugins install ``` ### Override Transport Layer Security @@ -100,7 +100,7 @@ grafana-cli --pluginUrl https://company.com/grafana/plugins/--.zip plugins install +grafana cli --insecure --pluginUrl https://company.com/grafana/plugins/-.zip plugins install ``` ### Enable debug logging @@ -110,7 +110,7 @@ grafana-cli --insecure --pluginUrl https://company.com/grafana/plugins/ +grafana cli --debug plugins install ``` ### Override a configuration setting @@ -122,7 +122,7 @@ For example, you can use it to redirect logging to another file (maybe to log pl **Example:** ```bash -grafana-cli --configOverrides cfg:default.paths.log=/dev/null plugins install +grafana cli --configOverrides cfg:default.paths.log=/dev/null plugins install ``` ### Override homepath value @@ -132,7 +132,7 @@ Sets the path for the Grafana install/home path, defaults to working directory. **Example:** ```bash -grafana-cli --homepath "/usr/share/grafana" admin reset-admin-password +grafana cli --homepath "/usr/share/grafana" admin reset-admin-password ``` ### Override config file @@ -142,7 +142,7 @@ grafana-cli --homepath "/usr/share/grafana" admin reset-admin-password +grafana cli plugins install ``` ### Install a specific version of a plugin ```bash -grafana-cli plugins install +grafana cli plugins install ``` ### List installed plugins ```bash -grafana-cli plugins ls +grafana cli plugins ls ``` ### Update all installed plugins ```bash -grafana-cli plugins update-all +grafana cli plugins update-all ``` ### Update one plugin ```bash -grafana-cli plugins update +grafana cli plugins update ``` ### Remove one plugin ```bash -grafana-cli plugins remove +grafana cli plugins remove ``` ## Admin commands @@ -200,12 +200,12 @@ Admin commands are only available in Grafana 4.1 and later. ### Show all admin commands ```bash -grafana-cli admin +grafana cli admin ``` ### Reset admin password -`grafana-cli admin reset-admin-password ` resets the password for the admin user using the CLI. You might need to do this if you lose the admin password. +`grafana cli admin reset-admin-password ` resets the password for the admin user using the CLI. You might need to do this if you lose the admin password. If there are two flags being used to set the homepath and the config file path, then running the command returns this error: @@ -214,7 +214,7 @@ If there are two flags being used to set the homepath and the config file path, To correct this, use the `--homepath` global option to specify the Grafana default homepath for this command: ```bash -grafana-cli --homepath "/usr/share/grafana" admin reset-admin-password +grafana cli --homepath "/usr/share/grafana" admin reset-admin-password ``` If you have not lost the admin password, we recommend that you change the user password either in the User Preferences or in the Server Admin > User tab. @@ -230,5 +230,5 @@ If you need to set the password in a script, then you can use the [Grafana User **Example:** ```bash -grafana-cli admin data-migration encrypt-datasource-passwords +grafana cli admin data-migration encrypt-datasource-passwords ``` diff --git a/docs/sources/dashboards/assess-dashboard-usage/index.md b/docs/sources/dashboards/assess-dashboard-usage/index.md index 4c33d059026..af2bca8d257 100644 --- a/docs/sources/dashboards/assess-dashboard-usage/index.md +++ b/docs/sources/dashboards/assess-dashboard-usage/index.md @@ -43,11 +43,9 @@ For every dashboard and data source, you can access usage information. ### Dashboard insights -> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +To see dashboard usage information, click the dashboard insights icon in the header. -To see dashboard usage information, click **Dashboard insights** in the top bar. - -{{< figure src="/static/img/docs/enterprise/dashboard_insights_button.png" max-width="400px" class="docs-image--no-shadow" >}} +{{< figure src="/media/docs/grafana/dashboards/screenshot-dashboard-insights.png" max-width="400px" class="docs-image--no-shadow" >}} Dashboard insights show the following information: @@ -56,9 +54,13 @@ Dashboard insights show the following information: {{< figure src="/static/img/docs/enterprise/dashboard_insights_stats.png" max-width="400px" class="docs-image--no-shadow" >}}{{< figure src="/static/img/docs/enterprise/dashboard_insights_users.png" max-width="400px" class="docs-image--no-shadow" >}} -### Data source insights +{{% admonition type="Note" %}} -> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +If you've enabled the `publicDashboards` feature toggle, you'll also see a Public dashboards tab in your analytics. + +{{% /admonition %}} + +### Data source insights Data source insights provides information about how a data source has been used in the past 30 days, such as: @@ -68,17 +70,16 @@ Data source insights provides information about how a data source has been used To find data source insights: -1. Go to the Data source list view. -1. Click on a data source. +1. Click **Connections** in the main navigation. +1. Under Your connections, click **Data sources**. +1. Click a data source. 1. Click the **Insights** tab. -{{< figure src="/static/img/docs/enterprise/datasource_insights.png" max-width="650px" class="docs-image--no-shadow" >}} +{{< figure src="/media/docs/grafana/dashboards/screenshot-data-source-insights-9.5.png" max-width="650px" class="docs-image--no-shadow" >}} ## Presence indicator -> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). - -When you are signed in and looking at a dashboard, you can know who is looking at the same dashboard as you are via a presence indicator, which displays avatars of users who have recently interacted with the dashboard. The default timeframe is 10 minutes. To see the user's name, hover over the user's avatar. The avatars come from [Gravatar](https://gravatar.com) based on the user's email. +When you are signed in and looking at a dashboard, you can know who is looking at the same dashboard as you are via a presence indicator, which displays avatars of users who have recently interacted with the dashboard. The default time frame is 10 minutes. To see the user's name, hover over the user's avatar. The avatars come from [Gravatar](https://gravatar.com) based on the user's email. When there are more active users on a dashboard than can fit within the presence indicator, click the **+X** icon. Doing so opens [dashboard insights]({{< relref "#dashboard-and-data-source-insights" >}}), which contains more details about recent user activity. @@ -94,23 +95,19 @@ recent_users_age = 10m ## Sort dashboards by using insights data -> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). - -In the search view, you can use insights data to help you find most-used, broken, and unused dashbaords. +In the search view, you can use insights data to help you find most-used, broken, and unused dashboards. You can sort the dashboards by: - Errors total -- Errors 30 days +- Errors 30 days (most and least) - Views total -- Views 30 days +- Views 30 days (most and least) -{{< figure src="/static/img/docs/enterprise/improved-search-7-5.png" max-width="650px" class="docs-image--no-shadow" >}} +{{< figure src="/media/docs/grafana/dashboards/screenshot-dashboard-sort-9.5.png" max-width="650px" class="docs-image--no-shadow" >}} ## Visualize usage insights data -> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). - If you set up your installation to [export logs of usage insights]({{< relref "../../setup-grafana/configure-security/export-logs/" >}}), we've created two dashboards to help you take advantage of this data. 1. [Usage Insights overview](/grafana/dashboards/13785) provides a top-level perspective of user activity. diff --git a/docs/sources/dashboards/build-dashboards/create-dashboard/index.md b/docs/sources/dashboards/build-dashboards/create-dashboard/index.md index ebefd198ad4..f1cb628faf9 100644 --- a/docs/sources/dashboards/build-dashboards/create-dashboard/index.md +++ b/docs/sources/dashboards/build-dashboards/create-dashboard/index.md @@ -26,11 +26,18 @@ Dashboards and panels allow you to show your data in visual form. Each panel nee 1. Click **Dashboards** in the left-side menu. 1. Click **New** and select **New Dashboard**. 1. On the empty dashboard, click **+ Add visualization**. + + ![Empty dashboard state](/media/docs/grafana/dashboards/empty-dashboard-9.5.png) + 1. In the first line of the **Query** tab, click the dropdown list and select a data source. 1. Write or construct a query in the query language of your data source. For more information about data sources, refer to [Data sources]({{< relref "../../../datasources/" >}}) for specific guidelines. +1. Click the Refresh dashboard icon to query the data source. + + ![Refresh dashboard icon](/media/docs/grafana/dashboards/screenshot-refresh-dashboard-9.5.png) + 1. In the visualization list, select a visualization type. ![Visualization selector](/media/docs/grafana/dashboards/screenshot-select-visualization-9-5.png) @@ -49,9 +56,15 @@ Dashboards and panels allow you to show your data in visual form. Each panel nee - [Configure thresholds]({{< relref "../../../panels-visualizations/configure-thresholds/" >}}) - [Configure standard options]({{< relref "../../../panels-visualizations/configure-standard-options/" >}}) -1. When you've finished editing your panel, click **Save** in the top right corner. +1. When you've finished editing your panel, click **Save** to save the dashboard. + + Alternatively, click **Apply** if you want to see your changes applied to the dashboard first. Then click the save icon in the dashboard header. + 1. Enter a name for your dashboard and select a folder, if applicable. 1. Click **Save**. +1. To add more panels to the dashboard, click **Add** in the dashboard header and select **Visualization** in the dropdown. + + ![Add dropdown](/media/docs/grafana/dashboards/screenshot-add-dropdown-9.5.png) ## Configure repeating rows diff --git a/docs/sources/dashboards/build-dashboards/modify-dashboard-settings/index.md b/docs/sources/dashboards/build-dashboards/modify-dashboard-settings/index.md index e748c204f4b..6532b102e68 100644 --- a/docs/sources/dashboards/build-dashboards/modify-dashboard-settings/index.md +++ b/docs/sources/dashboards/build-dashboards/modify-dashboard-settings/index.md @@ -1,6 +1,6 @@ --- title: Modify dashboard settings -menuTitle: Dashboard settings +menuTitle: Modify dashboard settings weight: 8 keywords: - time settings @@ -12,7 +12,7 @@ keywords: # Modify dashboard settings -The dashboard settings page enables you to: +The dashboard settings page allows you to: - Edit general dashboard properties, including time settings - Add annotation queries @@ -31,7 +31,7 @@ Adjust dashboard time settings when you want to change the dashboard timezone, t 1. On the **Dashboard settings** page, click **General**. 1. Navigate to the **Time Options** section. -1. Specify time settings according to the following descriptions. +1. Specify time settings as follows. - **Timezone:** Specify the local time zone of the service or system that you are monitoring. This can be helpful when monitoring a system or service that operates across several time zones. - **Default:** Grafana uses the default selected time zone for the user profile, team, or organization. If no time zone is specified for the user profile, a team the user is a member of, or the organization, then Grafana uses the local browser time. @@ -62,7 +62,7 @@ the dashboard. These dropdowns make it easy to change the data being displayed i For more information about variables, refer to [Variables]({{< relref "../../variables/" >}}). 1. On the **Dashboard settings** page, click **Variable** in the left side section menu and then the **Add variable** button. -1. In the **General** section, the the name of the variable. This is the name that you will later use in queries. +1. In the **General** section, the name of the variable. This is the name that you will later use in queries. 1. Select a variable **Type**. > **Note:** The variable type you select impacts which fields you populate on the page. @@ -74,7 +74,7 @@ For more information about variables, refer to [Variables]({{< relref "../../var Dashboard links enable you to place links to other dashboards and web sites directly below the dashboard header. Links provide for easy navigation to other, related dashboards and content. 1. On the **Dashboard settings** page, click **Links** in the left side section menu and then the **Add link** button. -1. Enter title and and in the **Type** field, select **Dashboard** or **Link**. +1. Enter title and in the **Type** field, select **Dashboard** or **Link**. 1. To add a dashboard link: a. Add an optional tag. Tags are useful creating a dynamic dropdown of dashboards that all have a specific tag. b. Select any of the dashboard link **Options**. diff --git a/docs/sources/dashboards/create-reports/index.md b/docs/sources/dashboards/create-reports/index.md index 3d11ae5e0cc..b278126de6a 100644 --- a/docs/sources/dashboards/create-reports/index.md +++ b/docs/sources/dashboards/create-reports/index.md @@ -43,10 +43,11 @@ When [RBAC]({{< relref "../../administration/roles-and-permissions/access-contro Only organization administrators can create reports by default. You can customize who can create reports with [Role-based access control]({{< relref "../../administration/roles-and-permissions/access-control/" >}}). -1. Click on the Reports icon in the side navigation menu. +1. Click **Dashboards > Reports** in the side navigation menu. - The Reports tab allows you to view, create, and update your reports. The report form has a multi-step layout. The steps do not need to be completed in succession and can be skipped over by clicking a step name. + The Reports page allows you to view, create, and update your reports. The report form has a multi-step layout. The steps do not need to be completed in succession and can be skipped over by clicking a step name. +1. Click **+ Create a new report**. 1. Select report dashboard. - **Source dashboard:** Select the dashboard from which you want to generate the report. - **Time range:** (optional) Use custom time range for the report. For more information, refer to [Report time range]({{< relref "#report-time-range" >}}). @@ -183,7 +184,7 @@ You can generate and save PDF files of any dashboard. > **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 6.7 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). -1. In the upper-right corner of the dashboard that you want to export as PDF, click the **Share dashboard** icon. +1. In the dashboard that you want to export as PDF, click the **Share dashboard** icon. 1. On the PDF tab, select a layout option for the exported dashboard: **Portrait** or **Landscape**. 1. Click **Save as PDF** to render the dashboard as a PDF file. @@ -230,7 +231,7 @@ font_italic = DejaVuSansCondensed-Oblique.ttf > **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.2 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). -You can configure organization-wide report settings in the **Settings** tab on the **Reporting** page. Settings are applied to all the reports for current organization. +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. diff --git a/docs/sources/dashboards/dashboard-public/index.md b/docs/sources/dashboards/dashboard-public/index.md index d6b85e8ea1b..60095c05e30 100644 --- a/docs/sources/dashboards/dashboard-public/index.md +++ b/docs/sources/dashboards/dashboard-public/index.md @@ -8,19 +8,28 @@ weight: 8 # Public dashboards -> **Note:** This is an opt-in alpha feature. +{{% admonition type="Note" %}} -> **Caution:** Making your dashboard public could result in a large number of queries to the datasources used by your dashboard. -> This can be mitigated by utilizing the enterprise [caching](https://grafana.com/docs/grafana/latest/enterprise/query-caching/) and/or rate limiting features. +This feature is in [public preview](/docs/release-life-cycle/). -Public dashboards allow you to share your Grafana dashboard with anyone. This is useful when you want to expose your -dashboard to the world. +{{% /admonition %}} + +{{% admonition type="Caution" %}} + +Making your dashboard public could result in a large number of queries to the data sources used by your dashboard. +This can be mitigated by utilizing the enterprise [caching]({{< relref "../../administration/data-source-management/#query-caching" >}}) and/or rate limiting features. + +{{% /admonition %}} + +Public dashboards allow you to share your Grafana dashboard with anyone. This is useful when you want to make your dashboard available to the world without requiring access to your Grafana organization. This differs from [dashboard sharing]({{< relref "../share-dashboards-panels" >}}), which either requires recipients to be users in the same Grafana organization or provides limited information, as with a snapshot. + +You can see a list of all your public dashboards in one place by navigating to **Dashboards > Public dashboards**. For each dashboard in the list, the page displays the status, a link to view the dashboard, a link to the public dashboard configuration, and the option to revoke the public URL. ## Security implications of making your dashboard public - Anyone with the URL can access the dashboard. - Public dashboards are read-only. -- Arbitrary queries **cannot** be run against your datasources through public dashboards. Public dashboards can only execute the +- Arbitrary queries **cannot** be run against your data sources through public dashboards. Public dashboards can only execute the queries stored on the original dashboard. ## Enable the feature @@ -38,19 +47,25 @@ If you are using Docker, use an environment variable to enable public dashboards --env GF_FEATURE_TOGGLES_ENABLE=publicDashboards ``` -> **Note:** For Grafana Cloud, contact support to have the feature enabled. +{{% admonition type="Note" %}} + +For Grafana Cloud (Pro and Advanced only), contact support to have the feature enabled. + +{{% /admonition %}} ## Make a dashboard public -1. Click the sharing icon to the right of the dashboard title. +1. Click the sharing icon in the dashboard header. 1. Click the **Public dashboard** tab. -1. Acknowledge the implications of making the dashboard public by checking all the checkboxes. +1. Acknowledge the implications of making the dashboard public by selecting all the checkboxes. 1. Click **Generate public URL** to make the dashboard public and make your link live. 1. Copy the public dashboard link if you'd like to share it. You can always come back later for it. +Once you've made the dashboard public, a **Public** tag is displayed in the header of the dashboard. + ## Pause access -1. Click the sharing icon to the right of the dashboard title. +1. Click the sharing icon in the dashboard header. 1. Click the **Public dashboard** tab. 1. Enable the **Pause sharing dashboard** toggle. @@ -58,21 +73,29 @@ The dashboard is no longer accessible, even with the link, until you make it sha ## Revoke access -1. Click the sharing icon to the right of the dashboard title. +1. Click the sharing icon in the dashboard header. 1. Click the **Public dashboard** tab. 1. Click **Revoke public URL** to delete the public dashboard. -The link no longer works. You must create a new public URL as in [Make a dashboard public](#make-a-dashboard-public). +The link no longer works. You must create a new public URL, as in [Make a dashboard public](#make-a-dashboard-public). ## Email sharing -> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud). +{{% admonition type="Note" %}} -Email sharing allows you to share your public dashboard with only specific people via email, instead of having it accessible to anyone with the URL. +Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud). + +{{% /admonition %}} + +Email sharing allows you to share your public dashboard with only specific people by email, instead of having it accessible to anyone with the URL. When you use email sharing, recipients receive a one-time use link that's valid for **one hour**. Once the link is used, the viewer has access to the public dashboard for **30 days**. ### Enable email sharing -> **Note:** For Grafana Cloud, contact support to have the feature enabled. +{{% admonition type="Note" %}} + +For Grafana Cloud (Pro and Advanced only), contact support to have the feature enabled. + +{{% /admonition %}} Add the `publicDashboardsEmailSharing` feature toggle to your `custom.ini` file. @@ -83,12 +106,14 @@ publicDashboardsEmailSharing = true ### Invite a viewer -1. Click the sharing icon to the right of the dashboard title. +1. Click the sharing icon in the dashboard header. 1. Click the **Public dashboard** tab. -1. Click **Only specified people**. +1. Acknowledge the implications of making the dashboard public by selecting all the checkboxes. +1. Click **Generate public URL** to make the dashboard public and make your link live. +1. Under Can view dashboard, click **Only specified people**. 1. Enter the email you want to share the public dashboard with. 1. Click **Invite**. -1. The recipient(s) will receive an email with a one-time use link. This link must be used within **one hour** or it expires. Once the link is used, the viewer has access to the public dashboard for **30 days**. +1. The recipient will receive an email with a one-time use link. ### Viewers requesting access @@ -98,7 +123,7 @@ If the viewer doesn't have an invitation or it's been revoked, you won't be noti ### Revoke access for a viewer -1. Click the sharing icon to the right of the dashboard title. +1. Click the sharing icon in the dashboard header. 1. Click the **Public dashboard** tab. 1. Click **Revoke** on the viewer you'd like to revoke access for. @@ -106,24 +131,44 @@ Immediately, the viewer no longer has access to the public dashboard, nor can th ### Reinvite a viewer -1. Click the sharing icon to the right of the dashboard title. +1. Click the sharing icon in the dashboard header. 1. Click the **Public dashboard** tab. 1. Click **Resend** on the viewer you'd like to re-share the public dashboard with. The viewer will receive an email with a new one-time use link. This will invalidate all previously issued links for that viewer. +### View public dashboard users + +To see a list of users who have accessed your dashboard by way of email sharing, take the following steps: + +1. In the main sidebar navigation, click **Administration**. +1. Click **Users**. +1. Click the **Public dashboard users** tab. + +From here, you can see the earliest time a user has been active in a dashboard, which public dashboards they have access to, and their role. + ### Access limitations -One-time use links use browser cookies, so when a viewer is granted access via one of these links, they will only have access through the browser they used to claim the link. +One-time use links use browser cookies, so when a viewer is granted access through one of these links, they will only have access on the browser they used to claim the link. A single viewer cannot generate multiple valid one-time use links. When a new one-time use link is issued for a viewer, all previous ones are invalidated. If a Grafana user has read access to the parent dashboard, they can view the public dashboard without needing to have access granted. +## Assess public dashboard usage + +> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud). + +You can check usage analytics about your public dashboard by clicking the insights icon in the dashboard header: + +{{< figure src="/media/docs/grafana/dashboards/screenshot-dashboard-insights.png" max-width="400px" class="docs-image--no-shadow" alt="Dashboard insights icon" >}} + +Learn more about the kind of information provided in the [dashboard insights documentation]({{< relref "../assess-dashboard-usage/#dashboard-insights" >}}). + ## Supported data sources -Public dashboards _should_ work with any datasource that has the properties `backend` and `alerting` both set to true in it's `package.json`. However, this cannot always be -guaranteed because plugin developers can override this functionality. The following lists include data sources confirmed to work with public dashboards and data sources that should work but have not been confirmed as compatible. +Public dashboards _should_ work with any data source that has the properties `backend` and `alerting` both set to true in its `package.json`. However, this can't always be +guaranteed because plugin developers can override this functionality. The following lists include data sources confirmed to work with public dashboards and data sources that should work, but have not been confirmed as compatible. ### Confirmed: @@ -158,7 +203,11 @@ guaranteed because plugin developers can override this functionality. The follow ### Unconfirmed: -> **Note:** If you've confirmed one of these datasources work with public dashboards, let us know in our [Github](https://github.com/grafana/grafana/discussions/49253) discussion, and we'll mark it as confirmed! +{{% admonition type="Note" %}} + +If you've confirmed one of these data sources work with public dashboards, let us know in our [Github](https://github.com/grafana/grafana/discussions/49253) discussion, and we'll mark it as confirmed! + +{{% /admonition %}} @@ -206,13 +255,13 @@ guaranteed because plugin developers can override this functionality. The follow
  • Snowflake
  • Splunk
  • Splunk Infrastructure Monitoring
  • -
  • Sqlyze Datasource
  • +
  • Sqlyze data source
  • TDengine
  • Vertica
  • Wavefront
  • X-Ray
  • kdb+
  • -
  • simple grpc datasource
  • +
  • simple grpc data source
  • @@ -220,17 +269,17 @@ guaranteed because plugin developers can override this functionality. The follow ## Limitations -- Panels that use frontend datasources will fail to fetch data. -- Template variables are currently not supported, but are planned to be in the future. +- Panels that use frontend data sources will fail to fetch data. +- Template variables are not currently supported, but support is planned in the future. - Exemplars will be omitted from the panel. -- Only annotations that query the `-- Grafana --` datasource are supported. +- Only annotations that query the `-- Grafana --` data source are supported. - Organization annotations are not supported. - Grafana Live and real-time event streams are not supported. -- Library panels are currently not supported, but are planned to be in the future. -- Datasources using Reverse Proxy functionality are not supported. +- Library panels are currently not supported, but support is planned in the future. +- Data sources using Reverse Proxy functionality are not supported. -We are excited to share this enhancement with you and we’d love your feedback! Please check out the [Github](https://github.com/grafana/grafana/discussions/49253) discussion and join the conversation. +We're excited to share this enhancement with you and we’d love your feedback! Please check out the [Github](https://github.com/grafana/grafana/discussions/49253) discussion and join the conversation. ## Custom branding -If you are a Grafana Enterprise customer, you can use custom branding to change the appearance of a public dashboard footer. For more information, refer to [Custom branding](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/configure-custom-branding/). +If you're a Grafana Enterprise customer, you can use custom branding to change the appearance of a public dashboard footer. For more information, refer to [Custom branding](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/configure-custom-branding/). diff --git a/docs/sources/dashboards/use-dashboards/index.md b/docs/sources/dashboards/use-dashboards/index.md index 9753b8884df..6b8cd5e2dee 100644 --- a/docs/sources/dashboards/use-dashboards/index.md +++ b/docs/sources/dashboards/use-dashboards/index.md @@ -29,26 +29,26 @@ The following image and descriptions highlights all dashboards features. {{< figure src="/media/docs/grafana/dashboards/screenshot-dashboard-annotated-9-5-0.png" width="700px" >}} -- **Grafana home** (1): Click **Home** in the breadcrumb to be redirected to the home page configured in the Grafana instance. -- **Dashboard title** (2): When you click the dashboard title you can search for dashboard contained in the current folder. -- **Share dashboard or panel** (3): Use this option to share the current dashboard or panel by link or snapshot. You can also export the dashboard definition from the share modal. -- **Add** (4): Use this option to add a panel, dashboard row, or library panel to the current dashboard. -- **Save dashboard** (5): Click to save changes to your dashboard. -- **Dashboard insights** (6): Click to view analytics about your dashboard including information about users, activity, query counts. -- **Dashboard settings** (7): Use this option to change dashboard name, folder, and tags and manage variables and annotation queries. For more information about dashboard settings, refer to [Modify dashboard settings]({{< relref "../build-dashboards/modify-dashboard-settings/" >}}). -- **Time picker dropdown** (8): Click to select relative time range options and set custom absolute time ranges. +- (1) **Grafana home**: Click **Home** in the breadcrumb to be redirected to the home page configured in the Grafana instance. +- (2) **Dashboard title**: When you click the dashboard title you can search for dashboard contained in the current folder. +- (3) **Share dashboard or panel**: Use this option to share the current dashboard or panel by link or snapshot. You can also export the dashboard definition from the share modal. +- (4) **Add**: Use this option to add a panel, dashboard row, or library panel to the current dashboard. +- (5) **Save dashboard**: Click to save changes to your dashboard. +- (6) **Dashboard insights**: Click to view analytics about your dashboard including information about users, activity, query counts. Learn more about [dashboard analytics]({{< relref "../assess-dashboard-usage/" >}}). +- (7) **Dashboard settings**: Use this option to change dashboard name, folder, and tags and manage variables and annotation queries. Learn more about [dashboard settings]({{< relref "../build-dashboards/modify-dashboard-settings/" >}}). +- (8) **Time picker dropdown**: Click to select relative time range options and set custom absolute time ranges. - You can change the **Timezone** and **fiscal year** settings from the time range controls by clicking the **Change time settings** button. - Time settings are saved on a per-dashboard basis. -- **Zoom out time range** (9): Click to zoom out the time range. For more information about how to use time range controls, refer to [Common time range controls]({{< relref "#common-time-range-controls" >}}). -- **Refresh dashboard** (10): Click to immediately trigger queries and refresh dashboard data. -- **Refresh dashboard time interval** (11): Click to select a dashboard auto refresh time interval. -- **View mode** (12): Click to display the dashboard on a large screen such as a TV or a kiosk. View mode hides irrelevant information such as navigation menus. For more information about view mode, refer to [How to Create Kiosks to Display Dashboards on a TV](https://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/). -- **Dashboard panel** (13): The primary building block of a dashboard is the panel. To add a new panel, dashboard row, or library panel, click **Add panel**. +- (9) **Zoom out time range**: Click to zoom out the time range. Learn more about how to use [common time range controls]({{< relref "#common-time-range-controls" >}}). +- (10) **Refresh dashboard**: Click to immediately trigger queries and refresh dashboard data. +- (11) **Refresh dashboard time interval**: Click to select a dashboard auto refresh time interval. +- (12) **View mode**: Click to display the dashboard on a large screen such as a TV or a kiosk. View mode hides irrelevant information such as navigation menus. Learn more about view 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/). +- (13) **Dashboard panel**: The primary building block of a dashboard is the panel. To add a new panel, dashboard row, or library panel, click **Add panel**. - Library panels can be shared among many dashboards. - To move a panel, drag the panel header to another location. - To resize a panel, click and drag the lower right corner of the panel. -- **Graph legend** (14): Change series colors, y-axis and series visibility directly from the legend. -- **Dashboard row** (15): A dashboard row is a logical divider within a dashboard that groups panels together. +- (14) **Graph legend**: Change series colors, y-axis and series visibility directly from the legend. +- (15) **Dashboard row**: A dashboard row is a logical divider within a dashboard that groups panels together. - Rows can be collapsed or expanded allowing you to hide parts of the dashboard. - Panels inside a collapsed row do not issue queries. - Use [repeating rows]({{< relref "../build-dashboards/create-dashboard/#configure-repeating-rows" >}}) to dynamically create rows based on a template variable. @@ -86,7 +86,7 @@ This section describes supported time units and relative ranges, the common time Grafana supports the following time units: `s (seconds)`, `m (minutes)`, `h (hours)`, `d (days)`, `w (weeks)`, `M (months)`, `Q (quarters)` and `y (years)`. -The minus operator enables you to step back in time, relative to now. If you want to display the full period of the unit (day, week, month, etc...), append `/ - + - - {{ end }} - + {{ end }} - - {{ end }} - + {{ end }} - - - {{ if .Annotations.SortedPairs }} - + + {{ if (without .Annotations.SortedPairs.Names "description" "summary") }} Annotations - - {{ range .Annotations.SortedPairs }} - + {{ range .Annotations.SortedPairs }} + {{ if and (ne .Name "description") (ne .Name "summary") }} - + - - {{ end }} - + {{ end }} + {{ end }} - - {{ end }} - + {{ end }} @@ -178,9 +130,7 @@ {{ end }}{{ if .PanelURL }} - - View panel - + View panel {{ end }} @@ -189,7 +139,8 @@ - Observed {{ ago .StartsAt }} before this notification was delivered, at {{ .StartsAt }} + Observed {{ ago .StartsAt }} before this notification was delivered, at + {{ .StartsAt }} diff --git a/go.mod b/go.mod index ea79e9db324..dd77125c1b3 100644 --- a/go.mod +++ b/go.mod @@ -7,55 +7,45 @@ go 1.19 // Also, use our fork with fixes for unimplemented methods (required for Go 1.16). replace github.com/denisenkom/go-mssqldb => github.com/grafana/go-mssqldb v0.9.2 -// Avoid using v2.0.0+incompatible Redigo used by dependencies as the latest maintained branch of Redigo is v1. -replace github.com/gomodule/redigo => github.com/gomodule/redigo v1.8.9 - // Override docker/docker to avoid: // go: github.com/drone-runners/drone-runner-docker@v1.8.2 requires // github.com/docker/docker@v0.0.0-00010101000000-000000000000: invalid version: unknown revision 000000000000 -replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 +replace github.com/docker/docker => github.com/moby/moby v23.0.4+incompatible // contains openapi encoder fixes. remove ASAP -replace cuelang.org/go => github.com/sdboyer/cue v0.5.0-beta.2.0.20221218111347-341999f48bdb - -// contains go generation fixes -replace github.com/deepmap/oapi-codegen => github.com/spinillos/oapi-codegen v1.12.5-0.20230206122001-6a05ca88e18e +replace cuelang.org/go => github.com/sdboyer/cue v0.5.0-beta.2.0.20230419165817-251c3ae823d8 // For some insane reason, client-go seems to have a broken v12.0.0 tag on it that forces us to // hoist a replace statement. replace k8s.io/client-go => k8s.io/client-go v0.25.3 -require ( - k8s.io/apiextensions-apiserver v0.26.2 // indirect - k8s.io/apimachinery v0.26.2 -) +require k8s.io/apimachinery v0.26.2 require ( cloud.google.com/go/storage v1.28.1 - cuelang.org/go v0.5.0-beta.2 + cuelang.org/go v0.5.0 github.com/Azure/azure-sdk-for-go v65.0.0+incompatible github.com/Azure/go-autorest/autorest v0.11.28 github.com/BurntSushi/toml v1.2.1 github.com/Masterminds/semver v1.5.0 github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f - github.com/aws/aws-sdk-go v1.44.171 + github.com/aws/aws-sdk-go v1.44.234 github.com/beevik/etree v1.1.0 - github.com/benbjohnson/clock v1.3.0 + github.com/benbjohnson/clock v1.3.3 github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b - github.com/centrifugal/centrifuge v0.25.0 + github.com/centrifugal/centrifuge v0.29.1 github.com/crewjam/saml v0.4.12 - github.com/fatih/color v1.13.0 + github.com/fatih/color v1.15.0 github.com/gchaincl/sqlhooks v1.3.0 - github.com/getsentry/sentry-go v0.13.0 github.com/go-git/go-git/v5 v5.4.2 github.com/go-ldap/ldap/v3 v3.4.4 - github.com/go-openapi/strfmt v0.21.3 + github.com/go-openapi/strfmt v0.21.7 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sourcemap/sourcemap v2.1.3+incompatible github.com/go-sql-driver/mysql v1.7.0 github.com/go-stack/stack v1.8.1 github.com/gobwas/glob v0.2.3 - github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 github.com/golang/mock v1.6.0 github.com/golang/snappy v0.0.4 @@ -63,44 +53,43 @@ require ( github.com/google/uuid v1.3.0 github.com/google/wire v0.5.0 github.com/gorilla/websocket v1.5.0 - github.com/grafana/alerting v0.0.0-20230315185333-d1e3c68ac064 - github.com/grafana/cuetsy v0.1.6 - github.com/grafana/grafana-aws-sdk v0.12.0 + github.com/grafana/alerting v0.0.0-20230428095912-33c5aa68a5ba + github.com/grafana/grafana-aws-sdk v0.15.0 github.com/grafana/grafana-azure-sdk-go v1.6.0 - github.com/grafana/grafana-plugin-sdk-go v0.158.0 + github.com/grafana/grafana-plugin-sdk-go v0.160.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 - github.com/hashicorp/go-hclog v1.2.0 - github.com/hashicorp/go-plugin v1.4.3 + github.com/hashicorp/go-hclog v1.5.0 + github.com/hashicorp/go-plugin v1.4.9 github.com/hashicorp/go-version v1.3.0 github.com/influxdata/influxdb-client-go/v2 v2.6.0 github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 github.com/jmespath/go-jmespath v0.4.0 github.com/json-iterator/go v1.1.12 github.com/jung-kurt/gofpdf v1.16.2 - github.com/lib/pq v1.10.7 + github.com/lib/pq v1.10.6 github.com/linkedin/goavro/v2 v2.10.0 github.com/m3db/prometheus_remote_client_golang v0.4.4 github.com/magefile/mage v1.14.0 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-sqlite3 v1.14.16 github.com/matttproud/golang_protobuf_extensions v1.0.4 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f - github.com/opentracing/opentracing-go v1.2.0 + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/errors v0.9.1 github.com/prometheus/alertmanager v0.25.0 - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.15.0 github.com/prometheus/client_model v0.3.0 - github.com/prometheus/common v0.40.0 + github.com/prometheus/common v0.42.0 github.com/prometheus/prometheus v1.8.2-0.20211217191541-41f1a8125e66 github.com/robfig/cron/v3 v3.0.1 github.com/russellhaering/goxmldsig v1.2.0 github.com/stretchr/testify v1.8.2 github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f - github.com/uber/jaeger-client-go v2.29.1+incompatible - github.com/urfave/cli/v2 v2.3.0 + github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect + github.com/urfave/cli/v2 v2.24.4 github.com/vectordotdev/go-datemath v0.1.1-0.20220323213446-f3954d0b18ae github.com/yalue/merged_fs v1.2.2 github.com/yudai/gojsondiff v1.0.0 @@ -111,17 +100,17 @@ require ( go.opentelemetry.io/otel/exporters/jaeger v1.0.0 go.opentelemetry.io/otel/sdk v1.14.0 go.opentelemetry.io/otel/trace v1.14.0 - golang.org/x/crypto v0.4.0 + golang.org/x/crypto v0.7.0 golang.org/x/exp v0.0.0-20221211140036-ad323defaf05 - golang.org/x/net v0.8.0 + golang.org/x/net v0.9.0 golang.org/x/oauth2 v0.6.0 golang.org/x/sync v0.1.0 - golang.org/x/time v0.2.0 - golang.org/x/tools v0.6.0 + golang.org/x/time v0.3.0 + golang.org/x/tools v0.7.0 gonum.org/v1/gonum v0.11.0 google.golang.org/api v0.104.0 google.golang.org/grpc v1.54.0 - google.golang.org/protobuf v1.28.1 + google.golang.org/protobuf v1.30.0 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 gopkg.in/mail.v2 v2.3.1 @@ -141,18 +130,17 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/FZambia/eagle v0.0.2 // indirect - github.com/FZambia/sentinel v1.1.0 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/andybalholm/brotli v1.0.4 github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect - github.com/centrifugal/protocol v0.8.10 // indirect + github.com/centrifugal/protocol v0.10.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect - github.com/deepmap/oapi-codegen v1.12.4 + github.com/deepmap/oapi-codegen v1.12.4 // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/go-units v0.5.0 // indirect @@ -166,15 +154,14 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/loads v0.21.2 github.com/go-openapi/runtime v0.25.0 // indirect - github.com/go-openapi/spec v0.20.7 // indirect + github.com/go-openapi/spec v0.20.8 // indirect github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-openapi/validate v0.22.0 // indirect - github.com/golang-jwt/jwt/v4 v4.4.3 // indirect + github.com/go-openapi/validate v0.22.1 // indirect + github.com/golang-jwt/jwt/v4 v4.4.3 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 - github.com/gomodule/redigo v2.0.0+incompatible // indirect + github.com/golang/protobuf v1.5.3 github.com/google/btree v1.1.2 // indirect github.com/google/flatbuffers v2.0.8+incompatible // indirect github.com/googleapis/gax-go/v2 v2.7.0 @@ -186,7 +173,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/igm/sockjs-go/v3 v3.0.2 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect github.com/jonboulle/clockwork v0.3.0 // indirect @@ -197,8 +184,7 @@ require ( github.com/mattetti/filebuffer v1.0.1 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/miekg/dns v1.1.50 // indirect - github.com/mitchellh/go-testing-interface v1.14.0 // indirect - github.com/mna/redisc v1.3.2 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect @@ -207,12 +193,12 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/exporter-toolkit v0.8.2 // indirect + github.com/prometheus/exporter-toolkit v0.9.1 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b // indirect - github.com/rs/cors v1.8.3 // indirect + github.com/rs/cors v1.9.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect - github.com/segmentio/encoding v0.3.5 + github.com/segmentio/encoding v0.3.6 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect @@ -221,15 +207,15 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - go.mongodb.org/mongo-driver v1.11.1 // indirect + go.mongodb.org/mongo-driver v1.11.3 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.10.0 go.uber.org/goleak v1.2.1 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 ) require ( @@ -244,12 +230,12 @@ require ( github.com/bufbuild/connect-go v1.4.1 github.com/dlmiddlecote/sqlstats v1.0.2 github.com/drone/drone-cli v1.6.1 - github.com/getkin/kin-openapi v0.112.0 + github.com/getkin/kin-openapi v0.115.0 github.com/golang-migrate/migrate/v4 v4.7.0 github.com/google/go-github/v45 v45.2.0 github.com/grafana/codejen v0.0.3 github.com/grafana/dskit v0.0.0-20230202092222-880a7f8141cc - github.com/grafana/phlare/api v0.1.3 + github.com/grafana/phlare/api v0.1.4-0.20230426005640-f90edba05413 github.com/huandu/xstrings v1.3.1 github.com/jmoiron/sqlx v1.3.5 github.com/matryer/is v1.4.0 @@ -266,28 +252,34 @@ require ( buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.4.1-20221222094228-8b1d3d0f62e6.1 buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.28.1-20221222094228-8b1d3d0f62e6.4 github.com/Masterminds/semver/v3 v3.1.1 + github.com/alicebob/miniredis/v2 v2.30.1 github.com/dave/dst v0.27.2 github.com/go-jose/go-jose/v3 v3.0.0 + github.com/grafana/cuetsy v0.1.8 + github.com/grafana/dataplane/examples v0.0.0-20230404174214-4d6fd58a18ad + github.com/grafana/dataplane/sdata v0.0.6 github.com/grafana/go-mssqldb v0.9.1 - github.com/grafana/kindsys v0.0.0-20230309200316-812b9884a375 - github.com/grafana/thema v0.0.0-20230302221249-6952e4a999b7 + github.com/grafana/kindsys v0.0.0-20230427152021-bb328815be7a + github.com/grafana/thema v0.0.0-20230417103609-99b482c479fe + github.com/redis/go-redis/v9 v9.0.2 github.com/weaveworks/common v0.0.0-20230208133027-16871410fca4 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f + go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0 + k8s.io/utils v0.0.0-20221107191617-1a15be271d1d ) require ( cloud.google.com/go v0.107.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect + github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/bmatcuk/doublestar v1.1.1 // indirect github.com/buildkite/yaml v2.1.0+incompatible // indirect - github.com/containerd/containerd v1.6.8 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -299,8 +291,10 @@ require ( github.com/drone/drone-go v1.7.1 // indirect github.com/drone/envsubst v1.0.3 // indirect github.com/drone/runner-go v1.12.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/envoyproxy/go-control-plane v0.10.3 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.13 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -309,7 +303,7 @@ require ( github.com/grafana/sqlds/v2 v2.3.10 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect github.com/hashicorp/memberlist v0.5.0 // indirect github.com/hetznercloud/hcloud-go v1.35.3 // indirect github.com/invopop/yaml v0.1.0 // indirect @@ -329,24 +323,24 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/rivo/uniseg v0.3.4 // indirect + github.com/rueian/rueidis v0.0.100-go1.18 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect - github.com/segmentio/asm v1.1.4 // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect github.com/unknwon/com v1.0.1 // indirect github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3 // indirect github.com/weaveworks/promrus v1.2.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/yuin/gopher-lua v1.1.0 // indirect go.opentelemetry.io/otel/metric v0.37.0 // indirect go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/klog/v2 v2.80.1 // indirect - k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + k8s.io/api v0.26.2 // indirect ) require ( @@ -358,7 +352,7 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect github.com/Masterminds/sprig/v3 v3.2.2 github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 github.com/RoaringBitmap/roaring v0.9.4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f // indirect @@ -373,7 +367,7 @@ require ( github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect - github.com/docker/docker v20.10.21+incompatible + github.com/docker/docker v23.0.4+incompatible github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect @@ -389,7 +383,7 @@ require ( github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/klauspost/compress v1.15.13 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/labstack/echo/v4 v4.10.0 // indirect + github.com/labstack/echo/v4 v4.10.2 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -402,12 +396,12 @@ require ( github.com/yudai/pp v2.0.1+incompatible // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect - golang.org/x/mod v0.8.0 // indirect + golang.org/x/mod v0.9.0 // indirect gopkg.in/warnings.v0 v0.1.2 // 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.13-0.20230203140620-5f476db5c00a +replace github.com/crewjam/saml => github.com/grafana/saml v0.4.13-0.20230331080031-67cbfa09c7b6 // Thema's thema CLI requires cobra, which eventually works its way down to go-hclog@v1.0.0. // Upgrading affects backend plugins: https://github.com/grafana/grafana/pull/47653#discussion_r850508593 @@ -423,7 +417,7 @@ replace xorm.io/xorm => github.com/grafana/xorm v0.8.3-0.20220614223926-2fcda756 // Use our fork of the upstream alertmanagers. // This is required in order to get notification delivery errors from the receivers API. -replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20230308154952-78fedf89728b +replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20230508090422-7d5630522a53 // grpc v1.46.0 removed "WithBalancerName()" API, still in use by weaveworks/commons. replace google.golang.org/grpc => google.golang.org/grpc v1.45.0 @@ -431,3 +425,5 @@ replace google.golang.org/grpc => google.golang.org/grpc v1.45.0 replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3 replace github.com/go-sql-driver/mysql => github.com/grafana/mysql v1.6.8 +// Use 1.10.6 of pq to avoid a change in 1.10.7 that has certificate validation issues. https://github.com/grafana/grafana/issues/65816 +replace github.com/lib/pq => github.com/lib/pq v1.10.6 diff --git a/go.sum b/go.sum index 3d3f7a3665d..7d26b1bf8b0 100644 --- a/go.sum +++ b/go.sum @@ -115,7 +115,6 @@ github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-sdk-for-go v41.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v55.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v57.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v59.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v59.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= @@ -150,7 +149,6 @@ github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUd github.com/Azure/go-autorest/autorest v0.11.9/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs= github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= @@ -163,7 +161,6 @@ github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMl github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= 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.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= github.com/Azure/go-autorest/autorest/adal v0.9.17/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= @@ -212,8 +209,6 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/FZambia/eagle v0.0.2 h1:35qHDuXSQevZ4w9A51k4wU7OE/tPHTEWXoywA93hvkY= github.com/FZambia/eagle v0.0.2/go.mod h1:xq6u/JeNZ5/8mrAQ76MMhzNTodASh9FavQlCgg4j48w= -github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8= -github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8= github.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4= github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= @@ -248,7 +243,6 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.18/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= @@ -283,17 +277,22 @@ github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20210912230133-d1bdfacee922/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.30.1 h1:HM1rlQjq1bm9yQcsawJqSZBJ9AYgxvjkMsNtddh90+g= +github.com/alicebob/miniredis/v2 v2.30.1/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -333,8 +332,9 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= @@ -348,11 +348,10 @@ github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zK github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.60/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.40.37/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.42.16/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.44.171 h1:maREiPAmibvuONMOEZIkCH2OTosLRnDelceTtH3SYfo= -github.com/aws/aws-sdk-go v1.44.171/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.234 h1:8YbQ5AhpgV/cC7jYX8qS34Am/vcn2ZoIFJ1qIgwOL+0= +github.com/aws/aws-sdk-go v1.44.234/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4= github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA= @@ -401,8 +400,8 @@ github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stg github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc= +github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/immutable v0.2.1/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= github.com/benbjohnson/tmpl v1.0.0/go.mod h1:igT620JFIi44B6awvU9IsDhR77IXWtFigTLil/RPdps= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -448,6 +447,8 @@ github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bsm/ginkgo/v2 v2.5.0 h1:aOAnND1T40wEdAtkGSkvSICWeQ8L3UASX7YVCqQx+eQ= +github.com/bsm/gomega v1.20.0 h1:JhAwLmtRzXFTx2AkALSLa8ijZafntmhSoU63Ok18Uq8= github.com/bsm/sarama-cluster v2.1.13+incompatible/go.mod h1:r7ao+4tTNXvWm+VRpRJchr2kQhqxgmAp2iEX5W96gMM= github.com/bufbuild/connect-go v1.4.1 h1:6usL3JGjKhxQpvDlizP7u8VfjAr1JkckcAUbrdcbgNY= github.com/bufbuild/connect-go v1.4.1/go.mod h1:9iNvh/NOsfhNBUH5CtvXeVUskQO1xsrEviH7ZArwZ3I= @@ -469,10 +470,10 @@ github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+M github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/centrifugal/centrifuge v0.25.0 h1:QivFZRPWcN8w3I/gZ8Zs9rMe/KGZoKIS9Kgo1/bY4JE= -github.com/centrifugal/centrifuge v0.25.0/go.mod h1:bFcSFalnROq/wcFeRiTG+wIbHsxEMW66QUnq95RY1d0= -github.com/centrifugal/protocol v0.8.10 h1:eezzBIU/4pWyl7a+NUnANYojJBASqbkPZcQh9b8YQRI= -github.com/centrifugal/protocol v0.8.10/go.mod h1:dlHBjKakr0r+f1pkfwSMfZ+cnpvidN7pQe1ZrsKfhtE= +github.com/centrifugal/centrifuge v0.29.1 h1:UJ8BjhvJQ2MMi0GQ67qiqohsN4ABU+9IbpLAedk9/zc= +github.com/centrifugal/centrifuge v0.29.1/go.mod h1:c66vMV0pUDdM3WjXCubTfhLKt9GB0/WYHikxTrAIxLw= +github.com/centrifugal/protocol v0.10.0 h1:Lac48ATVjVjirYPTHxbSMmiQXXajx7dhARKHy1UOL+A= +github.com/centrifugal/protocol v0.10.0/go.mod h1:Tq5I1mBpLHkLxNM9gfb3Gth+sTE2kKU5hH3cVgmVs9s= 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= @@ -497,7 +498,6 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -544,10 +544,7 @@ github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.4/go.mod h1:sx18RgvW6ABJ4iYUw7Q5x7bgFOAB9B6G7+yO0XBc4zw= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -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/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -630,6 +627,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b/go.mod h1:v9FBN7gdVTpiD/+LZ7Po0UKvROyT87uLVxTHVky/dlQ= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= @@ -656,9 +654,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= +github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= @@ -682,7 +681,6 @@ github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8 github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= github.com/digitalocean/godo v1.62.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.65.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= github.com/digitalocean/godo v1.72.0/go.mod h1:GBmu8MkjZmNARE7IXRPmkbbnocNN8+uBm0xbEVw2LCs= github.com/digitalocean/godo v1.88.0 h1:SAEdw63xOMmzlwCeCWjLH1GcyDPUjbSAR1Bh7VELxzc= github.com/digitalocean/godo v1.88.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= @@ -742,21 +740,19 @@ github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac/go.mod h1:Ro8st/El github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.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/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/envoyproxy/go-control-plane v0.9.9/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.1/go.mod h1:txg5va2Qkip90uYoSKH+nkAAmXrb2j3iq4FLwdrCbXQ= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.6.13 h1:TvDcILLkjuZV3ER58VkBmncKsLUBqBDxra/XctCzuMM= @@ -769,8 +765,9 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -791,6 +788,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= @@ -798,11 +796,11 @@ github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getkin/kin-openapi v0.112.0 h1:lnLXx3bAG53EJVI4E/w0N8i1Y/vUZUEsnrXkgnfn7/Y= -github.com/getkin/kin-openapi v0.112.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8= -github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo= -github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0= +github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQS4JyU= +github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= @@ -811,7 +809,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -821,8 +818,7 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T 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= github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -950,8 +946,8 @@ github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI= -github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -962,12 +958,12 @@ github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/strfmt v0.20.1/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= -github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= @@ -993,18 +989,15 @@ github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9G github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= -github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= @@ -1021,6 +1014,8 @@ github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +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/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= @@ -1054,8 +1049,6 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gocql/gocql v0.0.0-20200228163523-cd4b606dd2fb/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= @@ -1066,8 +1059,8 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= @@ -1084,7 +1077,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 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.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -1132,8 +1124,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -1142,8 +1135,6 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 h1:utua3L2IbQJmauC5IXdEA547bcoU5dozgQAfc8Onsg4= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= -github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -1213,7 +1204,6 @@ github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210827144239-02619b876842/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211122183932-1daafda22083/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -1250,7 +1240,6 @@ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.10.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= github.com/gophercloud/gophercloud v0.18.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/gophercloud v0.20.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gophercloud/gophercloud v0.23.0/go.mod h1:MRw6uyLj8uCGbIvBlqL7QW67t0QtNZnzydUzewo1Ioc= github.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k= github.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= @@ -1275,44 +1264,46 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/alerting v0.0.0-20230315185333-d1e3c68ac064 h1:MtsWzSTav7NGKolO+TaJQUcyR7VY0YpUROVsJX8ktIU= -github.com/grafana/alerting v0.0.0-20230315185333-d1e3c68ac064/go.mod h1:nHfrSTdV7/l74N5/ezqlQ+JwSvIChhN3G5+PjCfwG/E= +github.com/grafana/alerting v0.0.0-20230428095912-33c5aa68a5ba h1:aNTu22ojw4XY24DYNAuvw8v/5iFUNk2bkdTeeWQ5+0o= +github.com/grafana/alerting v0.0.0-20230428095912-33c5aa68a5ba/go.mod h1:5edgy6tQY4+W2wuJdi8g2GjbVmpJufguy7QGIRl2q4o= github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= -github.com/grafana/cuetsy v0.1.6 h1:61QGIDy1rVABU3OkoarOn0+qPdGopIJr34PyWVmGDfs= -github.com/grafana/cuetsy v0.1.6/go.mod h1:4KWkUOslwvRTpEv7wdQG0jDFTuJmU+0L9x0h4kWxa2A= +github.com/grafana/cuetsy v0.1.8 h1:l0AKXfHr0clu6qPirirDzNC/W5mqq5gG7iruOVolG34= +github.com/grafana/cuetsy v0.1.8/go.mod h1:KCOoGIutYLt/5EY9M7jXdbfjSbZ/CNhRkl+yVeG6BFc= +github.com/grafana/dataplane/examples v0.0.0-20230404174214-4d6fd58a18ad h1:bROIOQEkRlxAt+Epn9Pos8ytkVqoIAvnaUZOAb7EgEU= +github.com/grafana/dataplane/examples v0.0.0-20230404174214-4d6fd58a18ad/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg= +github.com/grafana/dataplane/sdata v0.0.6 h1:Ejlj8d1Hvy/uDLeI4sOvL34Y8WLlVDd9iN270F+8aTw= +github.com/grafana/dataplane/sdata v0.0.6/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= github.com/grafana/dskit v0.0.0-20230202092222-880a7f8141cc h1:lQFgXpsZNDdi0whUROW15r/akzLIdXAn6xr5vqlZucI= github.com/grafana/dskit v0.0.0-20230202092222-880a7f8141cc/go.mod h1:ulYLLoSd71AWIjxgifLO86Lndx82Yj+IcV+fFnh8tkI= github.com/grafana/go-mssqldb v0.9.1 h1:3CqteWF0CadwXV9f3FxoI+i3uSW3azjTlQipyOJtWtQ= github.com/grafana/go-mssqldb v0.9.1/go.mod h1:HTCsUqZdb7oIO7jc37YauiSB5C3P/13AnpctVWBhlus= github.com/grafana/go-mssqldb v0.9.2 h1:FkyRJR4ywsT07iMtpFMHStrl8uuNkGIwp253Fee06z8= github.com/grafana/go-mssqldb v0.9.2/go.mod h1:HTCsUqZdb7oIO7jc37YauiSB5C3P/13AnpctVWBhlus= -github.com/grafana/grafana-aws-sdk v0.12.0 h1:eUjFdFZeZE+nyu/RMRz+qFxTBew69ToLBrbRhTbjkfM= -github.com/grafana/grafana-aws-sdk v0.12.0/go.mod h1:rCXLYoMpPqF90U7XqgVJ1HIAopFVF0bB3SXBVEJIm3I= +github.com/grafana/grafana-aws-sdk v0.15.0 h1:ZOPHQcC5NUFi1bLTwnju91G0KmGh1z+qXOKj9nDfxNs= +github.com/grafana/grafana-aws-sdk v0.15.0/go.mod h1:rCXLYoMpPqF90U7XqgVJ1HIAopFVF0bB3SXBVEJIm3I= github.com/grafana/grafana-azure-sdk-go v1.6.0 h1:lxvH/mVY7gKBtJKhZ4B/6tIZFY7Jth97HxBA38olaxs= github.com/grafana/grafana-azure-sdk-go v1.6.0/go.mod h1:X4PdEQIYgHfn0KTa2ZTKvufhNz6jbCEKUQPZIlcyOGw= github.com/grafana/grafana-google-sdk-go v0.1.0 h1:LKGY8z2DSxKjYfr2flZsWgTRTZ6HGQbTqewE3JvRaNA= github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkrZvFtBws0qSZdXhQxRdJrE= github.com/grafana/grafana-plugin-sdk-go v0.94.0/go.mod h1:3VXz4nCv6wH5SfgB3mlW39s+c+LetqSCjFj7xxPC5+M= github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk= -github.com/grafana/grafana-plugin-sdk-go v0.157.0 h1:Qm0r5A4TsG+Tmy8Vagffqv21t3R/tzFUdYZZpd9COYE= -github.com/grafana/grafana-plugin-sdk-go v0.157.0/go.mod h1:FwinU1fti54nWWsacIdL8D2WfYcKOWB2VRMtxnBbY+o= -github.com/grafana/grafana-plugin-sdk-go v0.158.0 h1:kucTwr4LEa6hoSunnRqkuEeJOgbBTFrhdGNkJrMV4Zg= -github.com/grafana/grafana-plugin-sdk-go v0.158.0/go.mod h1:7/F3lL/w3MGuZTwu0jam5oL/4zNOQmhs2k8WZ9VlzhI= -github.com/grafana/kindsys v0.0.0-20230309200316-812b9884a375 h1:iWFjqAU5GjIV/HetIE/Me6VF3SRw29u5QviMVbJUVQo= -github.com/grafana/kindsys v0.0.0-20230309200316-812b9884a375/go.mod h1:213a3clTiegHfheYcLPLeEyHv/azK/fAF67OEbzuLzw= +github.com/grafana/grafana-plugin-sdk-go v0.160.0 h1:jELbsqee5kRz9vD1hZeP8+984xMZDU/M6uGv0RhOR34= +github.com/grafana/grafana-plugin-sdk-go v0.160.0/go.mod h1:dPhljkVno3Bg/ZYafMrR/BfYjtCRJD2hU2719Nl3QzM= +github.com/grafana/kindsys v0.0.0-20230427152021-bb328815be7a h1:i2YhC6eTyDp+7Ftv5c6VZDUQskmKX4oIPGf38qfiZiU= +github.com/grafana/kindsys v0.0.0-20230427152021-bb328815be7a/go.mod h1:GNcfpy5+SY6RVbNGQW264gC0r336Dm+0zgQ5vt6+M8Y= github.com/grafana/mysql v1.6.8 h1:kyo38C4NgghJMqaPrkWPy4rifAcipQVF+dKttfpgTCs= github.com/grafana/mysql v1.6.8/go.mod h1:T5l1aVEbD1U8q+mfo87Xqeg/hR3IncgnDF/3fXPnLNE= -github.com/grafana/phlare/api v0.1.3 h1:mYTaE9mLsAW/uzPXlW/PQSLsZ4ojBFA+oAMfR/PDdw8= -github.com/grafana/phlare/api v0.1.3/go.mod h1:29vcLwFDmZBDce2jwFIMtzvof7fzPadT8VMKw9ks7FU= -github.com/grafana/prometheus-alertmanager v0.25.1-0.20230308154952-78fedf89728b h1:VQOGGGJ2lKcVPANyzIESKYhSeA0QIvUQwfA3CbrkDfA= -github.com/grafana/prometheus-alertmanager v0.25.1-0.20230308154952-78fedf89728b/go.mod h1:MnBfDPXJqXmmfPwQlCLvVUdqfnvrAw+hSPtDeaaFwj4= -github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a h1:aWSTt/pTOI4uGY9DhBMG1l0GOnGjIYtaqxzYR3/q82o= -github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= +github.com/grafana/phlare/api v0.1.4-0.20230426005640-f90edba05413 h1:bBzCezZNRyYlJpXTkyZdY4fpPxHZUdyeyRWzhtw/P6I= +github.com/grafana/phlare/api v0.1.4-0.20230426005640-f90edba05413/go.mod h1:IvwuGG9xa/h96UH/exgvsfy3zE+ZpctkNT9o5aaGdrU= +github.com/grafana/prometheus-alertmanager v0.25.1-0.20230508090422-7d5630522a53 h1:X3Jl4PBIGCtlPSMa6Uiu2+3FDNWmddSjivp+1DDznQs= +github.com/grafana/prometheus-alertmanager v0.25.1-0.20230508090422-7d5630522a53/go.mod h1:QtbJShIif+fPgjXVPy3mEJaNebBHSU5NkoFtzsUS1c0= +github.com/grafana/saml v0.4.13-0.20230331080031-67cbfa09c7b6 h1:oHn/OOUkECNX06DPHksS7R3UY5Qdye04b/sBj2/OJ5E= +github.com/grafana/saml v0.4.13-0.20230331080031-67cbfa09c7b6/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= github.com/grafana/sqlds/v2 v2.3.10 h1:HWKhE0vR6LoEiE+Is8CSZOgaB//D1yqb2ntkass9Fd4= github.com/grafana/sqlds/v2 v2.3.10/go.mod h1:c6ibxnxRVGxV/0YkEgvy7QpQH/lyifFyV7K/14xvdIs= -github.com/grafana/thema v0.0.0-20230302221249-6952e4a999b7 h1:XOxaBjhozlleshff3mKNdp55ul74nXJEX3wz8ckjTpc= -github.com/grafana/thema v0.0.0-20230302221249-6952e4a999b7/go.mod h1:T6q1RVzq5fzVbm+QBeIsTmVzuXlMaArp4wxfLvcaMV0= +github.com/grafana/thema v0.0.0-20230417103609-99b482c479fe h1:Ws23A0XH6XYNaF/XhrOhNiC09rqGisvflCf0aHRhpTM= +github.com/grafana/thema v0.0.0-20230417103609-99b482c479fe/go.mod h1:hjlPua8wL3HO188Qg6GhTeYOPaUOneC04sFYRtcxSNw= github.com/grafana/xorm v0.8.3-0.20220614223926-2fcda7565af6 h1:I9dh1MXGX0wGyxdV/Sl7+ugnki4Dfsy8lv2s5Yf887o= github.com/grafana/xorm v0.8.3-0.20220614223926-2fcda7565af6/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -1340,7 +1331,6 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.11.0/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.15.3 h1:WYONYL2rxTXtlekAqblR2SCdJsizMDIj/uXb5wNy9zU= @@ -1375,8 +1365,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-plugin v1.2.2/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= -github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= -github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.9 h1:ESiK220/qE0aGxWdzKIvRH69iLiuN/PjoLTm69RoWtU= +github.com/hashicorp/go-plugin v1.4.9/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -1401,8 +1391,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= -github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= +github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -1427,10 +1417,9 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 h1:brI5vBRUlAlM34VFmnLPwjnCL/FxAJp9XvOdX6Zt+XE= -github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hetznercloud/hcloud-go v1.26.2/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33CevmZpQIIDPGYJI= -github.com/hetznercloud/hcloud-go v1.32.0/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= github.com/hetznercloud/hcloud-go v1.35.3 h1:WCmFAhLRooih2QHAsbCbEdpIHnshQQmrPqsr3rHE1Ow= github.com/hetznercloud/hcloud-go v1.35.3/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA= @@ -1441,7 +1430,6 @@ github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbc github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -1458,14 +1446,12 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= github.com/influxdata/flux v0.113.0/go.mod h1:3TJtvbm/Kwuo5/PEo5P6HUzwVg4bXWkb2wPQHPtQdlU= -github.com/influxdata/flux v0.120.1/go.mod h1:pGSAvyAA5d3et7SSzajaYShWYXmnRnJJq2qWi+WWZ2I= github.com/influxdata/flux v0.131.0/go.mod h1:CKvnYe6FHpTj/E0YGI7TcOZdGiYHoToOPSnoa12RtKI= github.com/influxdata/httprouter v1.3.1-0.20191122104820-ee83e2772f69/go.mod h1:pwymjR6SrP3gD3pRj9RJwdl1j5s3doEEV8gS4X9qSzA= github.com/influxdata/influxdb v1.7.6/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= github.com/influxdata/influxdb v1.9.2/go.mod h1:UEe3MeD9AaP5rlPIes102IhYua3FhIWZuOXNHxDjSrI= -github.com/influxdata/influxdb v1.9.3/go.mod h1:xD4ZjAgEJQO9/bX3NhFrssKtdNPi+ki1kjrttJRDhGc= github.com/influxdata/influxdb v1.9.5/go.mod h1:4uPVvcry9KWQVWLxyT9641qpkRXUBN+xa0MJFFNNLKo= github.com/influxdata/influxdb-client-go/v2 v2.3.1-0.20210518120617-5d1fff431040/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb-client-go/v2 v2.6.0 h1:bIOaGTgvvv1Na2hG+nIvqyv7PK2UiU2WrJN1ck1ykyM= @@ -1617,6 +1603,7 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM52 github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knadh/koanf v1.2.0/go.mod h1:xpPTwMhsA/aaQLAilyCCqfpEiY1gpa160AiCuWHJUjY= +github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b h1:iNjcivnc6lhbvJA3LD622NPrUponluJrBWPIwGG/3Bg= github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -1639,41 +1626,29 @@ github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLg github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 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/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA= -github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leoluk/perflib_exporter v0.1.0/go.mod h1:rpV0lYj7lemdTm31t7zpCqYqPnw7xs86f+BaaNBVYFM= -github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= -github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY= -github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= +github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linkedin/goavro/v2 v2.10.0 h1:eTBIRoInBM88gITGXYtUSqqxLTFXfOsJBiX8ZMW0o4U= github.com/linkedin/goavro/v2 v2.10.0/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA= github.com/linode/linodego v0.28.5/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= -github.com/linode/linodego v0.32.0/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= github.com/linode/linodego v1.2.1/go.mod h1:x/7+BoaKd4unViBmS2umdjYyVAmpFtBtEXZ0wou7FYQ= github.com/linode/linodego v1.9.3 h1:+lxNZw4avRxhCqGjwfPgQ2PvMT+vOL0OMsTdzixR7hQ= github.com/linode/linodego v1.9.3/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w= github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1701,15 +1676,17 @@ github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHef github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/matryer/moq v0.3.0/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM= github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -1729,8 +1706,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -1774,8 +1752,8 @@ 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.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= -github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +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= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -1797,17 +1775,16 @@ github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mjibson/esc v0.2.0/go.mod h1:9Hw9gxxfHulMF5OJKCyhYD7PzlSdhzXyaGEBRPH1OPs= -github.com/mna/redisc v1.3.2 h1:sc9C+nj6qmrTFnsXb70xkjAHpXKtjjBuE6v2UcQV0ZE= -github.com/mna/redisc v1.3.2/go.mod h1:CplIoaSTDi5h9icnj4FLbRgHoNKCHDNJDVRztWDGeSQ= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 h1:cvy4lBOYN3gKfKj8Lzz5Q9TfviP+L7koMHY7SvkyTKs= -github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/moby/moby v23.0.4+incompatible h1:A/pe8vi9KIKhNbzR0G3wW4ACKDsMgXILBveMqiJNa8M= +github.com/moby/moby v23.0.4+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -1897,7 +1874,7 @@ github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDs github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1958,8 +1935,9 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -1972,7 +1950,6 @@ github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= @@ -2012,8 +1989,9 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 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= @@ -2039,19 +2017,18 @@ github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMD github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.31.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= -github.com/prometheus/common v0.40.0 h1:Afz7EVRqGg2Mqqf4JuF9vdvp1pi220m55Pi9T2JnO4Q= -github.com/prometheus/common v0.40.0/go.mod h1:L65ZJPSmfn/UBWLQIHV7dBrKFidB/wPlF1y5TlSt9OE= +github.com/prometheus/common v0.41.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/exporter-toolkit v0.5.1/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= -github.com/prometheus/exporter-toolkit v0.6.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g= github.com/prometheus/exporter-toolkit v0.7.0/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g= -github.com/prometheus/exporter-toolkit v0.8.2 h1:sbJAfBXQFkG6sUkbwBun8MNdzW9+wd5YfPYofbmj0YM= github.com/prometheus/exporter-toolkit v0.8.2/go.mod h1:00shzmJL7KxcsabLWcONwpyNEuWhREOnFqZW7vadFS0= +github.com/prometheus/exporter-toolkit v0.9.1 h1:cNkC01riqiOS+kh3zdnNwRsbe/Blh0WwK3ij5rPJ9Sw= +github.com/prometheus/exporter-toolkit v0.9.1/go.mod h1:iFlTmFISCix0vyuyBmm0UqOUCTao9+RsAsKJP3YM9ec= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -2082,6 +2059,8 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RN github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE= +github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= @@ -2099,15 +2078,16 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= -github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= -github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rueian/rueidis v0.0.100-go1.18 h1:uMGspTtI5baJcF7ZUzn9lz58SzY8lhkj02OEGTNUrlg= +github.com/rueian/rueidis v0.0.100-go1.18/go.mod h1:BQELtOQXfogKB9dqRmbq044YyXjLtKlTN07Ap4ImPgo= github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -2128,17 +2108,17 @@ github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrY github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 h1:0roa6gXKgyta64uqh52AQG3wzZXH21unn+ltzQSXML0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= -github.com/sdboyer/cue v0.5.0-beta.2.0.20221218111347-341999f48bdb h1:X6XJsprVDQnlG4vT5TVb+cRlGMU78L/IKej8Q6SDFGY= -github.com/sdboyer/cue v0.5.0-beta.2.0.20221218111347-341999f48bdb/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= +github.com/sdboyer/cue v0.5.0-beta.2.0.20230419165817-251c3ae823d8 h1:R7AphSCwo1rqpRCftz9wn2azIGAtdgn1AjxTiSdFJ1o= +github.com/sdboyer/cue v0.5.0-beta.2.0.20230419165817-251c3ae823d8/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/securego/gosec v0.0.0-20200203094520-d13bb6d2420c/go.mod h1:gp0gaHj0WlmPh9BdsTmo1aq6C27yIPWdxCKGFGdVKBE= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= -github.com/segmentio/asm v1.1.4 h1:Q/FKBtrgnmDc0YMrurLROqG9mXE6Ndn276EtDnoWtMM= -github.com/segmentio/asm v1.1.4/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= -github.com/segmentio/encoding v0.3.5 h1:UZEiaZ55nlXGDL92scoVuw00RmiRCazIEmvPSbSvt8Y= -github.com/segmentio/encoding v0.3.5/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ= +github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ= @@ -2184,7 +2164,6 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2 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= -github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= @@ -2211,8 +2190,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= -github.com/spinillos/oapi-codegen v1.12.5-0.20230206122001-6a05ca88e18e h1:u26eIR7m9RHWnWtmzBXwiFgmgNvbHCNdUnnS9tksUmc= -github.com/spinillos/oapi-codegen v1.12.5-0.20230206122001-6a05ca88e18e/go.mod h1:rlTe8FCRfdpXt+2S5m4T281CO5fnzt1EhDDeW8ZvS1E= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -2281,9 +2258,11 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK github.com/uber/jaeger-lib v2.4.1+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/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI= github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8/go.mod h1:fVle4kNr08ydeohzYafr20oZzbAkhQT39gKK/pFQ5M4= @@ -2297,10 +2276,11 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= +github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +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= @@ -2344,11 +2324,15 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yalue/merged_fs v1.2.2 h1:vXHTpJBluJryju7BBpytr3PDIkzsPMpiEknxVGPhN/I= github.com/yalue/merged_fs v1.2.2/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= @@ -2366,6 +2350,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= +github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= @@ -2406,9 +2392,8 @@ go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R7 go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= -go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= -go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= +go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -2435,6 +2420,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0. go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0/go.mod h1:JQAtechjxLEL81EjmbRwxBq/XEzGaHcsPuDHAx54hg4= go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 h1:xdJjwy5t/8I+TZehMMQ+r2h50HREihH2oMUhimQ+jug= go.opentelemetry.io/contrib/propagators/jaeger v1.15.0/go.mod h1:tU0nwW4QTvKceNUP60/PQm0FI8zDSwey7gIFt3RR/yw= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0 h1:zRi6a8uX+cJGTPLXRPjFEzN27a26k5R7LiLK87ntXgg= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0/go.mod h1:pzJOLTppaPbiPjPZEwGRf0VWx6G07hhOqznjKXIMkEk= go.opentelemetry.io/contrib/zpages v0.0.0-20210722161726-7668016acb73/go.mod h1:NAkejuYm41lpyL43Fu1XdnCOYxN5NVV80/MJ03JQ/X8= go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= @@ -2537,6 +2524,7 @@ golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -2558,14 +2546,11 @@ golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2616,9 +2601,9 @@ golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -2697,7 +2682,6 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -2716,12 +2700,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2748,7 +2731,7 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2782,6 +2765,7 @@ golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2854,6 +2838,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2902,7 +2887,6 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2935,22 +2919,22 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908150016-7ac13a9a928d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2964,23 +2948,23 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= -golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -3049,7 +3033,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -3074,11 +3057,9 @@ golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyj golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3186,8 +3167,9 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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= @@ -3223,7 +3205,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/telebot.v3 v3.1.2/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= +gopkg.in/telebot.v3 v3.1.3/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -3249,6 +3231,7 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -3264,19 +3247,16 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s= -k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= -k8s.io/apiextensions-apiserver v0.26.2 h1:/yTG2B9jGY2Q70iGskMf41qTLhL9XeNN2KhI0uDgwko= -k8s.io/apiextensions-apiserver v0.26.2/go.mod h1:Y7UPgch8nph8mGCuVk0SK83LnS8Esf3n6fUBgew8SH8= +k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= k8s.io/apimachinery v0.17.5/go.mod h1:ioIo1G/a+uONV7Tv+ZmCbMG1/a3kVw5YcDdncd8ugQ0= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= @@ -3300,23 +3280,21 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.20.0/go.mod h1:Gm8eSIfQN6457haJuPaMxZw4wyP5k+ykPFlrhQDvhvw= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= k8s.io/kube-openapi v0.0.0-20190722073852-5e22f3d471e6/go.mod h1:RZvgC8MSN6DjiMV6oIfEE9pDL9CYXokkfaCKZeHm3nc= k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= @@ -3337,6 +3315,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/grafana-mixin/alerts/alerts.libsonnet b/grafana-mixin/alerts/alerts.libsonnet index fbd957b4fb9..1c92a59bf51 100644 --- a/grafana-mixin/alerts/alerts.libsonnet +++ b/grafana-mixin/alerts/alerts.libsonnet @@ -11,8 +11,8 @@ { alert: 'GrafanaRequestsFailing', expr: ||| - 100 * namespace_job_handler_statuscode:grafana_http_request_duration_seconds_count:rate5m{handler!~"/api/datasources/proxy/:id.*|/api/ds/query|/api/tsdb/query", status_code=~"5.."} - / ignoring (status_code) group_left + 100 * sum without (status_code) (namespace_job_handler_statuscode:grafana_http_request_duration_seconds_count:rate5m{handler!~"/api/datasources/proxy/:id.*|/api/ds/query|/api/tsdb/query", status_code=~"5.."}) + / sum without (status_code) (namespace_job_handler_statuscode:grafana_http_request_duration_seconds_count:rate5m{handler!~"/api/datasources/proxy/:id.*|/api/ds/query|/api/tsdb/query"}) > %(grafanaRequestsFailingThresholdPercent)s ||| % $._config, diff --git a/jest.config.js b/jest.config.js index 3bb98ec70d4..1bfdda0b359 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ // We set this specifically for 2 reasons. // 1. It makes sense for both CI tests and local tests to behave the same so issues are found earlier // 2. Any wrong timezone handling could be hidden if we use UTC/GMT local time (which would happen in CI). -process.env.TZ = 'Pacific/Easter'; +process.env.TZ = 'Pacific/Easter'; // UTC-06:00 or UTC-05:00 depending on daylight savings const esModules = ['ol', 'd3', 'd3-color', 'd3-interpolate', 'delaunator', 'internmap', 'robust-predicates'].join('|'); diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 970f8116fe5..6711c0deb61 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -16,120 +16,161 @@ lineage: seqs: [ schemas: [ // 0.0 { - @grafana(TSVeneer="type") + spec: { + // Unique numeric identifier for the dashboard. + // TODO must isolate or remove identifiers local to a Grafana instance...? + id?: int64 + // Unique dashboard identifier that can be generated by anyone. string (8-40) + uid?: string + // Title of dashboard. + title?: string + // Description of dashboard. + description?: string + // This property should only be used in dashboards defined by plugins. It is a quick check + // to see if the version has changed since the last time. Unclear why using the version property + // is insufficient. + revision?: int64 @grafanamaturity(NeedsExpertReview) + // For dashboards imported from the https://grafana.com/grafana/dashboards/ portal + gnetId?: string @grafanamaturity(NeedsExpertReview) + // Tags associated with dashboard. + tags?: [...string] @grafanamaturity(NeedsExpertReview) + // Theme of dashboard. + style: "light" | *"dark" @grafanamaturity(NeedsExpertReview) + // Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc". + timezone?: string | *"browser" + // Whether a dashboard is editable or not. + editable: bool | *true + // Configuration of dashboard cursor sync behavior. + graphTooltip: #DashboardCursorSync + // Time range for dashboard, e.g. last 6 hours, last 7 days, etc + time?: { + from: string | *"now-6h" + to: string | *"now" + } @grafanamaturity(NeedsExpertReview) - // Unique numeric identifier for the dashboard. - // TODO must isolate or remove identifiers local to a Grafana instance...? - id?: int64 - // Unique dashboard identifier that can be generated by anyone. string (8-40) - uid?: string - // Title of dashboard. - title?: string - // Description of dashboard. - description?: string - // This property should only be used in dashboards defined by plugins. It is a quick check - // to see if the version has changed since the last time. Unclear why using the version property - // is insufficient. - revision?: int64 @grafanamaturity(NeedsExpertReview) - // For dashboards imported from the https://grafana.com/grafana/dashboards/ portal - gnetId?: string @grafanamaturity(NeedsExpertReview) - // Tags associated with dashboard. - tags?: [...string] @grafanamaturity(NeedsExpertReview) - // Theme of dashboard. - style: "light" | *"dark" @grafanamaturity(NeedsExpertReview) - // Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc". - timezone?: string | *"browser" - // Whether a dashboard is editable or not. - editable: bool | *true - // Configuration of dashboard cursor sync behavior. - graphTooltip: #DashboardCursorSync - // Time range for dashboard, e.g. last 6 hours, last 7 days, etc - time?: { - from: string | *"now-6h" - to: string | *"now" - } @grafanamaturity(NeedsExpertReview) - - // TODO docs - // TODO this appears to be spread all over in the frontend. Concepts will likely need tidying in tandem with schema changes - timepicker?: { - // Whether timepicker is collapsed or not. - collapse: bool | *false - // Whether timepicker is enabled or not. - enable: bool | *true - // Whether timepicker is visible or not. - hidden: bool | *false - // Selectable intervals for auto-refresh. - refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] // TODO docs - time_options: [...string] | *["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] - } @grafanamaturity(NeedsExpertReview) - // The month that the fiscal year starts on. 0 = January, 11 = December - fiscalYearStartMonth?: uint8 & <12 | *0 - // 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 @grafanamaturity(NeedsExpertReview) - // TODO docs - weekStart?: string @grafanamaturity(NeedsExpertReview) + // TODO this appears to be spread all over in the frontend. Concepts will likely need tidying in tandem with schema changes + timepicker?: { + // Whether timepicker is collapsed or not. + collapse: bool | *false + // Whether timepicker is enabled or not. + enable: bool | *true + // Whether timepicker is visible or not. + hidden: bool | *false + // Selectable intervals for auto-refresh. + refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] + // TODO docs + time_options: [...string] | *["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + } @grafanamaturity(NeedsExpertReview) + // The month that the fiscal year starts on. 0 = January, 11 = December + fiscalYearStartMonth?: uint8 & <12 | *0 + // 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 @grafanamaturity(NeedsExpertReview) + // TODO docs + weekStart?: string @grafanamaturity(NeedsExpertReview) - // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". - refresh?: string | false - // Version of the JSON schema, incremented each time a Grafana update brings - // changes to said schema. - // TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion - schemaVersion: uint16 | *36 - // Version of the dashboard, incremented each time the dashboard is updated. - version?: uint32 @grafanamaturity(NeedsExpertReview) - panels?: [...(#Panel | #RowPanel | #GraphPanel | #HeatmapPanel)] @grafanamaturity(NeedsExpertReview) - // TODO docs - templating?: { - list?: [...#VariableModel] @grafanamaturity(NeedsExpertReview) - } - // TODO docs - annotations?: { - list?: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview) - } - // TODO docs - links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview) + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + refresh?: string | false + // Version of the JSON schema, incremented each time a Grafana update brings + // changes to said schema. + // TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion + schemaVersion: uint16 | *36 + // Version of the dashboard, incremented each time the dashboard is updated. + version?: uint32 @grafanamaturity(NeedsExpertReview) + panels?: [...(#Panel | #RowPanel | #GraphPanel | #HeatmapPanel)] @grafanamaturity(NeedsExpertReview) + // TODO docs + templating?: { + list?: [...#VariableModel] @grafanamaturity(NeedsExpertReview) + } - snapshot?: #Snapshot @grafanamaturity(NeedsExpertReview) + // TODO docs + annotations?: #AnnotationContainer + + // TODO docs + links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview) + + snapshot?: #Snapshot @grafanamaturity(NeedsExpertReview) + } @cuetsy(kind="interface") @grafana(TSVeneer="type") /////////////////////////////////////// // Definitions (referenced above) are declared below - // TODO docs + // TODO: this should be a regular DataQuery that depends on the selected dashboard + // these match the properties of the "grafana" datasouce that is default in most dashboards #AnnotationTarget: { - limit: int64 + // Only required/valid for the grafana datasource... + // but code+tests is already depending on it so hard to change + limit: int64 + // Only required/valid for the grafana datasource... + // but code+tests is already depending on it so hard to change matchAny: bool + // Only required/valid for the grafana datasource... + // but code+tests is already depending on it so hard to change tags: [...string] + // Only required/valid for the grafana datasource... + // but code+tests is already depending on it so hard to change type: string + ... // datasource will stick their raw DataQuery here } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) + #AnnotationPanelFilter: { + // Should the specified panels be included or excluded + exclude?: bool | *false + + // Panel IDs that should be included or excluded + ids: [...uint8] + } @cuetsy(kind="interface") + + // TODO -- should not be a public interface on its own, but required for Veneer + #AnnotationContainer: { + // annoying... but required so that the list is defined using the nested Veneer + @grafana(TSVeneer="type") + + list?: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview) + } @cuetsy(kind="interface") + // TODO docs // FROM: AnnotationQuery in grafana-data/src/types/annotations.ts #AnnotationQuery: { - // Datasource to use for annotation. + @grafana(TSVeneer="type") + + // Name of annotation. + name: string + + // TODO: Should be DataSourceRef datasource: { type?: string uid?: string } @grafanamaturity(NeedsExpertReview) - // Whether annotation is enabled. - enable: bool | *true @grafanamaturity(NeedsExpertReview) - // Name of annotation. - name?: string @grafanamaturity(NeedsExpertReview) - builtIn: uint8 | *0 @grafanamaturity(NeedsExpertReview) // TODO should this be persisted at all? - // Whether to hide annotation. - hide?: bool | *false @grafanamaturity(NeedsExpertReview) - // Annotation icon color. - iconColor?: string @grafanamaturity(NeedsExpertReview) - type: string | *"dashboard" @grafanamaturity(NeedsExpertReview) - // Query for annotation data. - rawQuery?: string @grafanamaturity(NeedsExpertReview) - showIn: uint8 | *0 @grafanamaturity(NeedsExpertReview) - target?: #AnnotationTarget @grafanamaturity(NeedsExpertReview) + // When enabled the annotation query is issued with every dashboard refresh + enable: bool | *true + + // Annotation queries can be toggled on or off at the top of the dashboard. + // When hide is true, the toggle is not shown in the dashboard. + hide?: bool | *false + + // Color to use for the annotation event markers + iconColor: string + + // Optionally + filter?: #AnnotationPanelFilter + + // TODO.. this should just be a normal query target + target?: #AnnotationTarget + + // TODO -- this should not exist here, it is based on the --grafana-- datasource + type?: string @grafanamaturity(NeedsExpertReview) + + // unless datasources have migrated to the target+mapping, + // they just spread their query into the base object :( + ... } @cuetsy(kind="interface") + #LoadingState: "NotStarted" | "Loading" | "Streaming" | "Done" | "Error" @cuetsy(kind="enum") @grafanamaturity(NeedsExpertReview) + // FROM: packages/grafana-data/src/types/templateVars.ts // TODO docs // TODO what about what's in public/app/features/types.ts? diff --git a/kinds/folder/folder_kind.cue b/kinds/folder/folder_kind.cue new file mode 100644 index 00000000000..b0b5a1b4cc1 --- /dev/null +++ b/kinds/folder/folder_kind.cue @@ -0,0 +1,30 @@ +package kind + +name: "Folder" +maturity: "merged" +description: "A folder is a collection of resources that are grouped together and can share permissions." + +lineage: seqs: [ + { + schemas: [ + //0.0 + { + + spec: { + // Unique folder id. (will be k8s name) + uid: string + + // Folder title + title: string + + // Description of the folder. + description?: string + } @cuetsy(kind="interface") + // + // TODO: + // common metadata will soon support setting the parent folder in the metadata + // + }, + ] + }, +] diff --git a/kinds/gen.go b/kinds/gen.go index bd6bb368c95..00225af2b31 100644 --- a/kinds/gen.go +++ b/kinds/gen.go @@ -38,14 +38,15 @@ func main() { // All the jennies that comprise the core kinds generator pipeline coreKindsGen.Append( - codegen.LatestJenny(cuectx.GoCoreKindParentPath, codegen.GoTypesJenny{}), + &codegen.ResourceGoTypesJenny{}, + &codegen.SubresourceGoTypesJenny{}, codegen.CoreKindJenny(cuectx.GoCoreKindParentPath, nil), codegen.BaseCoreRegistryJenny(filepath.Join("pkg", "registry", "corekind"), cuectx.GoCoreKindParentPath), - codegen.LatestMajorsOrXJenny(cuectx.TSCoreKindParentPath, codegen.TSTypesJenny{}), + codegen.LatestMajorsOrXJenny( + cuectx.TSCoreKindParentPath, + true, // forcing group so that we ignore the top level resource (for now) + codegen.TSResourceJenny{}), codegen.TSVeneerIndexJenny(filepath.Join("packages", "grafana-schema", "src")), - codegen.CRDTypesJenny(cuectx.GoCoreKindParentPath), - codegen.YamlCRDJenny(cuectx.GoCoreKindParentPath), - codegen.CRDKindRegistryJenny(filepath.Join("pkg", "registry", "corecrd")), codegen.DocsJenny(filepath.Join("docs", "sources", "developers", "kinds", "core")), ) diff --git a/kinds/librarypanel/librarypanel_kind.cue b/kinds/librarypanel/librarypanel_kind.cue index 82870b812d3..3b7b187a9df 100644 --- a/kinds/librarypanel/librarypanel_kind.cue +++ b/kinds/librarypanel/librarypanel_kind.cue @@ -14,35 +14,35 @@ lineage: seqs: [ schemas: [ // 0.0 { - @grafana(TSVeneer="type") + spec: { + // Folder UID + folderUid?: string @grafanamaturity(ToMetadata="sys") - // Folder UID - folderUid?: string @grafanamaturity(ToMetadata="sys") + // Library element UID + uid: string - // Library element UID - uid: string + // Panel name (also saved in the model) + name: string & strings.MinRunes(1) - // Panel name (also saved in the model) - name: string & strings.MinRunes(1) + // Panel description + description?: string - // Panel description - description?: string + // The panel type (from inside the model) + type: string & strings.MinRunes(1) - // The panel type (from inside the model) - type: string & strings.MinRunes(1) + // Dashboard version when this was saved (zero if unknown) + schemaVersion?: uint16 - // Dashboard version when this was saved (zero if unknown) - schemaVersion?: uint16 + // panel version, incremented each time the dashboard is updated. + version: int64 @grafanamaturity(NeedsExpertReview) - // panel version, incremented each time the dashboard is updated. - version: int64 @grafanamaturity(NeedsExpertReview) + // TODO: should be the same panel schema defined in dashboard + // Typescript: Omit; + model: {...} - // TODO: should be the same panel schema defined in dashboard - // Typescript: Omit; - model: {...} - - // Object storage metadata - meta?: #LibraryElementDTOMeta @grafanamaturity(ToMetadata="sys") + // Object storage metadata + meta?: #LibraryElementDTOMeta @grafanamaturity(ToMetadata="sys") + } @cuetsy(kind="interface") @grafana(TSVeneer="type") #LibraryElementDTOMetaUser: { id: int64 diff --git a/kinds/playlist/playlist_kind.cue b/kinds/playlist/playlist_kind.cue index 0bf700d3a66..6200ca52149 100644 --- a/kinds/playlist/playlist_kind.cue +++ b/kinds/playlist/playlist_kind.cue @@ -9,20 +9,22 @@ lineage: seqs: [ schemas: [ //0.0 { - // Unique playlist identifier. Generated on creation, either by the - // creator of the playlist of by the application. - uid: string + spec: { + // Unique playlist identifier. Generated on creation, either by the + // creator of the playlist of by the application. + uid: string - // Name of the playlist. - name: string + // Name of the playlist. + name: string - // Interval sets the time between switching views in a playlist. - // FIXME: Is this based on a standardized format or what options are available? Can datemath be used? - interval: string | *"5m" + // Interval sets the time between switching views in a playlist. + // FIXME: Is this based on a standardized format or what options are available? Can datemath be used? + interval: string | *"5m" - // The ordered list of items that the playlist will iterate over. - // FIXME! This should not be optional, but changing it makes the godegen awkward - items?: [...#PlaylistItem] + // The ordered list of items that the playlist will iterate over. + // FIXME! This should not be optional, but changing it makes the godegen awkward + items?: [...#PlaylistItem] + } @cuetsy(kind="interface") /////////////////////////////////////// // Definitions (referenced above) are declared below diff --git a/kinds/preferences/preferences_kind.cue b/kinds/preferences/preferences_kind.cue index 1e140035e66..2ab47fa6559 100644 --- a/kinds/preferences/preferences_kind.cue +++ b/kinds/preferences/preferences_kind.cue @@ -8,29 +8,31 @@ lineage: seqs: [ { schemas: [ { - // UID for the home dashboard - homeDashboardUID?: string + spec: { + // UID for the home dashboard + homeDashboardUID?: string - // The timezone selection - // TODO: this should use the timezone defined in common - timezone?: string + // The timezone selection + // TODO: this should use the timezone defined in common + timezone?: string - // day of the week (sunday, monday, etc) - weekStart?: string + // day of the week (sunday, monday, etc) + weekStart?: string - // light, dark, empty is default - theme?: string + // light, dark, empty is default + theme?: string - // Selected language (beta) - language?: string + // Selected language (beta) + language?: string - // Explore query history preferences - queryHistory?: #QueryHistoryPreference + // Explore query history preferences + queryHistory?: #QueryHistoryPreference + } @cuetsy(kind="interface") #QueryHistoryPreference: { - // one of: '' | 'query' | 'starred'; - homeTab?: string - } @cuetsy(kind="interface") //0.0 + // one of: '' | 'query' | 'starred'; + homeTab?: string + } @cuetsy(kind="interface") }, ] }, diff --git a/kinds/publicdashboard/public_dashboard_kind.cue b/kinds/publicdashboard/public_dashboard_kind.cue index 71f2494fc46..b78295fdb29 100644 --- a/kinds/publicdashboard/public_dashboard_kind.cue +++ b/kinds/publicdashboard/public_dashboard_kind.cue @@ -9,18 +9,20 @@ lineage: seqs: [ schemas: [ // 0.0 { - // Unique public dashboard identifier - uid: string - // Dashboard unique identifier referenced by this public dashboard - dashboardUid: string - // Unique public access token - accessToken?: string - // Flag that indicates if the public dashboard is enabled - isEnabled: bool - // Flag that indicates if annotations are enabled - annotationsEnabled: bool - // Flag that indicates if the time range picker is enabled - timeSelectionEnabled: bool + spec: { + // Unique public dashboard identifier + uid: string + // Dashboard unique identifier referenced by this public dashboard + dashboardUid: string + // Unique public access token + accessToken?: string + // Flag that indicates if the public dashboard is enabled + isEnabled: bool + // Flag that indicates if annotations are enabled + annotationsEnabled: bool + // Flag that indicates if the time range picker is enabled + timeSelectionEnabled: bool + } @cuetsy(kind="interface") }, ] }, diff --git a/kinds/serviceaccount/serviceaccount_kind.cue b/kinds/serviceaccount/serviceaccount_kind.cue index 27f54a17db7..ff2ac9682ce 100644 --- a/kinds/serviceaccount/serviceaccount_kind.cue +++ b/kinds/serviceaccount/serviceaccount_kind.cue @@ -1,7 +1,5 @@ package kind -import "time" - name: "ServiceAccount" maturity: "merged" description: "system account" @@ -11,35 +9,33 @@ lineage: seqs: [ schemas: [ // v0.0 { - // ID is the unique identifier of the service account in the database. - id: int64 @grafanamaturity(ToMetadata="sys") - // OrgId is the ID of an organisation the service account belongs to. - orgId: int64 @grafanamaturity(ToMetadata="sys") - // Name of the service account. - name: string - // Login of the service account. - login: string - // IsDisabled indicates if the service account is disabled. - isDisabled: bool - // Role is the Grafana organization role of the service account which can be 'Viewer', 'Editor', 'Admin'. - role: #OrgRole @grafanamaturity(ToMetadata="kind") - // Tokens is the number of active tokens for the service account. - // Tokens are used to authenticate the service account against Grafana. - tokens: int64 @grafanamaturity(ToMetadata="kind") - // AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front - // of the service account. - avatarUrl: string @grafanamaturity(ToMetadata="kind") - // AccessControl metadata associated with a given resource. - accessControl?: { - [string]: bool @grafanamaturity(ToMetadata="sys") - } + spec: { + // ID is the unique identifier of the service account in the database. + id: int64 @grafanamaturity(ToMetadata="sys") + // OrgId is the ID of an organisation the service account belongs to. + orgId: int64 @grafanamaturity(ToMetadata="sys") + // Name of the service account. + name: string + // Login of the service account. + login: string + // IsDisabled indicates if the service account is disabled. + isDisabled: bool + // Role is the Grafana organization role of the service account which can be 'Viewer', 'Editor', 'Admin'. + role: #OrgRole @grafanamaturity(ToMetadata="kind") + // Tokens is the number of active tokens for the service account. + // Tokens are used to authenticate the service account against Grafana. + tokens: int64 @grafanamaturity(ToMetadata="kind") + // AvatarUrl is the service account's avatar URL. It allows the frontend to display a picture in front + // of the service account. + avatarUrl: string @grafanamaturity(ToMetadata="kind") + // AccessControl metadata associated with a given resource. + accessControl?: { + [string]: bool @grafanamaturity(ToMetadata="sys") + } - // Teams is a list of teams the service account belongs to. - teams?: [...string] @grafanamaturity(ToMetadata="sys") - // Created indicates when the service account was created. - created?: string & time.Time - // Updated indicates when the service account was updated. - updated?: string & time.Time + // Teams is a list of teams the service account belongs to. + teams?: [...string] @grafanamaturity(ToMetadata="sys") + } @cuetsy(kind="interface") // OrgRole is a Grafana Organization Role which can be 'Viewer', 'Editor', 'Admin'. #OrgRole: "Admin" | "Editor" | "Viewer" @cuetsy(kind="type") diff --git a/kinds/team/team_kind.cue b/kinds/team/team_kind.cue index 58d285fa93c..d692dac8173 100644 --- a/kinds/team/team_kind.cue +++ b/kinds/team/team_kind.cue @@ -1,7 +1,5 @@ package kind -import "time" - name: "Team" maturity: "merged" description: "A team is a named grouping of Grafana users to which access control rules may be assigned." @@ -11,26 +9,24 @@ lineage: seqs: [ schemas: [ // v0.0 { - // OrgId is the ID of an organisation the team belongs to. - orgId: int64 @grafanamaturity(ToMetadata="sys") - // Name of the team. - name: string - // Email of the team. - email?: string - // AvatarUrl is the team's avatar URL. - avatarUrl?: string @grafanamaturity(MaybeRemove) - // MemberCount is the number of the team members. - memberCount: int64 @grafanamaturity(ToMetadata="kind") - // TODO - it seems it's a team_member.permission, unlikely it should belong to the team kind - permission: #Permission @grafanamaturity(ToMetadata="kind", MaybeRemove) - // AccessControl metadata associated with a given resource. - accessControl?: { - [string]: bool @grafanamaturity(ToMetadata="sys") - } - // Created indicates when the team was created. - created: string & time.Time - // Updated indicates when the team was updated. - updated: string & time.Time + spec: { + // OrgId is the ID of an organisation the team belongs to. + orgId: int64 @grafanamaturity(ToMetadata="sys") + // Name of the team. + name: string + // Email of the team. + email?: string + // AvatarUrl is the team's avatar URL. + avatarUrl?: string @grafanamaturity(MaybeRemove) + // MemberCount is the number of the team members. + memberCount: int64 @grafanamaturity(ToMetadata="kind") + // TODO - it seems it's a team_member.permission, unlikely it should belong to the team kind + permission: #Permission @grafanamaturity(ToMetadata="kind", MaybeRemove) + // AccessControl metadata associated with a given resource. + accessControl?: { + [string]: bool @grafanamaturity(ToMetadata="sys") + } + } @cuetsy(kind="interface") #Permission: 0 | 1 | 2 | 4 @cuetsy(kind="enum",memberNames="Member|Viewer|Editor|Admin") }, diff --git a/latest.json b/latest.json index bdb193dc663..b789bb2e2c8 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "stable": "9.4.7", - "testing": "9.4.7" + "stable": "9.5.1", + "testing": "9.5.1" } diff --git a/lerna.json b/lerna.json index 75716a4cd76..78f2726b208 100644 --- a/lerna.json +++ b/lerna.json @@ -1,8 +1,6 @@ { "npmClient": "yarn", "useWorkspaces": true, - "packages": [ - "packages/*" - ], - "version": "10.0.0-pre" + "packages": ["packages/*"], + "version": "10.1.0-pre" } diff --git a/package.json b/package.json index 1e8cf53df28..b3ac921b2f2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "license": "AGPL-3.0-only", "private": true, "name": "grafana", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "repository": "github:grafana/grafana", "scripts": { "build": "yarn i18n:compile && NODE_ENV=production webpack --progress --config scripts/webpack/webpack.prod.js", @@ -29,12 +29,6 @@ "packages:clean": "rimraf ./npm-artifacts && lerna run clean --parallel", "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:publish": "lerna exec --no-private -- npm publish package.tgz", - "packages:publishCanary": "lerna exec --no-private -- npm publish package.tgz --tag canary", - "packages:publishLatest": "lerna exec --no-private -- npm publish package.tgz", - "packages:publishNext": "lerna exec --no-private -- npm publish package.tgz --tag next", - "packages:publishTest": "lerna exec --no-private -- npm publish package.tgz --tag test", - "packages:publishDev": "lerna exec --no-private -- npm publish package.tgz --tag dev --registry http://localhost:4873", "packages:typecheck": "lerna run typecheck", "precommit": "yarn run lint-staged", "prettier:check": "prettier --check --list-different=false --loglevel=warn \"**/*.{ts,tsx,scss,md,mdx}\"", @@ -114,19 +108,18 @@ "@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1", "@swc/core": "1.3.38", "@swc/helpers": "0.4.14", - "@testing-library/dom": "8.20.0", + "@testing-library/dom": "9.0.1", "@testing-library/jest-dom": "5.16.5", - "@testing-library/react": "12.1.4", - "@testing-library/react-hooks": "8.0.1", + "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.4.3", "@types/angular": "1.8.4", "@types/angular-route": "1.7.2", + "@types/chance": "^1.1.3", "@types/common-tags": "^1.8.0", "@types/d3": "7.4.0", "@types/d3-force": "^3.0.0", "@types/d3-scale-chromatic": "3.0.0", "@types/debounce-promise": "3.1.6", - "@types/dompurify": "^2", "@types/eslint": "8.21.1", "@types/file-saver": "2.0.5", "@types/glob": "^8.0.0", @@ -140,21 +133,22 @@ "@types/jsurl": "^1.2.28", "@types/lodash": "4.14.191", "@types/logfmt": "^1.2.3", + "@types/marked": "^4", "@types/mousetrap": "1.6.11", "@types/node": "18.14.6", "@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.0.9", "@types/papaparse": "5.3.7", "@types/pluralize": "^0.0.29", "@types/prismjs": "1.26.0", - "@types/react": "17.0.42", + "@types/react": "18.0.28", "@types/react-beautiful-dnd": "13.1.3", - "@types/react-dom": "17.0.14", + "@types/react-dom": "18.0.11", "@types/react-grid-layout": "1.3.2", "@types/react-highlight-words": "0.16.4", "@types/react-redux": "7.1.25", "@types/react-router-dom": "5.3.3", "@types/react-table": "7.7.14", - "@types/react-test-renderer": "17.0.1", + "@types/react-test-renderer": "18.0.0", "@types/react-transition-group": "4.4.5", "@types/react-virtualized-auto-sizer": "1.0.1", "@types/react-window": "1.8.5", @@ -226,7 +220,7 @@ "react-refresh": "0.14.0", "react-select-event": "5.5.1", "react-simple-compat": "1.2.3", - "react-test-renderer": "17.0.2", + "react-test-renderer": "18.2.0", "redux-mock-store": "1.5.4", "rimraf": "4.4.0", "rudder-sdk-js": "2.25.0", @@ -256,6 +250,7 @@ "@daybrush/utils": "1.10.2", "@emotion/css": "11.10.6", "@emotion/react": "11.10.6", + "@glideapps/glide-data-grid": "^5.2.1", "@grafana/aws-sdk": "0.0.44", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", @@ -263,10 +258,10 @@ "@grafana/faro-core": "1.0.2", "@grafana/faro-web-sdk": "1.0.2", "@grafana/google-sdk": "0.1.1", - "@grafana/lezer-logql": "0.1.2", + "@grafana/lezer-logql": "0.1.3", "@grafana/monaco-logql": "^0.0.7", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "^0.3.0", + "@grafana/scenes": "^0.6.0", "@grafana/schema": "workspace:*", "@grafana/ui": "workspace:*", "@kusto/monaco-kusto": "5.3.6", @@ -291,10 +286,10 @@ "@react-stately/menu": "3.4.1", "@react-stately/tree": "3.3.1", "@reduxjs/toolkit": "1.9.3", - "@sentry/browser": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", + "@remix-run/router": "^1.5.0", + "@testing-library/react-hooks": "^8.0.1", "@types/react-resizable": "3.0.3", + "@types/trusted-types": "2.0.3", "@types/webpack-env": "1.18.0", "@visx/event": "3.0.1", "@visx/gradient": "3.0.0", @@ -345,6 +340,7 @@ "logfmt": "^1.3.2", "lru-cache": "7.17.0", "lru-memoize": "^1.1.0", + "marked": "^4.3.0", "memoize-one": "6.0.0", "moment": "2.29.4", "moment-timezone": "0.5.41", @@ -360,17 +356,17 @@ "prismjs": "1.29.0", "prop-types": "15.8.1", "pseudoizer": "^0.1.0", - "rc-cascader": "3.8.0", + "rc-cascader": "3.10.2", "rc-drawer": "6.1.3", "rc-slider": "10.1.1", "rc-time-picker": "3.7.3", "rc-tree": "5.7.2", "re-resizable": "6.9.9", - "react": "17.0.2", + "react": "18.2.0", "react-awesome-query-builder": "5.4.0", "react-beautiful-dnd": "13.1.1", "react-diff-viewer": "^3.1.1", - "react-dom": "17.0.2", + "react-dom": "18.2.0", "react-draggable": "4.4.5", "react-dropzone": "^14.2.3", "react-enable": "^3.1.0", @@ -384,8 +380,9 @@ "react-popper-tooltip": "4.4.2", "react-redux": "7.2.6", "react-resizable": "3.0.4", - "react-reverse-portal": "2.1.1", - "react-router-dom": "^5.2.0", + "react-responsive-carousel": "^3.2.23", + "react-router-dom": "5.3.3", + "react-router-dom-v5-compat": "^6.10.0", "react-select": "5.7.0", "react-split-pane": "0.1.92", "react-table": "7.8.0", @@ -437,7 +434,9 @@ "@storybook/manager-webpack5/webpack": "5.76.0", "ngtemplate-loader/loader-utils": "^2.0.0", "trim": "0.0.3", - "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" + "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", + "@storybook/addon-docs@6.5.16": "patch:@storybook/addon-docs@npm:6.5.16#.yarn/patches/@storybook-addon-docs-npm-6.5.16-56ecbd77e7.patch" }, "workspaces": { "packages": [ diff --git a/packages/README.md b/packages/README.md index 4fe45149509..6fc9c3cd7b7 100644 --- a/packages/README.md +++ b/packages/README.md @@ -36,20 +36,21 @@ Every commit to main that has changes within the `packages` directory is a subje > All of the steps below must be performed on a release branch, according to Grafana Release Guide. -> Make sure you are logged in to npm in your terminal and that you are a part of Grafana org on npm. +> You must be logged in to NPM as part of Grafana NPM org before attempting to publish to the npm registery. -1. Run `yarn packages:prepare` script from the root directory. This performs tests on the packages and prompts for the version of the packages. The version should be the same as the one being released. +1. Run `yarn packages:clean` script from the root directory. This will delete any previous builds of the packages. +2. Run `yarn packages:prepare` script from the root directory. This performs tests on the packages and prompts for the version of the packages. The version should be the same as the one being released. - Make sure you use semver convention. So, _place a dot between prerelease id and prerelease number_, i.e. 6.3.0-alpha.1 - Make sure you confirm the version bump when prompted! -2. Run `yarn packages:build` script that compiles distribution code in `packages/grafana-*/dist`. -3. Run `yarn packages:pack` script to zip each package into `.tgz`. This is required for yarn berry to replace properties in the package.json files declared in `publishConfig`. -4. Depending whether or not it's a prerelease: +3. Run `yarn packages:build` script that compiles distribution code in `packages/grafana-*/dist`. +4. Run `yarn packages:pack` script to compress each package into `npm-artifacts/*.tgz` files. This is required for yarn to replace properties in the package.json files declared in the `publishConfig` property. +5. Depending on whether or not it's a prerelease: - - When releasing a prerelease run `packages:publishNext` to publish new versions. - - When releasing a stable version run `packages:publishLatest` to publish new versions. - - When releasing a test version run `packages:publishTest` to publish test versions. + - When releasing a prerelease run `./scripts/publish-npm-packages.sh --dist-tag 'next' --registry 'https://registry.npmjs.org/'` to publish new versions. + - When releasing a stable version run `./scripts/publish-npm-packages.sh --dist-tag 'latest' --registry 'https://registry.npmjs.org/'` to publish new versions. + - When releasing a test version run `./scripts/publish-npm-packages.sh --dist-tag 'test' --registry 'https://registry.npmjs.org/'` to publish test versions. -5. Push version commit to the release branch. +6. Revert any changes made by the `packages:prepare` script. ### Building individual packages @@ -72,21 +73,21 @@ In this guide you will set up [Verdaccio](https://verdaccio.org/) registry local From your terminal: 1. Navigate to `devenv/local-npm` directory. -2. Run `docker-compose up`. This will start your local npm registry, available at http://localhost:4873/ -3. Run `npm login --registry=http://localhost:4873 --scope=@grafana` . This will allow you to publish any @grafana/\* package into the local registry. -4. Run `npm config set @grafana:registry http://localhost:4873`. This will config your npm to install @grafana scoped packages from your local registry. +2. Run `docker-compose up`. This will start your local npm registry, available at http://localhost:4873/. Note the verdaccio config allows +3. To test `@grafana` packages published to your local npm registry uncomment `npmScopes` and `unsafeHttpWhitelist` properties in the `.yarnrc` file. #### Publishing packages to local npm registry -You need to follow [manual packages release procedure](#manual-release). The only difference is you need to run `yarn packages:publishDev` task in order to publish to you local registry. +You need to follow [manual packages release procedure](#manual-release). The only difference is the last command in order to publish to you local registry. From your terminal: -1. Run `yarn packages:prepare`. -2. Run `yarn packages:build`. -3. Run `yarn packages:pack`. -4. Run `yarn packages:publishDev`. -5. Navigate to http://localhost:4873 and verify that version was published +1. Run `yarn packages:clean`. +2. Run `yarn packages:prepare`. +3. Run `yarn packages:build`. +4. Run `yarn packages:pack`. +5. Run `./scripts/publish-npm-packages.sh`. +6. Navigate to http://localhost:4873 and verify the version was published Locally published packages will be published under `dev` channel, so in your plugin package.json file you can use that channel. For example: diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 70497f3d1ed..088a1b76ee7 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/data", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana Data Library", "keywords": [ "typescript" @@ -36,8 +36,9 @@ }, "dependencies": { "@braintree/sanitize-url": "6.0.2", - "@grafana/schema": "10.0.0-pre", + "@grafana/schema": "10.1.0-pre", "@types/d3-interpolate": "^3.0.0", + "@types/string-hash": "1.1.1", "d3-interpolate": "3.0.1", "date-fns": "2.29.3", "dompurify": "^2.4.3", @@ -53,6 +54,7 @@ "react-use": "17.4.0", "regenerator-runtime": "0.13.11", "rxjs": "7.8.0", + "string-hash": "^1.1.3", "tinycolor2": "1.6.0", "tslib": "2.5.0", "uplot": "1.6.24", @@ -63,10 +65,9 @@ "@rollup/plugin-commonjs": "23.0.2", "@rollup/plugin-json": "5.0.1", "@rollup/plugin-node-resolve": "15.0.1", - "@testing-library/dom": "8.20.0", + "@testing-library/dom": "9.0.1", "@testing-library/jest-dom": "5.16.5", - "@testing-library/react": "12.1.4", - "@testing-library/react-hooks": "8.0.1", + "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.4.3", "@types/dompurify": "^2", "@types/history": "4.7.11", @@ -76,15 +77,15 @@ "@types/marked": "4.0.8", "@types/node": "18.14.6", "@types/papaparse": "5.3.7", - "@types/react": "17.0.42", - "@types/react-dom": "17.0.14", + "@types/react": "18.0.28", + "@types/react-dom": "18.0.11", "@types/sinon": "10.0.13", "@types/testing-library__jest-dom": "5.14.5", "@types/tinycolor2": "1.4.3", "esbuild": "0.16.17", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-test-renderer": "17.0.2", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-test-renderer": "18.2.0", "rimraf": "4.4.0", "rollup": "2.79.1", "rollup-plugin-dts": "^5.0.0", @@ -94,7 +95,7 @@ "typescript": "4.8.4" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" } } diff --git a/packages/grafana-data/src/dataframe/ArrayDataFrame.test.ts b/packages/grafana-data/src/dataframe/ArrayDataFrame.test.ts index c0980c39481..75222ede2d0 100644 --- a/packages/grafana-data/src/dataframe/ArrayDataFrame.test.ts +++ b/packages/grafana-data/src/dataframe/ArrayDataFrame.test.ts @@ -1,6 +1,6 @@ -import { FieldType, DataFrame } from '../types'; +import { DataFrame } from '../types'; -import { ArrayDataFrame } from './ArrayDataFrame'; +import { ArrayDataFrame, arrayToDataFrame } from './ArrayDataFrame'; import { toDataFrameDTO } from './processDataFrame'; describe('Array DataFrame', () => { @@ -15,29 +15,9 @@ describe('Array DataFrame', () => { const frame = new ArrayDataFrame(input); frame.name = 'Hello'; frame.refId = 'Z'; - frame.setFieldType('phantom', FieldType.string, (v) => '🦥'); const field = frame.fields.find((f) => f.name === 'value'); field!.config.unit = 'kwh'; - test('Should support functional methods', () => { - const expectedNames = input.map((row) => row.name); - - // Check map - expect(frame.map((row) => row.name)).toEqual(expectedNames); - - let names: string[] = []; - for (const row of frame) { - names.push(row.name); - } - expect(names).toEqual(expectedNames); - - names = []; - frame.forEach((row) => { - names.push(row.name); - }); - expect(names).toEqual(expectedNames); - }); - test('Should convert an array of objects to a dataframe', () => { expect(toDataFrameDTO(frame)).toMatchInlineSnapshot(` { @@ -83,19 +63,6 @@ describe('Array DataFrame', () => { 1100, ], }, - { - "config": {}, - "labels": undefined, - "name": "phantom", - "type": "string", - "values": [ - "🦥", - "🦥", - "🦥", - "🦥", - "🦥", - ], - }, ], "meta": undefined, "name": "Hello", @@ -113,4 +80,25 @@ describe('Array DataFrame', () => { expect(copy.length).toEqual(frame.length); expect(copy.length).toEqual(input.length); }); + + test('Handles any array input', () => { + const f = arrayToDataFrame([1, 2, 3]); + expect(f).toMatchInlineSnapshot(` + { + "fields": [ + { + "config": {}, + "name": "Value", + "type": "number", + "values": [ + 1, + 2, + 3, + ], + }, + ], + "length": 3, + } + `); + }); }); diff --git a/packages/grafana-data/src/dataframe/ArrayDataFrame.ts b/packages/grafana-data/src/dataframe/ArrayDataFrame.ts index 2b6132526ad..ae8b821175e 100644 --- a/packages/grafana-data/src/dataframe/ArrayDataFrame.ts +++ b/packages/grafana-data/src/dataframe/ArrayDataFrame.ts @@ -1,114 +1,69 @@ -import { Vector, QueryResultMeta } from '../types'; -import { Field, FieldType, DataFrame } from '../types/dataFrame'; -import { FunctionalVector } from '../vector/FunctionalVector'; -import { vectorToArray } from '../vector/vectorToArray'; +import { QueryResultMeta } from '../types'; +import { Field, FieldType, DataFrame, TIME_SERIES_VALUE_FIELD_NAME } from '../types/dataFrame'; -import { guessFieldTypeFromNameAndValue, toDataFrameDTO } from './processDataFrame'; - -/** @public */ -export type ValueConverter = (val: unknown) => T; - -const NOOP: ValueConverter = (v) => v; - -class ArrayPropertyVector implements Vector { - converter = NOOP; - - constructor(private source: any[], private prop: string) {} - - get length(): number { - return this.source.length; - } - - get(index: number): T { - return this.converter(this.source[index][this.prop]); - } - - toArray(): T[] { - return vectorToArray(this); - } - - toJSON(): T[] { - return vectorToArray(this); - } -} +import { guessFieldTypeForField } from './processDataFrame'; /** * The ArrayDataFrame takes an array of objects and presents it as a DataFrame * - * @alpha + * @deprecated use arrayToDataFrame */ -export class ArrayDataFrame extends FunctionalVector implements DataFrame { +export class ArrayDataFrame implements DataFrame { + fields: Field[] = []; + length = 0; name?: string; refId?: string; meta?: QueryResultMeta; - fields: Field[] = []; - length = 0; - - constructor(private source: T[], names?: string[]) { - super(); - - this.length = source.length; - const first: any = source.length ? source[0] : {}; - if (names) { - this.fields = names.map((name) => { - return { - name, - type: guessFieldTypeFromNameAndValue(name, first[name]), - config: {}, - values: new ArrayPropertyVector(source, name), - }; - }); - } else { - this.setFieldsFromObject(first); - } - } - - /** - * Add a field for each property in the object. This will guess the type - */ - setFieldsFromObject(obj: Record) { - this.fields = Object.keys(obj).map((name) => { - return { - name, - type: guessFieldTypeFromNameAndValue(name, obj[name]), - config: {}, - values: new ArrayPropertyVector(this.source, name), - }; - }); - } - - /** - * Configure how the object property is passed to the data frame - */ - setFieldType(name: string, type: FieldType, converter?: ValueConverter): Field { - let field = this.fields.find((f) => f.name === name); - if (field) { - field.type = type; - } else { - field = { - name, - type, - config: {}, - values: new ArrayPropertyVector(this.source, name), - }; - this.fields.push(field); - } - (field.values as any).converter = converter ?? NOOP; - return field; - } - - /** - * Get an object with a property for each field in the DataFrame - */ - get(idx: number): T { - return this.source[idx]; - } - - /** - * The simplified JSON values used in JSON.stringify() - */ - toJSON() { - return toDataFrameDTO(this); + constructor(source: T[], names?: string[]) { + return arrayToDataFrame(source, names) as ArrayDataFrame; // returns a standard DataFrame } } + +/** + * arrayToDataFrame will convert any array into a DataFrame + * + * @public + */ +export function arrayToDataFrame(source: any[], names?: string[]): DataFrame { + const df: DataFrame = { + fields: [], + length: source.length, + }; + if (!source?.length) { + return df; + } + + if (names) { + for (const name of names) { + df.fields.push( + makeFieldFromValues( + name, + source.map((v) => v[name]) + ) + ); + } + return df; + } + + const first = source.find((v) => v != null); // first not null|undefined + if (first != null) { + if (typeof first === 'object') { + df.fields = Object.keys(first).map((name) => { + return makeFieldFromValues( + name, + source.map((v) => v[name]) + ); + }); + } else { + df.fields.push(makeFieldFromValues(TIME_SERIES_VALUE_FIELD_NAME, source)); + } + } + return df; +} + +function makeFieldFromValues(name: string, values: unknown[]): Field { + const f = { name, config: {}, values, type: FieldType.other }; + f.type = guessFieldTypeForField(f) ?? FieldType.other; + return f; +} diff --git a/packages/grafana-data/src/dataframe/DataFrameJSON.test.ts b/packages/grafana-data/src/dataframe/DataFrameJSON.test.ts index fe6930eaf2f..035edd46550 100644 --- a/packages/grafana-data/src/dataframe/DataFrameJSON.test.ts +++ b/packages/grafana-data/src/dataframe/DataFrameJSON.test.ts @@ -1,4 +1,3 @@ -import { ArrayVector } from '..'; import { DataFrame, FieldType } from '../types/dataFrame'; import { DataFrameJSON, dataFrameFromJSON, dataFrameToJSON } from './DataFrameJSON'; @@ -203,13 +202,13 @@ describe('DataFrame JSON', () => { name: 'time1', type: FieldType.time, config: {}, - values: new ArrayVector([11, 12, 13]), + values: [11, 12, 13], }, { name: 'time2', type: FieldType.time, config: {}, - values: new ArrayVector([14, 15, 16]), + values: [14, 15, 16], nanos: [17, 18, 19], }, ], @@ -235,7 +234,7 @@ describe('DataFrame JSON', () => { }, }; - expect(dataFrameToJSON(inputFrame)).toStrictEqual(expectedJSON); + expect(dataFrameToJSON(inputFrame)).toEqual(expectedJSON); }); }); }); diff --git a/packages/grafana-data/src/dataframe/DataFrameJSON.ts b/packages/grafana-data/src/dataframe/DataFrameJSON.ts index 47b64fe72b6..c56842b33b6 100644 --- a/packages/grafana-data/src/dataframe/DataFrameJSON.ts +++ b/packages/grafana-data/src/dataframe/DataFrameJSON.ts @@ -1,5 +1,4 @@ import { DataFrame, FieldType, FieldConfig, Labels, QueryResultMeta, Field } from '../types'; -import { ArrayVector } from '../vector'; import { guessFieldTypeFromNameAndValue } from './processDataFrame'; @@ -203,7 +202,7 @@ export function dataFrameFromJSON(dto: DataFrameJSON): DataFrame { ...f, type: type ?? guessFieldType(f.name, buffer), config: f.config ?? {}, - values: new ArrayVector(buffer), + values: buffer, // the presence of this prop is an optimization signal & lookup for consumers entities: entities ?? {}, }; @@ -242,7 +241,7 @@ export function dataFrameToJSON(frame: DataFrame): DataFrameJSON { fields: frame.fields.map((f) => { const { values, nanos, state, display, ...sfield } = f; delete (sfield as any).entities; - data.values.push(values.toArray()); + data.values.push(values); if (nanos != null) { allNanos.push(nanos); diff --git a/packages/grafana-data/src/dataframe/DataFrameView.ts b/packages/grafana-data/src/dataframe/DataFrameView.ts index 9f75cc701e7..33cac6a5144 100644 --- a/packages/grafana-data/src/dataframe/DataFrameView.ts +++ b/packages/grafana-data/src/dataframe/DataFrameView.ts @@ -32,7 +32,7 @@ export class DataFrameView extends FunctionalVector { } fields[field.name] = field; - const getter = () => field.values.get(this.index); + const getter = () => field.values.get(this.index); // .get() to support all Vector types if (!(obj as any).hasOwnProperty(field.name)) { Object.defineProperty(obj, field.name, { diff --git a/packages/grafana-data/src/dataframe/FieldCache.test.ts b/packages/grafana-data/src/dataframe/FieldCache.test.ts index f6d5a90c8ca..b5f5b861b64 100644 --- a/packages/grafana-data/src/dataframe/FieldCache.test.ts +++ b/packages/grafana-data/src/dataframe/FieldCache.test.ts @@ -92,7 +92,7 @@ describe('FieldCache', () => { it('should get the first field with a duplicate name', () => { const field = fieldCache.getFieldByName('value'); expect(field!.name).toEqual('value'); - expect(field!.values.toArray()).toEqual([1, 2, 3]); + expect(field!.values).toEqual([1, 2, 3]); }); it('should return index of the field', () => { diff --git a/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts b/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts index a22ff61c746..e76f54d839d 100644 --- a/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts +++ b/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts @@ -15,15 +15,15 @@ describe('Reversing DataFrame', () => { const helper = new MutableDataFrame(frame); - expect(helper.fields[0].values.toArray()).toEqual([100, 200, 300]); - expect(helper.fields[1].values.toArray()).toEqual(['a', 'b', 'c']); - expect(helper.fields[2].values.toArray()).toEqual([1, 2, 3]); + expect(helper.fields[0].values).toEqual([100, 200, 300]); + expect(helper.fields[1].values).toEqual(['a', 'b', 'c']); + expect(helper.fields[2].values).toEqual([1, 2, 3]); helper.reverse(); - expect(helper.fields[0].values.toArray()).toEqual([300, 200, 100]); - expect(helper.fields[1].values.toArray()).toEqual(['c', 'b', 'a']); - expect(helper.fields[2].values.toArray()).toEqual([3, 2, 1]); + expect(helper.fields[0].values).toEqual([300, 200, 100]); + expect(helper.fields[1].values).toEqual(['c', 'b', 'a']); + expect(helper.fields[2].values).toEqual([3, 2, 1]); }); }); }); @@ -39,7 +39,7 @@ describe('Apending DataFrame', () => { }; const frame = new MutableDataFrame(dto); - expect(frame.fields[0].values.toArray()).toEqual([100, undefined, undefined]); + expect(frame.fields[0].values).toEqual([100, undefined, undefined]); // Set a value on the second row frame.set(1, { time: 200, name: 'BB', value: 20 }); diff --git a/packages/grafana-data/src/dataframe/MutableDataFrame.ts b/packages/grafana-data/src/dataframe/MutableDataFrame.ts index 8d5c6964147..7d96a8f592c 100644 --- a/packages/grafana-data/src/dataframe/MutableDataFrame.ts +++ b/packages/grafana-data/src/dataframe/MutableDataFrame.ts @@ -2,26 +2,31 @@ import { isString } from 'lodash'; import { QueryResultMeta } from '../types/data'; import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame'; -import { MutableVector, Vector } from '../types/vector'; import { makeFieldParser } from '../utils/fieldParser'; -import { ArrayVector } from '../vector/ArrayVector'; import { FunctionalVector } from '../vector/FunctionalVector'; import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame'; -export type MutableField = Field>; +/** @deprecated */ +export type MutableField = Field; -type MutableVectorCreator = (buffer?: any[]) => MutableVector; +/** @deprecated */ +type MutableVectorCreator = (buffer?: any[]) => any[]; export const MISSING_VALUE = undefined; // Treated as connected in new graph panel -export class MutableDataFrame extends FunctionalVector implements DataFrame, MutableVector { +/** + * MutableDataFrame is a complex wrapper around the DataFrame interface + * + * @deprecated use standard DataFrame, or create one with PartialDataFrame + */ +export class MutableDataFrame extends FunctionalVector implements DataFrame { name?: string; refId?: string; meta?: QueryResultMeta; fields: MutableField[] = []; - private first: Vector = new ArrayVector(); + private first: any[] = []; private creator: MutableVectorCreator; constructor(source?: DataFrame | DataFrameDTO, creator?: MutableVectorCreator) { @@ -31,7 +36,7 @@ export class MutableDataFrame extends FunctionalVector implements Da this.creator = creator ? creator : (buffer?: any[]) => { - return new ArrayVector(buffer); + return buffer ?? []; }; // Copy values from @@ -67,22 +72,18 @@ export class MutableDataFrame extends FunctionalVector implements Da return this.first.length; } - addFieldFor(value: unknown, name?: string): MutableField { + addFieldFor(value: unknown, name?: string): Field { return this.addField({ name: name || '', // Will be filled in type: guessFieldTypeFromValue(value), }); } - addField(f: Field | FieldDTO, startLength?: number): MutableField { + addField(f: Field | FieldDTO, startLength?: number): Field { let buffer: any[] | undefined = undefined; if (f.values) { - if (Array.isArray(f.values)) { - buffer = f.values; - } else { - buffer = (f.values as Vector).toArray(); - } + buffer = f.values; } let type = f.type; @@ -104,7 +105,7 @@ export class MutableDataFrame extends FunctionalVector implements Da name = `Field ${this.fields.length + 1}`; } - const field: MutableField = { + const field: Field = { ...f, name, type, @@ -125,7 +126,7 @@ export class MutableDataFrame extends FunctionalVector implements Da // Make sure the field starts with a given length if (startLength) { while (field.values.length < startLength) { - field.values.add(MISSING_VALUE); + field.values.push(MISSING_VALUE); } } else { this.validate(); @@ -143,20 +144,11 @@ export class MutableDataFrame extends FunctionalVector implements Da // Add empty elements until everything matches for (const field of this.fields) { while (field.values.length !== length) { - field.values.add(MISSING_VALUE); + field.values.push(MISSING_VALUE); } } } - /** - * Reverse all values - */ - reverse() { - for (const f of this.fields) { - f.values.reverse(); - } - } - private parsers: Map any> | undefined = undefined; /** @@ -206,14 +198,29 @@ export class MutableDataFrame extends FunctionalVector implements Da if (f.type !== FieldType.string && isString(v)) { v = this.parseValue(f, v); } - f.values.add(v); + f.values.push(v); } } + /** support standard array push syntax */ + push(...vals: T[]): number { + for (const v of vals) { + this.add(v); + } + return this.length; + } + + reverse() { + for (const field of this.fields) { + field.values.reverse(); + } + return this; + } + /** * Add values from an object to corresponding fields. Similar to appendRow but does not create new fields. */ - add(value: T) { + add(value: T): void { // Will add one value for every field const obj = value as any; for (const field of this.fields) { @@ -227,7 +234,7 @@ export class MutableDataFrame extends FunctionalVector implements Da val = MISSING_VALUE; } - field.values.add(val); + field.values.push(val); } } @@ -238,7 +245,7 @@ export class MutableDataFrame extends FunctionalVector implements Da const obj = (value as any) || {}; for (const field of this.fields) { - field.values.set(index, obj[field.name]); + field.values[index] = obj[field.name]; } } @@ -248,7 +255,7 @@ export class MutableDataFrame extends FunctionalVector implements Da get(idx: number): T { const v: any = {}; for (const field of this.fields) { - v[field.name] = field.values.get(idx); + v[field.name] = field.values[idx]; } return v as T; } diff --git a/packages/grafana-data/src/dataframe/dimensions.ts b/packages/grafana-data/src/dataframe/dimensions.ts index 629ddcbae7c..80058770514 100644 --- a/packages/grafana-data/src/dataframe/dimensions.ts +++ b/packages/grafana-data/src/dataframe/dimensions.ts @@ -29,11 +29,11 @@ export const getColumnFromDimension = (dimension: Dimension, column: number) => }; export const getValueFromDimension = (dimension: Dimension, column: number, row: number) => { - return dimension.columns[column].values.get(row); + return dimension.columns[column].values[row]; }; export const getAllValuesFromDimension = (dimension: Dimension, column: number, row: number) => { - return dimension.columns.map((c) => c.values.get(row)); + return dimension.columns.map((c) => c.values[row]); }; export const getDimensionByName = (dimensions: Dimensions, name: string) => dimensions[name]; diff --git a/packages/grafana-data/src/dataframe/processDataFrame.test.ts b/packages/grafana-data/src/dataframe/processDataFrame.test.ts index ad08566a507..4d5c6485004 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.test.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.test.ts @@ -2,8 +2,8 @@ import { dateTime } from '../datetime/moment_wrapper'; import { DataFrameDTO, FieldType, TableData, TimeSeries } from '../types/index'; import { ArrayDataFrame } from './ArrayDataFrame'; -import { MutableDataFrame } from './MutableDataFrame'; import { + createDataFrame, guessFieldTypeFromValue, guessFieldTypes, isDataFrame, @@ -83,10 +83,9 @@ describe('toDataFrame', () => { { a: 1, b: 2 }, { a: 3, b: 4 }, ]; - const array = new ArrayDataFrame(orig); + const array = new ArrayDataFrame(orig); // will return a simple DataFrame const frame = toDataFrame(array); expect(frame).toEqual(array); - expect(frame instanceof ArrayDataFrame).toEqual(true); expect(frame.length).toEqual(orig.length); expect(frame.fields.map((f) => f.name)).toEqual(['a', 'b']); }); @@ -131,7 +130,7 @@ describe('toDataFrame', () => { }); it('Guess Column Types from series', () => { - const series = new MutableDataFrame({ + const series = createDataFrame({ fields: [ { name: 'A (number)', values: [123, null] }, { name: 'B (strings)', values: [null, 'Hello'] }, @@ -198,7 +197,7 @@ describe('toDataFrame', () => { }, }; const dataFrame = toDataFrame(msg); - expect(dataFrame.fields.map((f) => ({ [f.name]: f.values.toArray() }))).toMatchInlineSnapshot(` + expect(dataFrame.fields.map((f) => ({ [f.name]: f.values }))).toMatchInlineSnapshot(` [ { "First": [ @@ -366,14 +365,14 @@ describe('sorted DataFrame', () => { it('Should sort numbers', () => { const sorted = sortDataFrame(frame, 0, true); expect(sorted.length).toEqual(3); - expect(sorted.fields[0].values.toArray()).toEqual([3, 2, 1]); - expect(sorted.fields[1].values.toArray()).toEqual(['c', 'b', 'a']); + expect(sorted.fields[0].values).toEqual([3, 2, 1]); + expect(sorted.fields[1].values).toEqual(['c', 'b', 'a']); }); it('Should sort strings', () => { const sorted = sortDataFrame(frame, 1, true); expect(sorted.length).toEqual(3); - expect(sorted.fields[0].values.toArray()).toEqual([3, 2, 1]); - expect(sorted.fields[1].values.toArray()).toEqual(['c', 'b', 'a']); + expect(sorted.fields[0].values).toEqual([3, 2, 1]); + expect(sorted.fields[1].values).toEqual(['c', 'b', 'a']); }); }); diff --git a/packages/grafana-data/src/dataframe/processDataFrame.ts b/packages/grafana-data/src/dataframe/processDataFrame.ts index fa60304f871..09e67d8349d 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.ts @@ -22,14 +22,11 @@ import { DataQueryResponseData, PanelData, LoadingState, + GraphSeriesValue, } from '../types/index'; -import { ArrayVector } from '../vector/ArrayVector'; -import { SortedVector } from '../vector/SortedVector'; -import { vectorToArray } from '../vector/vectorToArray'; -import { ArrayDataFrame } from './ArrayDataFrame'; +import { arrayToDataFrame } from './ArrayDataFrame'; import { dataFrameFromJSON } from './DataFrameJSON'; -import { MutableDataFrame } from './MutableDataFrame'; function convertTableToDataFrame(table: TableData): DataFrame { const fields = table.columns.map((c) => { @@ -38,7 +35,7 @@ function convertTableToDataFrame(table: TableData): DataFrame { return { name: text?.length ? text : c, // rename 'text' to the 'name' field config: (disp || {}) as FieldConfig, - values: new ArrayVector(), + values: [] as unknown[], type: type && Object.values(FieldType).includes(type as FieldType) ? (type as FieldType) : FieldType.other, }; }); @@ -49,7 +46,7 @@ function convertTableToDataFrame(table: TableData): DataFrame { for (const row of table.rows) { for (let i = 0; i < fields.length; i++) { - fields[i].values.buffer.push(row[i]); + fields[i].values.push(row[i]); } } @@ -87,7 +84,7 @@ function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame { name: TIME_SERIES_TIME_FIELD_NAME, type: FieldType.time, config: {}, - values: new ArrayVector(times), + values: times, }, { name: TIME_SERIES_VALUE_FIELD_NAME, @@ -95,7 +92,7 @@ function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame { config: { unit: timeSeries.unit, }, - values: new ArrayVector(values), + values: values, labels: timeSeries.tags, }, ]; @@ -118,13 +115,13 @@ function convertTimeSeriesToDataFrame(timeSeries: TimeSeries): DataFrame { * to DataFrame. See: https://github.com/grafana/grafana/issues/18528 */ function convertGraphSeriesToDataFrame(graphSeries: GraphSeriesXY): DataFrame { - const x = new ArrayVector(); - const y = new ArrayVector(); + const x: GraphSeriesValue[] = []; + const y: GraphSeriesValue[] = []; for (let i = 0; i < graphSeries.data.length; i++) { const row = graphSeries.data[i]; - x.buffer.push(row[1]); - y.buffer.push(row[0]); + x.push(row[1]); + y.push(row[0]); } return { @@ -145,7 +142,7 @@ function convertGraphSeriesToDataFrame(graphSeries: GraphSeriesXY): DataFrame { values: y, }, ], - length: x.buffer.length, + length: x.length, }; } @@ -159,12 +156,12 @@ function convertJSONDocumentDataToDataFrame(timeSeries: TimeSeries): DataFrame { unit: timeSeries.unit, filterable: (timeSeries as any).filterable, }, - values: new ArrayVector(), + values: [] as TimeSeriesValue[][], }, ]; for (const point of timeSeries.datapoints) { - fields[0].values.buffer.push(point); + fields[0].values.push(point); } return { @@ -263,7 +260,7 @@ export function guessFieldTypeForField(field: Field): FieldType | undefined { // 2. Check the first non-null value for (let i = 0; i < field.values.length; i++) { - const v = field.values.get(i); + const v = field.values[i]; if (v != null) { return guessFieldTypeFromValue(v); } @@ -317,7 +314,7 @@ export function toDataFrame(data: any): DataFrame { } // This will convert the array values into Vectors - return new MutableDataFrame(data as DataFrameDTO); + return createDataFrame(data as DataFrameDTO); } // Handle legacy docs/json type @@ -341,7 +338,7 @@ export function toDataFrame(data: any): DataFrame { } if (Array.isArray(data)) { - return new ArrayDataFrame(data); + return arrayToDataFrame(data); } console.warn('Can not convert', data); @@ -352,7 +349,7 @@ export const toLegacyResponseData = (frame: DataFrame): TimeSeries | TableData = const { fields } = frame; const rowCount = frame.length; - const rows: any[][] = []; + const rows: unknown[][] = []; if (fields.length === 2) { const { timeField, timeIndex } = getTimeField(frame); @@ -364,8 +361,8 @@ export const toLegacyResponseData = (frame: DataFrame): TimeSeries | TableData = // Make sure it is [value,time] for (let i = 0; i < rowCount; i++) { rows.push([ - valueField.values.get(i), // value - timeField.values.get(i), // time + valueField.values[i], // value + timeField.values[i], // time ]); } @@ -381,9 +378,9 @@ export const toLegacyResponseData = (frame: DataFrame): TimeSeries | TableData = } for (let i = 0; i < rowCount; i++) { - const row: any[] = []; + const row: unknown[] = []; for (let j = 0; j < fields.length; j++) { - row.push(fields[j].values.get(i)); + row.push(fields[j].values[i]); } rows.push(row); } @@ -392,7 +389,7 @@ export const toLegacyResponseData = (frame: DataFrame): TimeSeries | TableData = return { alias: fields[0].name || frame.name, target: fields[0].name || frame.name, - datapoints: fields[0].values.toArray(), + datapoints: fields[0].values, filterable: fields[0].config ? fields[0].config.filterable : undefined, type: 'docs', } as TimeSeries; @@ -436,7 +433,7 @@ export function sortDataFrame(data: DataFrame, sortIndex?: number, reverse = fal fields: data.fields.map((f) => { return { ...f, - values: new SortedVector(f.values, index), + values: f.values.map((v, i) => f.values[index[i]]), }; }), }; @@ -449,11 +446,11 @@ export function reverseDataFrame(data: DataFrame): DataFrame { return { ...data, fields: data.fields.map((f) => { - const copy = [...f.values.toArray()]; - copy.reverse(); + const values = [...f.values]; + values.reverse(); return { ...f, - values: new ArrayVector(copy), + values, }; }), }; @@ -462,10 +459,10 @@ export function reverseDataFrame(data: DataFrame): DataFrame { /** * Wrapper to get an array from each field value */ -export function getDataFrameRow(data: DataFrame, row: number): any[] { - const values: any[] = []; +export function getDataFrameRow(data: DataFrame, row: number): unknown[] { + const values: unknown[] = []; for (const field of data.fields) { - values.push(field.values.get(row)); + values.push(field.values[row]); } return values; } @@ -480,11 +477,7 @@ export function toDataFrameDTO(data: DataFrame): DataFrameDTO { export function toFilteredDataFrameDTO(data: DataFrame, fieldPredicate?: (f: Field) => boolean): DataFrameDTO { const filteredFields = fieldPredicate ? data.fields.filter(fieldPredicate) : data.fields; const fields: FieldDTO[] = filteredFields.map((f) => { - let values = f.values.toArray(); - // The byte buffers serialize like objects - if (values instanceof Float64Array) { - values = vectorToArray(f.values); - } + let values = f.values; return { name: f.name, type: f.type, @@ -573,3 +566,35 @@ export function preProcessPanelData(data: PanelData, lastResult?: PanelData): Pa timings: { dataProcessingTime: STOPTIME - STARTTIME }, }; } + +export interface PartialDataFrame extends Omit { + fields: Array>; +} + +export function createDataFrame(input: PartialDataFrame): DataFrame { + let length = 0; + const fields = input.fields.map((p, idx) => { + const { state, ...field } = p; + if (!field.name) { + field.name = `Field ${idx + 1}`; + } + if (!field.config) { + field.config = {}; + } + if (!field.values) { + field.values = new Array(length); + } else if (field.values.length > length) { + length = field.values.length; + } + if (!field.type) { + field.type = guessFieldTypeForField(field as Field) ?? FieldType.other; + } + return field as Field; + }); + + return { + ...input, + fields, + length, + }; +} diff --git a/packages/grafana-data/src/datetime/parser.test.ts b/packages/grafana-data/src/datetime/parser.test.ts index f9756c8b736..f19ddb87ae7 100644 --- a/packages/grafana-data/src/datetime/parser.test.ts +++ b/packages/grafana-data/src/datetime/parser.test.ts @@ -2,6 +2,11 @@ import { systemDateFormats, SystemDateFormatsState } from './formats'; import { dateTimeParse } from './parser'; describe('dateTimeParse', () => { + it('should parse using the systems configured timezone', () => { + const date = dateTimeParse('2020-03-02 15:00:22'); + expect(date.format()).toEqual('2020-03-02T15:00:22-05:00'); + }); + it('should be able to parse using default format', () => { const date = dateTimeParse('2020-03-02 15:00:22', { timeZone: 'utc' }); expect(date.format()).toEqual('2020-03-02T15:00:22Z'); diff --git a/packages/grafana-data/src/datetime/rangeutil.test.ts b/packages/grafana-data/src/datetime/rangeutil.test.ts index 7f48135d928..8fa40c02444 100644 --- a/packages/grafana-data/src/datetime/rangeutil.test.ts +++ b/packages/grafana-data/src/datetime/rangeutil.test.ts @@ -1,10 +1,57 @@ -import { TimeRange } from '../types/time'; +import { RawTimeRange, TimeRange } from '../types/time'; import { timeRangeToRelative } from './rangeutil'; import { dateTime, rangeUtil } from './index'; describe('Range Utils', () => { + // These tests probably wrap the dateTimeParser tests to some extent + describe('convertRawToRange', () => { + const DEFAULT_DATE_VALUE = '1996-07-30 16:00:00'; // Default format YYYY-MM-DD HH:mm:ss + const DEFAULT_DATE_VALUE_FORMATTED = '1996-07-30T16:00:00-06:00'; + const defaultRawTimeRange = { + from: DEFAULT_DATE_VALUE, + to: '1996-07-30 16:20:00', + }; + + it('should serialize the default format by default', () => { + const deserialized = rangeUtil.convertRawToRange(defaultRawTimeRange); + expect(deserialized.from.format()).toBe(DEFAULT_DATE_VALUE_FORMATTED); + }); + + it('should serialize using custom formats', () => { + const NON_DEFAULT_FORMAT = 'DD-MM-YYYY HH:mm:ss'; + const nonDefaultRawTimeRange: RawTimeRange = { + from: '30-07-1996 16:00:00', + to: '30-07-1996 16:20:00', + }; + + const deserializedTimeRange = rangeUtil.convertRawToRange( + nonDefaultRawTimeRange, + undefined, + undefined, + NON_DEFAULT_FORMAT + ); + expect(deserializedTimeRange.from.format()).toBe(DEFAULT_DATE_VALUE_FORMATTED); + }); + + it('should take timezone into account', () => { + const deserializedTimeRange = rangeUtil.convertRawToRange(defaultRawTimeRange, 'UTC'); + expect(deserializedTimeRange.from.format()).toBe('1996-07-30T16:00:00Z'); + }); + + it('should leave the raw part intact if it has calulactions', () => { + const timeRange = { + from: DEFAULT_DATE_VALUE, + to: 'now', + }; + + const deserialized = rangeUtil.convertRawToRange(timeRange); + expect(deserialized.raw).toStrictEqual(timeRange); + expect(deserialized.to.toString()).not.toBe(deserialized.raw.to); + }); + }); + describe('relative time', () => { it('should identify absolute vs relative', () => { expect( diff --git a/packages/grafana-data/src/datetime/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts index 626780d7f25..29e4d820cc3 100644 --- a/packages/grafana-data/src/datetime/rangeutil.ts +++ b/packages/grafana-data/src/datetime/rangeutil.ts @@ -198,9 +198,14 @@ export const describeTimeRangeAbbreviation = (range: TimeRange, timeZone?: TimeZ return parsed ? timeZoneAbbrevation(parsed, { timeZone }) : ''; }; -export const convertRawToRange = (raw: RawTimeRange, timeZone?: TimeZone, fiscalYearStartMonth?: number): TimeRange => { - const from = dateTimeParse(raw.from, { roundUp: false, timeZone, fiscalYearStartMonth }); - const to = dateTimeParse(raw.to, { roundUp: true, timeZone, fiscalYearStartMonth }); +export const convertRawToRange = ( + raw: RawTimeRange, + timeZone?: TimeZone, + fiscalYearStartMonth?: number, + format?: string +): TimeRange => { + 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 }; diff --git a/packages/grafana-data/src/events/common.ts b/packages/grafana-data/src/events/common.ts index e83ce88e0d6..f66cdbd3c13 100644 --- a/packages/grafana-data/src/events/common.ts +++ b/packages/grafana-data/src/events/common.ts @@ -43,7 +43,7 @@ export class AnnotationChangeEvent extends BusEventWithPayload = { - dashboardId: string; + dashboardId: string; // eeep, this should be UID orgId?: number; userId?: number; grafanaVersion?: string; diff --git a/packages/grafana-data/src/field/displayProcessor.test.ts b/packages/grafana-data/src/field/displayProcessor.test.ts index 415d383bed3..2997e8049d9 100644 --- a/packages/grafana-data/src/field/displayProcessor.test.ts +++ b/packages/grafana-data/src/field/displayProcessor.test.ts @@ -3,7 +3,6 @@ import { createTheme } from '../themes'; import { FieldConfig, FieldType, ThresholdsMode } from '../types'; import { DisplayProcessor, DisplayValue } from '../types/displayValue'; import { MappingType, ValueMapping } from '../types/valueMapping'; -import { ArrayVector } from '../vector'; import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor'; @@ -548,7 +547,7 @@ describe('Date display options', () => { field: { type: FieldType.time, config: {}, - values: new ArrayVector([Date.parse('2020-08-01T08:48:43.783337Z'), Date.parse('2020-08-01T08:49:15.123456Z')]), + values: [Date.parse('2020-08-01T08:48:43.783337Z'), Date.parse('2020-08-01T08:49:15.123456Z')], }, theme: createTheme(), }); @@ -562,7 +561,7 @@ describe('Date display options', () => { field: { type: FieldType.time, config: {}, - values: new ArrayVector([Date.parse('2020-08-01T08:49:15.123456Z'), Date.parse('2020-08-01T08:43:43.783337Z')]), + values: [Date.parse('2020-08-01T08:49:15.123456Z'), Date.parse('2020-08-01T08:43:43.783337Z')], }, theme: createTheme(), }); @@ -578,7 +577,7 @@ describe('Date display options', () => { config: { unit: 'time:YYYY-MM-DD HH:mm', }, - values: new ArrayVector([Date.parse('2020-08-01T08:48:43.783337Z'), Date.parse('2020-08-01T08:49:15.123456Z')]), + values: [Date.parse('2020-08-01T08:48:43.783337Z'), Date.parse('2020-08-01T08:49:15.123456Z')], }, theme: createTheme(), }); diff --git a/packages/grafana-data/src/field/displayProcessor.ts b/packages/grafana-data/src/field/displayProcessor.ts index 71ebcd998ed..8b5cad6c727 100644 --- a/packages/grafana-data/src/field/displayProcessor.ts +++ b/packages/grafana-data/src/field/displayProcessor.ts @@ -53,8 +53,8 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP unit = `dateTimeAsSystem`; hasDateUnit = true; if (field.values && field.values.length > 1) { - let start = field.values.get(0); - let end = field.values.get(field.values.length - 1); + let start = field.values[0]; + let end = field.values[field.values.length - 1]; if (typeof start === 'string') { start = dateTimeParse(start).unix(); end = dateTimeParse(end).unix(); diff --git a/packages/grafana-data/src/field/fieldColor.test.ts b/packages/grafana-data/src/field/fieldColor.test.ts index bf20bce0cf8..43813f1f10a 100644 --- a/packages/grafana-data/src/field/fieldColor.test.ts +++ b/packages/grafana-data/src/field/fieldColor.test.ts @@ -1,17 +1,17 @@ import { createTheme } from '../themes'; import { Field, FieldColorModeId, FieldType } from '../types'; -import { ArrayVector } from '../vector/ArrayVector'; import { fieldColorModeRegistry, FieldValueColorCalculator, getFieldSeriesColor } from './fieldColor'; -function getTestField(mode: string): Field { +function getTestField(mode: string, fixedColor?: string, name = 'name'): Field { return { - name: 'name', + name: name, type: FieldType.number, - values: new ArrayVector(), + values: [], config: { color: { mode: mode, + fixedColor: fixedColor, }, }, state: {}, @@ -21,10 +21,12 @@ function getTestField(mode: string): Field { interface GetCalcOptions { mode: string; seriesIndex?: number; + name?: string; + fixedColor?: string; } function getCalculator(options: GetCalcOptions): FieldValueColorCalculator { - const field = getTestField(options.mode); + const field = getTestField(options.mode, options.fixedColor, options.name); const mode = fieldColorModeRegistry.get(options.mode); field.state!.seriesIndex = options.seriesIndex; return mode.getCalculator(field, createTheme()); @@ -37,33 +39,51 @@ describe('fieldColorModeRegistry', () => { }); it('Palette classic with series index 0', () => { - const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 0 }); + const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 0, name: 'series1' }); expect(calcFn(70, 0, undefined)).toEqual('#73BF69'); }); it('Palette classic with series index 1', () => { - const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1 }); + const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1, name: 'series2' }); expect(calcFn(70, 0, undefined)).toEqual('#F2CC0C'); }); + it('Palette uses name', () => { + const calcFn1 = getCalculator({ mode: FieldColorModeId.PaletteClassicByName, seriesIndex: 0, name: 'same name' }); + const calcFn2 = getCalculator({ mode: FieldColorModeId.PaletteClassicByName, seriesIndex: 1, name: 'same name' }); + expect(calcFn1(12, 34, undefined)).toEqual(calcFn2(56, 78, undefined)); + }); + it('When color.seriesBy is set to last use that instead of v', () => { const field = getTestField('continuous-GrYlRd'); field.config.color!.seriesBy = 'last'; // min = -10, max = 10, last = 5 // last percent 75% - field.values = new ArrayVector([0, -10, 5, 10, 2, 5]); + field.values = [0, -10, 5, 10, 2, 5]; const color = getFieldSeriesColor(field, createTheme()); const calcFn = getCalculator({ mode: 'continuous-GrYlRd' }); expect(color.color).toEqual(calcFn(4, 0.75)); }); + + it('Shades should return selected color for index 0', () => { + const color = '#123456'; + const calcFn = getCalculator({ mode: FieldColorModeId.Shades, seriesIndex: 0, fixedColor: color }); + expect(calcFn(70, 0, undefined)).toEqual(color); + }); + + it('Shades should return different than selected color for index 1', () => { + const color = '#123456'; + const calcFn = getCalculator({ mode: FieldColorModeId.Shades, seriesIndex: 1, fixedColor: color }); + expect(calcFn(70, 0, undefined)).not.toEqual(color); + }); }); describe('getFieldSeriesColor', () => { const field = getTestField('continuous-GrYlRd'); - field.values = new ArrayVector([0, -10, 5, 10, 2, 5]); + field.values = [0, -10, 5, 10, 2, 5]; it('When color.seriesBy is last use that to calc series color', () => { field.config.color!.seriesBy = 'last'; diff --git a/packages/grafana-data/src/field/fieldColor.ts b/packages/grafana-data/src/field/fieldColor.ts index 8cd714e7f7a..289177150de 100644 --- a/packages/grafana-data/src/field/fieldColor.ts +++ b/packages/grafana-data/src/field/fieldColor.ts @@ -1,5 +1,8 @@ import { interpolateRgbBasis } from 'd3-interpolate'; +import stringHash from 'string-hash'; +import tinycolor from 'tinycolor2'; +import { colorManipulator } from '../themes'; import { GrafanaTheme2 } from '../themes/types'; import { reduceField } from '../transformations/fieldReducer'; import { FALLBACK_COLOR, Field, FieldColorModeId, Threshold } from '../types'; @@ -18,6 +21,7 @@ export interface FieldColorMode extends RegistryItem { getColors?: (theme: GrafanaTheme2) => string[]; isContinuous?: boolean; isByValue?: boolean; + useSeriesName?: boolean; } /** @internal */ @@ -29,6 +33,12 @@ export const fieldColorModeRegistry = new Registry(() => { description: 'Set a specific color', getCalculator: getFixedColor, }, + { + id: FieldColorModeId.Shades, + name: 'Shades of a color', + description: 'Select shades of a specific color', + getCalculator: getShadedColor, + }, { id: FieldColorModeId.Thresholds, name: 'From thresholds', @@ -50,6 +60,22 @@ export const fieldColorModeRegistry = new Registry(() => { return theme.visualization.palette; }, }), + new FieldColorSchemeMode({ + id: FieldColorModeId.PaletteClassicByName, + name: 'Classic palette (by series name)', + isContinuous: false, + isByValue: false, + useSeriesName: true, + getColors: (theme: GrafanaTheme2) => { + return theme.visualization.palette.filter( + (color) => + colorManipulator.getContrastRatio( + theme.visualization.getColorByName(color), + theme.colors.background.primary + ) >= theme.colors.contrastThreshold + ); + }, + }), new FieldColorSchemeMode({ id: FieldColorModeId.ContinuousGrYlRd, name: 'Green-Yellow-Red', @@ -130,6 +156,7 @@ interface FieldColorSchemeModeOptions { getColors: (theme: GrafanaTheme2) => string[]; isContinuous: boolean; isByValue: boolean; + useSeriesName?: boolean; } export class FieldColorSchemeMode implements FieldColorMode { @@ -138,6 +165,7 @@ export class FieldColorSchemeMode implements FieldColorMode { description?: string; isContinuous: boolean; isByValue: boolean; + useSeriesName?: boolean; colorCache?: string[]; colorCacheTheme?: GrafanaTheme2; interpolator?: (value: number) => string; @@ -150,6 +178,7 @@ export class FieldColorSchemeMode implements FieldColorMode { this.getNamedColors = options.getColors; this.isContinuous = options.isContinuous; this.isByValue = options.isByValue; + this.useSeriesName = options.useSeriesName; } getColors(theme: GrafanaTheme2): string[] { @@ -188,6 +217,10 @@ export class FieldColorSchemeMode implements FieldColorMode { return colors[percent * (colors.length - 1)]; }; } + } else if (this.useSeriesName) { + return (_: number, _percent: number, _threshold?: Threshold) => { + return colors[Math.abs(stringHash(field.name)) % colors.length]; + }; } else { return (_: number, _percent: number, _threshold?: Threshold) => { const seriesIndex = field.state?.seriesIndex ?? 0; @@ -236,3 +269,39 @@ function getFixedColor(field: Field, theme: GrafanaTheme2) { return theme.visualization.getColorByName(field.config.color?.fixedColor ?? FALLBACK_COLOR); }; } + +function getShadedColor(field: Field, theme: GrafanaTheme2) { + return () => { + const baseColorString: string = theme.visualization.getColorByName( + field.config.color?.fixedColor ?? FALLBACK_COLOR + ); + + const colors: string[] = [ + baseColorString, // start with base color + ]; + + const shadesCount = 6; + const maxHueSpin = 10; // hue spin, max is 360 + const maxDarken = 35; // max 100% + const maxBrighten = 35; // max 100% + + for (let i = 1; i < shadesCount; i++) { + // push alternating darker and brighter shades + colors.push( + tinycolor(baseColorString) + .spin((i / shadesCount) * maxHueSpin) + .brighten((i / shadesCount) * maxDarken) + .toHexString() + ); + colors.push( + tinycolor(baseColorString) + .spin(-(i / shadesCount) * maxHueSpin) + .darken((i / shadesCount) * maxBrighten) + .toHexString() + ); + } + + const seriesIndex = field.state?.seriesIndex ?? 0; + return colors[seriesIndex % colors.length]; + }; +} diff --git a/packages/grafana-data/src/field/fieldComparers.ts b/packages/grafana-data/src/field/fieldComparers.ts index 32182c74edf..972b435cd35 100644 --- a/packages/grafana-data/src/field/fieldComparers.ts +++ b/packages/grafana-data/src/field/fieldComparers.ts @@ -2,7 +2,6 @@ import { isNumber } from 'lodash'; import { dateTime, isDateTimeInput } from '../datetime'; import { Field, FieldType } from '../types/dataFrame'; -import { Vector } from '../types/vector'; type IndexComparer = (a: number, b: number) => number; @@ -18,7 +17,7 @@ export const fieldIndexComparer = (field: Field, reverse = false): IndexComparer case FieldType.boolean: return booleanIndexComparer(values, reverse); case FieldType.time: - if (typeof field.values.get(0) === 'number') { + if (typeof field.values[0] === 'number') { return timestampIndexComparer(values, reverse); } return timeIndexComparer(values, reverse); @@ -79,40 +78,39 @@ const falsyComparer = (a: unknown, b: unknown): number => { return 0; }; -const timestampIndexComparer = (values: Vector, reverse: boolean): IndexComparer => { - let vals = values.toArray(); +const timestampIndexComparer = (values: number[], reverse: boolean): IndexComparer => { let mult = reverse ? -1 : 1; - return (a: number, b: number): number => mult * (vals[a] - vals[b]); + return (a: number, b: number): number => mult * (values[a] - values[b]); }; -const timeIndexComparer = (values: Vector, reverse: boolean): IndexComparer => { +const timeIndexComparer = (values: unknown[], reverse: boolean): IndexComparer => { return (a: number, b: number): number => { - const vA = values.get(a); - const vB = values.get(b); + const vA = values[a]; + const vB = values[b]; return reverse ? timeComparer(vB, vA) : timeComparer(vA, vB); }; }; -const booleanIndexComparer = (values: Vector, reverse: boolean): IndexComparer => { +const booleanIndexComparer = (values: boolean[], reverse: boolean): IndexComparer => { return (a: number, b: number): number => { - const vA = values.get(a); - const vB = values.get(b); + const vA = values[a]; + const vB = values[b]; return reverse ? booleanComparer(vB, vA) : booleanComparer(vA, vB); }; }; -const numericIndexComparer = (values: Vector, reverse: boolean): IndexComparer => { +const numericIndexComparer = (values: number[], reverse: boolean): IndexComparer => { return (a: number, b: number): number => { - const vA = values.get(a); - const vB = values.get(b); + const vA = values[a]; + const vB = values[b]; return reverse ? numericComparer(vB, vA) : numericComparer(vA, vB); }; }; -const stringIndexComparer = (values: Vector, reverse: boolean): IndexComparer => { +const stringIndexComparer = (values: string[], reverse: boolean): IndexComparer => { return (a: number, b: number): number => { - const vA = values.get(a); - const vB = values.get(b); + const vA = values[a]; + const vB = values[b]; return reverse ? stringComparer(vB, vA) : stringComparer(vA, vB); }; }; diff --git a/packages/grafana-data/src/field/fieldDisplay.test.ts b/packages/grafana-data/src/field/fieldDisplay.test.ts index ebea46752bd..a9da5bf0182 100644 --- a/packages/grafana-data/src/field/fieldDisplay.test.ts +++ b/packages/grafana-data/src/field/fieldDisplay.test.ts @@ -508,8 +508,25 @@ function createEmptyDisplayOptions(extend = {}): GetFieldDisplayValuesOptions { } function createDisplayOptions(extend: Partial = {}): GetFieldDisplayValuesOptions { - const options: GetFieldDisplayValuesOptions = { - data: [ + const options = merge( + { + replaceVariables: (value: string) => { + return value; + }, + reduceOptions: { + calcs: [], + }, + fieldConfig: { + overrides: [], + defaults: {}, + }, + theme: createTheme(), + }, + extend + ); + + if (!options.data?.length) { + options.data = [ toDataFrame({ name: 'Series Name', fields: [ @@ -518,21 +535,9 @@ function createDisplayOptions(extend: Partial = {} { name: 'Field 3', values: [2, 4, 6] }, ], }), - ], - replaceVariables: (value: string) => { - return value; - }, - reduceOptions: { - calcs: [], - }, - fieldConfig: { - overrides: [], - defaults: {}, - }, - theme: createTheme(), - }; - - return merge(options, extend); + ]; + } + return options; } describe('fixCellTemplateExpressions', () => { diff --git a/packages/grafana-data/src/field/fieldDisplay.ts b/packages/grafana-data/src/field/fieldDisplay.ts index 6bd10b415fb..e875b5f0a4d 100644 --- a/packages/grafana-data/src/field/fieldDisplay.ts +++ b/packages/grafana-data/src/field/fieldDisplay.ts @@ -140,7 +140,7 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi field.state = setIndexForPaletteColor(field, values.length); const scopedVars = getFieldScopedVarsWithDataContexAndRowIndex(field, j); - const displayValue = display(field.values.get(j)); + const displayValue = display(field.values[j]); const rowName = getSmartDisplayNameForRow(dataFrame, field, j, replaceVariables, scopedVars); const overrideColor = lookupRowColorFromOverride(rowName, options.fieldConfig, theme); @@ -254,7 +254,7 @@ function getSmartDisplayNameForRow( } if (otherField.type === FieldType.string) { - const value = otherField.values.get(rowIndex) ?? ''; + const value = otherField.values[rowIndex] ?? ''; const mappedValue = otherField.display ? otherField.display(value).text : value; if (mappedValue.length > 0) { parts.push(mappedValue); diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index c0295144ab1..e136a5a6a12 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -1,4 +1,4 @@ -import { ArrayDataFrame, MutableDataFrame, toDataFrame } from '../dataframe'; +import { ArrayDataFrame, createDataFrame, toDataFrame } from '../dataframe'; import { rangeUtil } from '../datetime'; import { createTheme } from '../themes'; import { FieldMatcherID } from '../transformations'; @@ -17,7 +17,6 @@ import { } from '../types'; import { locationUtil, Registry } from '../utils'; import { mockStandardProperties } from '../utils/tests/mockStandardProperties'; -import { ArrayVector } from '../vector'; import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; import { getDisplayProcessor } from './displayProcessor'; @@ -178,11 +177,11 @@ describe('applyFieldOverrides', () => { }; describe('given multiple data frames', () => { - const f0 = new MutableDataFrame({ + const f0 = createDataFrame({ name: 'A', fields: [{ name: 'message', type: FieldType.string, values: [10, 20] }], }); - const f1 = new MutableDataFrame({ + const f1 = createDataFrame({ name: 'B', fields: [{ name: 'info', type: FieldType.string, values: [10, 20] }], }); @@ -677,7 +676,7 @@ describe('getLinksSupplier', () => { getTimeRangeForUrl: () => ({ from: 'now-7d', to: 'now' }), }); - const f0 = new MutableDataFrame({ + const f0 = createDataFrame({ name: 'A', fields: [ { @@ -713,7 +712,7 @@ describe('getLinksSupplier', () => { }); const datasourceUid = '1234'; - const f0 = new MutableDataFrame({ + const f0 = createDataFrame({ name: 'A', fields: [ { @@ -767,7 +766,7 @@ describe('getLinksSupplier', () => { const datasourceUid = '1234'; const range = rangeUtil.relativeToTimeRange({ from: 600, to: 0 }); - const f0 = new MutableDataFrame({ + const f0 = createDataFrame({ name: 'A', fields: [ { @@ -828,7 +827,7 @@ describe('getLinksSupplier', () => { it('handles link click handlers', () => { const onClickSpy = jest.fn(); const replaceSpy = jest.fn(); - const f0 = new MutableDataFrame({ + const f0 = createDataFrame({ name: 'A', fields: [ { @@ -868,7 +867,7 @@ describe('getLinksSupplier', () => { const replaceSpy = jest.fn().mockReturnValue('url interpolated 10'); const onBuildUrlSpy = jest.fn(); - const f0 = new MutableDataFrame({ + const f0 = createDataFrame({ name: 'A', fields: [ { @@ -936,7 +935,7 @@ describe('applyRawFieldOverrides', () => { const getDisplayValue = (frames: DataFrame[], frameIndex: number, fieldIndex: number) => { const field = frames[frameIndex].fields[fieldIndex]; - const value = field.values.get(0); + const value = field.values[0]; return field.display!(value); }; @@ -1010,14 +1009,14 @@ describe('applyRawFieldOverrides', () => { const numberAsEpoc: Field = { name: 'numberAsEpoc', type: FieldType.number, - values: new ArrayVector([1599045551050]), + values: [1599045551050], config: getNumberFieldConfig(), }; const numberWithDecimals: Field = { name: 'numberWithDecimals', type: FieldType.number, - values: new ArrayVector([3.14159265359]), + values: [3.14159265359], config: { ...getNumberFieldConfig(), decimals: 3, @@ -1027,28 +1026,28 @@ describe('applyRawFieldOverrides', () => { const numberAsBoolean: Field = { name: 'numberAsBoolean', type: FieldType.number, - values: new ArrayVector([0]), + values: [0], config: getNumberFieldConfig(), }; const boolean: Field = { name: 'boolean', type: FieldType.boolean, - values: new ArrayVector([0]), + values: [0], config: getEmptyConfig(), }; const string: Field = { name: 'string', type: FieldType.boolean, - values: new ArrayVector(['A - string']), + values: ['A - string'], config: getEmptyConfig(), }; const datetime: Field = { name: 'datetime', type: FieldType.time, - values: new ArrayVector([1599045551050]), + values: [1599045551050], config: { unit: 'dateTimeAsIso', }, diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index f3b248a5000..7247638d1d6 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -421,7 +421,7 @@ export const getLinksSupplier = if (href) { href = locationUtil.assureBaseUrl(href.replace(/\n/g, '')); - href = replaceVariables(href, dataLinkScopedVars, VariableFormatID.PercentEncode); + href = replaceVariables(href, dataLinkScopedVars, VariableFormatID.UriEncode); href = locationUtil.processUrl(href); } diff --git a/packages/grafana-data/src/field/fieldState.ts b/packages/grafana-data/src/field/fieldState.ts index 14e75bd8095..01f3a8760c1 100644 --- a/packages/grafana-data/src/field/fieldState.ts +++ b/packages/grafana-data/src/field/fieldState.ts @@ -55,7 +55,7 @@ export function getFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: /** * Get an appropriate display name. If the 'displayName' field config is set, use that. */ -function calculateFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: DataFrame[]): string { +export function calculateFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: DataFrame[]): string { const hasConfigTitle = field.config?.displayName && field.config?.displayName.length; let displayName = hasConfigTitle ? field.config!.displayName! : field.name; diff --git a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx index 92b9181fe72..b17ceef4cb6 100644 --- a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx +++ b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.test.tsx @@ -1,4 +1,4 @@ -import { MutableDataFrame, toDataFrame } from '../dataframe'; +import { createDataFrame, toDataFrame } from '../dataframe'; import { createTheme } from '../themes'; import { applyFieldOverrides } from './fieldOverrides'; @@ -85,7 +85,7 @@ describe('getFieldDisplayValuesProxy', () => { it('should use default display processor if display is not defined', () => { const p = getFieldDisplayValuesProxy({ - frame: new MutableDataFrame({ fields: [{ name: 'test', values: [1, 2] }] }), + frame: createDataFrame({ fields: [{ name: 'test', values: [1, 2] }] }), rowIndex: 0, }); expect(p.test.text).toBe('1'); diff --git a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.ts b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.ts index d39fc47292f..73686e7643f 100644 --- a/packages/grafana-data/src/field/getFieldDisplayValuesProxy.ts +++ b/packages/grafana-data/src/field/getFieldDisplayValuesProxy.ts @@ -49,7 +49,7 @@ export function getFieldDisplayValuesProxy(options: { // TODO: we could supply the field here for the getDisplayProcessor fallback but we would also need theme which // we do not have access to here const displayProcessor = field.display ?? getDisplayProcessor(); - const raw = field.values.get(options.rowIndex); + const raw = field.values[options.rowIndex]; const disp = displayProcessor(raw); disp.toString = () => formattedValueToString(disp); return disp; diff --git a/packages/grafana-data/src/field/scale.test.ts b/packages/grafana-data/src/field/scale.test.ts index 874d283fc44..9ba0cb6227b 100644 --- a/packages/grafana-data/src/field/scale.test.ts +++ b/packages/grafana-data/src/field/scale.test.ts @@ -1,6 +1,5 @@ import { createTheme } from '../themes'; import { ThresholdsMode, Field, FieldType, FieldColorModeId } from '../types'; -import { ArrayVector } from '../vector/ArrayVector'; import { getScaleCalculator } from './scale'; import { sortThresholds } from './thresholds'; @@ -17,7 +16,7 @@ describe('getScaleCalculator', () => { name: 'test', config: { thresholds: { mode: ThresholdsMode.Absolute, steps: sortThresholds(thresholds) } }, type: FieldType.number, - values: new ArrayVector([0, 50, 100]), + values: [0, 50, 100], }; const calc = getScaleCalculator(field, createTheme()); @@ -33,7 +32,7 @@ describe('getScaleCalculator', () => { name: 'test', config: {}, type: FieldType.boolean, - values: new ArrayVector([true, false, true]), + values: [true, false, true], }; const theme = createTheme(); @@ -55,7 +54,7 @@ describe('getScaleCalculator', () => { name: 'test', config: { color: { mode: FieldColorModeId.ContinuousGrYlRd } }, type: FieldType.number, - values: new ArrayVector([1]), + values: [1], }; const theme = createTheme(); diff --git a/packages/grafana-data/src/field/thresholds.test.ts b/packages/grafana-data/src/field/thresholds.test.ts index 500aae9170a..77df5edf76e 100644 --- a/packages/grafana-data/src/field/thresholds.test.ts +++ b/packages/grafana-data/src/field/thresholds.test.ts @@ -1,5 +1,4 @@ import { ThresholdsConfig, ThresholdsMode, FieldConfig, Threshold, Field, FieldType } from '../types'; -import { ArrayVector } from '../vector/ArrayVector'; import { validateFieldConfig } from './fieldOverrides'; import { sortThresholds, getActiveThreshold, getActiveThresholdForValue } from './thresholds'; @@ -50,7 +49,7 @@ describe('thresholds', () => { name: 'test', config: { thresholds: { mode: mode, steps: sortThresholds(steps) } }, type: FieldType.number, - values: new ArrayVector([]), + values: [], }; validateFieldConfig(field.config!); return getActiveThresholdForValue(field, value, percent); diff --git a/packages/grafana-data/src/text/index.ts b/packages/grafana-data/src/text/index.ts index 13d9631e63d..f9ca55a5472 100644 --- a/packages/grafana-data/src/text/index.ts +++ b/packages/grafana-data/src/text/index.ts @@ -8,6 +8,8 @@ import { sanitizeUrl, sanitizeTextPanelContent, sanitizeSVGContent, + sanitizeTrustedTypes, + sanitizeTrustedTypesRSS, } from './sanitize'; export const textUtil = { @@ -17,4 +19,6 @@ export const textUtil = { sanitizeTextPanelContent, sanitizeUrl, sanitizeSVGContent, + sanitizeTrustedTypes, + sanitizeTrustedTypesRSS, }; diff --git a/packages/grafana-data/src/text/sanitize.ts b/packages/grafana-data/src/text/sanitize.ts index 8f053b530b5..53e403d8e48 100644 --- a/packages/grafana-data/src/text/sanitize.ts +++ b/packages/grafana-data/src/text/sanitize.ts @@ -47,6 +47,19 @@ export function sanitize(unsanitizedString: string): string { } } +export function sanitizeTrustedTypesRSS(unsanitizedString: string): TrustedHTML { + return DOMPurify.sanitize(unsanitizedString, { + RETURN_TRUSTED_TYPE: true, + ADD_ATTR: ['xmlns:atom', 'version', 'property', 'content'], + ADD_TAGS: ['rss', 'meta', 'channel', 'title', 'link', 'description', 'atom:link', 'item', 'pubDate', 'guid'], + PARSER_MEDIA_TYPE: 'application/xhtml+xml', + }); +} + +export function sanitizeTrustedTypes(unsanitizedString: string): TrustedHTML { + return DOMPurify.sanitize(unsanitizedString, { RETURN_TRUSTED_TYPE: true }); +} + /** * Returns string safe from XSS attacks to be used in the Text panel plugin. * diff --git a/packages/grafana-data/src/themes/createColors.ts b/packages/grafana-data/src/themes/createColors.ts index c645630f1d3..d09827e3670 100644 --- a/packages/grafana-data/src/themes/createColors.ts +++ b/packages/grafana-data/src/themes/createColors.ts @@ -89,9 +89,9 @@ class DarkColors implements ThemeColorsBase> { whiteBase = '204, 204, 220'; border = { - weak: `rgba(${this.whiteBase}, 0.07)`, - medium: `rgba(${this.whiteBase}, 0.15)`, - strong: `rgba(${this.whiteBase}, 0.25)`, + weak: `rgba(${this.whiteBase}, 0.12)`, + medium: `rgba(${this.whiteBase}, 0.20)`, + strong: `rgba(${this.whiteBase}, 0.30)`, }; text = { @@ -280,7 +280,7 @@ export function createColors(colors: ThemeColorsInput): ThemeColors { color.shade = base.mode === 'light' ? darken(color.main, tonalOffset) : lighten(color.main, tonalOffset); } if (!color.transparent) { - color.transparent = base.mode === 'light' ? alpha(color.main, 0.08) : alpha(color.main, 0.15); + color.transparent = alpha(color.main, 0.15); } if (!color.contrastText) { color.contrastText = getContrastText(color.main); diff --git a/packages/grafana-data/src/themes/createComponents.ts b/packages/grafana-data/src/themes/createComponents.ts index 7646160521d..1273431458e 100644 --- a/packages/grafana-data/src/themes/createComponents.ts +++ b/packages/grafana-data/src/themes/createComponents.ts @@ -49,6 +49,9 @@ export interface ThemeComponents { horizontalDrawer: { defaultHeight: number; }; + table: { + rowHoverBackground: string; + }; } export function createComponents(colors: ThemeColors, shadows: ThemeShadows): ThemeComponents { @@ -102,5 +105,8 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th horizontalDrawer: { defaultHeight: 400, }, + table: { + rowHoverBackground: colors.emphasize(colors.background.primary, 0.03), + }, }; } diff --git a/packages/grafana-data/src/themes/createTypography.ts b/packages/grafana-data/src/themes/createTypography.ts index 5dfe3ebaf47..1473adc2bb8 100644 --- a/packages/grafana-data/src/themes/createTypography.ts +++ b/packages/grafana-data/src/themes/createTypography.ts @@ -5,7 +5,7 @@ import { ThemeColors } from './createColors'; /** @beta */ -export interface ThemeTypography { +export interface ThemeTypography extends ThemeTypographyVariantTypes { fontFamily: string; fontFamilyMonospace: string; fontSize: number; @@ -17,16 +17,6 @@ export interface ThemeTypography { // The font-size on the html element. htmlFontSize?: number; - h1: ThemeTypographyVariant; - h2: ThemeTypographyVariant; - h3: ThemeTypographyVariant; - h4: ThemeTypographyVariant; - h5: ThemeTypographyVariant; - h6: ThemeTypographyVariant; - - body: ThemeTypographyVariant; - bodySmall: ThemeTypographyVariant; - /** * @deprecated * from legacy old theme @@ -152,3 +142,14 @@ export function createTypography(colors: ThemeColors, typographyInput: ThemeTypo function round(value: number) { return Math.round(value * 1e5) / 1e5; } + +export interface ThemeTypographyVariantTypes { + h1: ThemeTypographyVariant; + h2: ThemeTypographyVariant; + h3: ThemeTypographyVariant; + h4: ThemeTypographyVariant; + h5: ThemeTypographyVariant; + h6: ThemeTypographyVariant; + body: ThemeTypographyVariant; + bodySmall: ThemeTypographyVariant; +} diff --git a/packages/grafana-data/src/themes/index.ts b/packages/grafana-data/src/themes/index.ts index a0f2f9fcc9d..9fac0e68a3c 100644 --- a/packages/grafana-data/src/themes/index.ts +++ b/packages/grafana-data/src/themes/index.ts @@ -5,7 +5,7 @@ export type { ThemeColors } from './createColors'; export type { ThemeBreakpoints, ThemeBreakpointsKey } from './breakpoints'; export type { ThemeShadows } from './createShadows'; export type { ThemeShape } from './createShape'; -export type { ThemeTypography, ThemeTypographyVariant } from './createTypography'; +export type { ThemeTypography, ThemeTypographyVariant, ThemeTypographyVariantTypes } from './createTypography'; export type { ThemeTransitions } from './createTransitions'; export type { ThemeSpacing } from './createSpacing'; export type { ThemeZIndices } from './zIndex'; diff --git a/packages/grafana-data/src/themes/palette.ts b/packages/grafana-data/src/themes/palette.ts index 2ac623b3da0..f9d9a780688 100644 --- a/packages/grafana-data/src/themes/palette.ts +++ b/packages/grafana-data/src/themes/palette.ts @@ -30,8 +30,8 @@ export const palette = { redDarkText: '#FF5286', greenDarkMain: '#1A7F4B', greenDarkText: '#6CCF8E', - orangeDarkMain: '#F5B73D', - orangeDarkText: '#F8D06B', + orangeDarkMain: '#FF9900', + orangeDarkText: '#fbad37', blueLightMain: '#3871DC', blueLightText: '#1F62E0', @@ -39,6 +39,6 @@ export const palette = { redLightText: '#CF0E5B', greenLightMain: '#1B855E', greenLightText: '#0A764E', - orangeLightMain: '#FAD34A', - orangeLightText: '#8A6C00', + orangeLightMain: '#FF9900', + orangeLightText: '#B5510D', }; diff --git a/packages/grafana-data/src/transformations/fieldReducer.test.ts b/packages/grafana-data/src/transformations/fieldReducer.test.ts index dd925166384..f4cc0b6b178 100644 --- a/packages/grafana-data/src/transformations/fieldReducer.test.ts +++ b/packages/grafana-data/src/transformations/fieldReducer.test.ts @@ -1,9 +1,7 @@ import { difference } from 'lodash'; -import { MutableDataFrame } from '../dataframe/MutableDataFrame'; -import { guessFieldTypeFromValue } from '../dataframe/processDataFrame'; +import { createDataFrame, guessFieldTypeFromValue } from '../dataframe/processDataFrame'; import { Field, FieldType } from '../types/index'; -import { ArrayVector } from '../vector/ArrayVector'; import { fieldReducers, ReducerID, reduceField } from './fieldReducer'; @@ -15,17 +13,17 @@ function reduce(field: Field, id: string) { } function createField(name: string, values?: T[], type?: FieldType): Field { - const arr = new ArrayVector(values); + const arr = values ?? []; return { name, config: {}, - type: type ? type : guessFieldTypeFromValue(arr.get(0)), + type: type ? type : guessFieldTypeFromValue(arr[0]), values: arr, }; } describe('Stats Calculators', () => { - const basicTable = new MutableDataFrame({ + const basicTable = createDataFrame({ fields: [ { name: 'a', values: [10, 20] }, { name: 'b', values: [20, 30] }, diff --git a/packages/grafana-data/src/transformations/fieldReducer.ts b/packages/grafana-data/src/transformations/fieldReducer.ts index 7a2a720bd46..103dc14309e 100644 --- a/packages/grafana-data/src/transformations/fieldReducer.ts +++ b/packages/grafana-data/src/transformations/fieldReducer.ts @@ -252,7 +252,7 @@ export const fieldReducers = new Registry(() => [ name: 'All values', description: 'Returns an array with all values', standard: false, - reduce: (field: Field) => ({ allValues: field.values.toArray() }), + reduce: (field: Field) => ({ allValues: field.values }), }, { id: ReducerID.uniqueValues, @@ -260,7 +260,7 @@ export const fieldReducers = new Registry(() => [ description: 'Returns an array with all unique values', standard: false, reduce: (field: Field) => ({ - uniqueValues: [...new Set(field.values.toArray())], + uniqueValues: [...new Set(field.values)], }), }, ]); @@ -291,12 +291,12 @@ export function doStandardCalcs(field: Field, ignoreNulls: boolean, nullAsZero: }; const data = field.values; - calcs.count = ignoreNulls ? data.length : data.toArray().filter((val) => val != null).length; + calcs.count = ignoreNulls ? data.length : data.filter((val) => val != null).length; const isNumberField = field.type === FieldType.number || FieldType.time; for (let i = 0; i < data.length; i++) { - let currentValue = data.get(i); + let currentValue = data[i]; if (i === 0) { calcs.first = currentValue; @@ -404,13 +404,13 @@ export function doStandardCalcs(field: Field, ignoreNulls: boolean, nullAsZero: } function calculateFirst(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs { - return { first: field.values.get(0) }; + return { first: field.values[0] }; } function calculateFirstNotNull(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs { const data = field.values; for (let idx = 0; idx < data.length; idx++) { - const v = data.get(idx); + const v = data[idx]; if (v != null && v !== undefined) { return { firstNotNull: v }; } @@ -420,14 +420,14 @@ function calculateFirstNotNull(field: Field, ignoreNulls: boolean, nullAsZero: b function calculateLast(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs { const data = field.values; - return { last: data.get(data.length - 1) }; + return { last: data[data.length - 1] }; } function calculateLastNotNull(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs { const data = field.values; let idx = data.length - 1; while (idx >= 0) { - const v = data.get(idx--); + const v = data[idx--]; if (v != null && v !== undefined) { return { lastNotNull: v }; } @@ -447,7 +447,7 @@ function calculateStdDev(field: Field, ignoreNulls: boolean, nullAsZero: boolean let runningNonNullCount = 0; const data = field.values; for (let i = 0; i < data.length; i++) { - const currentValue = data.get(i); + const currentValue = data[i]; if (currentValue != null) { runningNonNullCount++; let _oldMean = runningMean; @@ -468,7 +468,7 @@ function calculateChangeCount(field: Field, ignoreNulls: boolean, nullAsZero: bo let first = true; let last = null; for (let i = 0; i < data.length; i++) { - let currentValue = data.get(i); + let currentValue = data[i]; if (currentValue === null) { if (ignoreNulls) { continue; @@ -491,7 +491,7 @@ function calculateDistinctCount(field: Field, ignoreNulls: boolean, nullAsZero: const data = field.values; const distinct = new Set(); for (let i = 0; i < data.length; i++) { - let currentValue = data.get(i); + let currentValue = data[i]; if (currentValue === null) { if (ignoreNulls) { continue; diff --git a/packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts b/packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts index df611f50bcb..ec1ce239664 100644 --- a/packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts +++ b/packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts @@ -1,9 +1,20 @@ import { toDataFrame } from '../../dataframe/processDataFrame'; +import { FieldType, DataFrame } from '../../types'; import { getFieldMatcher } from '../matchers'; import { FieldMatcherID } from './ids'; import { ByNamesMatcherMode } from './nameMatcher'; +// mock the default window.grafanaBootData settings +// eslint-disable-next-line +(window as any).grafanaBootData = { + settings: { + featureToggles: { + dataplaneFrontendFallback: true, + }, + }, +}; + describe('Field Name by Regexp Matcher', () => { it('Match all with wildcard regex', () => { const seriesWithNames = toDataFrame({ @@ -385,6 +396,74 @@ describe('Field Regexp or Names Matcher', () => { expect(matcher(field, seriesWithNames, [seriesWithNames])).toBe(true); } }); + + it('Support fallback name matchers', () => { + const frame: DataFrame = { + fields: [ + { name: 'time', type: FieldType.time, config: {}, values: [1, 2] }, + { + name: 'UP', + type: FieldType.number, + config: {}, + values: [1, 2], + labels: { __name__: 'UP' }, + }, + ], + name: 'X', + length: 2, + }; + + let matcher = getFieldMatcher({ + id: FieldMatcherID.byName, + options: 'Value', + }); + expect(matcher(frame.fields[0], frame, [])).toBeFalsy(); + expect(matcher(frame.fields[1], frame, [])).toBeTruthy(); + + matcher = getFieldMatcher({ + id: FieldMatcherID.byName, + options: 'Time', + }); + expect(matcher(frame.fields[0], frame, [])).toBeTruthy(); + expect(matcher(frame.fields[1], frame, [])).toBeFalsy(); + }); +}); + +it('Support fallback multiple names matchers', () => { + const frame: DataFrame = { + fields: [ + { name: 'time', type: FieldType.time, config: {}, values: [1, 2] }, + { + name: 'UP', + type: FieldType.number, + config: {}, + values: [1, 2], + labels: { __name__: 'UP' }, + }, + ], + name: 'X', + length: 2, + }; + + let matcher = getFieldMatcher({ + id: FieldMatcherID.byNames, + options: { + mode: ByNamesMatcherMode.include, + names: ['Value'], + }, + }); + expect(matcher(frame.fields[0], frame, [])).toBeFalsy(); + expect(matcher(frame.fields[1], frame, [])).toBeTruthy(); + + matcher = getFieldMatcher({ + id: FieldMatcherID.byNames, + options: { + mode: ByNamesMatcherMode.include, + names: ['Time'], + }, + }); + expect(matcher(frame.fields[0], frame, [])).toBeTruthy(); + expect(matcher(frame.fields[1], frame, [])).toBeFalsy(); }); describe('Fields returned by query with refId', () => { diff --git a/packages/grafana-data/src/transformations/matchers/nameMatcher.ts b/packages/grafana-data/src/transformations/matchers/nameMatcher.ts index 45f3e75606b..0d31b613c62 100644 --- a/packages/grafana-data/src/transformations/matchers/nameMatcher.ts +++ b/packages/grafana-data/src/transformations/matchers/nameMatcher.ts @@ -1,10 +1,9 @@ import { getFieldDisplayName } from '../../field/fieldState'; import { stringToJsRegex } from '../../text/string'; -import { Field, DataFrame } from '../../types/dataFrame'; +import { Field, DataFrame, FieldType, TIME_SERIES_VALUE_FIELD_NAME } from '../../types/dataFrame'; import { FieldMatcherInfo, FrameMatcherInfo, FieldMatcher } from '../../types/transformations'; import { FieldMatcherID, FrameMatcherID } from './ids'; - export interface RegexpOrNamesMatcherOptions { pattern?: string; names?: string[]; @@ -40,8 +39,16 @@ const fieldNameMatcher: FieldMatcherInfo = { defaultOptions: '', get: (name: string): FieldMatcher => { + const uniqueNames = new Set([name] ?? []); + + const fallback = fieldNameFallback(uniqueNames); + return (field: Field, frame: DataFrame, allFrames: DataFrame[]) => { - return name === field.name || getFieldDisplayName(field, frame, allFrames) === name; + return ( + name === field.name || + name === getFieldDisplayName(field, frame, allFrames) || + Boolean(fallback && fallback(field, frame, allFrames)) + ); }; }, @@ -63,8 +70,14 @@ const multipleFieldNamesMatcher: FieldMatcherInfo = { const { names, mode = ByNamesMatcherMode.include } = options; const uniqueNames = new Set(names ?? []); + const fallback = fieldNameFallback(uniqueNames); + const matcher = (field: Field, frame: DataFrame, frames: DataFrame[]) => { - return uniqueNames.has(field.name) || uniqueNames.has(getFieldDisplayName(field, frame, frames)); + return ( + uniqueNames.has(field.name) || + uniqueNames.has(getFieldDisplayName(field, frame, frames)) || + Boolean(fallback && fallback(field, frame, frames)) + ); }; if (mode === ByNamesMatcherMode.exclude) { @@ -85,6 +98,35 @@ const multipleFieldNamesMatcher: FieldMatcherInfo = { }, }; +// In an effor to support migrating to a consistent data contract, the +// naming conventions need to get normalized. However many existing setups +// exist that would no longer match names if that changes. This injects +// fallback logic when when the data frame has not type version specified +export function fieldNameFallback(fields: Set) { + let fallback: FieldMatcher | undefined = undefined; + + // grafana-data does not have access to runtime so we are accessing the window object + // to get access to the feature toggle + // eslint-disable-next-line + const useMatcherFallback = (window as any)?.grafanaBootData?.settings?.featureToggles?.dataplaneFrontendFallback; + if (useMatcherFallback) { + if (fields.has(TIME_SERIES_VALUE_FIELD_NAME)) { + fallback = (field: Field, frame: DataFrame) => { + return ( + Boolean(field.labels) && // Value was reasonable when the name was set in labels or on the frame + field.labels?.__name__ === field.name + ); + }; + } else if (fields.has('Time') || fields.has('time')) { + fallback = (field: Field, frame: DataFrame) => { + return frame.meta?.typeVersion == null && field.type === FieldType.time; + }; + } + } + + return fallback; +} + const regexpFieldNameMatcher: FieldMatcherInfo = { id: FieldMatcherID.byRegexp, name: 'Field Name by Regexp', diff --git a/packages/grafana-data/src/transformations/matchers/valueMatchers/equalMatchers.ts b/packages/grafana-data/src/transformations/matchers/valueMatchers/equalMatchers.ts index 3b9e4c3721a..bdf66e71de6 100644 --- a/packages/grafana-data/src/transformations/matchers/valueMatchers/equalMatchers.ts +++ b/packages/grafana-data/src/transformations/matchers/valueMatchers/equalMatchers.ts @@ -10,7 +10,7 @@ const isEqualValueMatcher: ValueMatcherInfo = { description: 'Match where value for given field is equal to options value.', get: (options) => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; // eslint-disable-next-line eqeqeq return value == options.value; }; @@ -28,7 +28,7 @@ const isNotEqualValueMatcher: ValueMatcherInfo = { description: 'Match where value for given field is not equal to options value.', get: (options) => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; // eslint-disable-next-line eqeqeq return value != options.value; }; diff --git a/packages/grafana-data/src/transformations/matchers/valueMatchers/nullMatchers.ts b/packages/grafana-data/src/transformations/matchers/valueMatchers/nullMatchers.ts index 10e78201dbf..ba50ee3edfe 100644 --- a/packages/grafana-data/src/transformations/matchers/valueMatchers/nullMatchers.ts +++ b/packages/grafana-data/src/transformations/matchers/valueMatchers/nullMatchers.ts @@ -10,7 +10,7 @@ const isNullValueMatcher: ValueMatcherInfo = { description: 'Match where value for given field is null.', get: () => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; return value == null; }; }, @@ -27,7 +27,7 @@ const isNotNullValueMatcher: ValueMatcherInfo = { description: 'Match where value for given field is not null.', get: () => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; return value != null; }; }, diff --git a/packages/grafana-data/src/transformations/matchers/valueMatchers/numericMatchers.ts b/packages/grafana-data/src/transformations/matchers/valueMatchers/numericMatchers.ts index e2f24e6d98f..be8873175a3 100644 --- a/packages/grafana-data/src/transformations/matchers/valueMatchers/numericMatchers.ts +++ b/packages/grafana-data/src/transformations/matchers/valueMatchers/numericMatchers.ts @@ -10,7 +10,7 @@ const isGreaterValueMatcher: ValueMatcherInfo> description: 'Match when field value is greater than option.', get: (options) => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; if (isNaN(value)) { return false; } @@ -30,7 +30,7 @@ const isGreaterOrEqualValueMatcher: ValueMatcherInfo { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; if (isNaN(value)) { return false; } @@ -50,7 +50,7 @@ const isLowerValueMatcher: ValueMatcherInfo> = description: 'Match when field value is lower than option.', get: (options) => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; if (isNaN(value)) { return false; } @@ -70,7 +70,7 @@ const isLowerOrEqualValueMatcher: ValueMatcherInfo { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; if (isNaN(value)) { return false; } diff --git a/packages/grafana-data/src/transformations/matchers/valueMatchers/rangeMatchers.ts b/packages/grafana-data/src/transformations/matchers/valueMatchers/rangeMatchers.ts index 45c438c1830..a2f051945e4 100644 --- a/packages/grafana-data/src/transformations/matchers/valueMatchers/rangeMatchers.ts +++ b/packages/grafana-data/src/transformations/matchers/valueMatchers/rangeMatchers.ts @@ -10,7 +10,7 @@ const isBetweenValueMatcher: ValueMatcherInfo> description: 'Match when field value is between given option values.', get: (options) => { return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; if (isNaN(value)) { return false; } diff --git a/packages/grafana-data/src/transformations/matchers/valueMatchers/regexMatchers.ts b/packages/grafana-data/src/transformations/matchers/valueMatchers/regexMatchers.ts index 92ec6582f33..d5b9fb8fab9 100644 --- a/packages/grafana-data/src/transformations/matchers/valueMatchers/regexMatchers.ts +++ b/packages/grafana-data/src/transformations/matchers/valueMatchers/regexMatchers.ts @@ -12,7 +12,7 @@ const regexValueMatcher: ValueMatcherInfo> = { const regex = new RegExp(options.value); return (valueIndex: number, field: Field) => { - const value = field.values.get(valueIndex); + const value = field.values[valueIndex]; return regex.test(value); }; }, diff --git a/packages/grafana-data/src/transformations/transformDataFrame.test.ts b/packages/grafana-data/src/transformations/transformDataFrame.test.ts index 8970f2f189d..83643922401 100644 --- a/packages/grafana-data/src/transformations/transformDataFrame.test.ts +++ b/packages/grafana-data/src/transformations/transformDataFrame.test.ts @@ -3,7 +3,6 @@ import { map } from 'rxjs'; import { toDataFrame } from '../dataframe/processDataFrame'; import { CustomTransformOperator, FieldType } from '../types'; import { mockTransformationsRegistry } from '../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../vector/ArrayVector'; import { ReducerID } from './fieldReducer'; import { FrameMatcherID } from './matchers/ids'; @@ -31,7 +30,7 @@ const customTransform1: CustomTransformOperator = () => (source) => { fields: frame.fields.map((field) => { return { ...field, - values: new ArrayVector(field.values.toArray().map((v) => v / 100)), + values: field.values.map((v) => v / 100), }; }), }; @@ -50,7 +49,7 @@ const customTransform2: CustomTransformOperator = () => (source) => { fields: frame.fields.map((field) => { return { ...field, - values: new ArrayVector(field.values.toArray().map((v) => v * 2)), + values: field.values.map((v) => v * 2), }; }), }; @@ -86,7 +85,7 @@ describe('transformDataFrame', () => { const processed = received[0]; expect(processed[0].length).toEqual(1); expect(processed[0].fields.length).toEqual(1); - expect(processed[0].fields[0].values.get(0)).toEqual(3); + expect(processed[0].fields[0].values[0]).toEqual(3); }); }); @@ -113,7 +112,7 @@ describe('transformDataFrame', () => { const processed = received[0]; expect(processed[0].length).toEqual(1); expect(processed[0].fields.length).toEqual(2); - expect(processed[0].fields[0].values.get(0)).toEqual('temperature'); + expect(processed[0].fields[0].values[0]).toEqual('temperature'); }); }); @@ -143,7 +142,7 @@ describe('transformDataFrame', () => { // Only apply A await expect(transformDataFrame(cfg, [frameA, frameB])).toEmitValuesWith((received) => { - const processed = received[0].map((v) => v.fields[0].values.toArray()); + const processed = received[0].map((v) => v.fields[0].values); expect(processed).toBeTruthy(); expect(processed).toMatchObject([[5], [7, 8]]); }); @@ -151,7 +150,7 @@ describe('transformDataFrame', () => { // Only apply to B cfg[0].filter.options = 'B'; await expect(transformDataFrame(cfg, [frameA, frameB])).toEmitValuesWith((received) => { - const processed = received[0].map((v) => v.fields[0].values.toArray()); + const processed = received[0].map((v) => v.fields[0].values); expect(processed).toBeTruthy(); expect(processed).toMatchObject([[5, 6], [7]]); }); @@ -182,7 +181,7 @@ describe('transformDataFrame', () => { const processed = received[0]; expect(processed[0].length).toEqual(1); expect(processed[0].fields.length).toEqual(1); - expect(processed[0].fields[0].values.get(0)).toEqual(0.03); + expect(processed[0].fields[0].values[0]).toEqual(0.03); }); }); it('supports trailing custom transformation', async () => { @@ -209,7 +208,7 @@ describe('transformDataFrame', () => { const processed = received[0]; expect(processed[0].length).toEqual(1); expect(processed[0].fields.length).toEqual(1); - expect(processed[0].fields[0].values.get(0)).toEqual(0.03); + expect(processed[0].fields[0].values[0]).toEqual(0.03); }); }); @@ -238,7 +237,7 @@ describe('transformDataFrame', () => { const processed = received[0]; expect(processed[0].length).toEqual(1); expect(processed[0].fields.length).toEqual(1); - expect(processed[0].fields[0].values.get(0)).toEqual(0.06); + expect(processed[0].fields[0].values[0]).toEqual(0.06); }); }); }); diff --git a/packages/grafana-data/src/transformations/transformers/calculateField.test.ts b/packages/grafana-data/src/transformations/transformers/calculateField.test.ts index 8317e827d0d..f334c0e15e5 100644 --- a/packages/grafana-data/src/transformations/transformers/calculateField.test.ts +++ b/packages/grafana-data/src/transformations/transformers/calculateField.test.ts @@ -234,7 +234,7 @@ describe('calculateField transformer w/ timeseries', () => { await expect(transformDataFrame([cfg], [seriesBC])).toEmitValuesWith((received) => { const data = received[0][0]; expect(data.fields.length).toEqual(1); - expect(data.fields[0].values.toArray()).toEqual([0, 1]); + expect(data.fields[0].values).toEqual([0, 1]); }); }); diff --git a/packages/grafana-data/src/transformations/transformers/calculateField.ts b/packages/grafana-data/src/transformations/transformers/calculateField.ts index 1f1ac16eca9..3a5a9218ffc 100644 --- a/packages/grafana-data/src/transformations/transformers/calculateField.ts +++ b/packages/grafana-data/src/transformations/transformers/calculateField.ts @@ -3,11 +3,8 @@ import { map } from 'rxjs/operators'; import { getTimeField } from '../../dataframe/processDataFrame'; import { getFieldDisplayName } from '../../field'; -import { DataFrame, DataTransformerInfo, Field, FieldType, NullValueMode, Vector } from '../../types'; +import { DataFrame, DataTransformerInfo, Field, FieldType, NullValueMode } from '../../types'; import { BinaryOperationID, binaryOperators } from '../../utils/binaryOperators'; -import { ArrayVector, BinaryOperationVector, ConstantVector, IndexVector } from '../../vector'; -import { AsNumberVector } from '../../vector/AsNumberVector'; -import { RowVector } from '../../vector/RowVector'; import { doStandardCalcs, fieldReducers, ReducerID } from '../fieldReducer'; import { getFieldMatcher } from '../matchers'; import { FieldMatcherID } from '../matchers/ids'; @@ -61,7 +58,7 @@ export interface CalculateFieldTransformerOptions { // TODO: config?: FieldConfig; or maybe field overrides? since the UI exists } -type ValuesCreator = (data: DataFrame) => Vector; +type ValuesCreator = (data: DataFrame) => any[]; export const calculateFieldTransformer: DataTransformerInfo = { id: DataTransformerID.calculateField, @@ -104,11 +101,8 @@ export const calculateFieldTransformer: DataTransformerInfo { // Find the columns that should be examined - const columns: Vector[] = []; + const columns: any[] = []; for (const field of frame.fields) { if (matcher(field, frame, allFrames)) { columns.push(field.values); @@ -192,26 +186,31 @@ function getReduceRowCreator(options: ReduceOptions, allFrames: DataFrame[]): Va } // Prepare a "fake" field for the row - const iter = new RowVector(columns); + const size = columns.length; const row: Field = { name: 'temp', - values: iter, + values: new Array(size), type: FieldType.number, config: {}, }; const vals: number[] = []; for (let i = 0; i < frame.length; i++) { - iter.rowIndex = i; - const val = reducer(row, ignoreNulls, nullAsZero)[options.reducer]; - vals.push(val); + for (let j = 0; j < size; j++) { + row.values[j] = columns[j][i]; + } + vals.push(reducer(row, ignoreNulls, nullAsZero)[options.reducer]); } - return new ArrayVector(vals); + return vals; }; } -function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFrames: DataFrame[]): Vector | undefined { +function findFieldValuesWithNameOrConstant( + frame: DataFrame, + name: string, + allFrames: DataFrame[] +): number[] | undefined { if (!name) { return undefined; } @@ -219,7 +218,7 @@ function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFr for (const f of frame.fields) { if (name === getFieldDisplayName(f, frame, allFrames)) { if (f.type === FieldType.boolean) { - return new AsNumberVector(f.values); + return f.values.map((v) => (v ? 1 : 0)); } return f.values; } @@ -227,7 +226,7 @@ function findFieldValuesWithNameOrConstant(frame: DataFrame, name: string, allFr const v = parseFloat(name); if (!isNaN(v)) { - return new ConstantVector(v, frame.length); + return new Array(frame.length).fill(v); } return undefined; @@ -240,10 +239,14 @@ function getBinaryCreator(options: BinaryOptions, allFrames: DataFrame[]): Value const left = findFieldValuesWithNameOrConstant(frame, options.left, allFrames); const right = findFieldValuesWithNameOrConstant(frame, options.right, allFrames); if (!left || !right || !operator) { - return undefined as unknown as Vector; + return undefined as unknown as any[]; } - return new BinaryOperationVector(left, right, operator.operation); + const arr = new Array(left.length); + for (let i = 0; i < arr.length; i++) { + arr[i] = operator.operation(left[i], right[i]); + } + return arr; }; } diff --git a/packages/grafana-data/src/transformations/transformers/concat.ts b/packages/grafana-data/src/transformations/transformers/concat.ts index e19728f0686..a8cb3794e20 100644 --- a/packages/grafana-data/src/transformations/transformers/concat.ts +++ b/packages/grafana-data/src/transformations/transformers/concat.ts @@ -2,7 +2,6 @@ import { map } from 'rxjs/operators'; import { DataFrame, Field, TIME_SERIES_VALUE_FIELD_NAME } from '../../types/dataFrame'; import { DataTransformerInfo } from '../../types/transformations'; -import { ArrayVector } from '../../vector'; import { DataTransformerID } from './ids'; @@ -90,11 +89,11 @@ export function concatenateFields(data: DataFrame[], opts: ConcatenateTransforme if (f.values.length === maxLength) { return f; } - const values = f.values.toArray(); + const values = f.values; values.length = maxLength; return { ...f, - values: new ArrayVector(values), + values: values, }; }); } diff --git a/packages/grafana-data/src/transformations/transformers/convertFieldType.test.ts b/packages/grafana-data/src/transformations/transformers/convertFieldType.test.ts index 64cd6e05d6d..432ae01e9fe 100644 --- a/packages/grafana-data/src/transformations/transformers/convertFieldType.test.ts +++ b/packages/grafana-data/src/transformations/transformers/convertFieldType.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe/processDataFrame'; import { Field, FieldType } from '../../types/dataFrame'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { ensureTimeField, @@ -18,13 +17,13 @@ describe('field convert type', () => { const stringTime = { name: 'proper dates', type: FieldType.string, - values: new ArrayVector([ + values: [ '2021-07-19 00:00:00.000', '2021-07-23 00:00:00.000', '2021-07-25 00:00:00.000', '2021-08-01 00:00:00.000', '2021-08-02 00:00:00.000', - ]), + ], config: {}, }; @@ -32,7 +31,7 @@ describe('field convert type', () => { expect(timefield).toEqual({ name: 'proper dates', type: FieldType.time, - values: new ArrayVector([1626674400000, 1627020000000, 1627192800000, 1627797600000, 1627884000000]), + values: [1626674400000, 1627020000000, 1627192800000, 1627797600000, 1627884000000], config: {}, }); }); @@ -42,13 +41,13 @@ describe('field convert type', () => { const yearFormat = { name: 'format to year', type: FieldType.string, - values: new ArrayVector([ + values: [ '2017-07-19 00:00:00.000', '2018-07-23 00:00:00.000', '2019-07-25 00:00:00.000', '2020-08-01 00:00:00.000', '2021-08-02 00:00:00.000', - ]), + ], config: {}, }; @@ -56,7 +55,7 @@ describe('field convert type', () => { expect(timefield).toEqual({ name: 'format to year', type: FieldType.time, - values: new ArrayVector([1483246800000, 1514782800000, 1546318800000, 1577854800000, 1609477200000]), + values: [1483246800000, 1514782800000, 1546318800000, 1577854800000, 1609477200000], config: {}, }); }); @@ -67,7 +66,7 @@ describe('field convert type', () => { const misformattedStrings = { name: 'misformatted dates', type: FieldType.string, - values: new ArrayVector(['2021/08-01 00:00.00:000', '2021/08/01 00.00-000', '2021/08-01 00:00.00:000']), + values: ['2021/08-01 00:00.00:000', '2021/08/01 00.00-000', '2021/08-01 00:00.00:000'], config: { unit: 'time' }, }; @@ -75,7 +74,7 @@ describe('field convert type', () => { expect(timefield).toEqual({ name: 'misformatted dates', type: FieldType.time, - values: new ArrayVector([null, null, null]), + values: [null, null, null], config: { unit: 'time' }, }); }); @@ -86,7 +85,7 @@ describe('field convert type', () => { const stringyNumbers = { name: 'stringy nums', type: FieldType.string, - values: new ArrayVector(['10', '12', '30', '14', '10']), + values: ['10', '12', '30', '14', '10'], config: {}, }; @@ -95,19 +94,40 @@ describe('field convert type', () => { expect(numbers).toEqual({ name: 'stringy nums', type: FieldType.number, - values: new ArrayVector([10, 12, 30, 14, 10]), + values: [10, 12, 30, 14, 10], config: {}, }); }); }); +it('can convert proper numeric strings to numbers, but also treat edge-cases', () => { + const options = { targetField: 'stringy nums', destinationType: FieldType.number }; + + //there are scenarios where string fields have numeric values, or the strings are non-numeric and cannot pe converted + const stringyNumbers = { + name: 'stringy nums', + type: FieldType.string, + values: ['10', '1asd2', '30', 14, 10, '23', '', null], + config: {}, + }; + + const numbers = convertFieldType(stringyNumbers, options); + + expect(numbers).toEqual({ + name: 'stringy nums', + type: FieldType.number, + values: [10, null, 30, 14, 10, 23, 0, 0], + config: {}, + }); +}); + it('can convert strings with commas to numbers', () => { const options = { targetField: 'stringy nums', destinationType: FieldType.number }; const stringyNumbers = { name: 'stringy nums', type: FieldType.string, - values: new ArrayVector(['1,000', '1,000,000']), + values: ['1,000', '1,000,000'], config: {}, }; @@ -116,7 +136,7 @@ it('can convert strings with commas to numbers', () => { expect(numbers).toEqual({ name: 'stringy nums', type: FieldType.number, - values: new ArrayVector([1000, 1000000]), + values: [1000, 1000000], config: {}, }); }); @@ -127,7 +147,7 @@ it('converts booleans to numbers', () => { const stringyNumbers = { name: 'booleans', type: FieldType.boolean, - values: new ArrayVector([true, false]), + values: [true, false], config: {}, }; @@ -136,7 +156,7 @@ it('converts booleans to numbers', () => { expect(numbers).toEqual({ name: 'booleans', type: FieldType.number, - values: new ArrayVector([1, 0]), + values: [1, 0], config: {}, }); }); @@ -175,7 +195,7 @@ describe('field convert types transformer', () => { expect( numbers[0].fields.map((f) => ({ type: f.type, - values: f.values.toArray(), + values: f.values, })) ).toEqual([ { type: FieldType.number, values: [1, 2, 3, 4, 5] }, @@ -213,7 +233,7 @@ describe('field convert types transformer', () => { expect( booleans[0].fields.map((f) => ({ type: f.type, - values: f.values.toArray(), + values: f.values, })) ).toEqual([ { @@ -277,7 +297,7 @@ describe('field convert types transformer', () => { expect( complex[0].fields.map((f) => ({ type: f.type, - values: f.values.toArray(), + values: f.values, })) ).toEqual([ { @@ -325,7 +345,7 @@ describe('field convert types transformer', () => { expect( stringified[0].fields.map((f) => ({ type: f.type, - values: f.values.toArray(), + values: f.values, })) ).toEqual([ { @@ -355,7 +375,7 @@ describe('field convert types transformer', () => { ], }), ])[0].fields[0]; - expect(stringified.values.toArray()).toEqual([ + expect(stringified.values).toEqual([ '2021-07', '2021-07', '2021-07', // can group by month @@ -382,7 +402,7 @@ describe('ensureTimeField', () => { config: {}, name: 'proper dates', type: FieldType.time, - values: new ArrayVector([1626674400000, 1627020000000, 1627192800000, 1627797600000, 1627884000000]), + values: [1626674400000, 1627020000000, 1627192800000, 1627797600000, 1627884000000], }); }); }); @@ -396,14 +416,14 @@ describe('fieldToTimeField', () => { config: {}, name: 'ISO 8601 date strings', type: FieldType.time, - values: new ArrayVector(['2021-11-11T19:45:00Z']), + values: ['2021-11-11T19:45:00Z'], }; expect(fieldToTimeField(stringTimeField)).toEqual({ config: {}, name: 'ISO 8601 date strings', type: FieldType.time, - values: new ArrayVector([1636659900000]), + values: [1636659900000], }); }); @@ -412,7 +432,7 @@ describe('fieldToTimeField', () => { config: {}, name: 'ISO 8601 date strings', type: FieldType.time, - values: new ArrayVector([ + values: [ '2021-11-11T19:45:00+05:30', '2021-11-11T19:45:00-05:30', '2021-11-11T19:45:00+0530', @@ -421,17 +441,17 @@ describe('fieldToTimeField', () => { '2021-11-11T19:45:00.0000000000-0530', '2021-11-11T19:45:00.000Z', '2021-11-11T19:45:00.0000000000Z', - ]), + ], }; expect(fieldToTimeField(stringTimeField)).toEqual({ config: {}, name: 'ISO 8601 date strings', type: FieldType.time, - values: new ArrayVector([ + values: [ 1636640100000, 1636679700000, 1636640100000, 1636679700000, 1636640100000, 1636679700000, 1636659900000, 1636659900000, - ]), + ], }); }); }); diff --git a/packages/grafana-data/src/transformations/transformers/convertFieldType.ts b/packages/grafana-data/src/transformations/transformers/convertFieldType.ts index 25b9d04076b..9beb8cfdd5c 100644 --- a/packages/grafana-data/src/transformations/transformers/convertFieldType.ts +++ b/packages/grafana-data/src/transformations/transformers/convertFieldType.ts @@ -3,7 +3,6 @@ import { map } from 'rxjs/operators'; import { dateTimeParse } from '../../datetime'; import { SynchronousDataTransformerInfo } from '../../types'; import { DataFrame, EnumFieldConfig, Field, FieldType } from '../../types/dataFrame'; -import { ArrayVector } from '../../vector'; import { fieldMatchers } from '../matchers'; import { FieldMatcherID } from '../matchers/ids'; @@ -118,7 +117,7 @@ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3,})?(?:Z|[-+] export function fieldToTimeField(field: Field, dateFormat?: string): Field { let opts = dateFormat ? { format: dateFormat } : undefined; - const timeValues = field.values.toArray().slice(); + const timeValues = field.values.slice(); let firstDefined = timeValues.find((v) => v != null); @@ -136,19 +135,19 @@ export function fieldToTimeField(field: Field, dateFormat?: string): Field { return { ...field, type: FieldType.time, - values: new ArrayVector(timeValues), + values: timeValues, }; } function fieldToNumberField(field: Field): Field { - const numValues = field.values.toArray().slice(); + const numValues = field.values.slice(); const valuesAsStrings = numValues.some((v) => typeof v === 'string'); for (let n = 0; n < numValues.length; n++) { let toBeConverted = numValues[n]; - if (valuesAsStrings) { + if (valuesAsStrings && toBeConverted != null && typeof toBeConverted === 'string') { // some numbers returned from datasources have commas // strip the commas, coerce the string to a number toBeConverted = toBeConverted.replace(/,/g, ''); @@ -162,12 +161,12 @@ function fieldToNumberField(field: Field): Field { return { ...field, type: FieldType.number, - values: new ArrayVector(numValues), + values: numValues, }; } function fieldToBooleanField(field: Field): Field { - const booleanValues = field.values.toArray().slice(); + const booleanValues = field.values.slice(); for (let b = 0; b < booleanValues.length; b++) { booleanValues[b] = Boolean(!!booleanValues[b]); @@ -176,12 +175,12 @@ function fieldToBooleanField(field: Field): Field { return { ...field, type: FieldType.boolean, - values: new ArrayVector(booleanValues), + values: booleanValues, }; } function fieldToStringField(field: Field, dateFormat?: string): Field { - let values = field.values.toArray(); + let values = field.values; switch (field.type) { case FieldType.time: @@ -199,12 +198,12 @@ function fieldToStringField(field: Field, dateFormat?: string): Field { return { ...field, type: FieldType.string, - values: new ArrayVector(values), + values: values, }; } function fieldToComplexField(field: Field): Field { - const complexValues = field.values.toArray().slice(); + const complexValues = field.values.slice(); for (let s = 0; s < complexValues.length; s++) { try { @@ -217,7 +216,7 @@ function fieldToComplexField(field: Field): Field { return { ...field, type: FieldType.other, - values: new ArrayVector(complexValues), + values: complexValues, }; } @@ -230,7 +229,7 @@ function fieldToComplexField(field: Field): Field { * @public */ export function ensureTimeField(field: Field, dateFormat?: string): Field { - const firstValueTypeIsNumber = typeof field.values.get(0) === 'number'; + const firstValueTypeIsNumber = typeof field.values[0] === 'number'; if (field.type === FieldType.time && firstValueTypeIsNumber) { return field; //already time } @@ -245,7 +244,7 @@ export function ensureTimeField(field: Field, dateFormat?: string): Field { function fieldToEnumField(field: Field, cfg?: EnumFieldConfig): Field { const enumConfig = { ...cfg }; - const enumValues = field.values.toArray().slice(); + const enumValues = field.values.slice(); const lookup = new Map(); if (enumConfig.text) { for (let i = 0; i < enumConfig.text.length; i++) { @@ -273,6 +272,6 @@ function fieldToEnumField(field: Field, cfg?: EnumFieldConfig): Field { }, }, type: FieldType.enum, - values: new ArrayVector(enumValues), + values: enumValues, }; } diff --git a/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts b/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts index 96fd6b4dae0..37e836f38e2 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByValue.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe/processDataFrame'; import { DataTransformerConfig, FieldType, MatcherConfig } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { ValueMatcherID } from '../matchers/ids'; import { BasicValueMatcherOptions } from '../matchers/valueMatchers/types'; import { transformDataFrame } from '../transformDataFrame'; @@ -18,8 +17,8 @@ const seriesAWithSingleField = toDataFrame({ name: 'A', length: 7, fields: [ - { name: 'time', type: FieldType.time, values: new ArrayVector([1000, 2000, 3000, 4000, 5000, 6000, 7000]) }, - { name: 'numbers', type: FieldType.number, values: new ArrayVector([1, 2, 3, 4, 5, 6, 7]) }, + { name: 'time', type: FieldType.time, values: [1000, 2000, 3000, 4000, 5000, 6000, 7000] }, + { name: 'numbers', type: FieldType.number, values: [1, 2, 3, 4, 5, 6, 7] }, ], }); @@ -56,13 +55,13 @@ describe('FilterByValue transformer', () => { { name: 'time', type: FieldType.time, - values: new ArrayVector([6000, 7000]), + values: [6000, 7000], state: {}, }, { name: 'numbers', type: FieldType.number, - values: new ArrayVector([6, 7]), + values: [6, 7], state: {}, }, ]); @@ -97,13 +96,13 @@ describe('FilterByValue transformer', () => { { name: 'time', type: FieldType.time, - values: new ArrayVector([1000, 2000, 3000, 4000, 5000]), + values: [1000, 2000, 3000, 4000, 5000], state: {}, }, { name: 'numbers', type: FieldType.number, - values: new ArrayVector([1, 2, 3, 4, 5]), + values: [1, 2, 3, 4, 5], state: {}, }, ]); @@ -147,13 +146,13 @@ describe('FilterByValue transformer', () => { { name: 'time', type: FieldType.time, - values: new ArrayVector([1000, 2000, 3000, 4000, 7000]), + values: [1000, 2000, 3000, 4000, 7000], state: {}, }, { name: 'numbers', type: FieldType.number, - values: new ArrayVector([1, 2, 3, 4, 7]), + values: [1, 2, 3, 4, 7], state: {}, }, ]); @@ -197,13 +196,13 @@ describe('FilterByValue transformer', () => { { name: 'time', type: FieldType.time, - values: new ArrayVector([4000, 5000]), + values: [4000, 5000], state: {}, }, { name: 'numbers', type: FieldType.number, - values: new ArrayVector([4, 5]), + values: [4, 5], state: {}, }, ]); diff --git a/packages/grafana-data/src/transformations/transformers/filterByValue.ts b/packages/grafana-data/src/transformations/transformers/filterByValue.ts index 295316dee78..0e6f47e9b7b 100644 --- a/packages/grafana-data/src/transformations/transformers/filterByValue.ts +++ b/packages/grafana-data/src/transformations/transformers/filterByValue.ts @@ -3,7 +3,6 @@ import { map } from 'rxjs/operators'; import { getFieldDisplayName } from '../../field/fieldState'; import { DataFrame, Field } from '../../types/dataFrame'; import { DataTransformerInfo, MatcherConfig } from '../../types/transformations'; -import { ArrayVector } from '../../vector/ArrayVector'; import { getValueMatcher } from '../matchers'; import { DataTransformerID } from './ids'; @@ -101,12 +100,12 @@ export const filterByValueTransformer: DataTransformerInfo { { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'three']), + values: ['one', 'two', 'three'], config: {}, }, ]; @@ -110,13 +109,13 @@ describe('GroupBy transformer', () => { { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'three']), + values: ['one', 'two', 'three'], config: {}, }, { name: 'values (sum)', type: FieldType.number, - values: new ArrayVector([1, 4, 9]), + values: [1, 4, 9], config: {}, }, ]; @@ -161,25 +160,25 @@ describe('GroupBy transformer', () => { { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'three']), + values: ['one', 'two', 'three'], config: {}, }, { name: 'time (count)', type: FieldType.number, - values: new ArrayVector([1, 2, 3]), + values: [1, 2, 3], config: {}, }, { name: 'time (last)', type: FieldType.time, - values: new ArrayVector([3000, 5000, 8000]), + values: [3000, 5000, 8000], config: {}, }, { name: 'values (sum)', type: FieldType.number, - values: new ArrayVector([1, 4, 9]), + values: [1, 4, 9], config: {}, }, ]; @@ -230,13 +229,13 @@ describe('GroupBy transformer', () => { { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'three']), + values: ['one', 'two', 'three'], config: {}, }, { name: 'values (sum)', type: FieldType.number, - values: new ArrayVector([1, 4, 9]), + values: [1, 4, 9], config: {}, }, ]; @@ -245,13 +244,13 @@ describe('GroupBy transformer', () => { { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'three']), + values: ['one', 'two', 'three'], config: {}, }, { name: 'values (sum)', type: FieldType.number, - values: new ArrayVector([0, 7, 8]), + values: [0, 7, 8], config: {}, }, ]; @@ -292,13 +291,13 @@ describe('GroupBy transformer', () => { { name: 'message', type: FieldType.string, - values: new ArrayVector(['500', '404', 'one', 'two', '200']), + values: ['500', '404', 'one', 'two', '200'], config: {}, }, { name: 'values (sum)', type: FieldType.number, - values: new ArrayVector([1, 4, 6, 3, 4]), + values: [1, 4, 6, 3, 4], config: {}, }, ]; diff --git a/packages/grafana-data/src/transformations/transformers/groupBy.ts b/packages/grafana-data/src/transformations/transformers/groupBy.ts index 1c95825d529..5f990c59ec0 100644 --- a/packages/grafana-data/src/transformations/transformers/groupBy.ts +++ b/packages/grafana-data/src/transformations/transformers/groupBy.ts @@ -1,11 +1,9 @@ import { map } from 'rxjs/operators'; -import { MutableField } from '../../dataframe/MutableDataFrame'; import { guessFieldTypeForField } from '../../dataframe/processDataFrame'; import { getFieldDisplayName } from '../../field/fieldState'; import { DataFrame, Field, FieldType } from '../../types/dataFrame'; import { DataTransformerInfo } from '../../types/transformations'; -import { ArrayVector } from '../../vector/ArrayVector'; import { reduceField, ReducerID } from '../fieldReducer'; import { DataTransformerID } from './ids'; @@ -64,9 +62,9 @@ export const groupByTransformer: DataTransformerInfo // Group the values by fields and groups so we can get all values for a // group for a given field. - const valuesByGroupKey = new Map>(); + const valuesByGroupKey = new Map>(); for (let rowIndex = 0; rowIndex < frame.length; rowIndex++) { - const groupKey = String(groupByFields.map((field) => field.values.get(rowIndex))); + const groupKey = String(groupByFields.map((field) => field.values[rowIndex])); const valuesByField = valuesByGroupKey.get(groupKey) ?? {}; if (!valuesByGroupKey.has(groupKey)) { @@ -81,22 +79,22 @@ export const groupByTransformer: DataTransformerInfo name: fieldName, type: field.type, config: { ...field.config }, - values: new ArrayVector(), + values: [], }; } - valuesByField[fieldName].values.add(field.values.get(rowIndex)); + valuesByField[fieldName].values.push(field.values[rowIndex]); } } const fields: Field[] = []; for (const field of groupByFields) { - const values = new ArrayVector(); + const values: any[] = []; const fieldName = getFieldDisplayName(field); valuesByGroupKey.forEach((value) => { - values.add(value[fieldName].values.get(0)); + values.push(value[fieldName].values[0]); }); fields.push({ @@ -137,7 +135,7 @@ export const groupByTransformer: DataTransformerInfo for (const aggregation of aggregations) { const aggregationField: Field = { name: `${fieldName} (${aggregation})`, - values: new ArrayVector(valuesByAggregation[aggregation]), + values: valuesByAggregation[aggregation], type: FieldType.other, config: {}, }; diff --git a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts index 781d2401095..81a8c670931 100644 --- a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts +++ b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { DataTransformerConfig, FieldType, Field, SpecialValue } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { GroupingToMatrixTransformerOptions, groupingToMatrixTransformer } from './groupingToMatrix'; @@ -32,25 +31,25 @@ describe('Grouping to Matrix', () => { { name: 'Time\\Time', type: FieldType.string, - values: new ArrayVector([1000, 1001, 1002]), + values: [1000, 1001, 1002], config: {}, }, { name: '1000', type: FieldType.number, - values: new ArrayVector([1, '', '']), + values: [1, '', ''], config: {}, }, { name: '1001', type: FieldType.number, - values: new ArrayVector(['', 2, '']), + values: ['', 2, ''], config: {}, }, { name: '1002', type: FieldType.number, - values: new ArrayVector(['', '', 3]), + values: ['', '', 3], config: {}, }, ]; @@ -84,19 +83,19 @@ describe('Grouping to Matrix', () => { { name: 'Row\\Column', type: FieldType.string, - values: new ArrayVector(['R1', 'R2']), + values: ['R1', 'R2'], config: {}, }, { name: 'C1', type: FieldType.number, - values: new ArrayVector([1, 4]), + values: [1, 4], config: {}, }, { name: 'C2', type: FieldType.number, - values: new ArrayVector([5, '']), + values: [5, ''], config: {}, }, ]; @@ -127,19 +126,19 @@ describe('Grouping to Matrix', () => { { name: 'Time\\Time', type: FieldType.string, - values: new ArrayVector([1000, 1001]), + values: [1000, 1001], config: {}, }, { name: '1000', type: FieldType.number, - values: new ArrayVector([1, null]), + values: [1, null], config: {}, }, { name: '1001', type: FieldType.number, - values: new ArrayVector([null, 2]), + values: [null, 2], config: {}, }, ]; diff --git a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts index 85fa47edf15..741c2a50adf 100644 --- a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts +++ b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts @@ -1,8 +1,9 @@ import { map } from 'rxjs/operators'; -import { MutableDataFrame } from '../../dataframe'; import { getFieldDisplayName } from '../../field/fieldState'; -import { DataFrame, DataTransformerInfo, Field, FieldType, SpecialValue, Vector } from '../../types'; +import { DataFrame, DataTransformerInfo, Field, FieldType, SpecialValue } from '../../types'; +import { fieldMatchers } from '../matchers'; +import { FieldMatcherID } from '../matchers/ids'; import { DataTransformerID } from './ids'; @@ -18,6 +19,11 @@ const DEFAULT_ROW_FIELD = 'Time'; const DEFAULT_VALUE_FIELD = 'Value'; const DEFAULT_EMPTY_VALUE = SpecialValue.Empty; +// grafana-data does not have access to runtime so we are accessing the window object +// to get access to the feature toggle +// eslint-disable-next-line +const supportDataplaneFallback = (window as any)?.grafanaBootData?.settings?.featureToggles?.dataplaneFrontendFallback; + export const groupingToMatrixTransformer: DataTransformerInfo = { id: DataTransformerID.groupingToMatrix, name: 'Grouping to Matrix', @@ -57,9 +63,9 @@ export const groupingToMatrixTransformer: DataTransformerInfo(values: T[]): T[] { + const unique = new Set(); for (let index = 0; index < values.length; index++) { - unique.add(values.get(index)); + unique.add(values[index]); } return Array.from(unique); @@ -110,7 +130,16 @@ function findKeyField(frame: DataFrame, matchTitle: string): Field | null { for (let fieldIndex = 0; fieldIndex < frame.fields.length; fieldIndex++) { const field = frame.fields[fieldIndex]; - if (matchTitle === getFieldDisplayName(field)) { + // support for dataplane contract with Prometheus and change in location of field name + let matches: boolean; + if (supportDataplaneFallback) { + const matcher = fieldMatchers.get(FieldMatcherID.byName).get(matchTitle); + matches = matcher(field, frame, [frame]); + } else { + matches = matchTitle === getFieldDisplayName(field); + } + + if (matches) { return field; } } diff --git a/packages/grafana-data/src/transformations/transformers/histogram.test.ts b/packages/grafana-data/src/transformations/transformers/histogram.test.ts index dd1e24fb2d7..64a952d2196 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.test.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.test.ts @@ -26,7 +26,7 @@ describe('histogram frames frames', () => { expect( out.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, config: f.config, })) ).toMatchInlineSnapshot(` @@ -140,7 +140,7 @@ describe('histogram frames frames', () => { expect( out2.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, })) ).toMatchInlineSnapshot(` [ diff --git a/packages/grafana-data/src/transformations/transformers/histogram.ts b/packages/grafana-data/src/transformations/transformers/histogram.ts index c0da943552a..be56e154967 100644 --- a/packages/grafana-data/src/transformations/transformers/histogram.ts +++ b/packages/grafana-data/src/transformations/transformers/histogram.ts @@ -5,7 +5,6 @@ import { createTheme, GrafanaTheme2 } from '../../themes'; import { DataFrameType, SynchronousDataTransformerInfo } from '../../types'; import { DataFrame, Field, FieldConfig, FieldType } from '../../types/dataFrame'; import { roundDecimals } from '../../utils'; -import { ArrayVector } from '../../vector/ArrayVector'; import { DataTransformerID } from './ids'; import { AlignedData, join } from './joinDataFrames'; @@ -152,24 +151,24 @@ export function getHistogramFields(frame: DataFrame): HistogramFields | undefine // guess bucket size from single explicit bucket field if (!xMax && xMin && xMin.values.length > 1) { - let vals = xMin.values.toArray(); + let vals = xMin.values; let bucketSize = roundDecimals(vals[1] - vals[0], 6); xMax = { ...xMin, name: histogramFrameBucketMaxFieldName, - values: new ArrayVector(vals.map((v) => v + bucketSize)), + values: vals.map((v) => v + bucketSize), }; } if (!xMin && xMax && xMax?.values.length > 1) { - let vals = xMax.values.toArray(); + let vals = xMax.values; let bucketSize = roundDecimals(vals[1] - vals[0], 6); xMin = { ...xMax, name: histogramFrameBucketMinFieldName, - values: new ArrayVector(vals.map((v) => v - bucketSize)), + values: vals.map((v) => v - bucketSize), }; } @@ -200,7 +199,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform for (const frame of frames) { for (const field of frame.fields) { if (field.type === FieldType.number) { - allValues = allValues.concat(field.values.toArray()); + allValues = allValues.concat(field.values); } } } @@ -254,7 +253,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform for (const frame of frames) { for (const field of frame.fields) { if (field.type === FieldType.number) { - let fieldHist = histogram(field.values.toArray(), getBucket, histFilter, histSort) as AlignedData; + let fieldHist = histogram(field.values, getBucket, histFilter, histSort) as AlignedData; histograms.push(fieldHist); counts.push({ ...field, @@ -291,7 +290,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform const xMin: Field = { name: histogramFrameBucketMinFieldName, - values: new ArrayVector(joinedHists[0]), + values: joinedHists[0], type: FieldType.number, state: undefined, config: @@ -305,7 +304,7 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform const xMax = { ...xMin, name: histogramFrameBucketMaxFieldName, - values: new ArrayVector(joinedHists[0].map((v) => v + bucketSize!)), + values: joinedHists[0].map((v) => v + bucketSize!), }; if (options?.combine) { @@ -319,14 +318,14 @@ export function buildHistogram(frames: DataFrame[], options?: HistogramTransform { ...counts[0], name: 'count', - values: new ArrayVector(vals), + values: vals, type: FieldType.number, state: undefined, }, ]; } else { counts.forEach((field, i) => { - field.values = new ArrayVector(joinedHists[i + 1]); + field.values = joinedHists[i + 1]; }); } diff --git a/packages/grafana-data/src/transformations/transformers/joinByField.test.ts b/packages/grafana-data/src/transformations/transformers/joinByField.test.ts index 1a1026017b3..94208ba43fc 100644 --- a/packages/grafana-data/src/transformations/transformers/joinByField.test.ts +++ b/packages/grafana-data/src/transformations/transformers/joinByField.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { FieldType, DataTransformerConfig } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { DataTransformerID } from './ids'; @@ -257,9 +256,9 @@ describe('JOIN Transformer', () => { }, }; - everySecondSeries.fields[0].values = new ArrayVector(everySecondSeries.fields[0].values.toArray().reverse()); - everySecondSeries.fields[1].values = new ArrayVector(everySecondSeries.fields[1].values.toArray().reverse()); - everySecondSeries.fields[2].values = new ArrayVector(everySecondSeries.fields[2].values.toArray().reverse()); + everySecondSeries.fields[0].values = everySecondSeries.fields[0].values.reverse(); + everySecondSeries.fields[1].values = everySecondSeries.fields[1].values.reverse(); + everySecondSeries.fields[2].values = everySecondSeries.fields[2].values.reverse(); await expect(transformDataFrame([cfg], [everySecondSeries, everyOtherSecondSeries])).toEmitValuesWith( (received) => { @@ -771,9 +770,9 @@ describe('JOIN Transformer', () => { }, }; - seriesA.fields[0].values = new ArrayVector(seriesA.fields[0].values.toArray().reverse()); - seriesA.fields[1].values = new ArrayVector(seriesA.fields[1].values.toArray().reverse()); - seriesA.fields[2].values = new ArrayVector(seriesA.fields[2].values.toArray().reverse()); + seriesA.fields[0].values = seriesA.fields[0].values.reverse(); + seriesA.fields[1].values = seriesA.fields[1].values.reverse(); + seriesA.fields[2].values = seriesA.fields[2].values.reverse(); await expect(transformDataFrame([cfg], [seriesA, seriesB])).toEmitValuesWith((received) => { const data = received[0]; diff --git a/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts b/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts index 5f4ef23427b..6f353f356dd 100644 --- a/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts +++ b/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe/processDataFrame'; import { FieldType } from '../../types/dataFrame'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { calculateFieldTransformer } from './calculateField'; import { JoinMode } from './joinByField'; @@ -33,7 +32,7 @@ describe('align frames', () => { expect( out.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, })) ).toMatchInlineSnapshot(` [ @@ -86,7 +85,7 @@ describe('align frames', () => { expect( out.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, })) ).toMatchInlineSnapshot(` [ @@ -150,7 +149,7 @@ describe('align frames', () => { expect( out.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, state: f.state, })) ).toMatchInlineSnapshot(` @@ -243,7 +242,7 @@ describe('align frames', () => { expect( out.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, })) ).toMatchInlineSnapshot(` [ @@ -290,7 +289,7 @@ describe('align frames', () => { expect( out.fields.map((f) => ({ name: f.name, - values: f.values.toArray(), + values: f.values, })) ).toMatchInlineSnapshot(` [ @@ -321,42 +320,40 @@ describe('align frames', () => { describe('check ascending data', () => { it('simple ascending', () => { - const v = new ArrayVector([1, 2, 3, 4, 5]); + const v = [1, 2, 3, 4, 5]; expect(isLikelyAscendingVector(v)).toBeTruthy(); }); it('simple ascending with null', () => { - const v = new ArrayVector([null, 2, 3, 4, null]); + const v = [null, 2, 3, 4, null]; expect(isLikelyAscendingVector(v)).toBeTruthy(); }); it('single value', () => { - const v = new ArrayVector([null, null, null, 4, null]); + const v = [null, null, null, 4, null]; expect(isLikelyAscendingVector(v)).toBeTruthy(); - expect(isLikelyAscendingVector(new ArrayVector([4]))).toBeTruthy(); - expect(isLikelyAscendingVector(new ArrayVector([]))).toBeTruthy(); + expect(isLikelyAscendingVector([4])).toBeTruthy(); + expect(isLikelyAscendingVector([])).toBeTruthy(); }); it('middle values', () => { - const v = new ArrayVector([null, null, 5, 4, null]); + const v = [null, null, 5, 4, null]; expect(isLikelyAscendingVector(v)).toBeFalsy(); }); it('decending', () => { - expect(isLikelyAscendingVector(new ArrayVector([7, 6, null]))).toBeFalsy(); - expect(isLikelyAscendingVector(new ArrayVector([7, 8, 6]))).toBeFalsy(); + expect(isLikelyAscendingVector([7, 6, null])).toBeFalsy(); + expect(isLikelyAscendingVector([7, 8, 6])).toBeFalsy(); }); it('ascending first/last', () => { - expect(isLikelyAscendingVector(new ArrayVector([10, 20, 30, 5, 15, 7, 43, 29, 11]), 3)).toBeFalsy(); - expect( - isLikelyAscendingVector(new ArrayVector([null, 10, 20, 30, 5, null, 15, 7, 43, 29, 11, null]), 3) - ).toBeFalsy(); + expect(isLikelyAscendingVector([10, 20, 30, 5, 15, 7, 43, 29, 11], 3)).toBeFalsy(); + expect(isLikelyAscendingVector([null, 10, 20, 30, 5, null, 15, 7, 43, 29, 11, null], 3)).toBeFalsy(); }); it('null stuffs', () => { - expect(isLikelyAscendingVector(new ArrayVector([null, null, 1]), 3)).toBeTruthy(); - expect(isLikelyAscendingVector(new ArrayVector([1, null, null]), 3)).toBeTruthy(); - expect(isLikelyAscendingVector(new ArrayVector([null, null, null]), 3)).toBeTruthy(); - expect(isLikelyAscendingVector(new ArrayVector([null, 1, null]), 3)).toBeTruthy(); + expect(isLikelyAscendingVector([null, null, 1], 3)).toBeTruthy(); + expect(isLikelyAscendingVector([1, null, null], 3)).toBeTruthy(); + expect(isLikelyAscendingVector([null, null, null], 3)).toBeTruthy(); + expect(isLikelyAscendingVector([null, 1, null], 3)).toBeTruthy(); }); }); }); diff --git a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts index 767161d2f9a..167a3f0ec50 100644 --- a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts +++ b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts @@ -1,8 +1,7 @@ import intersect from 'fast_array_intersect'; import { getTimeField, sortDataFrame } from '../../dataframe'; -import { DataFrame, Field, FieldMatcher, FieldType, Vector } from '../../types'; -import { ArrayVector } from '../../vector'; +import { DataFrame, Field, FieldMatcher, FieldType } from '../../types'; import { fieldMatchers } from '../matchers'; import { FieldMatcherID } from '../matchers/ids'; @@ -208,10 +207,10 @@ export function joinDataFrames(options: JoinOptions): DataFrame | undefined { } nullModes.push(nullModesFrame); - const a: AlignedData = [join.values.toArray()]; // + const a: AlignedData = [join.values]; // for (const field of fields) { - a.push(field.values.toArray()); + a.push(field.values); originalFields.push(field); // clear field displayName state delete field.state?.displayName; @@ -227,7 +226,7 @@ export function joinDataFrames(options: JoinOptions): DataFrame | undefined { length: joined[0].length, fields: originalFields.map((f, index) => ({ ...f, - values: new ArrayVector(joined[index]), + values: joined[index], })), }; } @@ -352,7 +351,7 @@ export function join(tables: AlignedData[], nullModes?: number[][], mode: JoinMo // Test a few samples to see if the values are ascending // Only exported for tests -export function isLikelyAscendingVector(data: Vector, samples = 50) { +export function isLikelyAscendingVector(data: any[], samples = 50) { const len = data.length; // empty or single value @@ -364,11 +363,11 @@ export function isLikelyAscendingVector(data: Vector, samples = 50) { let firstIdx = 0; let lastIdx = len - 1; - while (firstIdx <= lastIdx && data.get(firstIdx) == null) { + while (firstIdx <= lastIdx && data[firstIdx] == null) { firstIdx++; } - while (lastIdx >= firstIdx && data.get(lastIdx) == null) { + while (lastIdx >= firstIdx && data[lastIdx] == null) { lastIdx--; } @@ -379,8 +378,8 @@ export function isLikelyAscendingVector(data: Vector, samples = 50) { const stride = Math.max(1, Math.floor((lastIdx - firstIdx + 1) / samples)); - for (let prevVal = data.get(firstIdx), i = firstIdx + stride; i <= lastIdx; i += stride) { - const v = data.get(i); + for (let prevVal = data[firstIdx], i = firstIdx + stride; i <= lastIdx; i += stride) { + const v = data[i]; if (v != null) { if (v <= prevVal) { diff --git a/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts b/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts index 1fe663527e4..1b97aa6a812 100644 --- a/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts +++ b/packages/grafana-data/src/transformations/transformers/labelsToFields.test.ts @@ -259,7 +259,7 @@ describe('Labels as Columns', () => { }); await expect(transformDataFrame([cfg], [source])).toEmitValuesWith((received) => { - expect(received[0][0].fields.map((f) => ({ [f.name]: f.values.toArray() }))).toMatchInlineSnapshot(` + expect(received[0][0].fields.map((f) => ({ [f.name]: f.values }))).toMatchInlineSnapshot(` [ { "time": [ @@ -346,9 +346,8 @@ describe('Labels as Columns', () => { }); await expect(transformDataFrame([cfg], [source])).toEmitValuesWith((received) => { - expect( - received[0].map((f) => ({ name: f.name, fields: f.fields.map((v) => ({ [v.name]: v.values.toArray() })) })) - ).toMatchInlineSnapshot(` + expect(received[0].map((f) => ({ name: f.name, fields: f.fields.map((v) => ({ [v.name]: v.values })) }))) + .toMatchInlineSnapshot(` [ { "fields": [ @@ -410,9 +409,8 @@ describe('Labels as Columns', () => { }); await expect(transformDataFrame([cfg], [source])).toEmitValuesWith((received) => { - expect( - received[0].map((f) => ({ name: f.name, fields: f.fields.map((v) => ({ [v.name]: v.values.toArray() })) })) - ).toMatchInlineSnapshot(` + expect(received[0].map((f) => ({ name: f.name, fields: f.fields.map((v) => ({ [v.name]: v.values })) }))) + .toMatchInlineSnapshot(` [ { "fields": [ @@ -457,7 +455,7 @@ describe('Labels as Columns', () => { function toSimpleObject(frame: DataFrame) { const obj: Record = {}; for (const field of frame.fields) { - obj[field.name] = field.values.toArray(); + obj[field.name] = field.values; } return obj; } diff --git a/packages/grafana-data/src/transformations/transformers/labelsToFields.ts b/packages/grafana-data/src/transformations/transformers/labelsToFields.ts index 1bdb30df52c..d785120c85b 100644 --- a/packages/grafana-data/src/transformations/transformers/labelsToFields.ts +++ b/packages/grafana-data/src/transformations/transformers/labelsToFields.ts @@ -2,7 +2,6 @@ import { map } from 'rxjs/operators'; import { getFieldDisplayName } from '../..'; import { DataFrame, Field, FieldType, SynchronousDataTransformerInfo } from '../../types'; -import { ArrayVector } from '../../vector'; import { DataTransformerID } from './ids'; @@ -83,7 +82,7 @@ export const labelsToFieldsTransformer: SynchronousDataTransformerInfo { { name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000]), + values: [3000, 4000, 5000], config: {}, }, { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'two']), + values: ['one', 'two', 'two'], config: {}, }, { name: 'values', type: FieldType.number, - values: new ArrayVector([1, 2, 2]), + values: [1, 2, 2], config: {}, }, ]; @@ -81,19 +80,19 @@ describe('Limit transformer', () => { { name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000, 7000, 8000]), + values: [3000, 4000, 5000, 6000, 7000, 8000], config: {}, }, { name: 'message', type: FieldType.string, - values: new ArrayVector(['one', 'two', 'two', 'three', 'three', 'three']), + values: ['one', 'two', 'two', 'three', 'three', 'three'], config: {}, }, { name: 'values', type: FieldType.number, - values: new ArrayVector([1, 2, 2, 3, 3, 3]), + values: [1, 2, 2, 3, 3, 3], config: {}, }, ]; diff --git a/packages/grafana-data/src/transformations/transformers/limit.ts b/packages/grafana-data/src/transformations/transformers/limit.ts index f0bfd04fa6c..5a2f5639639 100644 --- a/packages/grafana-data/src/transformations/transformers/limit.ts +++ b/packages/grafana-data/src/transformations/transformers/limit.ts @@ -1,7 +1,6 @@ import { map } from 'rxjs/operators'; import { DataTransformerInfo } from '../../types'; -import { ArrayVector } from '../../vector/ArrayVector'; import { DataTransformerID } from './ids'; @@ -28,10 +27,9 @@ export const limitTransformer: DataTransformerInfo = { return { ...frame, fields: frame.fields.map((f) => { - const vals = f.values.toArray(); return { ...f, - values: new ArrayVector(vals.slice(0, limitFieldMatch)), + values: f.values.slice(0, limitFieldMatch), }; }), length: limitFieldMatch, diff --git a/packages/grafana-data/src/transformations/transformers/merge.test.ts b/packages/grafana-data/src/transformations/transformers/merge.test.ts index 30c38aacd80..e837a98b086 100644 --- a/packages/grafana-data/src/transformations/transformers/merge.test.ts +++ b/packages/grafana-data/src/transformations/transformers/merge.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { DataTransformerConfig, DisplayProcessor, Field, FieldType } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { DataTransformerID } from './ids'; @@ -592,11 +591,9 @@ const createField = ( config = {}, display?: DisplayProcessor ): Field => { - return { name, type, values: new ArrayVector(values), config, labels: undefined, display }; + return { name, type, values: values, config, labels: undefined, display }; }; const unwrap = (fields: Field[]): Field[] => { - return fields.map((field) => - createField(field.name, field.type, field.values.toArray(), field.config, field.display) - ); + return fields.map((field) => createField(field.name, field.type, field.values, field.config, field.display)); }; diff --git a/packages/grafana-data/src/transformations/transformers/merge.ts b/packages/grafana-data/src/transformations/transformers/merge.ts index 55185c0756d..d1f23459922 100644 --- a/packages/grafana-data/src/transformations/transformers/merge.ts +++ b/packages/grafana-data/src/transformations/transformers/merge.ts @@ -4,7 +4,6 @@ import { map } from 'rxjs/operators'; import { MutableDataFrame } from '../../dataframe'; import { DataFrame, Field } from '../../types/dataFrame'; import { DataTransformerInfo } from '../../types/transformations'; -import { ArrayVector } from '../../vector/ArrayVector'; import { DataTransformerID } from './ids'; @@ -117,7 +116,7 @@ export const mergeTransformer: DataTransformerInfo = { const copyFieldStructure = (field: Field): Field => { return { ...omit(field, ['values', 'state', 'labels', 'config']), - values: new ArrayVector(), + values: [], config: { ...omit(field.config, 'displayName'), }, @@ -139,7 +138,7 @@ const createKeyFactory = ( return (frameIndex: number, valueIndex: number): string => { return factoryIndex[frameIndex].reduce((key: string, fieldIndex: number) => { - return key + data[frameIndex].fields[fieldIndex].values.get(valueIndex); + return key + data[frameIndex].fields[fieldIndex].values[valueIndex]; }, ''); }; }; @@ -174,7 +173,7 @@ const createValueMapper = ( continue; } - value[fieldName] = field.values.get(valueIndex); + value[fieldName] = field.values[valueIndex]; } return value; diff --git a/packages/grafana-data/src/transformations/transformers/order.test.ts b/packages/grafana-data/src/transformations/transformers/order.test.ts index f5b5624e55d..f123469c7da 100644 --- a/packages/grafana-data/src/transformations/transformers/order.test.ts +++ b/packages/grafana-data/src/transformations/transformers/order.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { FieldType, DataTransformerConfig } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { DataTransformerID } from './ids'; @@ -41,7 +40,7 @@ describe('Order Transformer', () => { config: {}, name: 'temperature', type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], labels: undefined, state: { displayName: 'temperature', @@ -52,7 +51,7 @@ describe('Order Transformer', () => { config: {}, name: 'humidity', type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], labels: undefined, state: { displayName: 'humidity', @@ -63,7 +62,7 @@ describe('Order Transformer', () => { config: {}, name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], labels: undefined, state: { displayName: 'time', @@ -95,7 +94,7 @@ describe('Order Transformer', () => { config: {}, name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], labels: undefined, state: { displayName: 'time', @@ -106,7 +105,7 @@ describe('Order Transformer', () => { config: {}, name: 'temperature', type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], labels: undefined, state: { displayName: 'temperature', @@ -117,7 +116,7 @@ describe('Order Transformer', () => { config: {}, name: 'humidity', type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], labels: undefined, state: { displayName: 'humidity', @@ -159,7 +158,7 @@ describe('Order Transformer', () => { config: {}, name: 'humidity', type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], labels: undefined, state: { displayName: 'humidity', @@ -170,7 +169,7 @@ describe('Order Transformer', () => { config: {}, name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], labels: undefined, state: { displayName: 'time', @@ -181,7 +180,7 @@ describe('Order Transformer', () => { config: {}, name: 'pressure', type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], labels: undefined, state: { displayName: 'pressure', @@ -219,19 +218,19 @@ describe('Order Transformer', () => { config: {}, name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], }, { config: {}, name: 'pressure', type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], }, { config: {}, name: 'humidity', type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], }, ]); }); diff --git a/packages/grafana-data/src/transformations/transformers/organize.test.ts b/packages/grafana-data/src/transformations/transformers/organize.test.ts index fd072d87983..712d9c17c15 100644 --- a/packages/grafana-data/src/transformations/transformers/organize.test.ts +++ b/packages/grafana-data/src/transformations/transformers/organize.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { FieldType, DataTransformerConfig } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { DataTransformerID } from './ids'; @@ -53,7 +52,7 @@ describe('OrganizeFields Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], }, { config: { @@ -66,7 +65,7 @@ describe('OrganizeFields Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], }, ]); }); @@ -116,7 +115,7 @@ describe('OrganizeFields Transformer', () => { multipleFrames: false, }, type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], }, { config: {}, @@ -127,7 +126,7 @@ describe('OrganizeFields Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], }, ]); }); diff --git a/packages/grafana-data/src/transformations/transformers/reduce.test.ts b/packages/grafana-data/src/transformations/transformers/reduce.test.ts index 6ad69449486..95c175f66a0 100644 --- a/packages/grafana-data/src/transformations/transformers/reduce.test.ts +++ b/packages/grafana-data/src/transformations/transformers/reduce.test.ts @@ -2,7 +2,6 @@ import { DataFrameView } from '../../dataframe'; import { toDataFrame } from '../../dataframe/processDataFrame'; import { DataTransformerConfig, Field, FieldType } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { ReducerID } from '../fieldReducer'; import { notTimeFieldMatcher } from '../matchers/predicates'; import { transformDataFrame } from '../transformDataFrame'; @@ -64,31 +63,31 @@ describe('Reducer Transformer', () => { { name: 'Field', type: FieldType.string, - values: new ArrayVector(['A temperature', 'A humidity', 'B temperature', 'B humidity']), + values: ['A temperature', 'A humidity', 'B temperature', 'B humidity'], config: {}, }, { name: 'First', type: FieldType.number, - values: new ArrayVector([3, 10000.3, 1, 11000.1]), + values: [3, 10000.3, 1, 11000.1], config: {}, }, { name: 'Min', type: FieldType.number, - values: new ArrayVector([3, 10000.3, 1, 11000.1]), + values: [3, 10000.3, 1, 11000.1], config: {}, }, { name: 'Max', type: FieldType.number, - values: new ArrayVector([6, 10000.6, 7, 11000.7]), + values: [6, 10000.6, 7, 11000.7], config: {}, }, { name: 'Last', type: FieldType.number, - values: new ArrayVector([6, 10000.6, 7, 11000.7]), + values: [6, 10000.6, 7, 11000.7], config: {}, }, ]; @@ -115,31 +114,31 @@ describe('Reducer Transformer', () => { { name: 'Field', type: FieldType.string, - values: new ArrayVector(['A temperature', 'B temperature']), + values: ['A temperature', 'B temperature'], config: {}, }, { name: 'First', type: FieldType.number, - values: new ArrayVector([3, 1]), + values: [3, 1], config: {}, }, { name: 'Min', type: FieldType.number, - values: new ArrayVector([3, 1]), + values: [3, 1], config: {}, }, { name: 'Max', type: FieldType.number, - values: new ArrayVector([6, 7]), + values: [6, 7], config: {}, }, { name: 'Last', type: FieldType.number, - values: new ArrayVector([6, 7]), + values: [6, 7], config: {}, }, ]; @@ -165,31 +164,31 @@ describe('Reducer Transformer', () => { { name: 'Field', type: FieldType.string, - values: new ArrayVector(['temperature', 'humidity']), + values: ['temperature', 'humidity'], config: {}, }, { name: 'First', type: FieldType.number, - values: new ArrayVector([3, 10000.3]), + values: [3, 10000.3], config: {}, }, { name: 'Min', type: FieldType.number, - values: new ArrayVector([3, 10000.3]), + values: [3, 10000.3], config: {}, }, { name: 'Max', type: FieldType.number, - values: new ArrayVector([6, 10000.6]), + values: [6, 10000.6], config: {}, }, { name: 'Last', type: FieldType.number, - values: new ArrayVector([6, 10000.6]), + values: [6, 10000.6], config: {}, }, ]; @@ -214,31 +213,31 @@ describe('Reducer Transformer', () => { { name: 'Field', type: FieldType.string, - values: new ArrayVector(['temperature']), + values: ['temperature'], config: {}, }, { name: 'First', type: FieldType.number, - values: new ArrayVector([3]), + values: [3], config: {}, }, { name: 'Min', type: FieldType.number, - values: new ArrayVector([3]), + values: [3], config: {}, }, { name: 'Max', type: FieldType.number, - values: new ArrayVector([6]), + values: [6], config: {}, }, { name: 'Last', type: FieldType.number, - values: new ArrayVector([6]), + values: [6], config: {}, }, ]; @@ -306,13 +305,13 @@ describe('Reducer Transformer', () => { { name: 'Field', type: FieldType.string, - values: new ArrayVector(['a', '2021']), + values: ['a', '2021'], config: {}, }, { name: 'Max', type: FieldType.number, - values: new ArrayVector([6, 10]), + values: [6, 10], config: {}, }, ]; @@ -351,13 +350,13 @@ describe('Reducer Transformer', () => { { name: 'Field', type: FieldType.string, - values: new ArrayVector(['a', '2021']), + values: ['a', '2021'], config: {}, }, { name: 'Max', type: FieldType.number, - values: new ArrayVector([6, 10]), + values: [6, 10], config: {}, }, ]; diff --git a/packages/grafana-data/src/transformations/transformers/reduce.ts b/packages/grafana-data/src/transformations/transformers/reduce.ts index 7a5beed909f..1997d233ab0 100644 --- a/packages/grafana-data/src/transformations/transformers/reduce.ts +++ b/packages/grafana-data/src/transformations/transformers/reduce.ts @@ -5,7 +5,6 @@ import { getFieldDisplayName } from '../../field'; import { KeyValue } from '../../types/data'; import { DataFrame, Field, FieldType } from '../../types/dataFrame'; import { DataTransformerInfo, FieldMatcher, MatcherConfig } from '../../types/transformations'; -import { ArrayVector } from '../../vector/ArrayVector'; import { fieldReducers, reduceField, ReducerID } from '../fieldReducer'; import { getFieldMatcher } from '../matchers'; import { alwaysFieldMatcher, notTimeFieldMatcher } from '../matchers/predicates'; @@ -80,7 +79,7 @@ export function reduceSeriesToRows( const size = source.length; const fields: Field[] = []; - const names = new ArrayVector(new Array(size)); + const names: string[] = new Array(size); fields.push({ name: 'Field', type: FieldType.string, @@ -88,10 +87,10 @@ export function reduceSeriesToRows( config: {}, }); - const labels: KeyValue = {}; + const labels: KeyValue = {}; if (labelsToFields) { for (const key of distinctLabels) { - labels[key] = new ArrayVector(new Array(size)); + labels[key] = new Array(size); fields.push({ name: key, type: FieldType.string, @@ -101,9 +100,9 @@ export function reduceSeriesToRows( } } - const calcs: KeyValue = {}; + const calcs: KeyValue = {}; for (const info of calculators) { - calcs[info.id] = new ArrayVector(new Array(size)); + calcs[info.id] = new Array(size); fields.push({ name: info.name, type: FieldType.other, // UNKNOWN until after we call the functions @@ -120,19 +119,19 @@ export function reduceSeriesToRows( }); if (labelsToFields) { - names.buffer[i] = field.name; + names[i] = field.name; if (field.labels) { for (const key of Object.keys(field.labels)) { - labels[key].set(i, field.labels[key]); + labels[key][i] = field.labels[key]; } } } else { - names.buffer[i] = getFieldDisplayName(field, series, data); + names[i] = getFieldDisplayName(field, series, data); } for (const info of calculators) { const v = results[info.id]; - calcs[info.id].buffer[i] = v; + calcs[info.id][i] = v; } } @@ -191,9 +190,9 @@ export function mergeResults(data: DataFrame[]): DataFrame | undefined { const isSameField = baseField.type === field.type && baseField.name === field.name; if (isFirstField || isSameField) { - const baseValues: any[] = baseField.values.toArray(); - const values: any[] = field.values.toArray(); - (baseField.values as unknown as ArrayVector).buffer = baseValues.concat(values); + const baseValues = baseField.values; + const values = field.values; + baseField.values = baseValues.concat(values); } } } @@ -224,7 +223,7 @@ export function reduceFields(data: DataFrame[], matcher: FieldMatcher, reducerId const value = results[reducer]; const copy = { ...field, - values: new ArrayVector([value]), + values: [value], }; copy.state = undefined; if (reducers.length > 1) { diff --git a/packages/grafana-data/src/transformations/transformers/rename.test.ts b/packages/grafana-data/src/transformations/transformers/rename.test.ts index d4bfa319a5f..ca9f379dcb9 100644 --- a/packages/grafana-data/src/transformations/transformers/rename.test.ts +++ b/packages/grafana-data/src/transformations/transformers/rename.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { FieldType, DataTransformerConfig } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { DataTransformerID } from './ids'; @@ -49,7 +48,7 @@ describe('Rename Transformer', () => { multipleFrames: false, }, type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], }, { config: { @@ -62,7 +61,7 @@ describe('Rename Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], }, { config: { @@ -75,7 +74,7 @@ describe('Rename Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], }, ]); }); @@ -119,7 +118,7 @@ describe('Rename Transformer', () => { multipleFrames: false, }, type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], }, { config: {}, @@ -130,7 +129,7 @@ describe('Rename Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], }, { config: { @@ -143,7 +142,7 @@ describe('Rename Transformer', () => { multipleFrames: false, }, type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], }, ]); }); @@ -176,19 +175,19 @@ describe('Rename Transformer', () => { config: {}, name: 'time', type: FieldType.time, - values: new ArrayVector([3000, 4000, 5000, 6000]), + values: [3000, 4000, 5000, 6000], }, { config: {}, name: 'pressure', type: FieldType.number, - values: new ArrayVector([10.3, 10.4, 10.5, 10.6]), + values: [10.3, 10.4, 10.5, 10.6], }, { config: {}, name: 'humidity', type: FieldType.number, - values: new ArrayVector([10000.3, 10000.4, 10000.5, 10000.6]), + values: [10000.3, 10000.4, 10000.5, 10000.6], }, ]); }); diff --git a/packages/grafana-data/src/transformations/transformers/seriesToRows.test.ts b/packages/grafana-data/src/transformations/transformers/seriesToRows.test.ts index 8a8b781ba88..49829c3e377 100644 --- a/packages/grafana-data/src/transformations/transformers/seriesToRows.test.ts +++ b/packages/grafana-data/src/transformations/transformers/seriesToRows.test.ts @@ -1,7 +1,6 @@ import { toDataFrame } from '../../dataframe'; import { DataTransformerConfig, Field, FieldType } from '../../types'; import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry'; -import { ArrayVector } from '../../vector'; import { transformDataFrame } from '../transformDataFrame'; import { DataTransformerID } from './ids'; @@ -246,9 +245,9 @@ describe('Series to rows', () => { }); const createField = (name: string, type: FieldType, values: unknown[], config = {}): Field => { - return { name, type, values: new ArrayVector(values), config, labels: undefined }; + return { name, type, values: values, config, labels: undefined }; }; const unwrap = (fields: Field[]): Field[] => { - return fields.map((field) => createField(field.name, field.type, field.values.toArray(), field.config)); + return fields.map((field) => createField(field.name, field.type, field.values, field.config)); }; diff --git a/packages/grafana-data/src/transformations/transformers/seriesToRows.ts b/packages/grafana-data/src/transformations/transformers/seriesToRows.ts index 3e32de48faa..69bb9ecef63 100644 --- a/packages/grafana-data/src/transformations/transformers/seriesToRows.ts +++ b/packages/grafana-data/src/transformations/transformers/seriesToRows.ts @@ -12,7 +12,6 @@ import { TIME_SERIES_VALUE_FIELD_NAME, } from '../../types/dataFrame'; import { DataTransformerInfo } from '../../types/transformations'; -import { ArrayVector } from '../../vector'; import { DataTransformerID } from './ids'; @@ -39,7 +38,7 @@ export const seriesToRowsTransformer: DataTransformerInfo { return { ...omit(field, ['values', 'state', 'labels', 'config', 'name']), name: name, - values: new ArrayVector(), + values: [], config: { ...omit(field.config, ['displayName', 'displayNameFromDS']), }, diff --git a/packages/grafana-data/src/transformations/transformers/sortBy.test.ts b/packages/grafana-data/src/transformations/transformers/sortBy.test.ts index 557a79e7308..039ac0167eb 100644 --- a/packages/grafana-data/src/transformations/transformers/sortBy.test.ts +++ b/packages/grafana-data/src/transformations/transformers/sortBy.test.ts @@ -95,5 +95,5 @@ describe('SortBy transformer', () => { }); function getFieldSnapshot(f: Field): Object { - return { name: f.name, values: f.values.toArray() }; + return { name: f.name, values: f.values }; } diff --git a/packages/grafana-data/src/types/annotations.ts b/packages/grafana-data/src/types/annotations.ts index da077daa76e..a35e0bea005 100644 --- a/packages/grafana-data/src/types/annotations.ts +++ b/packages/grafana-data/src/types/annotations.ts @@ -1,30 +1,23 @@ import { ComponentType } from 'react'; import { Observable } from 'rxjs'; +import { DataQuery, AnnotationQuery as SchemaAnnotationQuery } from '@grafana/schema'; + import { DataFrame } from './dataFrame'; import { QueryEditorProps } from './datasource'; -import { DataQuery, DataSourceRef } from './query'; /** * This JSON object is stored in the dashboard json model. */ -export interface AnnotationQuery { - datasource?: DataSourceRef | null; - - enable: boolean; - name: string; - iconColor: string; - hide?: boolean; - builtIn?: number; - type?: string; +export interface AnnotationQuery extends SchemaAnnotationQuery { snapshotData?: any; - // Standard datasource query - target?: TQuery; - // Convert a dataframe to an AnnotationEvent mappings?: AnnotationEventMappings; + // When using the 'grafana' datasource, this may be dashboard + type?: string; + // Sadly plugins can set any property directly on the main object [key: string]: any; } @@ -51,7 +44,7 @@ export interface AnnotationEvent { newState?: string; // Currently used to merge annotations from alerts and dashboard - source?: any; // source.type === 'dashboard' + source?: any; // source.type === 'dashboard' -- should be AnnotationQuery } export interface AnnotationEventUIModel { diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index 07c8186f92d..7331280cc1a 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -46,18 +46,6 @@ export interface LicenseInfo { trialExpiry?: number; } -/** - * Describes Sentry integration config - * - * @public - */ -export interface SentryConfig { - enabled: boolean; - dsn: string; - customEndpoint: string; - sampleRate: number; -} - /** * Describes GrafanaJavascriptAgentConfig integration config * @@ -74,6 +62,8 @@ export interface GrafanaJavascriptAgentConfig { export interface UnifiedAlertingConfig { minInterval: string; + // will be undefined if alerStateHistory is not enabled + alertStateHistoryBackend?: string; } /** Supported OAuth services @@ -196,6 +186,8 @@ export interface GrafanaConfig { viewersCanEdit: boolean; editorsCanAdmin: boolean; disableSanitizeHtml: boolean; + trustedTypesDefaultPolicyEnabled: boolean; + cspReportOnlyEnabled: boolean; liveEnabled: boolean; /** @deprecated Use `theme2` instead. */ theme: GrafanaTheme; @@ -205,7 +197,6 @@ export interface GrafanaConfig { licenseInfo: LicenseInfo; http2Enabled: boolean; dateFormats?: SystemDateFormatSettings; - sentry: SentryConfig; grafanaJavascriptAgent: GrafanaJavascriptAgentConfig; customTheme?: any; geomapDefaultBaseLayer?: MapLayerOptions; @@ -216,6 +207,7 @@ export interface GrafanaConfig { feedbackLinksEnabled: boolean; secretsManagerPluginEnabled: boolean; supportBundlesEnabled: boolean; + secureSocksDSProxyEnabled: boolean; googleAnalyticsId: string | undefined; googleAnalytics4Id: string | undefined; googleAnalytics4SendManualPageViews: boolean; @@ -223,6 +215,13 @@ export interface GrafanaConfig { rudderstackDataPlaneUrl: string | undefined; rudderstackSdkUrl: string | undefined; rudderstackConfigUrl: string | undefined; + sqlConnectionLimits: SqlConnectionLimits; +} + +export interface SqlConnectionLimits { + maxOpenConns: number; + maxIdleConns: number; + connMaxLifetime: number; } export interface AuthSettings { diff --git a/packages/grafana-data/src/types/dashboard.ts b/packages/grafana-data/src/types/dashboard.ts index 1608efd8eb8..36787674903 100644 --- a/packages/grafana-data/src/types/dashboard.ts +++ b/packages/grafana-data/src/types/dashboard.ts @@ -1,3 +1,5 @@ +import { DataTransformerConfig } from '@grafana/schema'; + import { FieldConfigSource } from './fieldOverrides'; import { DataQuery, DataSourceRef } from './query'; @@ -38,6 +40,9 @@ export interface PanelModel { /** The queries in a panel */ targets?: DataQuery[]; + /** Optionally process data after query */ + transformations?: DataTransformerConfig[]; + /** alerting v1 object */ alert?: any; } diff --git a/packages/grafana-data/src/types/data.ts b/packages/grafana-data/src/types/data.ts index 4a24a248e45..067d4af8f92 100644 --- a/packages/grafana-data/src/types/data.ts +++ b/packages/grafana-data/src/types/data.ts @@ -146,6 +146,8 @@ export interface QueryResultBase { export interface Labels { [key: string]: string; } + +/** @deprecated this is a very old (pre Grafana 7 + DataFrame) representation for tabular data */ export interface Column { text: string; // For a Column, the 'text' is the field name filterable?: boolean; @@ -153,6 +155,7 @@ export interface Column { custom?: Record; } +/** @deprecated this is a very old (pre Grafana 7 + DataFrame) representation for tabular data */ export interface TableData extends QueryResultBase { name?: string; columns: Column[]; @@ -160,10 +163,13 @@ export interface TableData extends QueryResultBase { type?: string; } +/** @deprecated this is a very old (pre Grafana 7 + DataFrame) representation for tabular data */ export type TimeSeriesValue = number | null; +/** @deprecated this is a very old (pre Grafana 7 + DataFrame) representation for tabular data */ export type TimeSeriesPoints = TimeSeriesValue[][]; +/** @deprecated this is a very old (pre Grafana 7 + DataFrame) representation for tabular data */ export interface TimeSeries extends QueryResultBase { target: string; /** diff --git a/packages/grafana-data/src/types/dataFrame.ts b/packages/grafana-data/src/types/dataFrame.ts index d7200416ce4..b46e912a5f0 100644 --- a/packages/grafana-data/src/types/dataFrame.ts +++ b/packages/grafana-data/src/types/dataFrame.ts @@ -29,12 +29,14 @@ export enum FieldType { */ export interface FieldConfig { /** - * The display value for this field. This supports template variables blank is auto + * The display value for this field. This supports template variables blank is auto. + * If you are a datasource plugin, do not set this. Use `field.value` and if that + * is not enough, use `field.config.displayNameFromDS`. */ displayName?: string; /** - * This can be used by data sources that return and explicit naming structure for values and labels + * This can be used by data sources that need to customize how values are named. * When this property is configured, this value is used rather than the default naming strategy. */ displayNameFromDS?: string; @@ -136,7 +138,13 @@ export interface Field> { * Meta info about how field and how to display it */ config: FieldConfig; - values: V; // The raw field values + + /** + * The raw field values + * In Grafana 10, this accepts both simple arrays and the Vector interface + * In Grafana 11, the Vector interface will be removed + */ + values: V | T[]; /** * When type === FieldType.Time, this can optionally store diff --git a/packages/grafana-data/src/types/dataLink.ts b/packages/grafana-data/src/types/dataLink.ts index 0e11beec147..c45a3ed7e7b 100644 --- a/packages/grafana-data/src/types/dataLink.ts +++ b/packages/grafana-data/src/types/dataLink.ts @@ -52,15 +52,20 @@ export interface DataLink { origin?: DataLinkConfigOrigin; } -/** @internal */ -export enum SupportedTransformationTypes { +/** + * We provide tooltips with information about these to guide the user, please + * check for validity when adding more transformation types. + * + * @internal + */ +export enum SupportedTransformationType { Regex = 'regex', Logfmt = 'logfmt', } /** @internal */ export interface DataLinkTransformationConfig { - type: SupportedTransformationTypes; + type: SupportedTransformationType; field?: string; expression?: string; mapValue?: string; diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index 02a196f0240..9a33ca35d5a 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -57,6 +57,7 @@ export class DataSourcePlugin< return this; } + /** @deprecated -- register the annotation support in the instance constructor */ setAnnotationQueryCtrl(AnnotationsQueryCtrl: any) { this.components.AnnotationsQueryCtrl = AnnotationsQueryCtrl; return this; @@ -259,7 +260,7 @@ abstract class DataSourceApi< * a TestingStatus object. Unknown errors and HTTP errors can be re-thrown and will be handled here: * public/app/features/datasources/state/actions.ts */ - abstract testDatasource(): Promise; + abstract testDatasource(): Promise; /** * Override to skip executing a query @@ -472,6 +473,13 @@ export interface DataQueryResponse { traceIds?: string[]; } +export interface TestDataSourceResponse { + status: string; + message: string; + error?: Error; + details?: { message?: string; verboseMessage?: string }; +} + export enum DataQueryErrorType { Cancelled = 'cancelled', Timeout = 'timeout', @@ -514,8 +522,6 @@ export interface DataQueryRequest { rangeRaw?: RawTimeRange; timeInfo?: string; // The query time description (blue text in the upper right) panelId?: number; - /** @deprecate */ - dashboardId?: number; dashboardUID?: string; publicDashboardAccessToken?: string; diff --git a/packages/grafana-data/src/types/explore.ts b/packages/grafana-data/src/types/explore.ts index 57568f95f2f..9ca6e18fab7 100644 --- a/packages/grafana-data/src/types/explore.ts +++ b/packages/grafana-data/src/types/explore.ts @@ -22,7 +22,7 @@ export interface ExploreTracePanelState { spanId?: string; } -export interface SplitOpenOptions { +export interface SplitOpenOptions { datasourceUid: string; /** @deprecated Will be removed in a future version. Use queries instead. */ query?: T; @@ -34,4 +34,4 @@ export interface SplitOpenOptions { /** * SplitOpen type is used in Explore and related components. */ -export type SplitOpen = (options?: SplitOpenOptions | undefined) => void; +export type SplitOpen = (options?: SplitOpenOptions | undefined) => void; diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index d2414f1c493..a3e1fd422c7 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -20,8 +20,6 @@ export interface FeatureToggles { trimDefaults?: boolean; disableEnvelopeEncryption?: boolean; - database_metrics?: boolean; - dashboardPreviews?: boolean; ['live-service-web-worker']?: boolean; queryOverLive?: boolean; panelTitleSearch?: boolean; @@ -29,15 +27,12 @@ export interface FeatureToggles { publicDashboards?: boolean; publicDashboardsEmailSharing?: boolean; lokiLive?: boolean; - lokiDataframeApi?: boolean; featureHighlights?: boolean; migrationLocking?: boolean; storage?: boolean; - k8s?: boolean; exploreMixedDatasource?: boolean; - newTraceView?: boolean; + newTraceViewHeader?: boolean; correlations?: boolean; - cloudWatchDynamicLabels?: boolean; datasourceQueryMultiStatus?: boolean; traceToMetrics?: boolean; newDBLibrary?: boolean; @@ -63,11 +58,9 @@ export interface FeatureToggles { accessControlOnCall?: boolean; nestedFolders?: boolean; accessTokenExpirationCheck?: boolean; - elasticsearchBackendMigration?: boolean; showTraceId?: boolean; datasourceOnboarding?: boolean; emptyDashboardPage?: boolean; - secureSocksDatasourceProxy?: boolean; authnService?: boolean; disablePrometheusExemplarSampling?: boolean; alertingBacktesting?: boolean; @@ -79,19 +72,29 @@ export interface FeatureToggles { lokiQuerySplittingConfig?: boolean; individualCookiePreferences?: boolean; onlyExternalOrgRoleSync?: boolean; - drawerDataSourcePicker?: boolean; traceqlSearch?: boolean; prometheusMetricEncyclopedia?: boolean; timeSeriesTable?: boolean; prometheusResourceBrowserCache?: boolean; influxdbBackendMigration?: boolean; clientTokenRotation?: boolean; - disableElasticsearchBackendExploreQuery?: boolean; prometheusDataplane?: boolean; + lokiMetricDataplane?: boolean; + dataplaneFrontendFallback?: boolean; + disableSSEDataplane?: boolean; alertStateHistoryLokiSecondary?: boolean; alertStateHistoryLokiPrimary?: boolean; alertStateHistoryLokiOnly?: boolean; unifiedRequestLog?: boolean; renderAuthJWT?: boolean; pyroscopeFlameGraph?: boolean; + externalServiceAuth?: boolean; + useCachingService?: boolean; + enableElasticsearchBackendQuerying?: boolean; + authenticationConfigUI?: boolean; + pluginsAPIManifestKey?: boolean; + advancedDataSourcePicker?: boolean; + opensearchDetectVersion?: boolean; + faroDatasourceSelector?: boolean; + enableDatagridEditing?: boolean; } diff --git a/packages/grafana-data/src/types/fieldColor.ts b/packages/grafana-data/src/types/fieldColor.ts index 465c268e1a5..8dcbb526aa3 100644 --- a/packages/grafana-data/src/types/fieldColor.ts +++ b/packages/grafana-data/src/types/fieldColor.ts @@ -4,6 +4,7 @@ export enum FieldColorModeId { Thresholds = 'thresholds', PaletteClassic = 'palette-classic', + PaletteClassicByName = 'palette-classic-by-name', PaletteSaturated = 'palette-saturated', ContinuousGrYlRd = 'continuous-GrYlRd', ContinuousRdYlGr = 'continuous-RdYlGr', @@ -16,6 +17,7 @@ export enum FieldColorModeId { ContinuousGreens = 'continuous-greens', ContinuousPurples = 'continuous-purples', Fixed = 'fixed', + Shades = 'shades', } /** diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts index 6bf12f650b8..67657309dd6 100644 --- a/packages/grafana-data/src/types/index.ts +++ b/packages/grafana-data/src/types/index.ts @@ -7,7 +7,6 @@ export * from './dashboard'; export * from './query'; export * from './annotations'; export * from './logs'; -export * from './logsVolume'; export * from './navModel'; export * from './select'; export * from './time'; diff --git a/packages/grafana-data/src/types/logs.ts b/packages/grafana-data/src/types/logs.ts index fda6ff0720a..a1a9966052c 100644 --- a/packages/grafana-data/src/types/logs.ts +++ b/packages/grafana-data/src/types/logs.ts @@ -1,9 +1,10 @@ import { Observable } from 'rxjs'; +import { DataQuery } from '@grafana/schema'; + import { Labels } from './data'; import { DataFrame } from './dataFrame'; import { DataQueryRequest, DataQueryResponse } from './datasource'; -import { DataQuery } from './query'; import { AbsoluteTimeRange } from './time'; export { LogsDedupStrategy, LogsSortOrder } from '@grafana/schema'; @@ -103,35 +104,6 @@ export interface LogLabelStatsModel { value: string; } -/** @deprecated will be removed in the next major version */ -export interface LogsParser { - /** - * Value-agnostic matcher for a field label. - * Used to filter rows, and first capture group contains the value. - */ - buildMatcher: (label: string) => RegExp; - - /** - * Returns all parsable substrings from a line, used for highlighting - */ - getFields: (line: string) => string[]; - - /** - * Gets the label name from a parsable substring of a line - */ - getLabelFromField: (field: string) => string; - - /** - * Gets the label value from a parsable substring of a line - */ - getValueFromField: (field: string) => string; - /** - * Function to verify if this is a valid parser for the given line. - * The parser accepts the line if it returns true. - */ - test: (line: string) => boolean; -} - export enum LogsDedupDescription { none = 'No de-duplication', exact = 'De-duplication of successive lines that are identical, ignoring ISO datetimes.', @@ -139,6 +111,16 @@ export enum LogsDedupDescription { signature = 'De-duplication of successive lines that have identical punctuation and whitespace.', } +export interface LogRowContextOptions { + direction?: LogRowContextQueryDirection; + limit?: number; +} + +export enum LogRowContextQueryDirection { + Backward = 'BACKWARD', + Forward = 'FORWARD', +} + /** * Data sources that allow showing context rows around the provided LowRowModel should implement this method. * This will enable "context" button in Logs Panel. @@ -147,11 +129,12 @@ export interface DataSourceWithLogsContextSupport( - row: LogRowModel, - options?: TContextQueryOptions, - query?: TQuery - ) => Promise; + getLogRowContext: (row: LogRowModel, options?: LogRowContextOptions, query?: TQuery) => Promise; + + /** + * Retrieve the context query object for a given log row. This is currently used to open LogContext queries in a split view. + */ + getLogRowContextQuery?: (row: LogRowModel, options?: LogRowContextOptions, query?: TQuery) => Promise; /** * This method can be used to show "context" button based on runtime conditions (for example row model data or plugin settings, etc.) @@ -163,7 +146,7 @@ export interface DataSourceWithLogsContextSupport void): React.ReactNode; + getLogRowContextUi?(row: LogRowModel, runContextQuery?: () => void, origQuery?: TQuery): React.ReactNode; } export const hasLogsContextSupport = (datasource: unknown): datasource is DataSourceWithLogsContextSupport => { @@ -185,6 +168,26 @@ export enum SupplementaryQueryType { LogsSample = 'LogsSample', } +/** + * @internal + */ +export type SupplementaryQueryOptions = LogsVolumeOption | LogsSampleOptions; + +/** + * @internal + */ +export type LogsVolumeOption = { + type: SupplementaryQueryType.LogsVolume; +}; + +/** + * @internal + */ +export type LogsSampleOptions = { + type: SupplementaryQueryType.LogsSample; + limit?: number; +}; + /** * Types of logs volume responses. A data source may return full range histogram (based on selected range) * or limited (based on returned results). This information is attached to DataFrame.meta.custom object. @@ -205,29 +208,6 @@ export type LogsVolumeCustomMetaData = { sourceQuery: DataQuery; }; -export const getLogsVolumeAbsoluteRange = ( - dataFrames: DataFrame[], - defaultRange: AbsoluteTimeRange -): AbsoluteTimeRange => { - return dataFrames[0].meta?.custom?.absoluteRange || defaultRange; -}; - -export const getLogsVolumeDataSourceInfo = (dataFrames: DataFrame[]): { name: string } | null => { - const customMeta = dataFrames[0]?.meta?.custom; - - if (customMeta && customMeta.datasourceName) { - return { - name: customMeta.datasourceName, - }; - } - - return null; -}; - -export const isLogsVolumeLimited = (dataFrames: DataFrame[]) => { - return dataFrames[0]?.meta?.custom?.logsVolumeType === LogsVolumeType.Limited; -}; - /** * Data sources that support supplementary queries in Explore. * This will enable users to see additional data when running original queries. @@ -251,7 +231,7 @@ export interface DataSourceWithSupplementaryQueriesSupport( diff --git a/packages/grafana-data/src/types/logsVolume.ts b/packages/grafana-data/src/types/logsVolume.ts deleted file mode 100644 index 273a53e91ce..00000000000 --- a/packages/grafana-data/src/types/logsVolume.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Observable } from 'rxjs'; - -import { DataQueryRequest, DataQueryResponse } from './datasource'; -import { DataQuery } from './query'; - -/** - * Support for DataSourceWithLogsVolumeSupport is deprecated and will be removed in the next major version. - * Use DataSourceWithSupplementaryQueriesSupport instead. - * - * @deprecated - */ -export interface DataSourceWithLogsVolumeSupport { - getLogsVolumeDataProvider(request: DataQueryRequest): Observable | undefined; -} - -/** - * Support for hasLogsVolumeSupport is deprecated and will be removed in the next major version. - * Use DataSourceWithSupplementaryQueriesSupport and hasSupplementaryQuerySupport instead. - * - * @deprecated - */ -export const hasLogsVolumeSupport = ( - datasource: unknown -): datasource is DataSourceWithLogsVolumeSupport => { - return (datasource as DataSourceWithLogsVolumeSupport).getLogsVolumeDataProvider !== undefined; -}; diff --git a/packages/grafana-data/src/types/navModel.ts b/packages/grafana-data/src/types/navModel.ts index 405924b4757..c20ea94656c 100644 --- a/packages/grafana-data/src/types/navModel.ts +++ b/packages/grafana-data/src/types/navModel.ts @@ -6,18 +6,13 @@ import { IconName } from './icon'; export interface NavLinkDTO { id?: string; text: string; - description?: string; - section?: NavSection; subTitle?: string; icon?: IconName; img?: string; url?: string; target?: LinkTarget; sortWeight?: number; - divider?: boolean; - hideFromMenu?: boolean; hideFromTabs?: boolean; - showIconInNavbar?: boolean; roundIcon?: boolean; /** * This is true for some sections that have no children (but is still a section) @@ -25,6 +20,7 @@ export interface NavLinkDTO { isSection?: boolean; children?: NavLinkDTO[]; highlightText?: string; + highlightId?: string; emptyMessageId?: string; // The ID of the plugin that registered the page (in case it was registered by a plugin, otherwise left empty) pluginId?: string; @@ -35,29 +31,13 @@ export interface NavLinkDTO { export interface NavModelItem extends NavLinkDTO { children?: NavModelItem[]; active?: boolean; - breadcrumbs?: NavModelBreadcrumb[]; parentItem?: NavModelItem; - showOrgSwitcher?: boolean; onClick?: () => void; - menuItemType?: NavMenuItemType; - highlightText?: string; - highlightId?: string; tabSuffix?: ComponentType<{ className?: string }>; hideFromBreadcrumbs?: boolean; emptyMessage?: string; } -export enum NavSection { - Core = 'core', - Plugin = 'plugin', - Config = 'config', -} - -export enum NavMenuItemType { - Section = 'section', - Item = 'item', -} - /** * Interface used to describe different kinds of page titles and page navigation. Navmodels are usually generated in the backend and stored in Redux. */ @@ -72,11 +52,6 @@ export interface NavModel { node: NavModelItem; } -export interface NavModelBreadcrumb { - title: string; - url?: string; -} - export type NavIndex = { [s: string]: NavModelItem }; export enum PageLayoutType { diff --git a/packages/grafana-data/src/types/queryRunner.ts b/packages/grafana-data/src/types/queryRunner.ts index cd0c0b3e12c..9634e5f5941 100644 --- a/packages/grafana-data/src/types/queryRunner.ts +++ b/packages/grafana-data/src/types/queryRunner.ts @@ -15,7 +15,7 @@ export interface QueryRunnerOptions { datasource: DataSourceRef | DataSourceApi | null; queries: DataQuery[]; panelId?: number; - dashboardId?: number; + dashboardUID?: string; timezone: TimeZone; timeRange: TimeRange; timeInfo?: string; // String description of time range for display diff --git a/packages/grafana-data/src/types/vector.ts b/packages/grafana-data/src/types/vector.ts index 76f2038e71c..b13e730d86a 100644 --- a/packages/grafana-data/src/types/vector.ts +++ b/packages/grafana-data/src/types/vector.ts @@ -1,35 +1,130 @@ -export interface Vector { +declare global { + interface Array { + /** @deprecated Use [idx]. This only exists to help migrate Vector to Array */ + get(idx: number): T; + /** @deprecated Use [idx]. This only exists to help migrate Vector to Array */ + set(idx: number, value: T): void; + /** @deprecated Use .push(value). This only exists to help migrate Vector to Array */ + add(value: T): void; + /** @deprecated this is not necessary. This only exists to help migrate Vector to Array */ + toArray(): T[]; + } +} + +// JS original sin +// this if condition is because Jest will re-exec this block multiple times (in a browser this only runs once) +if (!Object.getOwnPropertyDescriptor(Array.prototype, 'toArray')) { + Object.defineProperties(Array.prototype, { + get: { + value: function (idx: number): any { + return (this as any)[idx]; + }, + writable: false, + enumerable: false, + configurable: false, + }, + set: { + value: function (idx: number, value: any) { + (this as any)[idx] = value; + }, + writable: false, + enumerable: false, + configurable: false, + }, + add: { + value: function (value: any) { + (this as any).push(value); + }, + writable: false, + enumerable: false, + configurable: false, + }, + toArray: { + value: function () { + return this; + }, + writable: false, + enumerable: false, + configurable: false, + }, + }); +} + +/** @deprecated use a simple Array */ +export interface Vector extends Array { length: number; /** * Access the value by index (Like an array) + * + * @deprecated use a simple Array */ get(index: number): T; + /** + * Set a value + * + * @deprecated use a simple Array + */ + set: (index: number, value: T) => void; + + /** + * Adds the value to the vector + * Same as Array.push() + * + * @deprecated use a simple Array + */ + add: (value: T) => void; + /** * Get the results as an array. + * + * @deprecated use a simple Array */ toArray(): T[]; } /** * Apache arrow vectors are Read/Write + * + * @deprecated -- this is now part of the base Vector interface */ -export interface ReadWriteVector extends Vector { - set: (index: number, value: T) => void; -} +export interface ReadWriteVector extends Vector {} /** * Vector with standard manipulation functions + * + * @deprecated -- this is now part of the base Vector interface */ -export interface MutableVector extends ReadWriteVector { - /** - * Adds the value to the vector - */ - add: (value: T) => void; +export interface MutableVector extends ReadWriteVector {} - /** - * modifies the vector so it is now the opposite order - */ - reverse: () => void; +/** + * This is an extremely inefficient Vector wrapper that allows vectors to + * be treated as arrays. We should avoid using this wrapper, but it is helpful + * for a clean migration to arrays + * + * @deprecated + */ +export function makeArrayIndexableVector(v: T): T { + return new Proxy(v, { + get(target: Vector, property: string, receiver: Vector) { + if (typeof property !== 'symbol') { + const idx = +property; + if (String(idx) === property) { + return target.get(idx); + } + } + return Reflect.get(target, property, receiver); + }, + set(target: Vector, property: string, value: any, receiver: Vector) { + if (typeof property !== 'symbol') { + const idx = +property; + if (String(idx) === property) { + target.set(idx, value); + return true; + } + } + return Reflect.set(target, property, value, receiver); + }, + }) as T; } diff --git a/packages/grafana-data/src/utils/csv.ts b/packages/grafana-data/src/utils/csv.ts index 28a02cae94b..b1121927a2d 100644 --- a/packages/grafana-data/src/utils/csv.ts +++ b/packages/grafana-data/src/utils/csv.ts @@ -308,7 +308,7 @@ export function toCSV(data: DataFrame[], config?: CSVConfig): string { csv = csv + config.delimiter; } - const v = fields[j].values.get(i); + const v = fields[j].values[i]; if (v !== null) { csv = csv + writers[j](v); } diff --git a/packages/grafana-data/src/utils/dataLinks.test.ts b/packages/grafana-data/src/utils/dataLinks.test.ts index 1729fb0fa53..b2b0baca976 100644 --- a/packages/grafana-data/src/utils/dataLinks.test.ts +++ b/packages/grafana-data/src/utils/dataLinks.test.ts @@ -1,5 +1,4 @@ import { DataLink, FieldType, TimeRange } from '../types'; -import { ArrayVector } from '../vector'; import { mapInternalLinkToExplore } from './dataLinks'; @@ -24,7 +23,7 @@ describe('mapInternalLinkToExplore', () => { name: 'test', type: FieldType.number, config: {}, - values: new ArrayVector([2]), + values: [2], }, replaceVariables: (val) => val, }); @@ -65,7 +64,7 @@ describe('mapInternalLinkToExplore', () => { name: 'test', type: FieldType.number, config: {}, - values: new ArrayVector([2]), + values: [2], }, replaceVariables: (val) => val, }); @@ -112,7 +111,7 @@ describe('mapInternalLinkToExplore', () => { name: 'test', type: FieldType.number, config: {}, - values: new ArrayVector([2]), + values: [2], }, replaceVariables: (val, scopedVars) => val.replace(/\$var/g, scopedVars!['var1']!.value), }); diff --git a/packages/grafana-data/src/utils/flotPairs.test.ts b/packages/grafana-data/src/utils/flotPairs.test.ts index 1ab5d314b2c..a17de17b971 100644 --- a/packages/grafana-data/src/utils/flotPairs.test.ts +++ b/packages/grafana-data/src/utils/flotPairs.test.ts @@ -1,11 +1,11 @@ -import { MutableDataFrame } from '../dataframe/MutableDataFrame'; +import { createDataFrame } from '../dataframe'; import { dateTime } from '../datetime/moment_wrapper'; import { TimeRange } from '../types/time'; import { getFlotPairs, getFlotPairsConstant } from './flotPairs'; describe('getFlotPairs', () => { - const series = new MutableDataFrame({ + const series = createDataFrame({ fields: [ { name: 'a', values: [1, 2, 3] }, { name: 'b', values: [100, 200, 300] }, diff --git a/packages/grafana-data/src/utils/flotPairs.ts b/packages/grafana-data/src/utils/flotPairs.ts index 4780b5c5db0..06cea3086a5 100644 --- a/packages/grafana-data/src/utils/flotPairs.ts +++ b/packages/grafana-data/src/utils/flotPairs.ts @@ -23,8 +23,8 @@ export function getFlotPairs({ xField, yField, nullValueMode }: FlotPairsOptions const pairs: any[][] = []; for (let i = 0; i < length; i++) { - const x = vX.get(i); - let y = vY.get(i); + const x = vX[i]; + let y = vY[i]; if (y === null) { if (ignoreNulls) { diff --git a/packages/grafana-data/src/utils/index.ts b/packages/grafana-data/src/utils/index.ts index 5df29eb910b..53b6c632d5f 100644 --- a/packages/grafana-data/src/utils/index.ts +++ b/packages/grafana-data/src/utils/index.ts @@ -4,7 +4,6 @@ export * from './Registry'; export * from './datasource'; export * from './deprecationWarning'; export * from './csv'; -export * from './logs'; export * from './labels'; export * from './numbers'; export * from './object'; diff --git a/packages/grafana-data/src/utils/logs.test.ts b/packages/grafana-data/src/utils/logs.test.ts deleted file mode 100644 index f4c9966e981..00000000000 --- a/packages/grafana-data/src/utils/logs.test.ts +++ /dev/null @@ -1,372 +0,0 @@ -import { MutableDataFrame } from '../dataframe/MutableDataFrame'; -import { Labels } from '../types/data'; -import { LogLevel, LogsModel, LogRowModel, LogsSortOrder } from '../types/logs'; - -import { - getLogLevel, - calculateLogsLabelStats, - calculateFieldStats, - getParser, - LogsParsers, - calculateStats, - getLogLevelFromKey, - sortLogsResult, - checkLogsError, -} from './logs'; - -describe('getLoglevel()', () => { - it('returns no log level on empty line', () => { - expect(getLogLevel('')).toBe(LogLevel.unknown); - }); - - it('returns no log level on when level is part of a word', () => { - expect(getLogLevel('who warns us')).toBe(LogLevel.unknown); - }); - - it('returns same log level for long and short version', () => { - expect(getLogLevel('[Warn]')).toBe(LogLevel.warning); - expect(getLogLevel('[Warning]')).toBe(LogLevel.warning); - expect(getLogLevel('[Warn]')).toBe('warning'); - }); - - it('returns correct log level when level is capitalized', () => { - expect(getLogLevel('WARN')).toBe(LogLevel.warn); - }); - - it('returns log level on line contains a log level', () => { - expect(getLogLevel('warn: it is looking bad')).toBe(LogLevel.warn); - expect(getLogLevel('2007-12-12 12:12:12 [WARN]: it is looking bad')).toBe(LogLevel.warn); - }); - - it('returns first log level found', () => { - expect(getLogLevel('WARN this could be a debug message')).toBe(LogLevel.warn); - expect(getLogLevel('WARN this is a non-critical message')).toBe(LogLevel.warn); - }); -}); - -describe('getLogLevelFromKey()', () => { - it('returns correct log level', () => { - expect(getLogLevelFromKey('info')).toBe(LogLevel.info); - }); - it('returns correct log level when level is capitalized', () => { - expect(getLogLevelFromKey('INFO')).toBe(LogLevel.info); - }); - it('returns unknown log level when level is integer', () => { - expect(getLogLevelFromKey(1)).toBe(LogLevel.unknown); - }); -}); - -describe('calculateLogsLabelStats()', () => { - test('should return no stats for empty rows', () => { - expect(calculateLogsLabelStats([], '')).toEqual([]); - }); - - test('should return no stats of label is not found', () => { - const rows = [ - { - entry: 'foo 1', - labels: { - foo: 'bar', - } as Labels, - }, - ] as LogRowModel[]; - - expect(calculateLogsLabelStats(rows, 'baz')).toEqual([]); - }); - - test('should return stats for found labels', () => { - const rows = [ - { - entry: 'foo 1', - labels: { - foo: 'bar', - } as Labels, - }, - { - entry: 'foo 0', - labels: { - foo: 'xxx', - } as Labels, - }, - { - entry: 'foo 2', - labels: { - foo: 'bar', - } as Labels, - }, - ] as LogRowModel[]; - - expect(calculateLogsLabelStats(rows, 'foo')).toMatchObject([ - { - value: 'bar', - count: 2, - }, - { - value: 'xxx', - count: 1, - }, - ]); - }); -}); - -describe('LogsParsers', () => { - describe('logfmt', () => { - const parser = LogsParsers.logfmt; - - test('should detect format', () => { - expect(parser.test('foo')).toBeFalsy(); - expect(parser.test('foo=bar')).toBeTruthy(); - }); - - test('should return detected fields', () => { - expect( - parser.getFields( - 'foo=bar baz="42 + 1" msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1" time(ms)=50 label{foo}=bar' - ) - ).toEqual([ - 'foo=bar', - 'baz="42 + 1"', - 'msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1"', - 'time(ms)=50', - 'label{foo}=bar', - ]); - }); - - test('should return label for field', () => { - expect(parser.getLabelFromField('foo=bar')).toBe('foo'); - expect(parser.getLabelFromField('time(ms)=50')).toBe('time(ms)'); - }); - - test('should return value for field', () => { - expect(parser.getValueFromField('foo=bar')).toBe('bar'); - expect(parser.getValueFromField('time(ms)=50')).toBe('50'); - expect( - parser.getValueFromField( - 'msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1"' - ) - ).toBe('"[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1"'); - }); - - test('should build a valid value matcher', () => { - const matcher = parser.buildMatcher('foo'); - const match = 'foo=bar'.match(matcher); - expect(match).toBeDefined(); - expect(match![1]).toBe('bar'); - }); - - test('should build a valid complex value matcher', () => { - const matcher = parser.buildMatcher('time(ms)'); - const match = 'time(ms)=50'.match(matcher); - expect(match).toBeDefined(); - expect(match![1]).toBe('50'); - }); - }); - - describe('JSON', () => { - const parser = LogsParsers.JSON; - - test('should detect format', () => { - expect(parser.test('foo')).toBeFalsy(); - expect(parser.test('"foo"')).toBeFalsy(); - expect(parser.test('{"foo":"bar"}')).toBeTruthy(); - }); - - test('should return detected fields', () => { - expect(parser.getFields('{ "foo" : "bar", "baz" : 42 }')).toEqual(['"foo":"bar"', '"baz":42']); - }); - - test('should return detected fields for nested quotes', () => { - expect(parser.getFields(`{"foo":"bar: '[value=\\"42\\"]'"}`)).toEqual([`"foo":"bar: '[value=\\"42\\"]'"`]); - }); - - test('should return label for field', () => { - expect(parser.getLabelFromField('"foo" : "bar"')).toBe('foo'); - expect(parser.getLabelFromField('"docker.memory.fail.count":0')).toBe('docker.memory.fail.count'); - }); - - test('should return value for field', () => { - expect(parser.getValueFromField('"foo" : "bar"')).toBe('"bar"'); - expect(parser.getValueFromField('"foo" : 42')).toBe('42'); - expect(parser.getValueFromField('"foo" : 42.1')).toBe('42.1'); - }); - - test('should build a valid value matcher for strings', () => { - const matcher = parser.buildMatcher('foo'); - const match = '{"foo":"bar"}'.match(matcher); - expect(match).toBeDefined(); - expect(match![1]).toBe('bar'); - }); - - test('should build a valid value matcher for integers', () => { - const matcher = parser.buildMatcher('foo'); - const match = '{"foo":42.1}'.match(matcher); - expect(match).toBeDefined(); - expect(match![1]).toBe('42.1'); - }); - }); -}); - -describe('calculateFieldStats()', () => { - test('should return no stats for empty rows', () => { - expect(calculateFieldStats([], /foo=(.*)/)).toEqual([]); - }); - - test('should return no stats if extractor does not match', () => { - const rows = [ - { - entry: 'foo=bar', - }, - ] as LogRowModel[]; - - expect(calculateFieldStats(rows, /baz=(.*)/)).toEqual([]); - }); - - test('should return stats for found field', () => { - const rows = [ - { - entry: 'foo="42 + 1"', - }, - { - entry: 'foo=503 baz=foo', - }, - { - entry: 'foo="42 + 1"', - }, - { - entry: 't=2018-12-05T07:44:59+0000 foo=503', - }, - ] as LogRowModel[]; - - expect(calculateFieldStats(rows, /foo=("[^"]*"|\S+)/)).toMatchObject([ - { - value: '"42 + 1"', - count: 2, - }, - { - value: '503', - count: 2, - }, - ]); - }); -}); - -describe('calculateStats()', () => { - test('should return no stats for empty array', () => { - expect(calculateStats([])).toEqual([]); - }); - - test('should return correct stats', () => { - const values = ['one', 'one', null, undefined, 'two']; - expect(calculateStats(values)).toMatchObject([ - { - value: 'one', - count: 2, - proportion: 2 / 3, - }, - { - value: 'two', - count: 1, - proportion: 1 / 3, - }, - ]); - }); -}); - -describe('getParser()', () => { - test('should return no parser on empty line', () => { - expect(getParser('')).toBeUndefined(); - }); - - test('should return no parser on unknown line pattern', () => { - expect(getParser('To Be or not to be')).toBeUndefined(); - }); - - test('should return logfmt parser on key value patterns', () => { - expect(getParser('foo=bar baz="41 + 1')).toEqual(LogsParsers.logfmt); - }); - - test('should return JSON parser on JSON log lines', () => { - // TODO implement other JSON value types than string - expect(getParser('{"foo": "bar", "baz": "41 + 1"}')).toEqual(LogsParsers.JSON); - }); -}); - -describe('sortLogsResult', () => { - const firstRow: LogRowModel = { - rowIndex: 0, - entryFieldIndex: 0, - dataFrame: new MutableDataFrame(), - entry: '', - hasAnsi: false, - hasUnescapedContent: false, - labels: {}, - logLevel: LogLevel.info, - raw: '', - timeEpochMs: 0, - timeEpochNs: '0', - timeFromNow: '', - timeLocal: '', - timeUtc: '', - uid: '1', - }; - const sameAsFirstRow = firstRow; - const secondRow: LogRowModel = { - rowIndex: 1, - entryFieldIndex: 0, - dataFrame: new MutableDataFrame(), - entry: '', - hasAnsi: false, - hasUnescapedContent: false, - labels: {}, - logLevel: LogLevel.info, - raw: '', - timeEpochMs: 10, - timeEpochNs: '10000000', - timeFromNow: '', - timeLocal: '', - timeUtc: '', - uid: '2', - }; - - describe('when called with LogsSortOrder.Descending', () => { - it('then it should sort descending', () => { - const logsResult: LogsModel = { - rows: [firstRow, sameAsFirstRow, secondRow], - hasUniqueLabels: false, - }; - const result = sortLogsResult(logsResult, LogsSortOrder.Descending); - - expect(result).toEqual({ - rows: [secondRow, firstRow, sameAsFirstRow], - hasUniqueLabels: false, - }); - }); - }); - - describe('when called with LogsSortOrder.Ascending', () => { - it('then it should sort ascending', () => { - const logsResult: LogsModel = { - rows: [secondRow, firstRow, sameAsFirstRow], - hasUniqueLabels: false, - }; - const result = sortLogsResult(logsResult, LogsSortOrder.Ascending); - - expect(result).toEqual({ - rows: [firstRow, sameAsFirstRow, secondRow], - hasUniqueLabels: false, - }); - }); - }); -}); - -describe('checkLogsError()', () => { - const log = { - labels: { - __error__: 'Error Message', - foo: 'boo', - } as Labels, - } as LogRowModel; - test('should return correct error if error is present', () => { - expect(checkLogsError(log)).toStrictEqual({ hasError: true, errorMessage: 'Error Message' }); - }); -}); diff --git a/packages/grafana-data/src/utils/logs.ts b/packages/grafana-data/src/utils/logs.ts deleted file mode 100644 index e9ec99fb66c..00000000000 --- a/packages/grafana-data/src/utils/logs.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { countBy, chain, escapeRegExp } from 'lodash'; - -import { DataFrame, FieldType } from '../types/index'; -import { LogLevel, LogRowModel, LogLabelStatsModel, LogsParser, LogsModel, LogsSortOrder } from '../types/logs'; -import { ArrayVector } from '../vector/ArrayVector'; - -// This matches: -// first a label from start of the string or first white space, then any word chars until "=" -// second either an empty quotes, or anything that starts with quote and ends with unescaped quote, -// or any non whitespace chars that do not start with quote -const LOGFMT_REGEXP = /(?:^|\s)([\w\(\)\[\]\{\}]+)=(""|(?:".*?[^\\]"|[^"\s]\S*))/; - -/** - * Returns the log level of a log line. - * Parse the line for level words. If no level is found, it returns `LogLevel.unknown`. - * - * Example: `getLogLevel('WARN 1999-12-31 this is great') // LogLevel.warn` - */ -/** @deprecated will be removed in the next major version */ -export function getLogLevel(line: string): LogLevel { - if (!line) { - return LogLevel.unknown; - } - let level = LogLevel.unknown; - let currentIndex: number | undefined = undefined; - - for (const key of Object.keys(LogLevel)) { - const regexp = new RegExp(`\\b${key}\\b`, 'i'); - const result = regexp.exec(line); - - if (result) { - if (currentIndex === undefined || result.index < currentIndex) { - level = LogLevel[key as keyof typeof LogLevel]; - currentIndex = result.index; - } - } - } - return level; -} - -/** @deprecated will be removed in the next major version */ -export function getLogLevelFromKey(key: string | number): LogLevel { - const level = LogLevel[key.toString().toLowerCase() as keyof typeof LogLevel]; - if (level) { - return level; - } - - return LogLevel.unknown; -} - -/** @deprecated will be removed in the next major version */ -export function addLogLevelToSeries(series: DataFrame, lineIndex: number): DataFrame { - const levels = new ArrayVector(); - const lines = series.fields[lineIndex]; - for (let i = 0; i < lines.values.length; i++) { - const line = lines.values.get(lineIndex); - levels.buffer.push(getLogLevel(line)); - } - - return { - ...series, // Keeps Tags, RefID etc - fields: [ - ...series.fields, - { - name: 'LogLevel', - type: FieldType.string, - values: levels, - config: {}, - }, - ], - }; -} - -/** @deprecated will be removed in the next major version */ -export const LogsParsers: { [name: string]: LogsParser } = { - JSON: { - buildMatcher: (label) => new RegExp(`(?:{|,)\\s*"${label}"\\s*:\\s*"?([\\d\\.]+|[^"]*)"?`), - getFields: (line) => { - try { - const parsed = JSON.parse(line); - return Object.keys(parsed).map((key) => { - return `"${key}":${JSON.stringify(parsed[key])}`; - }); - } catch {} - return []; - }, - getLabelFromField: (field) => (field.match(/^"([^"]+)"\s*:/) || [])[1], - getValueFromField: (field) => (field.match(/:\s*(.*)$/) || [])[1], - test: (line) => { - let parsed; - try { - parsed = JSON.parse(line); - } catch (error) {} - // The JSON parser should only be used for log lines that are valid serialized JSON objects. - // If it would be used for a string, detected fields would include each letter as a separate field. - return typeof parsed === 'object'; - }, - }, - - logfmt: { - buildMatcher: (label) => new RegExp(`(?:^|\\s)${escapeRegExp(label)}=("[^"]*"|\\S+)`), - getFields: (line) => { - const fields: string[] = []; - line.replace(new RegExp(LOGFMT_REGEXP, 'g'), (substring) => { - fields.push(substring.trim()); - return ''; - }); - return fields; - }, - getLabelFromField: (field) => (field.match(LOGFMT_REGEXP) || [])[1], - getValueFromField: (field) => (field.match(LOGFMT_REGEXP) || [])[2], - test: (line) => LOGFMT_REGEXP.test(line), - }, -}; - -/** @deprecated will be removed in the next major version */ -export function calculateFieldStats(rows: LogRowModel[], extractor: RegExp): LogLabelStatsModel[] { - // Consider only rows that satisfy the matcher - const rowsWithField = rows.filter((row) => extractor.test(row.entry)); - const rowCount = rowsWithField.length; - - // Get field value counts for eligible rows - const countsByValue = countBy(rowsWithField, (r) => { - const row: LogRowModel = r; - const match = row.entry.match(extractor); - - return match ? match[1] : null; - }); - return getSortedCounts(countsByValue, rowCount); -} - -/** @deprecated will be removed in the next major version */ -export function calculateLogsLabelStats(rows: LogRowModel[], label: string): LogLabelStatsModel[] { - // Consider only rows that have the given label - const rowsWithLabel = rows.filter((row) => row.labels[label] !== undefined); - const rowCount = rowsWithLabel.length; - - // Get label value counts for eligible rows - const countsByValue = countBy(rowsWithLabel, (row) => row.labels[label]); - return getSortedCounts(countsByValue, rowCount); -} - -/** @deprecated will be removed in the next major version */ -export function calculateStats(values: unknown[]): LogLabelStatsModel[] { - const nonEmptyValues = values.filter((value) => value !== undefined && value !== null); - const countsByValue = countBy(nonEmptyValues); - return getSortedCounts(countsByValue, nonEmptyValues.length); -} - -const getSortedCounts = (countsByValue: { [value: string]: number }, rowCount: number) => { - return chain(countsByValue) - .map((count, value) => ({ count, value, proportion: count / rowCount })) - .sortBy('count') - .reverse() - .value(); -}; - -/** @deprecated will be removed in the next major version */ -export function getParser(line: string): LogsParser | undefined { - let parser; - try { - if (LogsParsers.JSON.test(line)) { - parser = LogsParsers.JSON; - } - } catch (error) {} - - if (!parser && LogsParsers.logfmt.test(line)) { - parser = LogsParsers.logfmt; - } - - return parser; -} - -/** @deprecated will be removed in the next major version */ -export const sortInAscendingOrder = (a: LogRowModel, b: LogRowModel) => { - // compare milliseconds - if (a.timeEpochMs < b.timeEpochMs) { - return -1; - } - - if (a.timeEpochMs > b.timeEpochMs) { - return 1; - } - - // if milliseconds are equal, compare nanoseconds - if (a.timeEpochNs < b.timeEpochNs) { - return -1; - } - - if (a.timeEpochNs > b.timeEpochNs) { - return 1; - } - - return 0; -}; - -/** @deprecated will be removed in the next major version */ -export const sortInDescendingOrder = (a: LogRowModel, b: LogRowModel) => { - // compare milliseconds - if (a.timeEpochMs > b.timeEpochMs) { - return -1; - } - - if (a.timeEpochMs < b.timeEpochMs) { - return 1; - } - - // if milliseconds are equal, compare nanoseconds - if (a.timeEpochNs > b.timeEpochNs) { - return -1; - } - - if (a.timeEpochNs < b.timeEpochNs) { - return 1; - } - - return 0; -}; - -/** @deprecated will be removed in the next major version */ -export const sortLogsResult = (logsResult: LogsModel | null, sortOrder: LogsSortOrder): LogsModel => { - const rows = logsResult ? sortLogRows(logsResult.rows, sortOrder) : []; - return logsResult ? { ...logsResult, rows } : { hasUniqueLabels: false, rows }; -}; - -/** @deprecated will be removed in the next major version */ -export const sortLogRows = (logRows: LogRowModel[], sortOrder: LogsSortOrder) => - sortOrder === LogsSortOrder.Ascending ? logRows.sort(sortInAscendingOrder) : logRows.sort(sortInDescendingOrder); - -// Currently supports only error condition in Loki logs -/** @deprecated will be removed in the next major version */ -export const checkLogsError = (logRow: LogRowModel): { hasError: boolean; errorMessage?: string } => { - if (logRow.labels.__error__) { - return { - hasError: true, - errorMessage: logRow.labels.__error__, - }; - } - return { - hasError: false, - }; -}; - -/** @deprecated will be removed in the next major version */ -export const escapeUnescapedString = (string: string) => - string.replace(/\\r\\n|\\n|\\t|\\r/g, (match: string) => (match.slice(1) === 't' ? '\t' : '\n')); diff --git a/packages/grafana-data/src/utils/series.test.ts b/packages/grafana-data/src/utils/series.test.ts index 5ab3d843a14..8ac46275c31 100644 --- a/packages/grafana-data/src/utils/series.test.ts +++ b/packages/grafana-data/src/utils/series.test.ts @@ -1,25 +1,24 @@ import { Field, FieldType } from '../types'; -import { ArrayVector } from '../vector'; import { getSeriesTimeStep, hasMsResolution } from './series'; const uniformTimeField: Field = { name: 'time', type: FieldType.time, - values: new ArrayVector([0, 100, 200, 300]), + values: [0, 100, 200, 300], config: {}, }; const nonUniformTimeField: Field = { name: 'time', type: FieldType.time, - values: new ArrayVector([0, 100, 300, 350]), + values: [0, 100, 300, 350], config: {}, }; const msResolutionTimeField: Field = { name: 'time', type: FieldType.time, - values: new ArrayVector([0, 1572951685007, 300, 350]), + values: [0, 1572951685007, 300, 350], config: {}, }; diff --git a/packages/grafana-data/src/utils/series.ts b/packages/grafana-data/src/utils/series.ts index 38937b9a95c..c264e0842d5 100644 --- a/packages/grafana-data/src/utils/series.ts +++ b/packages/grafana-data/src/utils/series.ts @@ -10,7 +10,7 @@ export const getSeriesTimeStep = (timeField: Field): number => { let returnTimeStep = Number.MAX_VALUE; for (let i = 0; i < timeField.values.length; i++) { - const currentTime = timeField.values.get(i); + const currentTime = timeField.values[i]; if (previousTime !== undefined) { const timeStep = currentTime - previousTime; @@ -34,7 +34,7 @@ export const getSeriesTimeStep = (timeField: Field): number => { */ export const hasMsResolution = (timeField: Field) => { for (let i = 0; i < timeField.values.length; i++) { - const value = timeField.values.get(i); + const value = timeField.values[i]; if (value !== null && value !== undefined) { const timestamp = value.toString(); if (timestamp.length === 13 && timestamp % 1000 !== 0) { diff --git a/packages/grafana-data/src/vector/AppendedVectors.test.ts b/packages/grafana-data/src/vector/AppendedVectors.test.ts index 00c1f4272b2..86e54546785 100644 --- a/packages/grafana-data/src/vector/AppendedVectors.test.ts +++ b/packages/grafana-data/src/vector/AppendedVectors.test.ts @@ -8,6 +8,9 @@ describe('Check Appending Vector', () => { appended.append(new ArrayVector([4, 5, 6])); appended.append(new ArrayVector([7, 8, 9])); expect(appended.length).toEqual(9); + expect(appended[0]).toEqual(1); + expect(appended[1]).toEqual(2); + expect(appended[100]).toEqual(undefined); appended.setLength(5); expect(appended.length).toEqual(5); diff --git a/packages/grafana-data/src/vector/AppendedVectors.ts b/packages/grafana-data/src/vector/AppendedVectors.ts index cc33811f731..af9873a69c2 100644 --- a/packages/grafana-data/src/vector/AppendedVectors.ts +++ b/packages/grafana-data/src/vector/AppendedVectors.ts @@ -1,5 +1,6 @@ -import { Vector } from '../types/vector'; +import { Vector, makeArrayIndexableVector } from '../types/vector'; +import { FunctionalVector } from './FunctionalVector'; import { vectorToArray } from './vectorToArray'; interface AppendedVectorInfo { @@ -12,13 +13,17 @@ interface AppendedVectorInfo { * This may be more trouble than it is worth. This trades some computation time for * RAM -- rather than allocate a new array the size of all previous arrays, this just * points the correct index to their original array values + * + * @deprecated use a simple Arrays. NOTE this is not used in grafana core */ -export class AppendedVectors implements Vector { +export class AppendedVectors extends FunctionalVector { length = 0; source: Array> = []; constructor(startAt = 0) { + super(); this.length = startAt; + return makeArrayIndexableVector(this); } /** @@ -58,7 +63,7 @@ export class AppendedVectors implements Vector { for (let i = 0; i < this.source.length; i++) { const src = this.source[i]; if (index >= src.start && index < src.end) { - return src.values.get(index - src.start); + return src.values[index - src.start]; } } return undefined as unknown as T; diff --git a/packages/grafana-data/src/vector/ArrayVector.test.ts b/packages/grafana-data/src/vector/ArrayVector.test.ts new file mode 100644 index 00000000000..119e42579df --- /dev/null +++ b/packages/grafana-data/src/vector/ArrayVector.test.ts @@ -0,0 +1,41 @@ +import { Field, FieldType } from '../types'; + +import { ArrayVector } from './ArrayVector'; + +describe('ArrayVector', () => { + it('should init 150k with 65k Array.push() chonking', () => { + const arr = Array.from({ length: 150e3 }, (v, i) => i); + const av = new ArrayVector(arr); + + expect(av.toArray()).toEqual(arr); + }); + + it('should support add and push', () => { + const av = new ArrayVector(); + av.add(1); + av.push(2); + av.push(3, 4); + + expect(av.toArray()).toEqual([1, 2, 3, 4]); + }); + + it('typescript should not re-define the ArrayVector based on input to the constructor', () => { + const field: Field = { + name: 'test', + config: {}, + type: FieldType.number, + values: new ArrayVector(), // this defaults to `new ArrayVector()` + }; + expect(field).toBeDefined(); + + // Before collapsing Vector, ReadWriteVector, and MutableVector these all worked fine + field.values = new ArrayVector(); + field.values = new ArrayVector(undefined); + field.values = new ArrayVector([1, 2, 3]); + field.values = new ArrayVector([]); + field.values = new ArrayVector([1, undefined]); + field.values = new ArrayVector([null]); + field.values = new ArrayVector(['a', 'b', 'c']); + expect(field.values.length).toBe(3); + }); +}); diff --git a/packages/grafana-data/src/vector/ArrayVector.ts b/packages/grafana-data/src/vector/ArrayVector.ts index d153cfb636c..121276503f5 100644 --- a/packages/grafana-data/src/vector/ArrayVector.ts +++ b/packages/grafana-data/src/vector/ArrayVector.ts @@ -1,43 +1,38 @@ -import { MutableVector } from '../types/vector'; - -import { FunctionalVector } from './FunctionalVector'; - /** * @public + * + * @deprecated use a simple Array */ -export class ArrayVector extends FunctionalVector implements MutableVector { - buffer: T[]; +export class ArrayVector extends Array { + get buffer() { + return this; + } - constructor(buffer?: T[]) { + set buffer(values: any[]) { + this.length = 0; + + const len = values?.length; + + if (len) { + let chonkSize = 65e3; + let numChonks = Math.ceil(len / chonkSize); + + for (let chonkIdx = 0; chonkIdx < numChonks; chonkIdx++) { + this.push.apply(this, values.slice(chonkIdx * chonkSize, (chonkIdx + 1) * chonkSize)); + } + } + } + + /** + * This any type is here to make the change type changes in v10 non breaking for plugins. + * Before you could technically assign field.values any typed ArrayVector no matter what the Field T type was. + */ + constructor(buffer?: any[]) { super(); - this.buffer = buffer ? buffer : []; - } - - get length() { - return this.buffer.length; - } - - add(value: T) { - this.buffer.push(value); - } - - get(index: number): T { - return this.buffer[index]; - } - - set(index: number, value: T) { - this.buffer[index] = value; - } - - reverse() { - this.buffer.reverse(); - } - - toArray(): T[] { - return this.buffer; + this.buffer = buffer ?? []; } toJSON(): T[] { - return this.buffer; + return [...this]; // copy to avoid circular reference (only for jest) } } diff --git a/packages/grafana-data/src/vector/AsNumberVector.ts b/packages/grafana-data/src/vector/AsNumberVector.ts index 775068dbe45..ac8644442be 100644 --- a/packages/grafana-data/src/vector/AsNumberVector.ts +++ b/packages/grafana-data/src/vector/AsNumberVector.ts @@ -1,22 +1,14 @@ import { Vector } from '../types'; -import { FunctionalVector } from './FunctionalVector'; - /** * This will force all values to be numbers * * @public + * @deprecated use a simple Arrays. NOTE: Not used in grafana core */ -export class AsNumberVector extends FunctionalVector { - constructor(private field: Vector) { +export class AsNumberVector extends Array { + constructor(field: Vector) { super(); - } - - get length() { - return this.field.length; - } - - get(index: number) { - return +this.field.get(index); + return field.map((v) => +v) as AsNumberVector; } } diff --git a/packages/grafana-data/src/vector/BinaryOperationVector.test.ts b/packages/grafana-data/src/vector/BinaryOperationVector.test.ts index d26ed06d3bb..e59ba476e33 100644 --- a/packages/grafana-data/src/vector/BinaryOperationVector.test.ts +++ b/packages/grafana-data/src/vector/BinaryOperationVector.test.ts @@ -11,9 +11,13 @@ describe('ScaledVector', () => { const operation = binaryOperators.get(BinaryOperationID.Multiply).operation; const v = new BinaryOperationVector(source, new ConstantVector(scale, source.length), operation); expect(v.length).toEqual(source.length); - // expect(v.push(10)).toEqual(source.length); // not implemented - for (let i = 0; i < 10; i++) { + // Accessed with getters + for (let i = 0; i < 4; i++) { expect(v.get(i)).toEqual(source.get(i) * scale); } + // Accessed with array index + for (let i = 0; i < 4; i++) { + expect(v[i]).toEqual(source[i] * scale); + } }); }); diff --git a/packages/grafana-data/src/vector/BinaryOperationVector.ts b/packages/grafana-data/src/vector/BinaryOperationVector.ts index a8ea3024bec..933d3dafe8a 100644 --- a/packages/grafana-data/src/vector/BinaryOperationVector.ts +++ b/packages/grafana-data/src/vector/BinaryOperationVector.ts @@ -1,27 +1,18 @@ import { Vector } from '../types/vector'; import { BinaryOperation } from '../utils/binaryOperators'; -import { vectorToArray } from './vectorToArray'; - /** * @public + * @deprecated use a simple Arrays. NOTE: Not used in grafana core */ -export class BinaryOperationVector implements Vector { - constructor(private left: Vector, private right: Vector, private operation: BinaryOperation) {} +export class BinaryOperationVector extends Array { + constructor(left: Vector, right: Vector, operation: BinaryOperation) { + super(); - get length(): number { - return this.left.length; - } - - get(index: number): number { - return this.operation(this.left.get(index), this.right.get(index)); - } - - toArray(): number[] { - return vectorToArray(this); - } - - toJSON(): number[] { - return vectorToArray(this); + const arr = new Array(left.length); + for (let i = 0; i < arr.length; i++) { + arr[i] = operation(left[i], right[i]); + } + return arr as BinaryOperationVector; } } diff --git a/packages/grafana-data/src/vector/CircularVector.test.ts b/packages/grafana-data/src/vector/CircularVector.test.ts index abffd15846b..0c582e8e155 100644 --- a/packages/grafana-data/src/vector/CircularVector.test.ts +++ b/packages/grafana-data/src/vector/CircularVector.test.ts @@ -5,6 +5,10 @@ describe('Check Circular Vector', () => { const buffer = [1, 2, 3]; const v = new CircularVector({ buffer }); // tail is default option expect(v.toArray()).toEqual([1, 2, 3]); + expect(v[0]).toEqual(1); + expect(v[1]).toEqual(2); + expect(v[2]).toEqual(3); + expect(v[3]).toEqual(1); // loops back to one v.add(4); expect(v.toArray()).toEqual([2, 3, 4]); @@ -20,6 +24,9 @@ describe('Check Circular Vector', () => { v.add(8); expect(v.toArray()).toEqual([6, 7, 8]); + + v.push(9, 10); + expect(v.toArray()).toEqual([8, 9, 10]); }); it('should grow buffer until it hits capacity (append)', () => { diff --git a/packages/grafana-data/src/vector/CircularVector.ts b/packages/grafana-data/src/vector/CircularVector.ts index 6d848ad40f4..aa70ae27578 100644 --- a/packages/grafana-data/src/vector/CircularVector.ts +++ b/packages/grafana-data/src/vector/CircularVector.ts @@ -1,7 +1,6 @@ -import { MutableVector } from '../types/vector'; +import { makeArrayIndexableVector } from '../types'; import { FunctionalVector } from './FunctionalVector'; -import { vectorToArray } from './vectorToArray'; interface CircularOptions { buffer?: T[]; @@ -17,8 +16,9 @@ interface CircularOptions { * to match a configured capacity. * * @public + * @deprecated use a simple Arrays */ -export class CircularVector extends FunctionalVector implements MutableVector { +export class CircularVector extends FunctionalVector { private buffer: T[]; private index: number; private capacity: number; @@ -36,6 +36,7 @@ export class CircularVector extends FunctionalVector implements Muta if (options.capacity) { this.setCapacity(options.capacity); } + return makeArrayIndexableVector(this); } /** @@ -43,7 +44,7 @@ export class CircularVector extends FunctionalVector implements Muta * * head vs tail * * growing buffer vs overwriting values */ - private getAddFunction() { + private getAddFunction(): (value: T) => void { // When we are not at capacity, it should actually modify the buffer if (this.capacity > this.buffer.length) { if (this.tail) { @@ -114,31 +115,18 @@ export class CircularVector extends FunctionalVector implements Muta } reverse() { - this.buffer.reverse(); + return this.buffer.reverse(); } - /** - * Add the value to the buffer - */ - add: (value: T) => void; - get(index: number) { return this.buffer[(index + this.index) % this.buffer.length]; } - set(index: number, value: T) { + set(index: number, value: any) { this.buffer[(index + this.index) % this.buffer.length] = value; } get length() { return this.buffer.length; } - - toArray(): T[] { - return vectorToArray(this); - } - - toJSON(): T[] { - return vectorToArray(this); - } } diff --git a/packages/grafana-data/src/vector/ConstantVector.test.ts b/packages/grafana-data/src/vector/ConstantVector.test.ts index b1b942209ee..2d94fc34428 100644 --- a/packages/grafana-data/src/vector/ConstantVector.test.ts +++ b/packages/grafana-data/src/vector/ConstantVector.test.ts @@ -10,8 +10,9 @@ describe('ConstantVector', () => { expect(v.get(1)).toEqual(value); // Now check all of them - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 7; i++) { expect(v.get(i)).toEqual(value); + expect(v[i]).toEqual(value); } }); }); diff --git a/packages/grafana-data/src/vector/ConstantVector.ts b/packages/grafana-data/src/vector/ConstantVector.ts index 2024514f506..65a96ec4143 100644 --- a/packages/grafana-data/src/vector/ConstantVector.ts +++ b/packages/grafana-data/src/vector/ConstantVector.ts @@ -1,25 +1,10 @@ -import { Vector } from '../types/vector'; - /** * @public + * @deprecated use a simple Arrays. NOTE: Not used in grafana core. */ -export class ConstantVector implements Vector { - constructor(private value: T, private len: number) {} - - get length() { - return this.len; - } - - get(index: number): T { - return this.value; - } - - toArray(): T[] { - const arr = new Array(this.length); - return arr.fill(this.value); - } - - toJSON(): T[] { - return this.toArray(); +export class ConstantVector extends Array { + constructor(value: T, len: number) { + super(); + return new Array(len).fill(value) as ConstantVector; } } diff --git a/packages/grafana-data/src/vector/FormattedVector.ts b/packages/grafana-data/src/vector/FormattedVector.ts index 277342173ed..6dfbf087f6d 100644 --- a/packages/grafana-data/src/vector/FormattedVector.ts +++ b/packages/grafana-data/src/vector/FormattedVector.ts @@ -2,22 +2,13 @@ import { DisplayProcessor } from '../types'; import { Vector } from '../types/vector'; import { formattedValueToString } from '../valueFormats'; -import { FunctionalVector } from './FunctionalVector'; - /** * @public + * @deprecated use a simple Arrays. NOTE: not used in grafana core. */ -export class FormattedVector extends FunctionalVector { - constructor(private source: Vector, private formatter: DisplayProcessor) { +export class FormattedVector extends Array { + constructor(source: Vector, formatter: DisplayProcessor) { super(); - } - - get length() { - return this.source.length; - } - - get(index: number): string { - const v = this.source.get(index); - return formattedValueToString(this.formatter(v)); + return source.map((v) => formattedValueToString(formatter(v))) as FormattedVector; } } diff --git a/packages/grafana-data/src/vector/FunctionalVector.ts b/packages/grafana-data/src/vector/FunctionalVector.ts index b9f4e22907b..62782fe718f 100644 --- a/packages/grafana-data/src/vector/FunctionalVector.ts +++ b/packages/grafana-data/src/vector/FunctionalVector.ts @@ -2,8 +2,11 @@ import { Vector } from '../types'; import { vectorToArray } from './vectorToArray'; -/** @public */ -export abstract class FunctionalVector implements Vector, Iterable { +/** + * @public + * @deprecated use a simple Arrays + */ +export abstract class FunctionalVector implements Vector { abstract get length(): number; abstract get(index: number): T; @@ -15,34 +18,180 @@ export abstract class FunctionalVector implements Vector, Iterable void) { + forEach(iterator: (row: T, index: number, array: T[]) => void): void { return vectorator(this).forEach(iterator); } - map(transform: (item: T, index: number) => V) { + map(transform: (item: T, index: number, array: T[]) => V): V[] { return vectorator(this).map(transform); } - filter(predicate: (item: T) => boolean): T[] { + filter(predicate: (item: T, index: number, array: T[]) => boolean): T[] { return vectorator(this).filter(predicate); } + at(index: number): T | undefined { + return this.get(index); + } + toArray(): T[] { return vectorToArray(this); } + join(separator?: string | undefined): string { + return this.toArray().join(separator); + } + toJSON(): any { return this.toArray(); } + + //-------------------------- + // Method not implemented + //-------------------------- + + [n: number]: T; + + pop(): T | undefined { + throw new Error('Method not implemented.'); + } + concat(...items: Array>): T[]; + concat(...items: Array>): T[] { + throw new Error('Method not implemented.'); + } + reverse(): T[] { + throw new Error('Method not implemented.'); + } + shift(): T | undefined { + throw new Error('Method not implemented.'); + } + sort(compareFn?: ((a: T, b: T) => number) | undefined): this { + throw new Error('Method not implemented.'); + } + splice(start: number, deleteCount?: number | undefined): T[]; + splice(start: number, deleteCount: number, ...items: T[]): T[] { + throw new Error('Method not implemented.'); + } + unshift(...items: T[]): number { + throw new Error('Method not implemented.'); + } + fill(value: T, start?: number | undefined, end?: number | undefined): this { + throw new Error('Method not implemented.'); + } + copyWithin(target: number, start: number, end?: number | undefined): this { + throw new Error('Method not implemented.'); + } + + [Symbol.unscopables](): { + copyWithin: boolean; + entries: boolean; + fill: boolean; + find: boolean; + findIndex: boolean; + keys: boolean; + values: boolean; + } { + throw new Error('Method not implemented.'); + } + + //-------------------------------------------------------------------------------- + // Delegated Array function -- these will not be efficient :grimmice: + //-------------------------------------------------------------------------------- + + slice(start?: number | undefined, end?: number | undefined): T[] { + return this.toArray().slice(start, end); + } + indexOf(searchElement: T, fromIndex?: number | undefined): number { + return this.toArray().indexOf(searchElement, fromIndex); + } + lastIndexOf(searchElement: T, fromIndex?: number | undefined): number { + return this.toArray().lastIndexOf(searchElement, fromIndex); + } + every(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): this is S[]; + every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; + every(predicate: any, thisArg?: unknown): boolean { + return this.toArray().every(predicate, thisArg); + } + some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean { + return this.toArray().some(predicate, thisArg); + } + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; + reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + reduce(callbackfn: unknown, initialValue?: unknown): T { + throw new Error('Method not implemented.'); + } + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; + reduceRight( + callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, + initialValue: T + ): T; + reduceRight( + callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, + initialValue: U + ): U; + reduceRight(callbackfn: unknown, initialValue?: unknown): T { + throw new Error('Method not implemented.'); + } + find( + predicate: (this: void, value: T, index: number, obj: T[]) => value is S, + thisArg?: any + ): S | undefined; + find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined { + return this.toArray().find(predicate, thisArg); + } + findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number { + return this.toArray().findIndex(predicate, thisArg); + } + entries(): IterableIterator<[number, T]> { + return this.toArray().entries(); + } + keys(): IterableIterator { + return this.toArray().keys(); + } + values(): IterableIterator { + return this.toArray().values(); + } + includes(searchElement: T, fromIndex?: number | undefined): boolean { + return this.toArray().includes(searchElement, fromIndex); + } + flatMap( + callback: (this: This, value: T, index: number, array: T[]) => U | readonly U[], + thisArg?: This | undefined + ): U[] { + return this.toArray().flatMap(callback, thisArg); + } + flat(this: A, depth?: D | undefined): Array> { + throw new Error('Method not implemented.'); + } } +const emptyarray: any[] = []; + /** * Use functional programming with your vector + * + * @deprecated use a simple Arrays */ export function vectorator(vector: Vector) { return { @@ -52,25 +201,26 @@ export function vectorator(vector: Vector) { } }, - forEach(iterator: (row: T) => void) { + forEach(iterator: (row: T, index: number, array: T[]) => void): void { for (let i = 0; i < vector.length; i++) { - iterator(vector.get(i)); + iterator(vector.get(i), i, emptyarray); } }, - map(transform: (item: T, index: number) => V) { + map(transform: (item: T, index: number, array: T[]) => V): V[] { const result: V[] = []; for (let i = 0; i < vector.length; i++) { - result.push(transform(vector.get(i), i)); + result.push(transform(vector.get(i), i, emptyarray)); } return result; }, /** Add a predicate where you return true if it should *keep* the value */ - filter(predicate: (item: T) => boolean): T[] { + filter(predicate: (item: T, index: number, array: T[]) => boolean): T[] { const result: T[] = []; + let count = 0; for (const val of this) { - if (predicate(val)) { + if (predicate(val, count++, emptyarray)) { result.push(val); } } diff --git a/packages/grafana-data/src/vector/IndexVector.ts b/packages/grafana-data/src/vector/IndexVector.ts index 4a7fc3dfc27..c32eb98ca60 100644 --- a/packages/grafana-data/src/vector/IndexVector.ts +++ b/packages/grafana-data/src/vector/IndexVector.ts @@ -1,27 +1,26 @@ import { Field, FieldType } from '../types'; -import { FunctionalVector } from './FunctionalVector'; - /** * IndexVector is a simple vector implementation that returns the index value * for each element in the vector. It is functionally equivolant a vector backed * by an array with values: `[0,1,2,...,length-1]` + * + * @deprecated use a simple Arrays. NOTE: not used in grafana core */ -export class IndexVector extends FunctionalVector { - constructor(private len: number) { +export class IndexVector extends Array { + constructor(len: number) { super(); - } - - get length() { - return this.len; - } - - get(index: number): number { - return index; + const arr = new Array(len); + for (let i = 0; i < len; i++) { + arr[i] = i; + } + return arr as IndexVector; } /** * Returns a field representing the range [0 ... length-1] + * + * @deprecated */ static newField(len: number): Field { return { diff --git a/packages/grafana-data/src/vector/RowVector.ts b/packages/grafana-data/src/vector/RowVector.ts deleted file mode 100644 index 21746be48fd..00000000000 --- a/packages/grafana-data/src/vector/RowVector.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Vector } from '../types'; - -import { vectorToArray } from './vectorToArray'; - -/** - * RowVector makes the row values look like a vector - * @internal - */ -export class RowVector implements Vector { - constructor(private columns: Vector[]) {} - - rowIndex = 0; - - get length(): number { - return this.columns.length; - } - - get(index: number): number { - return this.columns[index].get(this.rowIndex); - } - - toArray(): number[] { - return vectorToArray(this); - } - - toJSON(): number[] { - return vectorToArray(this); - } -} diff --git a/packages/grafana-data/src/vector/SortedVector.test.ts b/packages/grafana-data/src/vector/SortedVector.test.ts new file mode 100644 index 00000000000..c8418e87298 --- /dev/null +++ b/packages/grafana-data/src/vector/SortedVector.test.ts @@ -0,0 +1,13 @@ +import { ArrayVector } from './ArrayVector'; +import { SortedVector } from './SortedVector'; + +describe('SortedVector', () => { + it('Should support sorting', () => { + const values = new ArrayVector([1, 5, 2, 4]); + const sorted = new SortedVector(values, [0, 2, 3, 1]); + expect(sorted.toArray()).toEqual([1, 2, 4, 5]); + + // The proxy should still be an instance of SortedVector (used in timeseries) + expect(sorted instanceof SortedVector).toBeTruthy(); + }); +}); diff --git a/packages/grafana-data/src/vector/SortedVector.ts b/packages/grafana-data/src/vector/SortedVector.ts index e2a318e95d4..5e3dd5176ba 100644 --- a/packages/grafana-data/src/vector/SortedVector.ts +++ b/packages/grafana-data/src/vector/SortedVector.ts @@ -1,12 +1,18 @@ -import { Vector } from '../types/vector'; +import { makeArrayIndexableVector, Vector } from '../types/vector'; +import { FunctionalVector } from './FunctionalVector'; import { vectorToArray } from './vectorToArray'; /** * Values are returned in the order defined by the input parameter + * + * @deprecated use a simple Arrays */ -export class SortedVector implements Vector { - constructor(private source: Vector, private order: number[]) {} +export class SortedVector extends FunctionalVector { + constructor(private source: Vector, private order: number[]) { + super(); + return makeArrayIndexableVector(this); + } get length(): number { return this.source.length; diff --git a/packages/grafana-data/src/vector/vectorToArray.ts b/packages/grafana-data/src/vector/vectorToArray.ts index a0a17d71cb9..7ec23d9c261 100644 --- a/packages/grafana-data/src/vector/vectorToArray.ts +++ b/packages/grafana-data/src/vector/vectorToArray.ts @@ -1,5 +1,6 @@ import { Vector } from '../types/vector'; +/** @deprecated use a simple Arrays */ export function vectorToArray(v: Vector): T[] { const arr: T[] = Array(v.length); for (let i = 0; i < v.length; i++) { diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index a3dbb13e104..2027e7be469 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/e2e-selectors", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana End-to-End Test Selectors Library", "keywords": [ "cli", diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index 7ba3ebf6068..1e303539e32 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -77,6 +77,10 @@ export const Components = { containerByTitle: (title: string) => `${title} panel`, headerCornerInfo: (mode: string) => `Panel header ${mode}`, loadingBar: () => `Panel loading bar`, + HoverWidget: { + container: 'data-test-id hover-header-container', + dragIcon: 'data-testid drag-icon', + }, }, Visualization: { Graph: { @@ -416,4 +420,8 @@ export const Components = { Variables: { variableOption: 'data-testid variable-option', }, + Annotations: { + annotationsTypeInput: 'annotations-type-input', + annotationsChoosePanelInput: 'choose-panels-input', + }, }; diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 6cfe30a4f0e..ca4ab410469 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -20,7 +20,7 @@ export const Pages = { name: 'Data source settings page name input field', delete: 'Data source settings page Delete button', readOnly: 'Data source settings page read only message', - saveAndTest: 'Data source settings page Save and Test button', + saveAndTest: 'data-testid Data source settings page Save and Test button', alert: 'Data source settings page Alert', }, DataSources: { @@ -38,7 +38,7 @@ export const Pages = { dataSourcePluginsV2: (pluginName: string) => `Add new data source ${pluginName}`, }, ConfirmModal: { - delete: 'Confirm Modal Danger Button', + delete: 'data-testid Confirm Modal Danger Button', }, AddDashboard: { url: '/dashboard/new', @@ -66,6 +66,11 @@ export const Pages = { 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}`, + }, }, Settings: { Actions: { @@ -93,6 +98,11 @@ export const Pages = { Settings: { name: 'Annotations settings name input', }, + NewAnnotation: { + panelFilterSelect: 'data-testid annotations-panel-filter', + showInLabel: 'show-in-label', + previewInDashboard: 'data-testid annotations-preview', + }, }, Variables: { List: { @@ -167,6 +177,9 @@ export const Pages = { }, }, }, + Annotations: { + marker: 'data-testid annotation-marker', + }, }, Dashboards: { url: '/dashboards', @@ -256,6 +269,12 @@ export const Pages = { interval: 'Playlist interval', itemDelete: 'Delete playlist item', }, + BrowseDashbards: { + table: { + row: (uid: string) => `data-testid ${uid} row`, + checkbox: (uid: string) => `data-testid ${uid} checkbox`, + }, + }, Search: { url: '/?search=openn', FolderView: { @@ -270,11 +289,32 @@ export const Pages = { }, }, UserListPage: { + tabs: { + allUsers: 'data-testid all-users-tab', + orgUsers: 'data-testid org-users-tab', + publicDashboardsUsers: 'data-testid public-dashboards-users-tab', + users: 'data-testid users-tab', + }, org: { url: '/org/users', }, admin: { url: '/admin/users', }, + publicDashboards: { + container: 'data-testid public-dashboards-users-list', + }, + UserListAdminPage: { + container: 'data-testid user-list-admin-page', + }, + UsersListPage: { + container: 'data-testid users-list-page', + }, + UsersListPublicDashboardsPage: { + container: 'data-testid users-list-public-dashboards-page', + DashboardsListModal: { + listItem: (uid: string) => `data-testid dashboards-list-item-${uid}`, + }, + }, }, }; diff --git a/packages/grafana-e2e/package.json b/packages/grafana-e2e/package.json index 0b85a93aa11..707329e54b6 100644 --- a/packages/grafana-e2e/package.json +++ b/packages/grafana-e2e/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/e2e", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana End-to-End Test Library", "keywords": [ "cli", @@ -63,7 +63,7 @@ "@babel/core": "7.20.5", "@babel/preset-env": "7.20.2", "@cypress/webpack-preprocessor": "5.17.0", - "@grafana/e2e-selectors": "10.0.0-pre", + "@grafana/e2e-selectors": "10.1.0-pre", "@grafana/tsconfig": "^1.2.0-rc1", "@mochajs/json-file-reporter": "^1.2.0", "babel-loader": "9.1.2", diff --git a/packages/grafana-e2e/src/flows/addDataSource.ts b/packages/grafana-e2e/src/flows/addDataSource.ts index 4737d1e726a..38779572bd4 100644 --- a/packages/grafana-e2e/src/flows/addDataSource.ts +++ b/packages/grafana-e2e/src/flows/addDataSource.ts @@ -14,6 +14,7 @@ export interface AddDataSourceConfig { skipTlsVerify: boolean; type: string; timeout?: number; + awaitHealth?: boolean; } // @todo this actually returns type `Cypress.Chainable` @@ -40,8 +41,15 @@ export const addDataSource = (config?: Partial) => { skipTlsVerify, type, timeout, + awaitHealth, } = fullConfig; + if (awaitHealth) { + e2e() + .intercept(/health/) + .as('health'); + } + e2e().logToConsole('Adding data source with name:', name); e2e.pages.AddDataSource.visit(); e2e.pages.AddDataSource.dataSourcePluginsV2(type) @@ -76,6 +84,10 @@ export const addDataSource = (config?: Partial) => { e2e.pages.DataSource.saveAndTest().click(); + if (awaitHealth) { + e2e().wait('@health', { timeout: timeout ?? e2e.config().defaultCommandTimeout }); + } + // use the timeout passed in if it exists, otherwise, continue to use the default e2e.pages.DataSource.alert() .should('exist') diff --git a/packages/grafana-e2e/src/flows/configurePanel.ts b/packages/grafana-e2e/src/flows/configurePanel.ts index 7181c06fa05..c6225fb4a0d 100644 --- a/packages/grafana-e2e/src/flows/configurePanel.ts +++ b/packages/grafana-e2e/src/flows/configurePanel.ts @@ -34,6 +34,7 @@ interface ConfigurePanelOptional { panelTitle?: string; timeRange?: TimeRangeConfig; visualizationName?: string; + timeout?: number; } interface ConfigurePanelRequired { @@ -80,6 +81,7 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC timeRange, visitDashboardAtStart, visualizationName, + timeout, } = fullConfig; if (visitDashboardAtStart) { @@ -141,7 +143,6 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC if (queriesForm) { queriesForm(fullConfig); - e2e().wait('@chartData'); // Wait for a possible complex visualization to render (or something related, as this isn't necessary on the dashboard page) // Can't assert that its HTML changed because a new query could produce the same results @@ -158,10 +159,8 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC // Avoid annotations flakiness e2e.components.RefreshPicker.runButtonV2().first().click({ force: true }); - e2e().wait('@chartData'); - // Wait for RxJS - e2e().wait(500); + e2e().wait(timeout ?? e2e.config().defaultCommandTimeout); if (matchScreenshot) { let visualization; diff --git a/packages/grafana-e2e/src/flows/importDashboard.ts b/packages/grafana-e2e/src/flows/importDashboard.ts index d3b01c2be6b..2d9edeee690 100644 --- a/packages/grafana-e2e/src/flows/importDashboard.ts +++ b/packages/grafana-e2e/src/flows/importDashboard.ts @@ -20,10 +20,9 @@ export const importDashboard = (dashboardToImport: Dashboard, queryTimeout?: num e2e().visit(fromBaseUrl('/dashboard/import')); // Note: normally we'd use 'click' and then 'type' here, but the json object is so big that using 'val' is much faster - e2e.components.DashboardImportPage.textarea() - .should('be.visible') - .click() - .invoke('val', JSON.stringify(dashboardToImport)); + e2e.components.DashboardImportPage.textarea().should('be.visible'); + e2e.components.DashboardImportPage.textarea().click(); + e2e.components.DashboardImportPage.textarea().invoke('val', JSON.stringify(dashboardToImport)); e2e.components.DashboardImportPage.submit().should('be.visible').click(); e2e.components.ImportDashboardForm.name().should('be.visible').click().clear().type(dashboardToImport.title); e2e.components.ImportDashboardForm.submit().should('be.visible').click(); diff --git a/packages/grafana-e2e/src/flows/openPanelMenuItem.ts b/packages/grafana-e2e/src/flows/openPanelMenuItem.ts index 7a08cd9440f..1ccd5206826 100644 --- a/packages/grafana-e2e/src/flows/openPanelMenuItem.ts +++ b/packages/grafana-e2e/src/flows/openPanelMenuItem.ts @@ -7,26 +7,51 @@ export enum PanelMenuItems { Extensions = 'Extensions', } -export const openPanelMenuItem = (menu: PanelMenuItems, panelTitle = 'Panel Title', isReactPanel = false) => { +export const openPanelMenuItem = (menu: PanelMenuItems, panelTitle = 'Panel Title') => { // we changed the way we open the panel menu in react panels with the new panel header - if (isReactPanel) { - e2e.components.Panels.Panel.menu(panelTitle).click({ force: true }); // force click because menu is hidden and show on hover - e2e.components.Panels.Panel.menuItems(menu).should('be.visible').click(); - } else { - // this is the old way of opening the panel menu from the title - e2e.components.Panels.Panel.title(panelTitle).should('be.visible').click(); - e2e.components.Panels.Panel.headerItems(menu).should('be.visible').click(); - } + detectPanelType(panelTitle, (isAngularPanel) => { + if (isAngularPanel) { + e2e.components.Panels.Panel.title(panelTitle).should('be.visible').click(); + e2e.components.Panels.Panel.headerItems(menu).should('be.visible').click(); + } else { + e2e.components.Panels.Panel.menu(panelTitle).click({ force: true }); // force click because menu is hidden and show on hover + e2e.components.Panels.Panel.menuItems(menu).should('be.visible').click(); + } + }); }; export const openPanelMenuExtension = (extensionTitle: string, panelTitle = 'Panel Title') => { - e2e.components.Panels.Panel.title(panelTitle).should('be.visible').click(); - - e2e.components.Panels.Panel.headerItems(PanelMenuItems.Extensions) - .should('be.visible') - .parent() - .parent() - .invoke('addClass', 'open'); - - e2e.components.Panels.Panel.headerItems(extensionTitle).should('be.visible').click(); + const menuItem = PanelMenuItems.Extensions; + // we changed the way we open the panel menu in react panels with the new panel header + detectPanelType(panelTitle, (isAngularPanel) => { + if (isAngularPanel) { + e2e.components.Panels.Panel.title(panelTitle).should('be.visible').click(); + e2e.components.Panels.Panel.headerItems(menuItem) + .should('be.visible') + .parent() + .parent() + .invoke('addClass', 'open'); + e2e.components.Panels.Panel.headerItems(extensionTitle).should('be.visible').click(); + } else { + e2e.components.Panels.Panel.menu(panelTitle).click({ force: true }); // force click because menu is hidden and show on hover + e2e.components.Panels.Panel.menuItems(menuItem).trigger('mouseover', { force: true }); + e2e.components.Panels.Panel.menuItems(extensionTitle).click({ force: true }); + } + }); }; + +function detectPanelType(panelTitle: string, detected: (isAngularPanel: boolean) => void) { + e2e.components.Panels.Panel.title(panelTitle).then((el) => { + const isAngularPanel = el.find('plugin-component.ng-scope').length > 0; + + if (isAngularPanel) { + Cypress.log({ + name: 'detectPanelType', + displayName: 'detector', + message: 'Angular panel detected, will use legacy selectors.', + }); + } + + detected(isAngularPanel); + }); +} diff --git a/packages/grafana-e2e/src/flows/selectOption.ts b/packages/grafana-e2e/src/flows/selectOption.ts index 703fc9ad4a1..d47918c9fb5 100644 --- a/packages/grafana-e2e/src/flows/selectOption.ts +++ b/packages/grafana-e2e/src/flows/selectOption.ts @@ -19,7 +19,12 @@ export const selectOption = (config: SelectOptionConfig): any => { container.within(() => { if (clickToOpen) { - e2e().get('[class$="-input-suffix"]').click(); + e2e() + .get('[class$="-input-suffix"]', { timeout: 1000 }) + .then((element) => { + expect(Cypress.dom.isAttached(element)).to.eq(true); + e2e().get('[class$="-input-suffix"]', { timeout: 1000 }).click({ force: true }); + }); } }); diff --git a/packages/grafana-eslint-rules/package.json b/packages/grafana-eslint-rules/package.json index 45daca50272..71fd2085f4b 100644 --- a/packages/grafana-eslint-rules/package.json +++ b/packages/grafana-eslint-rules/package.json @@ -1,7 +1,7 @@ { "name": "@grafana/eslint-plugin", "description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "main": "./index.cjs", "author": "Grafana Labs", "license": "Apache-2.0", diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 67798bf4475..8d8cd121b1c 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/runtime", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana Runtime Library", "keywords": [ "grafana", @@ -37,11 +37,10 @@ "postpack": "mv package.json.bak package.json" }, "dependencies": { - "@grafana/data": "10.0.0-pre", - "@grafana/e2e-selectors": "10.0.0-pre", + "@grafana/data": "10.1.0-pre", + "@grafana/e2e-selectors": "10.1.0-pre", "@grafana/faro-web-sdk": "1.0.2", - "@grafana/ui": "10.0.0-pre", - "@sentry/browser": "6.19.7", + "@grafana/ui": "10.1.0-pre", "history": "4.10.1", "lodash": "4.17.21", "rxjs": "7.8.0", @@ -52,21 +51,20 @@ "@grafana/tsconfig": "^1.2.0-rc1", "@rollup/plugin-commonjs": "23.0.2", "@rollup/plugin-node-resolve": "15.0.1", - "@testing-library/dom": "8.20.0", - "@testing-library/react": "12.1.4", - "@testing-library/react-hooks": "8.0.1", + "@testing-library/dom": "9.0.1", + "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.4.3", "@types/angular": "1.8.4", "@types/history": "4.7.11", "@types/jest": "29.2.3", "@types/lodash": "4.14.191", - "@types/react": "17.0.42", - "@types/react-dom": "17.0.14", + "@types/react": "18.0.28", + "@types/react-dom": "18.0.11", "@types/systemjs": "^0.20.6", "esbuild": "0.16.17", "lodash": "4.17.21", - "react": "17.0.2", - "react-dom": "17.0.2", + "react": "18.2.0", + "react-dom": "18.2.0", "rimraf": "4.4.0", "rollup": "2.79.1", "rollup-plugin-dts": "^5.0.0", @@ -77,7 +75,7 @@ "typescript": "4.8.4" }, "peerDependencies": { - "react": "17.0.2", - "react-dom": "17.0.2" + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" } } diff --git a/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx b/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx index 75b52e2cf59..40c2a65bdc9 100644 --- a/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx +++ b/packages/grafana-runtime/src/analytics/plugins/usePluginInteractionReporter.test.tsx @@ -1,4 +1,4 @@ -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react'; import React from 'react'; import { diff --git a/packages/grafana-runtime/src/analytics/types.ts b/packages/grafana-runtime/src/analytics/types.ts index 5f6812ead7e..742b9aac991 100644 --- a/packages/grafana-runtime/src/analytics/types.ts +++ b/packages/grafana-runtime/src/analytics/types.ts @@ -9,6 +9,7 @@ import { EchoEvent, EchoEventType } from '../services/EchoSrv'; * @public */ export interface DashboardInfo { + /** @deprecated -- use UID not internal ID */ dashboardId: number; dashboardUid: string; dashboardName: string; diff --git a/packages/grafana-runtime/src/components/PanelDataErrorView.tsx b/packages/grafana-runtime/src/components/PanelDataErrorView.tsx index 61b88a63032..05902a3348a 100644 --- a/packages/grafana-runtime/src/components/PanelDataErrorView.tsx +++ b/packages/grafana-runtime/src/components/PanelDataErrorView.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { FieldConfigSource, PanelData } from '@grafana/data'; +import { FieldConfigSource, PanelData, VisualizationSuggestion } from '@grafana/data'; /** * Describes the properties that can be passed to the PanelDataErrorView. @@ -15,7 +15,7 @@ export interface PanelDataErrorViewProps { needsTimeField?: boolean; needsNumberField?: boolean; needsStringField?: boolean; - // suggestions?: VisualizationSuggestion[]; <<< for sure optional + suggestions?: VisualizationSuggestion[]; } /** diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 64b0450317d..39748fd3546 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -67,6 +67,7 @@ export class GrafanaBootConfig implements GrafanaConfig { jwtUrlLogin = false; sigV4AuthEnabled = false; azureAuthEnabled = false; + secureSocksDSProxyEnabled = false; samlEnabled = false; samlName = ''; autoAssignOrg = true; @@ -80,6 +81,8 @@ export class GrafanaBootConfig implements GrafanaConfig { viewersCanEdit = false; editorsCanAdmin = false; disableSanitizeHtml = false; + trustedTypesDefaultPolicyEnabled = false; + cspReportOnlyEnabled = false; liveEnabled = true; /** @deprecated Use `theme2` instead. */ theme: GrafanaTheme; @@ -100,12 +103,6 @@ export class GrafanaBootConfig implements GrafanaConfig { supportBundlesEnabled = false; http2Enabled = false; dateFormats?: SystemDateFormatSettings; - sentry = { - enabled: false, - dsn: '', - customEndpoint: '', - sampleRate: 1, - }; grafanaJavascriptAgent = { enabled: false, customEndpoint: '', @@ -132,7 +129,7 @@ export class GrafanaBootConfig implements GrafanaConfig { geomapDefaultBaseLayerConfig?: MapLayerOptions; geomapDisableCustomBaseLayer?: boolean; unifiedAlertingEnabled = false; - unifiedAlerting = { minInterval: '' }; + unifiedAlerting = { minInterval: '', alertStateHistoryBackend: undefined }; applicationInsightsConnectionString?: string; applicationInsightsEndpointUrl?: string; recordedQueries = { @@ -151,6 +148,11 @@ export class GrafanaBootConfig implements GrafanaConfig { rudderstackDataPlaneUrl: undefined; rudderstackSdkUrl: undefined; rudderstackConfigUrl: undefined; + sqlConnectionLimits = { + maxOpenConns: 100, + maxIdleConns: 100, + connMaxLifetime: 14400, + }; tokenExpirationDayLimit: undefined; @@ -236,7 +238,7 @@ function overrideFeatureTogglesFromUrl(config: GrafanaBootConfig) { if (key.startsWith('__feature.')) { const featureToggles = config.featureToggles as Record; const featureName = key.substring(10); - const toggleState = value === 'true'; + const toggleState = value === 'true' || value === ''; // browser rewrites true as '' if (toggleState !== featureToggles[key]) { featureToggles[featureName] = toggleState; console.log(`Setting feature toggle ${featureName} = ${toggleState}`); diff --git a/packages/grafana-runtime/src/services/EchoSrv.ts b/packages/grafana-runtime/src/services/EchoSrv.ts index 4bde55d82ac..d2dc93920d3 100644 --- a/packages/grafana-runtime/src/services/EchoSrv.ts +++ b/packages/grafana-runtime/src/services/EchoSrv.ts @@ -78,7 +78,6 @@ export interface EchoEvent { export enum EchoEventType { Performance = 'performance', MetaAnalytics = 'meta-analytics', - Sentry = 'sentry', Pageview = 'pageview', Interaction = 'interaction', ExperimentView = 'experimentview', diff --git a/packages/grafana-runtime/src/utils/DataSourceWithBackend.test.ts b/packages/grafana-runtime/src/utils/DataSourceWithBackend.test.ts index 40b17f3fdb0..b4048689c98 100644 --- a/packages/grafana-runtime/src/utils/DataSourceWithBackend.test.ts +++ b/packages/grafana-runtime/src/utils/DataSourceWithBackend.test.ts @@ -8,7 +8,7 @@ import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceRef, - MutableDataFrame, + createDataFrame, } from '@grafana/data'; import { @@ -240,10 +240,12 @@ describe('DataSourceWithBackend', () => { let obs = toStreamingDataResponse(rsp, request, standardStreamOptionsProvider); expect(obs).toBeDefined(); - let frame = new MutableDataFrame(); - frame.meta = { - channel: 'a/b/c', - }; + let frame = createDataFrame({ + meta: { + channel: 'a/b/c', + }, + fields: [], + }); rsp.data = [frame]; obs = toStreamingDataResponse(rsp, request, standardStreamOptionsProvider); expect(obs).toBeDefined(); @@ -307,6 +309,8 @@ describe('DataSourceWithBackend', () => { expect(isExpressionReference('Expression')).toBeTruthy(); // Name expect(isExpressionReference({ type: '__expr__' })).toBeTruthy(); expect(isExpressionReference({ type: '-100' })).toBeTruthy(); + expect(isExpressionReference(null)).toBeFalsy(); + expect(isExpressionReference(undefined)).toBeFalsy(); }); }); }); diff --git a/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts b/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts index 5b3793cfcbf..f1f9d13fcb9 100644 --- a/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts +++ b/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts @@ -7,6 +7,7 @@ import { DataQuery, DataQueryRequest, DataQueryResponse, + TestDataSourceResponse, DataSourceApi, DataSourceInstanceSettings, DataSourceJsonData, @@ -342,7 +343,7 @@ class DataSourceWithBackend< * Checks the plugin health * see public/app/features/datasources/state/actions.ts for what needs to be returned here */ - async testDatasource(): Promise { + async testDatasource(): Promise { return this.callHealthCheck().then((res) => { if (res.status === HealthStatus.OK) { return { @@ -351,7 +352,11 @@ class DataSourceWithBackend< }; } - throw new HealthCheckError(res.message, res.details); + return Promise.reject({ + status: 'error', + message: res.message, + error: new HealthCheckError(res.message, res.details), + }); }); } } diff --git a/packages/grafana-runtime/src/utils/logging.ts b/packages/grafana-runtime/src/utils/logging.ts index 5443f264cba..a6c26c717dd 100644 --- a/packages/grafana-runtime/src/utils/logging.ts +++ b/packages/grafana-runtime/src/utils/logging.ts @@ -1,76 +1,54 @@ -import { captureMessage, captureException, Severity as LogLevel } from '@sentry/browser'; - -import { faro, LogLevel as GrafanaLogLevel } from '@grafana/faro-web-sdk'; +import { faro, LogLevel } from '@grafana/faro-web-sdk'; import { config } from '../config'; export { LogLevel }; -// a bit stricter than what Sentry allows type Contexts = Record>>; /** - * Log a message at INFO level. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry - * + * Log a message at INFO level * @public */ export function logInfo(message: string, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { - level: GrafanaLogLevel.INFO, + level: LogLevel.INFO, context: contexts, }); } - if (config.sentry.enabled) { - captureMessage(message, { - level: LogLevel.Info, - contexts, - }); - } } /** - * Log a message at WARNING level. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry + * Log a message at WARNING level * * @public */ export function logWarning(message: string, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { - level: GrafanaLogLevel.WARN, + level: LogLevel.WARN, context: contexts, }); } - if (config.sentry.enabled) { - captureMessage(message, { - level: LogLevel.Warning, - contexts, - }); - } } /** - * Log a message at DEBUG level. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry + * Log a message at DEBUG level * * @public */ export function logDebug(message: string, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushLog([message], { - level: GrafanaLogLevel.DEBUG, + level: LogLevel.DEBUG, context: contexts, }); } - if (config.sentry.enabled) { - captureMessage(message, { - level: LogLevel.Debug, - contexts, - }); - } } /** - * Log an error. Depending on configuration might be forwarded to backend and logged to stdout or sent to Sentry + * Log an error * * @public */ @@ -78,7 +56,4 @@ export function logError(err: Error, contexts?: Contexts) { if (config.grafanaJavascriptAgent.enabled) { faro.api.pushError(err); } - if (config.sentry.enabled) { - captureException(err, { contexts }); - } } diff --git a/packages/grafana-runtime/src/utils/queryResponse.ts b/packages/grafana-runtime/src/utils/queryResponse.ts index 707550f9909..19b0d4b9e7f 100644 --- a/packages/grafana-runtime/src/utils/queryResponse.ts +++ b/packages/grafana-runtime/src/utils/queryResponse.ts @@ -234,7 +234,7 @@ export function frameToMetricFindValue(frame: DataFrame): MetricFindValue[] { } if (field) { for (let i = 0; i < field.values.length; i++) { - values.push({ text: '' + field.values.get(i) }); + values.push({ text: '' + field.values[i] }); } } return values; diff --git a/packages/grafana-schema/package.json b/packages/grafana-schema/package.json index 0655787fd14..70d02dfc459 100644 --- a/packages/grafana-schema/package.json +++ b/packages/grafana-schema/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/schema", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana Schema Library", "keywords": [ "typescript" diff --git a/packages/grafana-schema/src/common/common.gen.ts b/packages/grafana-schema/src/common/common.gen.ts index 75c2d74a010..d574469dd30 100644 --- a/packages/grafana-schema/src/common/common.gen.ts +++ b/packages/grafana-schema/src/common/common.gen.ts @@ -52,19 +52,39 @@ export interface DataQuery { } export interface BaseDimensionConfig { + /** + * fixed: T -- will be added by each element + */ field?: string; - fixed: (string | number); +} + +export enum ScaleDimensionMode { + Linear = 'linear', + Quad = 'quad', } export interface ScaleDimensionConfig extends BaseDimensionConfig { + fixed?: number; max: number; min: number; + mode?: ScaleDimensionMode; // | *"linear" } -/** - * This is actually an empty interface used mainly for naming? - */ -export interface ColorDimensionConfig extends BaseDimensionConfig {} +export interface ColorDimensionConfig extends BaseDimensionConfig { + fixed?: string; // color value +} + +export enum ScalarDimensionMode { + Clamped = 'clamped', + Mod = 'mod', +} + +export interface ScalarDimensionConfig extends BaseDimensionConfig { + fixed?: number; + max: number; + min: number; + mode?: ScalarDimensionMode; +} export enum TextDimensionMode { Field = 'field', @@ -72,6 +92,17 @@ export enum TextDimensionMode { Template = 'template', } +export interface TextDimensionConfig extends BaseDimensionConfig { + fixed?: string; + mode: TextDimensionMode; +} + +export enum ResourceDimensionMode { + Field = 'field', + Fixed = 'fixed', + Mapping = 'mapping', +} + export interface MapLayerOptions { /** * Custom options depending on the type @@ -664,7 +695,7 @@ export interface TableFooterOptions { countRows?: boolean; enablePagination?: boolean; fields?: Array; - reducer: Array; + reducer: Array; // actually 1 value show: boolean; } @@ -771,6 +802,7 @@ export enum VariableFormatID { SQLString = 'sqlstring', SingleQuote = 'singlequote', Text = 'text', + UriEncode = 'uriencode', } export interface DataSourceRef { @@ -784,8 +816,12 @@ export interface DataSourceRef { uid?: string; } -export interface TextDimensionConfig extends BaseDimensionConfig { - mode: TextDimensionMode; +/** + * Links to a resource (image/svg path) + */ +export interface ResourceDimensionConfig extends BaseDimensionConfig { + fixed?: string; + mode: ResourceDimensionMode; } export interface FrameGeometrySource { @@ -846,7 +882,7 @@ export interface TableFieldOptions { */ displayMode?: TableCellDisplayMode; filterable?: boolean; - hidden?: boolean; + hidden?: boolean; // ?? default is missing or false ?? inspect: boolean; minWidth?: number; width?: number; diff --git a/packages/grafana-schema/src/common/dimensions.cue b/packages/grafana-schema/src/common/dimensions.cue index 14899a59c47..08c0b8ac452 100644 --- a/packages/grafana-schema/src/common/dimensions.cue +++ b/packages/grafana-schema/src/common/dimensions.cue @@ -2,19 +2,32 @@ package common BaseDimensionConfig: { field?: string - fixed: string | number + // fixed: T -- will be added by each element }@cuetsy(kind="interface") +ScaleDimensionMode: "linear" | "quad" @cuetsy(kind="enum") + ScaleDimensionConfig: { BaseDimensionConfig - min: int32 - max: int32 + min: number + max: number + fixed?: number + mode?: ScaleDimensionMode // | *"linear" }@cuetsy(kind="interface") -// This is actually an empty interface used mainly for naming? ColorDimensionConfig: { BaseDimensionConfig - _empty: _ + fixed?: string // color value +}@cuetsy(kind="interface") + +ScalarDimensionMode: "mod" | "clamped" @cuetsy(kind="enum") + +ScalarDimensionConfig: { + BaseDimensionConfig + min: number + max: number + fixed?: number + mode?: ScalarDimensionMode }@cuetsy(kind="interface") TextDimensionMode: "fixed" | "field" | "template" @cuetsy(kind="enum") @@ -22,4 +35,15 @@ TextDimensionMode: "fixed" | "field" | "template" @cuetsy(kind="enum") TextDimensionConfig: { BaseDimensionConfig mode: TextDimensionMode + fixed?: string }@cuetsy(kind="interface") + +ResourceDimensionMode: "fixed" | "field" | "mapping" @cuetsy(kind="enum") + +// Links to a resource (image/svg path) +ResourceDimensionConfig: { + BaseDimensionConfig + mode: ResourceDimensionMode + fixed?: string +}@cuetsy(kind="interface") + diff --git a/packages/grafana-schema/src/common/table.cue b/packages/grafana-schema/src/common/table.cue index 2f45cf0d4eb..b5045dae96f 100644 --- a/packages/grafana-schema/src/common/table.cue +++ b/packages/grafana-schema/src/common/table.cue @@ -79,7 +79,7 @@ TableCellOptions: TableAutoCellOptions | TableSparklineCellOptions | TableBarGau TableFieldOptions: { width?: number minWidth?: number - align: FieldTextAlignment | *"auto" + align: FieldTextAlignment & (*"auto" | _) // This field is deprecated in favor of using cellOptions displayMode?: TableCellDisplayMode cellOptions: TableCellOptions diff --git a/packages/grafana-schema/src/common/variables.cue b/packages/grafana-schema/src/common/variables.cue index 4d706e05257..e16db2f54a1 100644 --- a/packages/grafana-schema/src/common/variables.cue +++ b/packages/grafana-schema/src/common/variables.cue @@ -2,36 +2,38 @@ package common // Optional formats for the template variable replace functions // See also https://grafana.com/docs/grafana/latest/dashboards/variables/variable-syntax/#advanced-variable-format-options -VariableFormatID: +VariableFormatID: // Values are lucene escaped and multi-valued variables generate an OR expression - "lucene" | + "lucene" | // Raw values - "raw" | + "raw" | // Values are regex escaped and multi-valued variables generate a (|) expression - "regex" | + "regex" | // Values are separated by | character - "pipe" | + "pipe" | // Multiple values are formatted like variable=value - "distributed" | + "distributed" | // Comma seperated values - "csv" | + "csv" | // HTML escaped - "html" | + "html" | // JSON values - "json" | + "json" | // Percent encode - "percentencode" | + "percentencode" | + // Uri encode + "uriencode" | // Single quote - "singlequote" | + "singlequote" | // Double quote - "doublequote" | + "doublequote" | // SQL string quoting and commas for use in IN statements and other scenarios - "sqlstring" | + "sqlstring" | // Date - "date" | + "date" | // Format multi-valued variables using glob syntax, example {value1,value2} - "glob" | + "glob" | // Format variables in their text representation. Example in multi-variable scenario A + B + C. - "text" | + "text" | // Format variables as URL parameters. Example in multi-variable scenario A + B + C => var-foo=A&var-foo=B&var-foo=C. - "queryparam" @cuetsy(kind="enum",memberNames="Lucene|Raw|Regex|Pipe|Distributed|CSV|HTML|JSON|PercentEncode|SingleQuote|DoubleQuote|SQLString|Date|Glob|Text|QueryParam") \ No newline at end of file + "queryparam" @cuetsy(kind="enum",memberNames="Lucene|Raw|Regex|Pipe|Distributed|CSV|HTML|JSON|PercentEncode|UriEncode|SingleQuote|DoubleQuote|SQLString|Date|Glob|Text|QueryParam") diff --git a/packages/grafana-schema/src/index.gen.ts b/packages/grafana-schema/src/index.gen.ts index cefd43cc220..aa647f38e81 100644 --- a/packages/grafana-schema/src/index.gen.ts +++ b/packages/grafana-schema/src/index.gen.ts @@ -10,7 +10,7 @@ // Raw generated types from Dashboard kind. export type { AnnotationTarget, - AnnotationQuery, + AnnotationPanelFilter, DashboardLink, DashboardLinkType, VariableType, @@ -34,7 +34,7 @@ export type { // Raw generated enums and default consts from dashboard kind. export { defaultAnnotationTarget, - defaultAnnotationQuery, + defaultAnnotationPanelFilter, LoadingState, defaultDashboardLink, FieldColorModeId, @@ -59,6 +59,8 @@ export { // TODO generate code such that tsc enforces type compatibility between raw and veneer decls export type { Dashboard, + AnnotationContainer, + AnnotationQuery, VariableModel, DataSourceRef, DataTransformerConfig, @@ -79,6 +81,8 @@ export type { // TODO generate code such that tsc enforces type compatibility between raw and veneer decls export { defaultDashboard, + defaultAnnotationContainer, + defaultAnnotationQuery, defaultVariableModel, VariableHide, defaultPanel, @@ -87,6 +91,9 @@ export { defaultFieldConfig } from './veneer/dashboard.types'; +// Raw generated types from Folder kind. +export type { Folder } from './raw/folder/x/folder_types.gen'; + // Raw generated types from LibraryPanel kind. export type { LibraryElementDTOMetaUser, 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 b1332f1b72c..144f939eee2 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 @@ -3,18 +3,35 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. /** - * TODO docs + * TODO: this should be a regular DataQuery that depends on the selected dashboard + * these match the properties of the "grafana" datasouce that is default in most dashboards */ export interface AnnotationTarget { + /** + * Only required/valid for the grafana datasource... + * but code+tests is already depending on it so hard to change + */ limit: number; + /** + * Only required/valid for the grafana datasource... + * but code+tests is already depending on it so hard to change + */ matchAny: boolean; + /** + * Only required/valid for the grafana datasource... + * but code+tests is already depending on it so hard to change + */ tags: Array; + /** + * Only required/valid for the grafana datasource... + * but code+tests is already depending on it so hard to change + */ type: string; } @@ -22,52 +39,89 @@ export const defaultAnnotationTarget: Partial = { tags: [], }; +export interface AnnotationPanelFilter { + /** + * Should the specified panels be included or excluded + */ + exclude?: boolean; + /** + * Panel IDs that should be included or excluded + */ + ids: Array; +} + +export const defaultAnnotationPanelFilter: Partial = { + exclude: false, + ids: [], +}; + +/** + * TODO -- should not be a public interface on its own, but required for Veneer + */ +export interface AnnotationContainer { + list?: Array; +} + +export const defaultAnnotationContainer: Partial = { + list: [], +}; + /** * TODO docs * FROM: AnnotationQuery in grafana-data/src/types/annotations.ts */ export interface AnnotationQuery { - builtIn: number; /** - * Datasource to use for annotation. + * TODO: Should be DataSourceRef */ datasource: { type?: string; uid?: string; }; /** - * Whether annotation is enabled. + * When enabled the annotation query is issued with every dashboard refresh */ enable: boolean; /** - * Whether to hide annotation. + * Optionally + */ + filter?: AnnotationPanelFilter; + /** + * Annotation queries can be toggled on or off at the top of the dashboard. + * When hide is true, the toggle is not shown in the dashboard. */ hide?: boolean; /** - * Annotation icon color. + * Color to use for the annotation event markers */ - iconColor?: string; + iconColor: string; /** * Name of annotation. */ - name?: string; + name: string; /** - * Query for annotation data. + * TODO.. this should just be a normal query target */ - rawQuery?: string; - showIn: number; target?: AnnotationTarget; - type: string; + /** + * TODO -- this should not exist here, it is based on the --grafana-- datasource + */ + type?: string; } export const defaultAnnotationQuery: Partial = { - builtIn: 0, enable: true, hide: false, - showIn: 0, - type: 'dashboard', }; +export enum LoadingState { + Done = 'Done', + Error = 'Error', + Loading = 'Loading', + NotStarted = 'NotStarted', + Streaming = 'Streaming', +} + /** * FROM: packages/grafana-data/src/types/templateVars.ts * TODO docs @@ -107,14 +161,6 @@ export enum VariableHide { hideVariable = 2, } -export enum LoadingState { - Done = 'Done', - Error = 'Error', - Loading = 'Loading', - NotStarted = 'NotStarted', - Streaming = 'Streaming', -} - /** * Ref to a DataSource instance */ @@ -193,7 +239,7 @@ export interface FieldColor { /** * The main color scheme mode */ - mode: FieldColorModeId; + mode: (FieldColorModeId | string); /** * Some visualizations need to know how to assign a series color from by value color schemes */ @@ -662,9 +708,7 @@ export interface Dashboard { /** * TODO docs */ - annotations?: { - list?: Array; - }; + annotations?: AnnotationContainer; /** * Description of dashboard. */ diff --git a/packages/grafana-schema/src/raw/folder/x/folder_types.gen.ts b/packages/grafana-schema/src/raw/folder/x/folder_types.gen.ts new file mode 100644 index 00000000000..45922122c71 --- /dev/null +++ b/packages/grafana-schema/src/raw/folder/x/folder_types.gen.ts @@ -0,0 +1,24 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. +// +// Generated by: +// kinds/gen.go +// Using jennies: +// TSResourceJenny +// LatestMajorsOrXJenny +// +// Run 'make gen-cue' from repository root to regenerate. + +export interface Folder { + /** + * Description of the folder. + */ + description?: string; + /** + * Folder title + */ + title: string; + /** + * Unique folder id. (will be k8s name) + */ + uid: string; +} diff --git a/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts b/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts index 97cba2a5c5e..d3b967e7a76 100644 --- a/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts +++ b/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts @@ -3,7 +3,7 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. diff --git a/packages/grafana-schema/src/raw/playlist/x/playlist_types.gen.ts b/packages/grafana-schema/src/raw/playlist/x/playlist_types.gen.ts index 03c1c69cce7..4f98abe19bf 100644 --- a/packages/grafana-schema/src/raw/playlist/x/playlist_types.gen.ts +++ b/packages/grafana-schema/src/raw/playlist/x/playlist_types.gen.ts @@ -3,7 +3,7 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. diff --git a/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts b/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts index e18a9375d62..8921226ac94 100644 --- a/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts +++ b/packages/grafana-schema/src/raw/preferences/x/preferences_types.gen.ts @@ -3,7 +3,7 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. diff --git a/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts b/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts index b94e4baefe3..9a1ae8596fd 100644 --- a/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/publicdashboard/x/publicdashboard_types.gen.ts @@ -3,7 +3,7 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. diff --git a/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts b/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts index d2713bf993d..0f93321ec0d 100644 --- a/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts +++ b/packages/grafana-schema/src/raw/serviceaccount/x/serviceaccount_types.gen.ts @@ -3,7 +3,7 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. @@ -23,10 +23,6 @@ export interface ServiceAccount { * of the service account. */ avatarUrl: string; - /** - * Created indicates when the service account was created. - */ - created?: string; /** * ID is the unique identifier of the service account in the database. */ @@ -60,10 +56,6 @@ export interface ServiceAccount { * Tokens are used to authenticate the service account against Grafana. */ tokens: number; - /** - * Updated indicates when the service account was updated. - */ - updated?: string; } export const defaultServiceAccount: Partial = { diff --git a/packages/grafana-schema/src/raw/team/x/team_types.gen.ts b/packages/grafana-schema/src/raw/team/x/team_types.gen.ts index 3482c0c758c..938d69c4455 100644 --- a/packages/grafana-schema/src/raw/team/x/team_types.gen.ts +++ b/packages/grafana-schema/src/raw/team/x/team_types.gen.ts @@ -3,7 +3,7 @@ // Generated by: // kinds/gen.go // Using jennies: -// TSTypesJenny +// TSResourceJenny // LatestMajorsOrXJenny // // Run 'make gen-cue' from repository root to regenerate. @@ -24,10 +24,6 @@ export interface Team { * AvatarUrl is the team's avatar URL. */ avatarUrl?: string; - /** - * Created indicates when the team was created. - */ - created: string; /** * Email of the team. */ @@ -48,8 +44,4 @@ export interface Team { * TODO - it seems it's a team_member.permission, unlikely it should belong to the team kind */ permission: Permission; - /** - * Updated indicates when the team was updated. - */ - updated: string; } diff --git a/packages/grafana-schema/src/veneer/common.types.ts b/packages/grafana-schema/src/veneer/common.types.ts index f5a38ac2502..2ece21b6e66 100644 --- a/packages/grafana-schema/src/veneer/common.types.ts +++ b/packages/grafana-schema/src/veneer/common.types.ts @@ -23,10 +23,18 @@ export interface BaseDimensionConfig extends Omit, Omit {} +export interface ScalarDimensionConfig extends BaseDimensionConfig, Omit {} + export interface TextDimensionConfig extends BaseDimensionConfig, Omit {} export interface ColorDimensionConfig extends BaseDimensionConfig, Omit {} +export interface ColorDimensionConfig extends BaseDimensionConfig, Omit {} + +export interface ResourceDimensionConfig + extends BaseDimensionConfig, + Omit {} + export * from '../common/common.gen'; // TODO remove when https://github.com/grafana/cuetsy/issues/74 is fixed diff --git a/packages/grafana-schema/src/veneer/dashboard.types.ts b/packages/grafana-schema/src/veneer/dashboard.types.ts index bf7a5299cde..41c63816ce9 100644 --- a/packages/grafana-schema/src/veneer/dashboard.types.ts +++ b/packages/grafana-schema/src/veneer/dashboard.types.ts @@ -1,6 +1,8 @@ -import { DataSourceRef as CommonDataSourceRef } from '../common/common.gen'; +import { DataSourceRef as CommonDataSourceRef, DataSourceRef } from '../common/common.gen'; import * as raw from '../raw/dashboard/x/dashboard_types.gen'; +import { DataQuery } from './common.types'; + export type { CommonDataSourceRef as DataSourceRef }; export interface Panel, TCustomFieldConfig = Record> @@ -28,13 +30,24 @@ export interface VariableModel datasource: CommonDataSourceRef | null; } -export interface Dashboard extends Omit { +export interface Dashboard extends Omit { panels?: Array; + annotations?: AnnotationContainer; templating?: { list?: VariableModel[]; }; } +export interface AnnotationQuery + extends Omit { + datasource?: DataSourceRef | null; + target?: TQuery; +} + +export interface AnnotationContainer extends Omit { + list?: AnnotationQuery[]; // use the version from this file +} + export interface FieldConfig> extends raw.FieldConfig { custom?: TOptions & Record; } @@ -69,3 +82,6 @@ export const defaultPanel: Partial = raw.defaultPanel; export const defaultFieldConfig: Partial = raw.defaultFieldConfig; export const defaultFieldConfigSource: Partial = raw.defaultFieldConfigSource; export const defaultMatcherConfig: Partial = raw.defaultMatcherConfig; +export const defaultAnnotationQuery: Partial = raw.defaultAnnotationQuery as AnnotationQuery; +export const defaultAnnotationContainer: Partial = + raw.defaultAnnotationContainer as AnnotationContainer; diff --git a/packages/grafana-toolkit/README.md b/packages/grafana-toolkit/README.md index 800492fa133..c3aa5d75e35 100644 --- a/packages/grafana-toolkit/README.md +++ b/packages/grafana-toolkit/README.md @@ -1,381 +1,15 @@ -> **WARNING: @grafana/toolkit is currently in BETA**. +> **WARNING: @grafana/toolkit is deprecated**. # grafana-toolkit grafana-toolkit is a CLI that enables efficient development of Grafana plugins. We want to help our community focus on the core value of their plugins rather than all the setup required to develop them. -## Getting started - -Set up a new plugin with `grafana-toolkit plugin:create` command: - -```sh -npx @grafana/toolkit plugin:create my-grafana-plugin -cd my-grafana-plugin -yarn install -yarn dev -``` - -> **Note:** If running NPM 7+ the `npx` commands mentioned in this article may hang. The workaround is to use `npx --legacy-peer-deps `. - -## Update your plugin to use grafana-toolkit - -Follow the steps below to start using grafana-toolkit in your existing plugin. - -1. Add `@grafana/toolkit` package to your project by running `yarn add @grafana/toolkit` or `npm install @grafana/toolkit`. -2. Create `tsconfig.json` file in the root dir of your plugin and paste the code below: - -```json -{ - "extends": "./node_modules/@grafana/toolkit/src/config/tsconfig.plugin.json", - "include": ["src", "types"], - "compilerOptions": { - "rootDir": "./src", - "baseUrl": "./src", - "typeRoots": ["./node_modules/@types"] - } -} -``` - -3. Create `.prettierrc.js` file in the root dir of your plugin and paste the code below: - -```js -module.exports = { - ...require('./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json'), -}; -``` - -4. In your `package.json` file add following scripts: - -```json -"scripts": { - "build": "grafana-toolkit plugin:build", - "test": "grafana-toolkit plugin:test", - "dev": "grafana-toolkit plugin:dev", - "watch": "grafana-toolkit plugin:dev --watch" -}, -``` - -## Usage - -With grafana-toolkit, we give you a CLI that addresses common tasks performed when working on Grafana plugin: - -- `grafana-toolkit plugin:create` -- `grafana-toolkit plugin:dev` -- `grafana-toolkit plugin:test` -- `grafana-toolkit plugin:build` -- `grafana-toolkit plugin:sign` - -### Create your plugin - -`grafana-toolkit plugin:create plugin-name` - -This command creates a new Grafana plugin from template. - -If `plugin-name` is provided, then the template is downloaded to `./plugin-name` directory. Otherwise, it will be downloaded to the current directory. - -### Develop your plugin - -`grafana-toolkit plugin:dev` - -This command creates a development build that's easy to play with and debug using common browser tooling. - -Available options: - -- `-w`, `--watch` - run development task in a watch mode - -### Test your plugin - -`grafana-toolkit plugin:test` - -This command runs Jest against your codebase. - -Available options: - -- `--watch` - Runs tests in interactive watch mode. -- `--coverage` - Reports code coverage. -- `-u`, `--updateSnapshot` - Performs snapshots update. -- `--testNamePattern=` - Runs test with names that match provided regex (https://jestjs.io/docs/en/cli#testnamepattern-regex). -- `--testPathPattern=` - Runs test with paths that match provided regex (https://jestjs.io/docs/en/cli#testpathpattern-regex). -- `--maxWorkers=|` - Limit number of Jest workers spawned (https://jestjs.io/docs/en/cli#--maxworkersnumstring) - -### Build your plugin - -`grafana-toolkit plugin:build` - -This command creates a production-ready build of your plugin. - -Available options: - -- `--skipTest` - Skip running tests as part of build. Useful if you're running the build as part of a larger pipeline. -- `--skipLint` - Skip linting as part of build. Useful if you're running the build as part of a larger pipeline. -- `--coverage` - Reports code coverage after the test step of the build. -- `--preserveConsole` - Preserves console statements in the code. - -### Sign your plugin - -`grafana-toolkit plugin:sign` - -This command creates a signed MANIFEST.txt file which Grafana uses to validate the integrity of the plugin. - -Available options: - -- `--signatureType` - The [type of Signature](https://grafana.com/legal/plugins/) you are generating: `private`, `community` or `commercial` -- `--rootUrls` - For private signatures, a list of the Grafana instance URLs that the plugin will be used on - -To generate a signature, you will need to sign up for a free account on https://grafana.com, create an API key with the Plugin Publisher role, and pass that in the `GRAFANA_API_KEY` environment variable. - ## FAQ ### Which version of grafana-toolkit should I use? -See [Grafana packages versioning guide](https://github.com/grafana/grafana/blob/main/packages/README.md#versioning). - -### What tools does grafana-toolkit use? - -grafana-toolkit comes with TypeScript, ESLint, Prettier, Jest, CSS and SASS support. - -### How to start using grafana-toolkit in my plugin? - -See [Updating your plugin to use grafana-toolkit](#updating-your-plugin-to-use-grafana-toolkit). - -### Can I use TypeScript to develop Grafana plugins? - -Yes! grafana-toolkit supports TypeScript by default. - -### How can I test my plugin? - -grafana-toolkit comes with Jest as a test runner. - -Internally at Grafana we use React Testing Library. - -You can also set up Jest with shims of your needs by creating `jest-shim.ts` file in the same directory: `/config/jest-shim.ts` - -### Can I provide custom setup for Jest? - -You can provide custom Jest configuration with a `package.json` file. For more details, see [Jest docs](https://jest-bot.github.io/jest/docs/configuration.html). - -Currently we support following Jest configuration properties: - -- [`snapshotSerializers`](https://jest-bot.github.io/jest/docs/configuration.html#snapshotserializers-array-string) -- [`moduleNameMapper`](https://jestjs.io/docs/en/configuration#modulenamemapper-object-string-string) - -### I want to use monaco-editor, is there anything particular I should pay attention? - -Yes there is. -We already bundled monaco-editor in grafana. See [webpack config](https://github. -com/grafana/grafana/blob/main/scripts/webpack/webpack.common.js#L49-L58). -We use `@monaco-editor/react` package with loader-config https://github.com/suren-atoyan/monaco-react#loader-config -By default, monaco files are being downloaded from CDN. We use the bundled version. -Initialization: https://github.com/grafana/grafana/blob/main/scripts/webpack/webpack.common.js#L49-L58t -We suggest to use the bundled version to prevent unwanted issues related to version mismatch. - -### How can I customize Webpack rules or plugins? - -You can provide your own `webpack.config.js` file that exports a `getWebpackConfig` function. We recommend that you extend the standard configuration, but you are free to create your own: - -```js -const CustomPlugin = require('custom-plugin'); - -module.exports.getWebpackConfig = (config, options) => ({ - ...config, - plugins: [...config.plugins, new CustomPlugin()], -}); -``` - -### How can I style my plugin? - -We support pure CSS, SASS, and CSS-in-JS approach (via [Emotion](https://emotion.sh/)). - -#### Single CSS or SASS file - -Create your CSS or SASS file and import it in your plugin entry point (typically `module.ts`): - -```ts -import 'path/to/your/css_or_sass'; -``` - -The styles will be injected via `style` tag during runtime. - -> **Note:** that imported static assets will be inlined as base64 URIs. _This can be subject of change in the future!_ - -#### Theme-specific stylesheets - -If you want to provide different stylesheets for dark/light theme, then create `dark.[css|scss]` and `light.[css|scss]` files in the `src/styles` directory of your plugin. grafana-toolkit generates theme-specific stylesheets that are stored in `dist/styles` directory. - -In order for Grafana to pick up your theme stylesheets, you need to use `loadPluginCss` from `@grafana/runtime` package. Typically you would do that in the entry point of your plugin: - -```ts -import { loadPluginCss } from '@grafana/runtime'; - -loadPluginCss({ - dark: 'plugins//styles/dark.css', - light: 'plugins//styles/light.css', -}); -``` - -You must add `@grafana/runtime` to your plugin dependencies by running `yarn add @grafana/runtime` or `npm install @grafana/runtime`. - -> **Note:** that in this case static files (png, svg, json, html) are all copied to dist directory when the plugin is bundled. Relative paths to those files does not change! - -#### Emotion - -Starting from Grafana 6.2 _our suggested way_ for styling plugins is by using [Emotion](https://emotion.sh). It's a CSS-in-JS library that we use internally at Grafana. The biggest advantage of using Emotion is that you can access Grafana Theme variables. - -To start using Emotion, you first must add it to your plugin dependencies: - -``` - yarn add "emotion"@10.0.27 -``` - -Then, import `css` function from Emotion: - -```ts -import { css } from 'emotion'; -``` - -Now you are ready to implement your styles: - -```tsx -const MyComponent = () => { - return ( -
    - ); -}; -``` - -To learn more about using Grafana theme please refer to [Theme usage guide](https://github.com/grafana/grafana/blob/main/style_guides/themes.md#react) - -> We do not support Emotion's `css` prop. Use className instead! - -### Can I adjust TypeScript configuration to suit my needs? - -Yes! However, it's important that your `tsconfig.json` file contains the following lines: - -```json -{ - "extends": "./node_modules/@grafana/toolkit/src/config/tsconfig.plugin.json", - "include": ["src"], - "compilerOptions": { - "rootDir": "./src", - "typeRoots": ["./node_modules/@types"] - } -} -``` - -### Can I adjust ESLint configuration to suit my needs? - -grafana-toolkit comes with [default config for ESLint](https://github.com/grafana/grafana/blob/main/packages/grafana-toolkit/src/config/eslint.plugin.json). For now, there is no way to customise ESLint config. - -### How is Prettier integrated into grafana-toolkit workflow? - -When building plugin with [`grafana-toolkit plugin:build`](#building-plugin) task, grafana-toolkit performs Prettier check. If the check detects any Prettier issues, the build will not pass. To avoid such situation we suggest developing plugin with [`grafana-toolkit plugin:dev --watch`](#developing-plugin) task running. This task tries to fix Prettier issues automatically. - -### My editor does not respect Prettier config, what should I do? - -In order for your editor to pick up our Prettier config you need to create `.prettierrc.js` file in the root directory of your plugin with following content: - -```js -module.exports = { - ...require('./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json'), -}; -``` - -### How do I add third-party dependencies that are not npm packages? - -Put them in the `static` directory in the root of your project. The `static` directory is copied when the plugin is built. - -### I am getting this message when I run yarn install: `Request failed \"404 Not Found\"` - -If you are using version `canary`, this error occurs because a `canary` release unpublishes previous versions leaving `yarn.lock` outdated. Remove `yarn.lock` and run `yarn install` again. - -### I am getting this message when I run my plugin: `Unable to dynamically transpile ES module A loader plugin needs to be configured via SystemJS.config({ transpiler: 'transpiler-module' }).` - -This error occurs when you bundle your plugin using the `grafana-toolkit plugin:dev` task and your code comments include ES2016 code. - -There are two issues at play: - -- The `grafana-toolkit plugin:dev` task does not remove comments from your bundled package. -- Grafana does not support [ES modules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/). - -If your comments include ES2016 code, then SystemJS v0.20.19, which Grafana uses internally to load plugins, interprets your code as an ESM and fails. - -To fix this error, remove the ES2016 code from your comments. - -### I would like to dynamically import modules in my plugin - -Create a webpack.config.js with this content (in the root of _your_ plugin) - -```ts -// webpack.config.js -const pluginJson = require('./src/plugin.json'); -module.exports.getWebpackConfig = (config, options) => ({ - ...config, - output: { - ...config.output, - publicPath: `public/plugins/${pluginJson.id}/`, - }, -}); -``` - -The plugin id is the id written in the plugin.json file. +You should not use grafana-toolkit anymore. [create-plugin](https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin/) is the tool to create new plugins and existing plugins should migrate. ## Contribute to grafana-toolkit -You can contribute to grafana-toolkit by helping develop it or by debugging it. - -### Develop grafana-toolkit - -Typically plugins should be developed using the `@grafana/toolkit` installed from npm. However, when working on the toolkit, you might want to use the local version. There are two ways to run the local toolkit version: - -### Using `NODE_OPTIONS` to load the yarn pnp file: - -You can run grafana toolkit directly from the grafana repository with this command. - -`NODE_OPTIONS="--require $GRAFANA_REPO/.pnp.cjs" $GRAFANA_REPO/packages/grafana-toolkit/dist/bin/grafana-toolkit.js [command]` - -You can replace `$GRAFANA_REPO` with the path to your grafana clone or set it in your environment. e.g.: `export GRAFANA_REPO=/home/dev/your_grafana_clone` - -> Note: This will run grafana toolkit from your local clone but it won't change the dependencies of what you are trying to build. - -### Using yarn berry linking. - -#### Prerequisites - -For the following to work make sure to have a plugin that uses yarn berry and yarn PnP to test changes against. Feel free to clone the panel plugin [here](https://github.com/jackw/plugin-yarn-berry) if need be. - -1. Clone [Grafana repository](https://github.com/grafana/grafana). -2. Navigate to the directory you have cloned Grafana repo to and then run `yarn install --immutable`. -3. Open `packages/grafana-toolkit/package.json`, delete any mention of `@grafana/data` and `@grafana/ui`. Then add the following: - -``` - "devDependencies": { - "@grafana/data": "workspace:*", - "@grafana/ui": "workspace:*" - }, - "peerDependencies": { - "@grafana/data": "*", - "@grafana/ui": "*" - } -``` - -**DO NOT commit this `package.json` code change. It is required to resolve these `@grafana` packages from the plugin for development purposes only.** - -4. Run `yarn` in the grafana repo. -5. Navigate to the directory where your plugin code is and then run `yarn link /packages/grafana-toolkit`. This uses yarn berry's `portal` protocol to "link" the grafana-toolkit package and resolve it's dependencies into your plugin code allowing you to develop toolkit and test changes against plugin code. -6. Make the required changes to Grafana toolkit. - -### Debug grafana-toolkit - -To debug grafana-toolkit you can use standard [NodeJS debugging methods](https://nodejs.org/de/docs/guides/debugging-getting-started/#enable-inspector) (`node --inspect`, `node --inspect-brk`). - -To run grafana-toolkit in a debugging session use the following command in the toolkit's directory: - -`node --inspect-brk ./bin/grafana-toolkit.js [task]` - -To run [linked](#develop-grafana-toolkit) grafana-toolkit in a debugging session use the following command in the plugin's directory: - -`node --inspect-brk ./node_modules/@grafana/toolkit/bin/grafana-toolkit.js [task]` +Grafana Toolkit is now deprecated. diff --git a/packages/grafana-toolkit/config/circleci/config.yml b/packages/grafana-toolkit/config/circleci/config.yml deleted file mode 100644 index d1438c2be92..00000000000 --- a/packages/grafana-toolkit/config/circleci/config.yml +++ /dev/null @@ -1,295 +0,0 @@ -version: 2.1 - -parameters: - ssh-fingerprint: - type: string - default: ${GITHUB_SSH_FINGERPRINT} - -aliases: - # Workflow filters - - &filter-only-master - branches: - only: master - - &filter-only-release - branches: - only: /^v[1-9]*[0-9]+\.[1-9]*[0-9]+\.x$/ - -workflows: - plugin_workflow: - jobs: - - yarn_install - - build_docs: - requires: - - yarn_install - - build_frontend: - requires: - - yarn_install - - build_backend: - requires: - - yarn_install - - package: - requires: - - build_frontend - - build_backend - - build_docs - - provisioning: - requires: - - build_frontend - - build_backend - - build_docs - - e2e_canary: - requires: - - provisioning - - package - - report: - requires: - - package - - e2e_canary - - approve_release: - type: approval - requires: - - report - filters: *filter-only-release - - publish_github_release: - requires: - - approve_release - filters: *filter-only-release - -executors: - default_exec: # declares a reusable executor - docker: - - image: srclosson/grafana-plugin-ci-alpine:latest - e2e_exec: - docker: - - image: srclosson/grafana-plugin-ci-e2e:latest - -jobs: - yarn_install: - executor: default_exec - steps: - - checkout - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: Install dependencies - command: | - mkdir ci - yarn bin grafana-toolkit ] || yarn install --immutable - - save_cache: - name: save yarn cache - paths: - - ~/project/.yarn - key: build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - save_cache: - name: save cypress cache - paths: - - ~/.cache/Cypress - key: cypress-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - build_docs: - executor: default_exec - steps: - - checkout - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: Build docs - command: | - yarn run grafana-toolkit plugin:ci-docs - [ -d "dist" ] || circleci-agent step halt - - persist_to_workspace: - root: . - paths: - - dist - - build_frontend: - executor: default_exec - steps: - - checkout - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: Build and test frontend - command: yarn run grafana-toolkit plugin:ci-build - - persist_to_workspace: - root: . - paths: - - dist - - build_backend: - executor: default_exec - steps: - - checkout - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: Exit if no backend - command: | - [ -f "Magefile.go" ] || circleci-agent step halt - - run: - name: Build backend - command: mage -v buildAll - - run: - name: Test backend - command: | - mage -v lint - mage -v coverage - - persist_to_workspace: - root: . - paths: - - dist - - package: - executor: default_exec - steps: - - checkout - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - attach_workspace: - at: . - - run: - name: Move results to ci folder - command: yarn run grafana-toolkit plugin:ci-build --finish - - run: - name: Package distribution - command: | - yarn run grafana-toolkit plugin:ci-package - - persist_to_workspace: - root: . - paths: - - ci/jobs/package - - ci/packages - - ci/dist - - ci/grafana-test-env - - store_artifacts: - path: ci - - provisioning: - executor: default_exec - steps: - - run: - name: Continue if this plugin has a provisioning path - command: | - [ -z "${PROVISIONING_REPO_PATH}" ] && circleci-agent step halt - - checkout - - add_ssh_keys: - fingerprints: - - << pipeline.parameters.ssh-fingerprint >> - - run: - name: Checkout provisioning files - command: git clone --depth 1 git@github.com:grafana/plugin-provisioning.git - - run: - name: Prepare task output dir - command: | - mkdir ci # Avoid error if not exists - mkdir ci/jobs # Avoid error if not exists - mkdir ci/jobs/provisioning - mv plugin-provisioning/${PROVISIONING_REPO_PATH}/* ci/jobs/provisioning - - persist_to_workspace: - root: . - paths: - - ci/jobs/provisioning - - e2e_canary: - executor: e2e_exec - steps: - - run: - name: Continue if this plugin has a provisioning path - command: | - [ -z "${PROVISIONING_REPO_PATH}" ] && circleci-agent step halt - - checkout - - attach_workspace: - at: . - - restore_cache: - name: Restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - restore_cache: - name: Restore cypress cache - keys: - - cypress-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: Exit if no e2e tests configured - command: | - [ -d cypress ] || circleci-agent step halt - - run: - name: Setup Grafana (local install) - command: | - ginstall canary - /opt/grafana/bin/grafana-server -config ci/grafana-test-env/custom.ini -homepath /opt/grafana & - ## To make sure grafana has started up - while ! $(netstat -tulpn | grep 3000 >/dev/null 2>&1); do sleep 1; done - /opt/grafana/bin/grafana-cli --version - - run: - name: Copy provisioning files - command: cp -r ci/jobs/provisioning provisioning/ - - run: - name: Run e2e tests - command: | - # If the tests fail, but GRAFANACI_STRICT_E2E=0, don't worry about it - yarn run grafana-e2e run \ - || ( - [ "$GRAFANACI_STRICT_E2E" -eq 0 ] && echo "Bypassing fail. ci-nostrict enabled" - ) - - run: - when: always - name: Prepare task output dir - command: | - # TODO: probably move all of this to `@grafana/toolkit plugin:ci-test` - mkdir ci/jobs/e2e_canary - # only copy if they exist - cp cypress/screenshots/*.* ci/jobs/e2e_canary 2>/dev/null || : - cp cypress/videos/*.* ci/jobs/e2e_canary 2>/dev/null || : - - persist_to_workspace: - root: . - paths: - - ci/jobs/e2e_canary - - store_test_results: - path: ci/jobs/e2e_canary - - store_artifacts: - path: ci/jobs/e2e_canary - - report: - executor: default_exec - steps: - - checkout - - attach_workspace: - at: . - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: Toolkit report - command: | - yarn run grafana-toolkit plugin:ci-report - - store_artifacts: - path: ci - - publish_github_release: - executor: default_exec - steps: - - checkout - - add_ssh_keys: - fingerprints: - - "dc:60:ab:c7:2d:8c:82:50:2a:2a:97:1a:c0:66:83:14" - - attach_workspace: - at: . - - restore_cache: - name: restore yarn cache - keys: - - build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }} - - run: - name: "Publish Release on GitHub" - command: | - yarn run grafana-toolkit plugin:github-publish diff --git a/packages/grafana-toolkit/package.json b/packages/grafana-toolkit/package.json index 5637ed8f190..a7467ee05e3 100644 --- a/packages/grafana-toolkit/package.json +++ b/packages/grafana-toolkit/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/toolkit", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana Toolkit", "keywords": [ "grafana", @@ -30,98 +30,20 @@ "LICENSE_APACHE2" ], "scripts": { - "build": "yarn clean && tsc -p tsconfig.json && yarn copy-files", - "copy-files": "node ./scripts/copy-files.js", - "clean": "rimraf ./dist ./compiled ./sass ./package.tgz", + "build": "yarn clean && tsc -p tsconfig.json", + "clean": "rm -rf ./dist ./compiled ./sass ./package.tgz", "prepack": "mv ./src ./src_bak && cp -r ./dist/src ./src", - "postpack": "rimraf ./src && mv ./src_bak ./src", + "postpack": "rm -rf ./src && mv ./src_bak ./src", "typecheck": "tsc --noEmit" }, "main": "src/index.ts", "dependencies": { - "@babel/core": "7.18.9", - "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", - "@babel/plugin-proposal-object-rest-spread": "7.18.9", - "@babel/plugin-proposal-optional-chaining": "7.18.9", - "@babel/plugin-syntax-dynamic-import": "7.8.3", - "@babel/plugin-transform-react-constant-elements": "7.18.9", - "@babel/plugin-transform-runtime": "7.18.10", - "@babel/plugin-transform-typescript": "7.19.0", - "@babel/preset-env": "7.18.9", - "@babel/preset-react": "7.18.6", - "@babel/preset-typescript": "7.18.6", - "@grafana/data": "10.0.0-pre", - "@grafana/eslint-config": "5.1.0", "@grafana/tsconfig": "^1.2.0-rc1", - "@grafana/ui": "10.0.0-pre", - "@jest/core": "27.5.1", - "@types/command-exists": "^1.2.0", - "@types/eslint": "8.4.1", - "@types/fs-extra": "^9.0.13", - "@types/inquirer": "^8.2.1", - "@types/jest": "27.4.1", - "@types/lodash": "4.14.181", "@types/node": "16.11.26", - "@types/prettier": "2.6.3", - "@types/react-dev-utils": "9.0.10", - "@types/rimraf": "3.0.2", - "@types/semver": "7.3.9", - "@types/tmp": "0.2.3", - "@typescript-eslint/eslint-plugin": "5.36.2", - "@typescript-eslint/parser": "5.36.2", - "axios": "^0.26.1", - "babel-jest": "27.5.1", - "babel-loader": "^8.2.5", - "babel-plugin-angularjs-annotate": "0.10.0", "chalk": "^4.1.2", - "command-exists": "^1.2.9", "commander": "^9.2.0", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^6.7.1", - "css-minimizer-webpack-plugin": "^3.4.1", - "eslint": "8.20.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-jsdoc": "39.6.2", - "eslint-plugin-react": "7.29.4", - "eslint-plugin-react-hooks": "4.6.0", - "execa": "^5.1.1", - "fork-ts-checker-webpack-plugin": "^7.2.1", - "fs-extra": "^10.0.0", - "globby": "^11.0.4", - "html-loader": "^3.1.0", - "html-webpack-plugin": "^5.5.0", - "inquirer": "^8.2.2", - "jest": "27.5.1", - "jest-canvas-mock": "2.3.1", - "jest-junit": "13.1.0", - "less": "^4.1.2", - "less-loader": "^10.2.0", - "lodash": "^4.17.21", - "md5-file": "^5.0.0", - "mini-css-extract-plugin": "^2.6.0", - "ora": "^5.4.1", - "pixelmatch": "^5.2.1", - "pngjs": "^6.0.0", - "postcss": "^8.4.12", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-preset-env": "7.4.3", - "prettier": "2.7.1", - "react-dev-utils": "^12.0.0", - "replace-in-file-webpack-plugin": "1.0.6", - "rimraf": "3.0.2", - "sass": "^1.49.9", - "sass-loader": "^12.6.0", - "semver": "^7.3.7", - "simple-git": "^3.6.0", - "style-loader": "^3.3.1", - "terser-webpack-plugin": "^5.3.1", - "ts-jest": "27.1.3", - "ts-loader": "^9.3.1", "ts-node": "^9.1.0", - "tslib": "2.4.0", - "typescript": "4.8.2", - "webpack": "^5.76.0" + "tslib": "2.5.0", + "typescript": "4.8.2" } } diff --git a/packages/grafana-toolkit/scripts/copy-files.js b/packages/grafana-toolkit/scripts/copy-files.js deleted file mode 100644 index 8ebf3a81068..00000000000 --- a/packages/grafana-toolkit/scripts/copy-files.js +++ /dev/null @@ -1,38 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -function copyFiles(files, cwd, distDir) { - for (const file of files) { - const basedir = path.dirname(`${distDir}/${file}`); - const name = file.replace('.generated', ''); - if (!fs.existsSync(basedir)) { - fs.mkdirSync(basedir, { recursive: true }); - } - fs.copyFileSync(`${cwd}/${file}`, `${distDir}/${name}`); - } -} -const configFilesToCopy = [ - 'src/config/prettier.plugin.config.json', - 'src/config/prettier.plugin.rc.js', - 'src/config/tsconfig.plugin.json', - 'src/config/tsconfig.plugin.local.json', - 'src/config/eslint.plugin.js', - 'src/config/styles.mock.js', - 'src/config/jest.babel.config.js', - 'src/config/jest.plugin.config.local.js', - 'src/config/matchMedia.js', - 'src/config/react-inlinesvg.tsx', -]; -const sassFilesToCopy = [ - '_variables.generated.scss', - '_variables.dark.generated.scss', - '_variables.light.generated.scss', -]; - -const cwd = path.resolve(__dirname, '../'); -const distPath = path.resolve(cwd, 'dist'); -const sassPath = path.resolve(cwd, 'sass'); -const grafanaSassPath = path.resolve(cwd, '../../public/sass'); - -copyFiles(configFilesToCopy, cwd, distPath); -copyFiles(sassFilesToCopy, grafanaSassPath, sassPath); diff --git a/packages/grafana-toolkit/src/cli/index.ts b/packages/grafana-toolkit/src/cli/index.ts index f166b4ce3f7..a45ee3bdbb5 100644 --- a/packages/grafana-toolkit/src/cli/index.ts +++ b/packages/grafana-toolkit/src/cli/index.ts @@ -1,104 +1,19 @@ import chalk from 'chalk'; import { program } from 'commander'; -import { componentCreateTask } from './tasks/component.create'; -import { nodeVersionCheckerTask } from './tasks/nodeVersionChecker'; -import { buildPackageTask } from './tasks/package.build'; -import { pluginBuildTask } from './tasks/plugin.build'; -import { ciBuildPluginTask, ciPackagePluginTask, ciPluginReportTask } from './tasks/plugin.ci'; -import { pluginCreateTask } from './tasks/plugin.create'; -import { pluginDevTask } from './tasks/plugin.dev'; -import { pluginSignTask } from './tasks/plugin.sign'; -import { pluginTestTask } from './tasks/plugin.tests'; -import { pluginUpdateTask } from './tasks/plugin.update'; -import { getToolkitVersion, githubPublishTask } from './tasks/plugin.utils'; -import { bundleManagedTask } from './tasks/plugin/bundle.managed'; -import { searchTestDataSetupTask } from './tasks/searchTestDataSetup'; +import { getToolkitVersion } from './tasks/plugin.utils'; import { templateTask } from './tasks/template'; -import { toolkitBuildTask } from './tasks/toolkit.build'; import { execTask } from './utils/execTask'; export const run = (includeInternalScripts = false) => { if (includeInternalScripts) { program.option('-d, --depreciate ', 'Inform about npm script deprecation', (v) => v.split(',')); - program - .command('package:build') - .option('-s, --scope ', 'packages=[data|runtime|ui|toolkit|e2e|e2e-selectors]') - .description('Builds @grafana/* package to packages/grafana-*/dist') - .action(async (cmd) => { - console.warn( - '@grafana/toolkit package:build task is deprecated and will be removed in @grafana/toolkit@10.0.0.' - ); - await execTask(buildPackageTask)({ - scope: cmd.scope, - }); - }); - - program - .command('node-version-check') - .description('[deprecated] Verify node version') - .action(async () => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - console.log( - 'if you were reliant on this command we recommend https://www.npmjs.com/package/check-node-version' - ); - - await execTask(nodeVersionCheckerTask)({}); - }); - program .command('debug:template') .description('Just testing') - .action(async (cmd) => { - await execTask(templateTask)({}); - }); - - program - .command('toolkit:build') - .description('[Deprecated] Prepares grafana/toolkit dist package') - .action(async (cmd) => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - await execTask(toolkitBuildTask)({}); - }); - - program - .command('searchTestData') - .option('-c, --count ', 'Specify number of dashboards') - .description('[deprecated] Setup test data for search') - .action(async (cmd) => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - await execTask(searchTestDataSetupTask)({ count: cmd.count }); - }); - - // React generator - program - .command('component:create') - .description( - '[deprecated] Scaffold React components. Optionally add test, story and .mdx files. The components are created in the same dir the script is run from.' - ) .action(async () => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - console.log( - 'if you were reliant on this command we recommend https://www.npmjs.com/package/react-gen-component' - ); - await execTask(componentCreateTask)({}); + await execTask(templateTask)({}); }); } @@ -109,13 +24,12 @@ export const run = (includeInternalScripts = false) => { program .command('plugin:create [name]') - .description('[Deprecated] Creates plugin from template') - .action(async (cmd) => { - console.log(chalk.yellow('\n⚠️ DEPRECATED. This command is deprecated and will be removed in v10. ⚠️')); + .description('[removed] Use grafana create-plugin instead') + .action(async () => { console.log( - 'Please migrate to grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n' + 'No longer supported. Use grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n' ); - await execTask(pluginCreateTask)({ name: cmd, silent: true }); + process.exit(1); }); program @@ -125,63 +39,12 @@ export const run = (includeInternalScripts = false) => { .option('--skipTest', 'Skip running tests (for pipelines that run it separate)', false) .option('--skipLint', 'Skip running lint (for pipelines that run it separate)', false) .option('--preserveConsole', 'Preserves console calls', false) - .description('[Deprecated] Prepares plugin dist package') - .action(async (cmd) => { - console.log(chalk.yellow('\n⚠️ DEPRECATED. This command is deprecated and will be removed in v10. ⚠️')); + .description('[removed] Use grafana create-plugin instead') + .action(async () => { console.log( - 'Please migrate to grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n' + 'No longer supported. Use grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n' ); - - await execTask(pluginBuildTask)({ - coverage: cmd.coverage, - silent: true, - maxJestWorkers: cmd.maxJestWorkers, - preserveConsole: cmd.preserveConsole, - skipLint: cmd.skipLint, - skipTest: cmd.skipTest, - }); - }); - - program - .command('plugin:dev') - .option('-w, --watch', 'Run plugin development mode with watch enabled') - .description('[Deprecated] Starts plugin dev mode') - .action(async (cmd) => { - console.log(chalk.yellow('\n⚠️ DEPRECATED. This command is deprecated and will be removed in v10. ⚠️')); - console.log( - 'Please migrate to grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n' - ); - - await execTask(pluginDevTask)({ - watch: !!cmd.watch, - silent: true, - }); - }); - - program - .command('plugin:test') - .option('-u, --updateSnapshot', 'Run snapshots update') - .option('--coverage', 'Run code coverage') - .option('--watch', 'Run tests in interactive watch mode') - .option('--testPathPattern ', 'Run only tests with a path that matches the regex') - .option('--testNamePattern ', 'Run only tests with a name that matches the regex') - .option('--maxWorkers |', 'Limit number of workers spawned') - .description('[Deprecated] Executes plugin tests') - .action(async (cmd) => { - console.log(chalk.yellow('\n⚠️ DEPRECATED. This command is deprecated and will be removed in v10. ⚠️')); - console.log( - 'Please migrate to grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n' - ); - - await execTask(pluginTestTask)({ - updateSnapshot: !!cmd.updateSnapshot, - coverage: !!cmd.coverage, - watch: !!cmd.watch, - testPathPattern: cmd.testPathPattern, - testNamePattern: cmd.testNamePattern, - maxWorkers: cmd.maxWorkers, - silent: true, - }); + process.exit(1); }); program @@ -202,96 +65,12 @@ export const run = (includeInternalScripts = false) => { }, [] ) - .description('[Deprecated] Create a plugin signature') - .action(async (cmd) => { + .description('[removed] Use grafana sign-plugin instead') + .action(() => { console.log( - chalk.yellow('\n⚠️ DEPRECATED. This command is deprecated and will be removed in v10. ⚠️') + - '\nPlease migrate to grafana sign-plugin https://github.com/grafana/plugin-tools/tree/main/packages/sign-plugin' + 'No longer supported. Use grafana sign-plugin https://github.com/grafana/plugin-tools/tree/main/packages/sign-plugin\n' ); - await execTask(pluginSignTask)({ - signatureType: cmd.signatureType, - rootUrls: cmd.rootUrls, - silent: true, - }); - }); - - program - .command('plugin:ci-build') - .option('--finish', 'move all results to the jobs folder', false) - .option('--maxJestWorkers |', 'Limit number of Jest workers spawned') - .description('[deprecated] Build the plugin, leaving results in /dist and /coverage') - .action(async (cmd) => { - await execTask(ciBuildPluginTask)({ - finish: cmd.finish, - maxJestWorkers: cmd.maxJestWorkers, - }); - }); - - program - .command('plugin:ci-package') - .option('--signatureType ', 'Signature Type') - .option('--rootUrls ', 'Root URLs') - .option('--signing-admin', 'Use the admin API endpoint for signing the manifest. (deprecated)', false) - .description('[deprecated] Create a zip packages for the plugin') - .action(async (cmd) => { - await execTask(ciPackagePluginTask)({ - signatureType: cmd.signatureType, - rootUrls: cmd.rootUrls, - }); - }); - - program - .command('plugin:ci-report') - .description('[deprecated] Build a report for this whole process') - .option('--upload', 'upload packages also') - .action(async (cmd) => { - await execTask(ciPluginReportTask)({ - upload: cmd.upload, - }); - }); - - program - .command('plugin:bundle-managed') - .description('[Deprecated] Builds managed plugins') - .action(async (cmd) => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - await execTask(bundleManagedTask)({}); - }); - - program - .command('plugin:github-publish') - .option('--dryrun', 'Do a dry run only', false) - .option('--verbose', 'Print verbose', false) - .option('--commitHash ', 'Specify the commit hash') - .description('[Deprecated] Publish to github') - .action(async (cmd) => { - console.log( - chalk.yellow.bold(`⚠️ This command is deprecated and will be removed . No further support will be provided. ⚠️`) - ); - console.log( - 'We recommend using github actions directly for plugin releasing. You can find an example here: https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin/templates/github/ci/.github/workflows' - ); - await execTask(githubPublishTask)({ - dryrun: cmd.dryrun, - verbose: cmd.verbose, - commitHash: cmd.commitHash, - }); - }); - - program - .command('plugin:update-circleci') - .description('[Deprecated] Update plugin') - .action(async (cmd) => { - console.log( - chalk.yellow.bold( - `⚠️ This command is deprecated and will be removed in v10. No further support will be provided. ⚠️` - ) - ); - await execTask(pluginUpdateTask)({}); + process.exit(1); }); program.on('command:*', () => { diff --git a/packages/grafana-toolkit/src/cli/tasks/component.create.ts b/packages/grafana-toolkit/src/cli/tasks/component.create.ts deleted file mode 100644 index 311c4223a82..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/component.create.ts +++ /dev/null @@ -1,88 +0,0 @@ -import fs from 'fs'; -import { prompt } from 'inquirer'; -import { template as _template } from 'lodash'; - -import { componentTpl, docsTpl, storyTpl, testTpl } from '../templates'; -import { pascalCase } from '../utils/pascalCase'; -import { promptConfirm, promptInput, promptList } from '../utils/prompt'; - -import { Task, TaskRunner } from './task'; - -interface Details { - name?: string; - hasStory: boolean; - group?: string; - isStoryPublic: boolean; - hasTests: boolean; -} - -interface GeneratorOptions { - details: Details; - path: string; -} - -type ComponentGenerator = (options: GeneratorOptions) => Promise; - -const componentGroups = [ - { name: 'General', value: 'General' }, - { name: 'Forms', value: 'Forms' }, - { name: 'Panel', value: 'Panel' }, - { name: 'Visualizations', value: 'Visualizations' }, - { name: 'Others', value: 'Others' }, -]; - -export const promptDetails = () => { - return prompt
    ([ - promptInput('name', 'Component name', true), - promptConfirm('hasTests', "Generate component's test file?"), - promptConfirm('hasStory', "Generate component's story file?"), - promptConfirm( - 'isStoryPublic', - 'Generate public story? (Selecting "No" will create an internal story)', - true, - ({ hasStory }) => hasStory - ), - promptList( - 'group', - 'Select component group for the story (e.g. Forms, Layout)', - () => componentGroups, - 0, - ({ hasStory }) => hasStory - ), - ]); -}; - -export const generateComponents: ComponentGenerator = async ({ details, path }) => { - const name = pascalCase(details.name); - const getCompiled = (template: string) => { - return _template(template)({ ...details, name }); - }; - const filePath = `${path}/${name}`; - let paths = []; - - fs.writeFileSync(`${filePath}.tsx`, getCompiled(componentTpl)); - paths.push(`${filePath}.tsx`); - - if (details.hasTests) { - fs.writeFileSync(`${filePath}.test.tsx`, getCompiled(testTpl)); - paths.push(`${filePath}.test.tsx`); - } - - if (details.hasStory) { - const storyExt = details.isStoryPublic ? '.story.tsx' : '.story.internal.tsx'; - fs.writeFileSync(`${filePath}${storyExt}`, getCompiled(storyTpl)); - fs.writeFileSync(`${filePath}.mdx`, getCompiled(docsTpl)); - paths.push(`${filePath}${storyExt}`, `${filePath}.mdx`); - } - - console.log('Generated files:'); - console.log(paths.join('\n')); -}; - -const componentCreateRunner: TaskRunner = async () => { - const destPath = process.cwd(); - const details = await promptDetails(); - await generateComponents({ details, path: destPath }); -}; - -export const componentCreateTask = new Task('component:create', componentCreateRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts b/packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts deleted file mode 100644 index d7bcbe0ff8e..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts +++ /dev/null @@ -1,77 +0,0 @@ -import chalk from 'chalk'; -import { readFileSync } from 'fs'; -import { coerce, satisfies } from 'semver'; - -import { Task, TaskRunner } from './task'; - -interface FailedVersionCheck { - file: string; - line: string; -} - -interface NodeVersionCheckerOptions {} - -const pattern = /(circleci\/|FROM )node\:([0-9]+(\.[0-9]+){0,2})/gm; -const packageJsonFile = 'package.json'; - -const failures: FailedVersionCheck[] = []; - -export const nodeVersionFiles = [packageJsonFile, 'Dockerfile']; - -const nodeVersionCheckerRunner: TaskRunner = async () => { - // Read version from package json and treat that as the expected version in all other locations - const packageJson = require(`${process.cwd()}/${packageJsonFile}`); - const expectedVersion = packageJson.engines.node; - - console.log(chalk.yellow(`Specified node version in package.json is: ${expectedVersion}`)); - - for (const file of nodeVersionFiles) { - const fileContent = readFileSync(`${process.cwd()}/${file}`); - const matches = fileContent.toString('utf8').match(pattern); - - if (!matches) { - continue; - } - - for (const match of matches) { - const actualVersion = coerce(match); - if (!actualVersion) { - failures.push({ - file, - line: match, - }); - continue; - } - - const satisfied = satisfies(actualVersion, expectedVersion); - if (!satisfied) { - failures.push({ - file, - line: match, - }); - } - } - } - - if (failures.length > 0) { - console.log(chalk.red('--------------------------------------------------------------------')); - console.log(chalk.red(`These entries don't satisfy the engine version in ${packageJsonFile}`)); - console.log(chalk.red('--------------------------------------------------------------------')); - - for (let index = 0; index < failures.length; index++) { - const failure = failures[index]; - console.log(chalk.green(`\tIn ${failure.file} the line ${failure.line} does not satisfy ${expectedVersion}.`)); - } - - throw new Error('Node versions not in sync'); - } - - console.log(chalk.yellow('--------------------------------------------------------------------')); - console.log(chalk.yellow('All node versions seem ok.')); - console.log(chalk.yellow('--------------------------------------------------------------------')); -}; - -export const nodeVersionCheckerTask = new Task( - 'Node Version Checker', - nodeVersionCheckerRunner -); diff --git a/packages/grafana-toolkit/src/cli/tasks/package.build.ts b/packages/grafana-toolkit/src/cli/tasks/package.build.ts deleted file mode 100644 index 844cc76e798..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/package.build.ts +++ /dev/null @@ -1,92 +0,0 @@ -import chalk from 'chalk'; -import execa = require('execa'); -import { promises as fs } from 'fs'; -import globby from 'globby'; -import { cloneDeep } from 'lodash'; -import * as path from 'path'; - -import { useSpinner } from '../utils/useSpinner'; - -import { Task, TaskRunner } from './task'; - -const clean = (cwd: string) => useSpinner('Cleaning', () => execa('npm', ['run', 'clean'], { cwd })); - -const compile = (cwd: string) => - useSpinner('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json'], { cwd })); - -const bundle = (cwd: string) => useSpinner('Bundling', () => execa('npm', ['run', 'bundle'], { cwd })); - -const preparePackage = async (packageDist: string, pkg: any) => { - pkg = cloneDeep(pkg); // avoid mutations - - pkg.main = 'index.js'; - pkg.types = 'index.d.ts'; - - const version: string = pkg.version; - const name: string = pkg.name; - const deps: any = pkg.dependencies; - - // Below we are adding cross-dependencies to Grafana's packages - // with the version being published - if (name.endsWith('/ui')) { - deps['@grafana/data'] = version; - } else if (name.endsWith('/runtime')) { - deps['@grafana/data'] = version; - deps['@grafana/ui'] = version; - } else if (name.endsWith('/toolkit')) { - deps['@grafana/data'] = version; - deps['@grafana/ui'] = version; - } - - await useSpinner('Updating package.json', () => - fs.writeFile(`${packageDist}/package.json`, JSON.stringify(pkg, null, 2)) - ); -}; - -const moveFiles = (fromPath: string, toPath: string) => { - const files = ['README.md', 'CHANGELOG.md', 'index.js']; - - return useSpinner(`Moving ${files.join(', ')} files`, () => { - const promises = files.map((file) => fs.copyFile(`${fromPath}/${file}`, `${toPath}/${file}`)); - return Promise.all(promises); - }); -}; - -const moveStaticFiles = async (packageRoot: string, pkg: any) => { - if (pkg.name.endsWith('/ui')) { - return useSpinner('Moving static files', async () => { - const staticFiles = await globby(`${packageRoot}/src/**/*.{png,svg,gif,jpg}`); - const pathSearch = new RegExp(`^${packageRoot}/src`); - const pathReplace = `${packageRoot}/compiled`; - const promises = staticFiles.map((file) => fs.copyFile(file, file.replace(pathSearch, pathReplace))); - await Promise.all(promises); - }); - } -}; - -interface PackageBuildOptions { - scope: string; -} - -const buildTaskRunner: TaskRunner = async ({ scope }) => { - if (!scope) { - throw new Error('Provide packages with -s, --scope '); - } - - const scopes = scope.split(',').map(async (s) => { - const packageRoot = path.resolve(__dirname, `../../../../grafana-${s}`); - const packageDist = `${packageRoot}/dist`; - const pkg = require(`${packageRoot}/package.json`); - console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`)); - await clean(packageRoot); - await compile(packageRoot); - await moveStaticFiles(packageRoot, pkg); - await bundle(packageRoot); - await preparePackage(packageDist, pkg); - await moveFiles(packageRoot, packageDist); - }); - - await Promise.all(scopes); -}; - -export const buildPackageTask = new Task('Package build', buildTaskRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.build.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.build.ts deleted file mode 100644 index beacb204b58..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.build.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { ESLint } from 'eslint'; -import execa from 'execa'; -import { constants as fsConstants, promises as fs } from 'fs'; -import globby from 'globby'; -import { resolve as resolvePath } from 'path'; -import rimrafCallback from 'rimraf'; -import { promisify } from 'util'; - -import { useSpinner } from '../utils/useSpinner'; - -import { bundlePlugin as bundleFn, PluginBundleOptions } from './plugin/bundle'; -import { testPlugin } from './plugin/tests'; -import { Task, TaskRunner } from './task'; - -const { access, copyFile } = fs; -const { COPYFILE_EXCL } = fsConstants; -const rimraf = promisify(rimrafCallback); - -interface PluginBuildOptions { - coverage: boolean; - maxJestWorkers?: string; - preserveConsole?: boolean; - skipTest?: boolean; - skipLint?: boolean; -} - -interface Fixable { - fix?: boolean; -} - -const bundlePlugin = (options: PluginBundleOptions) => useSpinner('Compiling...', () => bundleFn(options)); - -// @ts-ignore -const clean = () => useSpinner('Cleaning', () => rimraf(`${process.cwd()}/dist`)); - -const copyIfNonExistent = (srcPath: string, destPath: string) => - copyFile(srcPath, destPath, COPYFILE_EXCL) - .then(() => console.log(`Created: ${destPath}`)) - .catch((error) => { - if (error.code !== 'EEXIST') { - throw error; - } - }); - -export const prepare = () => - useSpinner('Preparing', () => - Promise.all([ - // Remove local dependencies for @grafana/data/node_modules - // See: https://github.com/grafana/grafana/issues/26748 - rimraf(resolvePath(__dirname, 'node_modules/@grafana/data/node_modules')), - // Copy only if local tsconfig does not exist. Otherwise this will work, but have odd behavior - copyIfNonExistent( - resolvePath(__dirname, '../../config/tsconfig.plugin.local.json'), - resolvePath(process.cwd(), 'tsconfig.json') - ), - // Copy only if local prettierrc does not exist. Otherwise this will work, but have odd behavior - copyIfNonExistent( - resolvePath(__dirname, '../../config/prettier.plugin.rc.js'), - resolvePath(process.cwd(), '.prettierrc.js') - ), - ]) - ); - -export const versions = async () => { - try { - const nodeVersion = await execa('node', ['--version']); - console.log(`Using Node.js ${nodeVersion.stdout}`); - - const toolkitVersion = await execa('grafana-toolkit', ['--version']); - console.log(`Using @grafana/toolkit ${toolkitVersion.stdout}`); - } catch (err) { - console.log(`Error reading versions`, err); - } -}; - -// @ts-ignore -const typecheckPlugin = () => useSpinner('Typechecking', () => execa('tsc', ['--noEmit'])); - -// @ts-ignore -const getStylesSources = () => globby(resolvePath(process.cwd(), 'src/**/*.+(scss|css)')); - -export const lintPlugin = ({ fix }: Fixable = {}) => - useSpinner('Linting', async () => { - try { - // Show a warning if the tslint file exists - await access(resolvePath(process.cwd(), 'tslint.json')); - console.log('\n'); - console.log('--------------------------------------------------------------'); - console.log('NOTE: @grafana/toolkit has migrated to use eslint'); - console.log('Update your configs to use .eslintrc rather than tslint.json'); - console.log('--------------------------------------------------------------'); - } catch { - // OK: tslint does not exist - } - - // @todo should remove this because the config file could be in a parent dir or within package.json - const configFile = await globby(resolvePath(process.cwd(), '.eslintrc?(.cjs|.js|.json|.yaml|.yml)')).then( - (filePaths) => { - if (filePaths.length > 0) { - return filePaths[0]; - } else { - return resolvePath(__dirname, '../../config/eslint.plugin.js'); - } - } - ); - - const eslint = new ESLint({ - extensions: ['.ts', '.tsx'], - overrideConfigFile: configFile, - fix, - useEslintrc: false, - }); - - const results = await eslint.lintFiles(resolvePath(process.cwd(), 'src')); - - if (fix) { - await ESLint.outputFixes(results); - } - - const { errorCount, warningCount } = results.reduce>( - (acc, value) => { - acc.errorCount += value.errorCount; - acc.warningCount += value.warningCount; - return acc; - }, - { errorCount: 0, warningCount: 0 } - ); - - const formatter = await eslint.loadFormatter('stylish'); - const resultText = formatter.format(results); - - if (errorCount > 0 || warningCount > 0) { - console.log('\n'); - console.log(resultText); - console.log('\n'); - } - - if (errorCount > 0) { - throw new Error(`${errorCount} linting errors found in ${results.length} files`); - } - }); - -export const pluginBuildRunner: TaskRunner = async ({ - coverage, - maxJestWorkers, - preserveConsole, - skipTest, - skipLint, -}) => { - await versions(); - await prepare(); - if (!skipLint) { - await lintPlugin({ fix: false }); - } - if (!skipTest) { - await testPlugin({ updateSnapshot: false, coverage, maxWorkers: maxJestWorkers, watch: false }); - } - await bundlePlugin({ watch: false, production: true, preserveConsole }); -}; - -export const pluginBuildTask = new Task('Build plugin', pluginBuildRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts deleted file mode 100644 index 53078e4185e..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts +++ /dev/null @@ -1,258 +0,0 @@ -import execa = require('execa'); -import fs from 'fs-extra'; -import path = require('path'); -import rimrafCallback from 'rimraf'; -import { promisify } from 'util'; - -import { getPluginId } from '../../config/utils/getPluginId'; -import { assertRootUrlIsValid, getPluginJson } from '../../config/utils/pluginValidation'; -import { - getJobFolder, - writeJobStats, - getCiFolder, - getPluginBuildInfo, - getPullRequestNumber, - getCircleDownloadBaseURL, -} from '../../plugins/env'; -import { buildManifest, signManifest, saveManifest } from '../../plugins/manifest'; -import { PluginPackageDetails, PluginBuildReport } from '../../plugins/types'; -import { getPackageDetails, getGrafanaVersions, readGitLog } from '../../plugins/utils'; -import { agregateWorkflowInfo, agregateCoverageInfo, agregateTestInfo } from '../../plugins/workflow'; - -import { pluginBuildRunner } from './plugin.build'; -import { Task, TaskRunner } from './task'; -const rimraf = promisify(rimrafCallback); - -export interface PluginCIOptions { - finish?: boolean; - upload?: boolean; - signatureType?: string; - rootUrls?: string[]; - maxJestWorkers?: string; -} - -/** - * 1. BUILD - * - * when platform exists it is building backend, otherwise frontend - * - * Each build writes data: - * ~/ci/jobs/build_xxx/ - * - * Anything that should be put into the final zip file should be put in: - * ~/ci/jobs/build_xxx/dist - * - * @deprecated -- this task was written with a specific circle-ci build in mind. That system - * has been replaced with Drone, and this is no longer the best practice. Any new work - * should be defined in the grafana build pipeline tool or drone configs directly. - */ -const buildPluginRunner: TaskRunner = async ({ finish, maxJestWorkers }) => { - const start = Date.now(); - - if (finish) { - const workDir = getJobFolder(); - await rimraf(workDir); - fs.mkdirSync(workDir); - - // Move local folders to the scoped job folder - for (const name of ['dist', 'coverage']) { - const dir = path.resolve(process.cwd(), name); - if (fs.existsSync(dir)) { - fs.moveSync(dir, path.resolve(workDir, name)); - } - } - writeJobStats(start, workDir); - } else { - // Do regular build process with coverage - await pluginBuildRunner({ coverage: true, maxJestWorkers }); - } -}; - -export const ciBuildPluginTask = new Task('Build Plugin', buildPluginRunner); - -/** - * 2. Package - * - * Take everything from `~/ci/job/{any}/dist` and - * 1. merge it into: `~/ci/dist` - * 2. zip it into packages in `~/ci/packages` - * 3. prepare grafana environment in: `~/ci/grafana-test-env` - * - * - * @deprecated -- this task was written with a specific circle-ci build in mind. That system - * has been replaced with Drone, and this is no longer the best practice. Any new work - * should be defined in the grafana build pipeline tool or drone configs directly. - */ -const packagePluginRunner: TaskRunner = async ({ signatureType, rootUrls }) => { - const start = Date.now(); - const ciDir = getCiFolder(); - const packagesDir = path.resolve(ciDir, 'packages'); - const distDir = path.resolve(ciDir, 'dist'); - const docsDir = path.resolve(ciDir, 'docs'); - const jobsDir = path.resolve(ciDir, 'jobs'); - - fs.exists(jobsDir, (jobsDirExists) => { - if (!jobsDirExists) { - throw new Error('You must run plugin:ci-build prior to running plugin:ci-package'); - } - }); - - const grafanaEnvDir = path.resolve(ciDir, 'grafana-test-env'); - await execa('rimraf', [packagesDir, distDir, grafanaEnvDir]); - fs.mkdirSync(packagesDir); - fs.mkdirSync(distDir); - - // Updating the dist dir to have a pluginId named directory in it - // The zip needs to contain the plugin code wrapped in directory with a pluginId name - const distContentDir = path.resolve(distDir, getPluginId()); - fs.mkdirSync(grafanaEnvDir); - - console.log('Build Dist Folder'); - - // 1. Check for a local 'dist' folder - const d = path.resolve(process.cwd(), 'dist'); - if (fs.existsSync(d)) { - await execa('cp', ['-rn', d + '/.', distContentDir]); - } - - // 2. Look for any 'dist' folders under ci/job/XXX/dist - const dirs = fs.readdirSync(path.resolve(ciDir, 'jobs')); - for (const j of dirs) { - const contents = path.resolve(ciDir, 'jobs', j, 'dist'); - if (fs.existsSync(contents)) { - try { - await execa('cp', ['-rn', contents + '/.', distContentDir]); - } catch (er) { - throw new Error('Duplicate files found in dist folders'); - } - } - } - - console.log('Save the source info in plugin.json'); - const pluginJsonFile = path.resolve(distContentDir, 'plugin.json'); - const pluginInfo = getPluginJson(pluginJsonFile); - pluginInfo.info.build = await getPluginBuildInfo(); - fs.writeFileSync(pluginJsonFile, JSON.stringify(pluginInfo, null, 2), { encoding: 'utf-8' }); - - // Write a MANIFEST.txt file in the dist folder - try { - const manifest = await buildManifest(distContentDir); - if (signatureType) { - manifest.signatureType = signatureType; - } - if (rootUrls && rootUrls.length > 0) { - rootUrls.forEach(assertRootUrlIsValid); - manifest.rootUrls = rootUrls; - } - const signedManifest = await signManifest(manifest); - await saveManifest(distContentDir, signedManifest); - } catch (err) { - console.warn(`Error signing manifest: ${distContentDir}`, err); - } - - console.log('Building ZIP'); - let zipName = pluginInfo.id + '-' + pluginInfo.info.version + '.zip'; - let zipFile = path.resolve(packagesDir, zipName); - await execa('zip', ['-r', zipFile, '.'], { cwd: distDir }); - - const zipStats = fs.statSync(zipFile); - if (zipStats.size < 100) { - throw new Error('Invalid zip file: ' + zipFile); - } - - // Make a copy so it is easy for report to read - await execa('cp', [pluginJsonFile, distDir]); - - const info: PluginPackageDetails = { - plugin: await getPackageDetails(zipFile, distDir), - }; - - console.log('Setup Grafana Environment'); - let p = path.resolve(grafanaEnvDir, 'plugins', pluginInfo.id); - fs.mkdirSync(p, { recursive: true }); - await execa('unzip', [zipFile, '-d', p]); - - // If docs exist, zip them into packages - if (fs.existsSync(docsDir)) { - console.log('Creating documentation zip'); - zipName = pluginInfo.id + '-' + pluginInfo.info.version + '-docs.zip'; - zipFile = path.resolve(packagesDir, zipName); - await execa('zip', ['-r', zipFile, '.'], { cwd: docsDir }); - - info.docs = await getPackageDetails(zipFile, docsDir); - } - - p = path.resolve(packagesDir, 'info.json'); - fs.writeFileSync(p, JSON.stringify(info, null, 2), { encoding: 'utf-8' }); - - // Write the custom settings - p = path.resolve(grafanaEnvDir, 'custom.ini'); - const customIniBody = - `# Autogenerated by @grafana/toolkit \n` + - `[paths] \n` + - `plugins = ${path.resolve(grafanaEnvDir, 'plugins')}\n` + - `\n`; // empty line - fs.writeFileSync(p, customIniBody, { encoding: 'utf-8' }); - - writeJobStats(start, getJobFolder()); -}; - -export const ciPackagePluginTask = new Task('Bundle Plugin', packagePluginRunner); - -/** - * 4. Report - * - * Create a report from all the previous steps - * - * @deprecated -- this task was written with a specific circle-ci build in mind. That system - * has been replaced with Drone, and this is no longer the best practice. Any new work - * should be defined in the grafana build pipeline tool or drone configs directly. - */ -const pluginReportRunner: TaskRunner = async ({ upload }) => { - const ciDir = path.resolve(process.cwd(), 'ci'); - const packageDir = path.resolve(ciDir, 'packages'); - const packageInfo = require(path.resolve(packageDir, 'info.json')) as PluginPackageDetails; - - const pluginJsonFile = path.resolve(ciDir, 'dist', 'plugin.json'); - console.log('Load info from: ' + pluginJsonFile); - - const pluginMeta = getPluginJson(pluginJsonFile); - const report: PluginBuildReport = { - plugin: pluginMeta, - packages: packageInfo, - workflow: agregateWorkflowInfo(), - coverage: agregateCoverageInfo(), - tests: agregateTestInfo(), - artifactsBaseURL: await getCircleDownloadBaseURL(), - grafanaVersion: getGrafanaVersions(), - git: await readGitLog(), - }; - const pr = getPullRequestNumber(); - if (pr) { - report.pullRequest = pr; - } - - // Save the report to disk - const file = path.resolve(ciDir, 'report.json'); - fs.writeFileSync(file, JSON.stringify(report, null, 2), { encoding: 'utf-8' }); - - const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; - if (!GRAFANA_API_KEY) { - console.log('Enter a GRAFANA_API_KEY to upload the plugin report'); - return; - } - const url = `https://grafana.com/api/plugins/${report.plugin.id}/ci`; - - console.log('Sending report to:', url); - const axios = require('axios'); - const info = await axios.post(url, report, { - headers: { Authorization: 'Bearer ' + GRAFANA_API_KEY }, - }); - if (info.status === 200) { - console.log('OK: ', info.data); - } else { - console.warn('Error: ', info); - } -}; - -export const ciPluginReportTask = new Task('Generate Plugin Report', pluginReportRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.create.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.create.ts deleted file mode 100644 index b276b869716..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.create.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { prompt } from 'inquirer'; -import path from 'path'; - -import { promptConfirm } from '../utils/prompt'; - -import { - fetchTemplate, - formatPluginDetails, - getPluginIdFromName, - prepareJsonFiles, - printGrafanaTutorialsDetails, - promptPluginDetails, - promptPluginType, - removeGitFiles, - verifyGitExists, - removeLockFile, -} from './plugin/create'; -import { Task, TaskRunner } from './task'; - -interface PluginCreateOptions { - name?: string; -} - -const pluginCreateRunner: TaskRunner = async ({ name }) => { - const destPath = path.resolve(process.cwd(), getPluginIdFromName(name || '')); - let pluginDetails; - - // 1. Verifying if git exists in user's env as templates are cloned from git templates - await verifyGitExists(); - - // 2. Prompt plugin template - const { type } = await promptPluginType(); - - // 3. Fetch plugin template from GitHub - await fetchTemplate({ type, dest: destPath }); - - // 4. Prompt plugin details - do { - pluginDetails = await promptPluginDetails(name); - formatPluginDetails(pluginDetails); - } while ((await prompt<{ confirm: boolean }>(promptConfirm('confirm', 'Is that ok?'))).confirm === false); - - // 5. Update json files (package.json, src/plugin.json) - await prepareJsonFiles({ type: type, pluginDetails, pluginPath: destPath }); - - // 6. Starter templates include `yarn.lock` files which will rarely (if ever) be in sync with `latest` dist-tag - // so best to remove it after cloning. - removeLockFile({ pluginPath: destPath }); - - // 7. Remove cloned repository .git dir - await removeGitFiles(destPath); - - // 8. Promote Grafana Tutorials :) - printGrafanaTutorialsDetails(type); -}; - -export const pluginCreateTask = new Task('plugin:create task', pluginCreateRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.dev.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.dev.ts deleted file mode 100644 index 78f363aa827..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.dev.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useSpinner } from '../utils/useSpinner'; - -import { lintPlugin } from './plugin.build'; -import { bundlePlugin as bundleFn, PluginBundleOptions } from './plugin/bundle'; -import { Task, TaskRunner } from './task'; - -const bundlePlugin = (options: PluginBundleOptions) => - useSpinner('Bundling plugin in dev mode', () => bundleFn(options)); - -const pluginDevRunner: TaskRunner = async (options) => { - if (options.watch) { - await bundleFn(options); - } else { - // Always fix lint in dev mode - await lintPlugin({ fix: true }); - - const result = await bundlePlugin(options); - return result; - } -}; - -export const pluginDevTask = new Task('Dev plugin', pluginDevRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.sign.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.sign.ts deleted file mode 100644 index bec898243f1..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.sign.ts +++ /dev/null @@ -1,42 +0,0 @@ -import path from 'path'; - -import { assertRootUrlIsValid } from '../../config/utils/pluginValidation'; -import { buildManifest, signManifest, saveManifest } from '../../plugins/manifest'; - -import { getToolkitVersion } from './plugin.utils'; -import { Task, TaskRunner } from './task'; -interface PluginSignOptions { - signatureType?: string; - rootUrls?: string[]; -} - -const pluginSignRunner: TaskRunner = async ({ signatureType, rootUrls }) => { - const distContentDir = path.resolve('dist'); - - try { - console.log('Building manifest...'); - const manifest = await buildManifest(distContentDir); - // console.log(manifest); - - console.log('Signing manifest...'); - if (signatureType) { - manifest.signatureType = signatureType; - } - if (rootUrls && rootUrls.length > 0) { - rootUrls.forEach(assertRootUrlIsValid); - manifest.rootUrls = rootUrls; - } - - manifest.toolkit = { version: getToolkitVersion() }; - const signedManifest = await signManifest(manifest); - - console.log('Saving signed manifest...'); - await saveManifest(distContentDir, signedManifest); - - console.log('Signed successfully'); - } catch (err) { - console.warn(err); - } -}; - -export const pluginSignTask = new Task('plugin:sign task', pluginSignRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.tests.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.tests.ts deleted file mode 100644 index ff75d91d1d6..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.tests.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { testPlugin, PluginTestOptions } from './plugin/tests'; -import { Task, TaskRunner } from './task'; - -const pluginTestRunner: TaskRunner = async (options) => { - await testPlugin(options); -}; - -export const pluginTestTask = new Task('Test plugin', pluginTestRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.update.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.update.ts deleted file mode 100644 index fee3286b395..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.update.ts +++ /dev/null @@ -1,24 +0,0 @@ -import fs = require('fs'); -import path = require('path'); - -import { useSpinner } from '../utils/useSpinner'; - -import { Task, TaskRunner } from './task'; - -interface UpdatePluginTask {} - -const updateCiConfig = () => - useSpinner('Updating CircleCI config', async () => { - const ciConfigPath = path.join(process.cwd(), '.circleci'); - if (!fs.existsSync(ciConfigPath)) { - fs.mkdirSync(ciConfigPath); - } - - const sourceFile = require.resolve('@grafana/toolkit/config/circleci/config.yml'); - const destFile = path.join(ciConfigPath, 'config.yml'); - fs.copyFileSync(sourceFile, destFile); - }); - -const pluginUpdateRunner: TaskRunner = () => updateCiConfig(); - -export const pluginUpdateTask = new Task('Update Plugin', pluginUpdateRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts b/packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts index 7852bd1d8b6..78254f43065 100644 --- a/packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts +++ b/packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts @@ -1,147 +1,4 @@ -import execa = require('execa'); import { readFileSync } from 'fs'; -import path = require('path'); - -import { getPluginId } from '../../config/utils/getPluginId'; -import { getPluginJson } from '../../config/utils/pluginValidation'; -import { getCiFolder } from '../../plugins/env'; -import { GitHubRelease } from '../utils/githubRelease'; -import { useSpinner } from '../utils/useSpinner'; - -import { Task, TaskRunner } from './task'; - -interface Command extends Array {} -const DEFAULT_EMAIL_ADDRESS = 'eng@grafana.com'; -const DEFAULT_USERNAME = 'CircleCI Automation'; - -const releaseNotes = async (): Promise => { - const { stdout } = await execa(`awk 'BEGIN {FS="##"; RS="##"} FNR==3 {print "##" $1; exit}' CHANGELOG.md`, { - shell: true, - }); - return stdout; -}; - -const checkoutBranch = async (branchName: string): Promise => { - const currentBranch = await execa(`git rev-parse --abbrev-ref HEAD`, { shell: true }); - const branchesAvailable = await execa( - `(git branch -a | grep "${branchName}$" | grep -v remote) || echo 'No release found'`, - { shell: true } - ); - - if (currentBranch.stdout !== branchName) { - console.log('available', branchesAvailable.stdout.trim()); - if (branchesAvailable.stdout.trim() === branchName) { - return ['git', ['checkout', branchName]]; - } else { - return ['git', ['checkout', '-b', branchName]]; - } - } - return []; -}; - -const gitUrlParse = (url: string): { owner: string; name: string } => { - let matchResult: RegExpMatchArray | null = []; - - if (url.match(/^git@github.com/)) { - // We have an ssh style url. - matchResult = url.match(/^git@github.com:(.*?)\/(.*?)\.git/); - } - - if (url.match(/^https:\/\/github.com\//)) { - // We have an https style url - matchResult = url.match(/^https:\/\/github.com\/(.*?)\/(.*?)\/.git/); - } - - if (matchResult && matchResult.length > 2) { - return { - owner: matchResult[1], - name: matchResult[2], - }; - } - - throw `Could not find a suitable git repository. Received [${url}]`; -}; - -const prepareRelease = ({ dryrun, verbose }: any) => - useSpinner('Preparing release', async () => { - const ciDir = getCiFolder(); - const distDir = path.resolve(ciDir, 'dist'); - const distContentDir = path.resolve(distDir, getPluginId()); - const pluginJsonFile = path.resolve(distContentDir, 'plugin.json'); - const pluginJson = getPluginJson(pluginJsonFile); - - const githubPublishScript: Command = [ - ['git', ['config', 'user.email', DEFAULT_EMAIL_ADDRESS]], - ['git', ['config', 'user.name', DEFAULT_USERNAME]], - await checkoutBranch(`release-${pluginJson.info.version}`), - ['/bin/rm', ['-rf', 'dist'], { dryrun }], - ['mv', ['-v', distContentDir, 'dist']], - ['git', ['add', '--force', 'dist'], { dryrun }], - ['/bin/rm', ['-rf', 'src'], { enterprise: true }], - ['git', ['rm', '-rf', 'src'], { enterprise: true }], - [ - 'git', - ['commit', '-m', `automated release ${pluginJson.info.version} [skip ci]`], - { - dryrun, - okOnError: [/nothing to commit/g, /nothing added to commit/g, /no changes added to commit/g], - }, - ], - ['git', ['push', '-f', 'origin', `release-${pluginJson.info.version}`], { dryrun }], - ['git', ['tag', '-f', `v${pluginJson.info.version}`]], - ['git', ['push', '-f', 'origin', `v${pluginJson.info.version}`]], - ]; - - for (let line of githubPublishScript) { - const opts = line.length === 3 ? line[2] : {}; - const command = line[0]; - const args = line[1]; - - try { - if (verbose) { - console.log('executing >>', line); - } - - if (line.length > 0 && line[0].length > 0) { - if (opts['dryrun']) { - line[1].push('--dry-run'); - } - - // Exit if the plugin is NOT an enterprise plugin - if (pluginJson.enterprise && !opts['enterprise']) { - continue; - } - - const { stdout } = await execa(command, args); - if (verbose) { - console.log(stdout); - } - } else { - if (verbose) { - console.log('skipping empty line'); - } - } - } catch (ex: any) { - const err: string = ex.message; - if (opts['okOnError'] && Array.isArray(opts['okOnError'])) { - let trueError = true; - for (let regex of opts['okOnError']) { - if (err.match(regex)) { - trueError = false; - break; - } - } - - if (!trueError) { - // This is not an error - continue; - } - } - console.error(err); - process.exit(-1); - } - } - }); export const getToolkitVersion = () => { const pkg = readFileSync(`${__dirname}/../../../package.json`, 'utf8'); @@ -151,73 +8,3 @@ export const getToolkitVersion = () => { } return version; }; -interface GithubPublishReleaseOptions { - commitHash?: string; - githubToken: string; - githubUser: string; - gitRepoName: string; -} - -const createRelease = ({ commitHash, githubUser, githubToken, gitRepoName }: GithubPublishReleaseOptions) => - useSpinner('Creating release', async () => { - const gitRelease = new GitHubRelease(githubToken, githubUser, gitRepoName, await releaseNotes(), commitHash); - return gitRelease.release(); - }); - -export interface GithubPublishOptions { - dryrun?: boolean; - verbose?: boolean; - commitHash?: string; - dev?: boolean; -} - -const githubPublishRunner: TaskRunner = async ({ dryrun, verbose, commitHash }) => { - let repoUrl: string | undefined = process.env.DRONE_REPO_LINK || process.env.CIRCLE_REPOSITORY_URL; - if (!repoUrl) { - // Try and figure it out - const repo = await execa('git', ['config', '--local', 'remote.origin.url']); - if (repo && repo.stdout) { - repoUrl = repo.stdout; - } else { - throw new Error( - 'The release plugin requires you specify the repository url as environment variable DRONE_REPO_LINK or ' + - 'CIRCLE_REPOSITORY_URL' - ); - } - } - - if (!process.env['GITHUB_ACCESS_TOKEN']) { - // Try to use GITHUB_TOKEN, which may be set. - if (process.env['GITHUB_TOKEN']) { - process.env['GITHUB_ACCESS_TOKEN'] = process.env['GITHUB_TOKEN']; - } else { - throw new Error( - `GitHub publish requires that you set the environment variable GITHUB_ACCESS_TOKEN to a valid github api token. - See: https://github.com/settings/tokens for more details.` - ); - } - } - - if (!process.env['GITHUB_USERNAME']) { - // We can default this one - process.env['GITHUB_USERNAME'] = DEFAULT_EMAIL_ADDRESS; - } - - const parsedUrl = gitUrlParse(repoUrl); - const githubToken = process.env['GITHUB_ACCESS_TOKEN']; - const githubUser = parsedUrl.owner; - - await prepareRelease({ - dryrun, - verbose, - }); - - await createRelease({ - commitHash, - githubUser, - githubToken, - gitRepoName: parsedUrl.name, - }); -}; - -export const githubPublishTask = new Task('GitHub Publish', githubPublishRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts b/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts deleted file mode 100644 index 7ffd6c32d6d..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.managed.ts +++ /dev/null @@ -1,34 +0,0 @@ -import execa = require('execa'); -import { promises as fs } from 'fs'; - -import { Task, TaskRunner } from '../task'; - -interface BundeManagedOptions {} - -const MANAGED_PLUGINS_PATH = `${process.cwd()}/plugins-bundled`; -const MANAGED_PLUGINS_SCOPES = ['internal', 'external']; - -const bundleManagedPluginsRunner: TaskRunner = async () => { - await Promise.all( - MANAGED_PLUGINS_SCOPES.map(async (scope) => { - try { - const plugins = await fs.readdir(`${MANAGED_PLUGINS_PATH}/${scope}`); - if (plugins.length > 0) { - for (const plugin of plugins) { - try { - console.log(`[${scope}]: ${plugin} building...`); - await execa('yarn', ['build'], { cwd: `${MANAGED_PLUGINS_PATH}/${scope}/${plugin}` }); - console.log(`[${scope}]: ${plugin} bundled`); - } catch (e: any) { - console.log(e.stdout); - } - } - } - } catch (e) { - console.log(e); - } - }) - ); -}; - -export const bundleManagedTask = new Task('Bundle managed plugins', bundleManagedPluginsRunner); diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.ts b/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.ts deleted file mode 100644 index 9743f402a63..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin/bundle.ts +++ /dev/null @@ -1,79 +0,0 @@ -import clearConsole from 'react-dev-utils/clearConsole'; -import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages'; -import webpack from 'webpack'; - -import { loadWebpackConfig } from '../../../config/webpack.plugin.config'; - -export interface PluginBundleOptions { - watch: boolean; - production?: boolean; - preserveConsole?: boolean; -} - -export const bundlePlugin = async ({ watch, production, preserveConsole }: PluginBundleOptions) => { - const compiler = webpack( - await loadWebpackConfig({ - watch, - production, - preserveConsole, - }) - ); - - const webpackPromise = new Promise((resolve, reject) => { - if (watch) { - console.log('Started watching plugin for changes...'); - compiler.watch({ ignored: ['**/node_modules', '**/dist'] }, (err, stats) => {}); - - compiler.hooks.invalid.tap('invalid', () => { - clearConsole(); - console.log('Compiling...'); - }); - - compiler.hooks.done.tap('done', (stats) => { - clearConsole(); - const json = stats.toJson(); - const output = formatWebpackMessages(json); - - if (!output.errors.length && !output.warnings.length) { - console.log('Compiled successfully!\n'); - console.log(stats.toString({ colors: true })); - } - - if (output.errors.length) { - console.log('Compilation failed!'); - output.errors.forEach((e) => console.log(e)); - - if (output.warnings.length) { - console.log('Warnings:'); - output.warnings.forEach((w) => console.log(w)); - } - } - if (output.errors.length === 0 && output.warnings.length) { - console.log('Compiled with warnings!'); - output.warnings.forEach((w) => console.log(w)); - } - }); - } else { - compiler.run((err, stats) => { - if (err) { - reject(err); - return; - } - - if (stats?.hasErrors()) { - stats.compilation.errors.forEach((e) => { - console.log(e.message); - }); - - reject('Build failed'); - return; - } - - console.log('\n', stats?.toString({ colors: true }), '\n'); - resolve(); - }); - } - }); - - return webpackPromise; -}; diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/create.ts b/packages/grafana-toolkit/src/cli/tasks/plugin/create.ts deleted file mode 100644 index 5073acadfe9..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin/create.ts +++ /dev/null @@ -1,199 +0,0 @@ -import chalk from 'chalk'; -import commandExists from 'command-exists'; -import { promises as fs, readFileSync, existsSync, unlinkSync } from 'fs'; -import { prompt } from 'inquirer'; -import { kebabCase } from 'lodash'; -import path from 'path'; -import gitPromise from 'simple-git'; - -import { promptConfirm, promptInput } from '../../utils/prompt'; -import { rmdir } from '../../utils/rmdir'; -import { useSpinner } from '../../utils/useSpinner'; - -const simpleGit = gitPromise(process.cwd()); - -interface PluginDetails { - name: string; - org: string; - description: string; - author: boolean | string; - url: string; - keywords: string; -} - -type PluginType = 'panel-plugin' | 'datasource-plugin' | 'backend-datasource-plugin'; - -const PluginNames: Record = { - 'panel-plugin': 'Grafana Panel Plugin', - 'datasource-plugin': 'Grafana Data Source Plugin', - 'backend-datasource-plugin': 'Grafana Backend Datasource Plugin', -}; -const RepositoriesPaths: Record = { - 'panel-plugin': 'https://github.com/grafana/simple-react-panel.git', - 'datasource-plugin': 'https://github.com/grafana/simple-datasource.git', - 'backend-datasource-plugin': 'https://github.com/grafana/simple-datasource-backend.git', -}; -const TutorialPaths: Record = { - 'panel-plugin': 'https://grafana.com/tutorials/build-a-panel-plugin', - 'datasource-plugin': 'https://grafana.com/tutorials/build-a-data-source-plugin', - 'backend-datasource-plugin': 'TODO', -}; - -export const getGitUsername = async () => { - const name = await simpleGit.raw(['config', '--global', 'user.name']); - return name || ''; -}; -export const getPluginIdFromName = (name: string) => kebabCase(name); -export const getPluginId = (pluginDetails: PluginDetails) => - `${kebabCase(pluginDetails.org)}-${getPluginIdFromName(pluginDetails.name)}`; - -export const getPluginKeywords = (pluginDetails: PluginDetails) => - pluginDetails.keywords - .split(',') - .map((k) => k.trim()) - .filter((k) => k !== ''); - -export const verifyGitExists = async () => { - return new Promise((resolve, reject) => { - commandExists('git', (err, exists) => { - if (exists) { - resolve(true); - } - reject(new Error('git is not installed')); - }); - }); -}; - -export const promptPluginType = async () => - prompt<{ type: PluginType }>([ - { - type: 'list', - message: 'Select plugin type', - name: 'type', - choices: [ - { name: 'Panel Plugin', value: 'panel-plugin' }, - { name: 'Datasource Plugin', value: 'datasource-plugin' }, - { name: 'Backend Datasource Plugin', value: 'backend-datasource-plugin' }, - ], - }, - ]); - -export const promptPluginDetails = async (name?: string) => { - const username = (await getGitUsername()).trim(); - const responses = await prompt([ - promptInput('name', 'Plugin name', true, name), - promptInput('org', 'Organization (used as part of plugin ID)', true), - promptInput('description', 'Description'), - promptInput('keywords', 'Keywords (separated by comma)'), - // Try using git specified username - promptConfirm('author', `Author (${username})`, username, username !== ''), - // Prompt for manual author entry if no git user.name specified - promptInput('author', `Author`, true, undefined, (answers: any) => !answers.author || username === ''), - promptInput('url', 'Your URL (i.e. organisation url)'), - ]); - - return { - ...responses, - author: responses.author === true ? username : responses.author, - }; -}; - -export const fetchTemplate = ({ type, dest }: { type: PluginType; dest: string }) => - useSpinner('Fetching plugin template...', async () => { - const url = RepositoriesPaths[type]; - if (!url) { - throw new Error('Unknown plugin type'); - } - - await simpleGit.clone(url, dest); - }); - -export const prepareJsonFiles = ({ - type, - pluginDetails, - pluginPath, -}: { - type: PluginType; - pluginDetails: PluginDetails; - pluginPath: string; -}) => - useSpinner('Saving package.json and plugin.json files', async () => { - const packageJsonPath = path.resolve(pluginPath, 'package.json'); - const pluginJsonPath = path.resolve(pluginPath, 'src/plugin.json'); - const packageJson: any = JSON.parse(readFileSync(packageJsonPath, 'utf8')); - const pluginJson: any = JSON.parse(readFileSync(pluginJsonPath, 'utf8')); - - const pluginId = `${kebabCase(pluginDetails.org)}-${getPluginIdFromName(pluginDetails.name)}`; - packageJson.name = pluginId; - packageJson.author = pluginDetails.author; - packageJson.description = pluginDetails.description; - - pluginJson.name = pluginDetails.name; - pluginJson.id = pluginId; - - if (type === 'backend-datasource-plugin') { - pluginJson.backend = true; - pluginJson.executable = 'gpx_' + pluginDetails.name; - } - - pluginJson.info = { - ...pluginJson.info, - description: pluginDetails.description, - author: { - name: pluginDetails.author, - url: pluginDetails.url, - }, - keywords: getPluginKeywords(pluginDetails), - }; - - await Promise.all( - [packageJson, pluginJson].map((f, i) => { - const filePath = i === 0 ? packageJsonPath : pluginJsonPath; - return fs.writeFile(filePath, JSON.stringify(f, null, 2)); - }) - ); - }); - -export const removeGitFiles = (pluginPath: string) => - useSpinner('Cleaning', async () => rmdir(`${path.resolve(pluginPath, '.git')}`)); - -/* eslint-disable no-console */ -export const formatPluginDetails = (details: PluginDetails) => { - console.group(); - console.log(); - console.log(chalk.bold.yellow('Your plugin details')); - console.log('---'); - console.log(chalk.bold('Name: '), details.name); - console.log(chalk.bold('ID: '), getPluginId(details)); - console.log(chalk.bold('Description: '), details.description); - console.log(chalk.bold('Keywords: '), getPluginKeywords(details)); - console.log(chalk.bold('Author: '), details.author); - console.log(chalk.bold('Organisation: '), details.org); - console.log(chalk.bold('Website: '), details.url); - console.log(); - console.groupEnd(); -}; - -export const printGrafanaTutorialsDetails = (type: PluginType) => { - console.group(); - console.log(); - console.log(chalk.bold.yellow(`Congrats! You have just created ${PluginNames[type]}.`)); - console.log('Please run `yarn install` to install frontend dependencies.'); - console.log(); - if (type !== 'backend-datasource-plugin') { - console.log(`${PluginNames[type]} tutorial: ${TutorialPaths[type]}`); - } - console.log( - 'Learn more about Grafana Plugins at https://grafana.com/docs/grafana/latest/plugins/developing/development/' - ); - console.log(); - console.groupEnd(); -}; -/* eslint-enable no-console */ - -export const removeLockFile = ({ pluginPath }: { pluginPath: string }) => { - const lockFilePath = path.resolve(pluginPath, 'yarn.lock'); - if (existsSync(lockFilePath)) { - unlinkSync(lockFilePath); - } -}; diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/test_exclude_image_suffix_in_manifest.png b/packages/grafana-toolkit/src/cli/tasks/plugin/test_exclude_image_suffix_in_manifest.png deleted file mode 100644 index bb07b075925..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin/test_exclude_image_suffix_in_manifest.png +++ /dev/null @@ -1 +0,0 @@ -NOTE: not a real image, but should be excluded from hashed files \ No newline at end of file diff --git a/packages/grafana-toolkit/src/cli/tasks/plugin/tests.ts b/packages/grafana-toolkit/src/cli/tasks/plugin/tests.ts deleted file mode 100644 index 7b8970bf769..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/plugin/tests.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { runCLI } from '@jest/core'; - -import { loadJestPluginConfig } from '../../../config/jest.plugin.config'; -import { useSpinner } from '../../utils/useSpinner'; - -export interface PluginTestOptions { - updateSnapshot: boolean; - coverage: boolean; - watch: boolean; - testPathPattern?: string; - testNamePattern?: string; - maxWorkers?: string; -} - -export const testPlugin = ({ - updateSnapshot, - coverage, - watch, - testPathPattern, - testNamePattern, - maxWorkers, -}: PluginTestOptions) => - useSpinner('Running tests', async () => { - const testConfig = loadJestPluginConfig(); - - const cliConfig = { - config: JSON.stringify(testConfig), - updateSnapshot, - coverage, - watch, - testPathPattern: testPathPattern ? [testPathPattern] : [], - testNamePattern: testNamePattern ? [testNamePattern] : [], - passWithNoTests: true, - maxWorkers, - }; - - // @ts-ignore - const runJest = () => runCLI(cliConfig, [process.cwd()]); - - if (watch) { - runJest(); - } else { - // @ts-ignore - const results = await runJest(); - - if (results.results.numFailedTests > 0 || results.results.numFailedTestSuites > 0) { - throw new Error('Tests failed'); - } - } - }); diff --git a/packages/grafana-toolkit/src/cli/tasks/searchTestDataSetup.ts b/packages/grafana-toolkit/src/cli/tasks/searchTestDataSetup.ts deleted file mode 100644 index 6f8fc346e6d..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/searchTestDataSetup.ts +++ /dev/null @@ -1,117 +0,0 @@ -import axios from 'axios'; - -import { Task, TaskRunner } from './task'; - -interface SearchTestDataSetupOptions { - count: number; -} - -const client = axios.create({ - baseURL: 'http://localhost:3000/api', - auth: { - username: 'admin', - password: 'admin2', - }, -}); - -export async function getUser(user: any): Promise { - console.log('Creating user ' + user.name); - const search = await client.get('/users/search', { - params: { query: user.login }, - }); - - if (search.data.totalCount === 1) { - user.id = search.data.users[0].id; - return user; - } - - const rsp = await client.post('/admin/users', user); - user.id = rsp.data.id; - return user; -} - -export async function getTeam(team: any): Promise { - // delete if exists - const teams = await client.get('/teams/search'); - for (const existing of teams.data.teams) { - if (existing.name === team.name) { - console.log('Team exists, deleting'); - await client.delete('/teams/' + existing.id); - } - } - - console.log('Creating team ' + team.name); - const teamRsp = await client.post(`/teams`, team); - team.id = teamRsp.data.teamId; - return team; -} - -export async function addToTeam(team: any, user: any): Promise { - console.log(`Adding user ${user.name} to team ${team.name}`); - await client.post(`/teams/${team.id}/members`, { userId: user.id }); -} - -export async function setDashboardAcl(dashboardId: any, aclList: any) { - console.log('Setting Dashboard ACL ' + dashboardId); - await client.post(`/dashboards/id/${dashboardId}/permissions`, { items: aclList }); -} - -const searchTestDataSetupRunner: TaskRunner = async ({ count }) => { - const user1 = await getUser({ - name: 'searchTestUser1', - email: 'searchTestUser@team.com', - login: 'searchTestUser1', - password: '12345', - }); - - const team1 = await getTeam({ name: 'searchTestTeam1', email: 'searchtestdata@team.com' }); - addToTeam(team1, user1); - - // create or update folder - const folder: any = { - uid: 'search-test-data', - title: 'Search test data folder', - version: 1, - }; - - try { - await client.delete(`/folders/${folder.uid}`); - } catch (err) {} - - console.log('Creating folder'); - - const rsp = await client.post(`/folders`, folder); - folder.id = rsp.data.id; - folder.url = rsp.data.url; - - await setDashboardAcl(folder.id, []); - - console.log('Creating dashboards'); - - const dashboards: any = []; - - for (let i = 0; i < count; i++) { - const dashboard: any = { - uid: 'search-test-dash-' + i.toString().padStart(5, '0'), - title: 'Search test dash ' + i.toString().padStart(5, '0'), - }; - - const rsp = await client.post(`/dashboards/db`, { - dashboard: dashboard, - folderId: folder.id, - overwrite: true, - }); - - dashboard.id = rsp.data.id; - dashboard.url = rsp.data.url; - - console.log('Created dashboard ' + dashboard.title); - dashboards.push(dashboard); - await setDashboardAcl(dashboard.id, [{ userId: 0, teamId: team1.id, permission: 4 }]); - } -}; - -export const searchTestDataSetupTask = new Task( - 'Search test data setup', - searchTestDataSetupRunner -); diff --git a/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts b/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts deleted file mode 100644 index 4c4510545c0..00000000000 --- a/packages/grafana-toolkit/src/cli/tasks/toolkit.build.ts +++ /dev/null @@ -1,99 +0,0 @@ -import chalk from 'chalk'; -import execa = require('execa'); -import * as fs from 'fs'; - -import { useSpinner } from '../utils/useSpinner'; - -import { Task, TaskRunner } from './task'; - -const path = require('path'); - -let distDir: string, cwd: string; - -const clean = () => useSpinner('Cleaning', () => execa('npm', ['run', 'clean'])); - -const compile = () => - useSpinner('Compiling sources', async () => { - try { - await execa('tsc', ['-p', './tsconfig.json']); - } catch (e) { - console.log(e); - throw e; - } - }); - -const copyFiles = () => { - const files = [ - 'src/config/prettier.plugin.config.json', - 'src/config/prettier.plugin.rc.js', - 'src/config/tsconfig.plugin.json', - 'src/config/tsconfig.plugin.local.json', - 'src/config/eslint.plugin.js', - 'src/config/styles.mock.js', - 'src/config/jest.babel.config.js', - 'src/config/jest.plugin.config.local.js', - 'src/config/matchMedia.js', - 'src/config/react-inlinesvg.tsx', - ]; - - return useSpinner(`Moving ${files.join(', ')} files`, async () => { - const promises = files.map((file) => { - return new Promise((resolve, reject) => { - const basedir = path.dirname(`${distDir}/${file}`); - if (!fs.existsSync(basedir)) { - fs.mkdirSync(basedir, { recursive: true }); - } - fs.copyFile(`${cwd}/${file}`, `${distDir}/${file}`, (err) => { - if (err) { - reject(err); - return; - } - resolve(); - }); - }); - }); - - await Promise.all(promises); - }); -}; - -const copySassFiles = () => { - const files = ['_variables.generated.scss', '_variables.dark.generated.scss', '_variables.light.generated.scss']; - const exportDir = `${cwd}/sass`; - return useSpinner(`Copy scss files ${files.join(', ')} files`, async () => { - const sassDir = path.resolve(cwd, '../../public/sass/'); - if (!fs.existsSync(exportDir)) { - fs.mkdirSync(exportDir); - } - const promises = files.map((file) => { - return new Promise((resolve, reject) => { - const name = file.replace('.generated', ''); - fs.copyFile(`${sassDir}/${file}`, `${exportDir}/${name}`, (err) => { - if (err) { - reject(err); - return; - } - resolve(); - }); - }); - }); - - await Promise.all(promises); - }); -}; - -interface ToolkitBuildOptions {} - -const toolkitBuildTaskRunner: TaskRunner = async () => { - cwd = path.resolve(__dirname, '../../../'); - distDir = `${cwd}/dist`; - const pkg = require(`${cwd}/package.json`); - console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`)); - - await clean(); - await compile(); - await copyFiles(); - await copySassFiles(); -}; - -export const toolkitBuildTask = new Task('@grafana/toolkit build', toolkitBuildTaskRunner); diff --git a/packages/grafana-toolkit/src/cli/templates/component.test.tsx.template.ts b/packages/grafana-toolkit/src/cli/templates/component.test.tsx.template.ts deleted file mode 100644 index b6737feab08..00000000000 --- a/packages/grafana-toolkit/src/cli/templates/component.test.tsx.template.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const testTpl = ` -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { <%= name %> } from './<%= name %>'; - - -describe('<%= name %>', () => { - it.skip('should render', () => { - - }); -}); -`; diff --git a/packages/grafana-toolkit/src/cli/templates/component.tsx.template.ts b/packages/grafana-toolkit/src/cli/templates/component.tsx.template.ts deleted file mode 100644 index 4c7903597a7..00000000000 --- a/packages/grafana-toolkit/src/cli/templates/component.tsx.template.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const componentTpl = `import React from 'react'; - -export interface Props {}; - -export const <%= name %> = (props: Props) => { - return ( -
    Hello world!
    - ) -}; -`; diff --git a/packages/grafana-toolkit/src/cli/templates/docs.mdx.template.ts b/packages/grafana-toolkit/src/cli/templates/docs.mdx.template.ts deleted file mode 100644 index b605f755c6b..00000000000 --- a/packages/grafana-toolkit/src/cli/templates/docs.mdx.template.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const docsTpl = `import { ArgsTable } from '@storybook/addon-docs/blocks'; -import { <%= name %> } from './<%= name %>'; - -# <%= name %> - -### Usage - -\`\`\`jsx -import { <%= name %> } from '@grafana/ui'; - -<<%= name %> /> -\`\`\` - -### Props -} /> -`; diff --git a/packages/grafana-toolkit/src/cli/templates/index.ts b/packages/grafana-toolkit/src/cli/templates/index.ts deleted file mode 100644 index 57a7319ccbb..00000000000 --- a/packages/grafana-toolkit/src/cli/templates/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { componentTpl } from './component.tsx.template'; -export { storyTpl } from './story.tsx.template'; -export { docsTpl } from './docs.mdx.template'; -export { testTpl } from './component.test.tsx.template'; diff --git a/packages/grafana-toolkit/src/cli/templates/story.tsx.template.ts b/packages/grafana-toolkit/src/cli/templates/story.tsx.template.ts deleted file mode 100644 index 21c87a1f20b..00000000000 --- a/packages/grafana-toolkit/src/cli/templates/story.tsx.template.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const storyTpl = ` -import React from 'react'; -import { <%= name %> } from './<%= name %>'; -import { withCenteredStory } from '@grafana/ui/src/utils/storybook/withCenteredStory'; -import mdx from './<%= name %>.mdx'; - - -export default { - title: '<%= group %>/<%= name %>', - component: <%= name %>, - decorators: [withCenteredStory], - parameters: { - docs: { - page: mdx, - }, - }, -}; - -export const Basic = () => { - return <<%= name %> />; -}; -`; diff --git a/packages/grafana-toolkit/src/cli/utils/githubClient.test.ts b/packages/grafana-toolkit/src/cli/utils/githubClient.test.ts deleted file mode 100644 index ff6cfb0b132..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/githubClient.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import GithubClient from './githubClient'; - -const fakeClient = jest.fn(); - -beforeEach(() => { - delete process.env.GITHUB_USERNAME; - delete process.env.GITHUB_ACCESS_TOKEN; -}); - -afterEach(() => { - delete process.env.GITHUB_USERNAME; - delete process.env.GITHUB_ACCESS_TOKEN; -}); - -describe('GithubClient', () => { - it('should initialise a GithubClient', () => { - const github = new GithubClient(); - const githubEnterprise = new GithubClient({ enterprise: true }); - expect(github).toBeInstanceOf(GithubClient); - expect(githubEnterprise).toBeInstanceOf(GithubClient); - }); - - describe('#client', () => { - it('it should contain a grafana client', () => { - // @ts-ignore - const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient); - - const github = new GithubClient(); - const client = github.client; - - expect(spy).toHaveBeenCalledWith({ - baseURL: 'https://api.github.com/repos/grafana/grafana', - timeout: 10000, - }); - expect(client).toEqual(fakeClient); - }); - - it('it should contain a grafana enterprise client', () => { - // @ts-ignore - const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient); - - const github = new GithubClient({ enterprise: true }); - const client = github.client; - - expect(spy).toHaveBeenCalledWith({ - baseURL: 'https://api.github.com/repos/grafana/grafana-enterprise', - timeout: 10000, - }); - expect(client).toEqual(fakeClient); - }); - - describe('when the credentials are required', () => { - it('should create the client when the credentials are defined', () => { - const username = 'grafana'; - const token = 'averysecureaccesstoken'; - - process.env.GITHUB_USERNAME = username; - process.env.GITHUB_ACCESS_TOKEN = token; - - // @ts-ignore - const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient); - - const github = new GithubClient({ required: true }); - const client = github.client; - - expect(spy).toHaveBeenCalledWith({ - baseURL: 'https://api.github.com/repos/grafana/grafana', - timeout: 10000, - auth: { username, password: token }, - }); - - expect(client).toEqual(fakeClient); - }); - - it('should create the enterprise client when the credentials are defined', () => { - const username = 'grafana'; - const token = 'averysecureaccesstoken'; - - process.env.GITHUB_USERNAME = username; - process.env.GITHUB_ACCESS_TOKEN = token; - - // @ts-ignore - const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient); - - const github = new GithubClient({ required: true, enterprise: true }); - const client = github.client; - - expect(spy).toHaveBeenCalledWith({ - baseURL: 'https://api.github.com/repos/grafana/grafana-enterprise', - timeout: 10000, - auth: { username, password: token }, - }); - - expect(client).toEqual(fakeClient); - }); - - describe('when the credentials are not defined', () => { - it('should throw an error', () => { - expect(() => { - // eslint-disable-next-line - new GithubClient({ required: true }); - }).toThrow(/operation needs a GITHUB_USERNAME and GITHUB_ACCESS_TOKEN environment variables/); - }); - }); - }); - }); -}); diff --git a/packages/grafana-toolkit/src/cli/utils/githubClient.ts b/packages/grafana-toolkit/src/cli/utils/githubClient.ts deleted file mode 100644 index 9643c166874..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/githubClient.ts +++ /dev/null @@ -1,49 +0,0 @@ -import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; - -const grafanaURL = (owner: string, repo: string) => `https://api.github.com/repos/${owner}/${repo}`; -const enterpriseURL = 'https://api.github.com/repos/grafana/grafana-enterprise'; - -// Encapsulates the creation of a client for the GitHub API -// -// Two key things: -// 1. You can specify whenever you want the credentials to be required or not when imported. -// 2. If the credentials are available as part of the environment, even if -// they're not required - the library will use them. This allows us to overcome -// any API rate limiting imposed without authentication. - -interface GithubClientProps { - required?: boolean; - enterprise?: boolean; - owner?: string; - repo?: string; -} - -class GithubClient { - client: AxiosInstance; - - constructor({ required = false, enterprise = false, owner = 'grafana', repo = 'grafana' }: GithubClientProps = {}) { - const username = process.env.GITHUB_USERNAME; - const token = process.env.GITHUB_ACCESS_TOKEN; - - const clientConfig: AxiosRequestConfig = { - baseURL: enterprise ? enterpriseURL : grafanaURL(owner, repo), - timeout: 10000, - }; - - if (required && !username && !token) { - throw new Error('operation needs a GITHUB_USERNAME and GITHUB_ACCESS_TOKEN environment variables'); - } - - if (username && token) { - clientConfig.auth = { username: username, password: token }; - } - - this.client = this.createClient(clientConfig); - } - - private createClient(clientConfig: AxiosRequestConfig) { - return axios.create(clientConfig); - } -} - -export default GithubClient; diff --git a/packages/grafana-toolkit/src/cli/utils/githubRelease.test.ts b/packages/grafana-toolkit/src/cli/utils/githubRelease.test.ts deleted file mode 100644 index 74fedc2a759..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/githubRelease.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { GitHubRelease } from './githubRelease'; - -describe('GithubRelease', () => { - it('should initialise a GithubRelease', () => { - process.env.GITHUB_ACCESS_TOKEN = '12345'; - process.env.GITHUB_USERNAME = 'test@grafana.com'; - const github = new GitHubRelease('A token', 'A username', 'A repo', 'Some release notes'); - expect(github).toBeInstanceOf(GitHubRelease); - }); -}); diff --git a/packages/grafana-toolkit/src/cli/utils/githubRelease.ts b/packages/grafana-toolkit/src/cli/utils/githubRelease.ts deleted file mode 100644 index 1d59e2d4516..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/githubRelease.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { AxiosResponse } from 'axios'; -import fs = require('fs'); -import path = require('path'); - -import { getPluginId } from '../../config/utils/getPluginId'; -import { getPluginJson } from '../../config/utils/pluginValidation'; -import { getCiFolder } from '../../plugins/env'; - -import GithubClient from './githubClient'; - -const resolveContentType = (extension: string): string => { - if (extension.startsWith('.')) { - extension = extension.slice(1); - } - switch (extension) { - case 'zip': - return 'application/zip'; - case 'json': - return 'application/json'; - case 'sha1': - return 'text/plain'; - default: - return 'application/octet-stream'; - } -}; - -class GitHubRelease { - token: string; - username: string; - repository: string; - releaseNotes: string; - commitHash?: string; - git: GithubClient; - - constructor(token: string, username: string, repository: string, releaseNotes: string, commitHash?: string) { - this.token = token; - this.username = username; - this.repository = repository; - this.releaseNotes = releaseNotes; - this.commitHash = commitHash; - - this.git = new GithubClient({ - required: true, - owner: username, - repo: repository, - }); - } - - publishAssets(srcLocation: string, destUrl: string) { - // Add the assets. Loop through files in the ci/dist folder and upload each asset. - const files = fs.readdirSync(srcLocation); - - return files.map(async (file: string) => { - const fileStat = fs.statSync(`${srcLocation}/${file}`); - const fileData = fs.readFileSync(`${srcLocation}/${file}`); - return this.git.client.post(`${destUrl}?name=${file}`, fileData, { - headers: { - 'Content-Type': resolveContentType(path.extname(file)), - 'Content-Length': fileStat.size, - }, - maxContentLength: fileStat.size * 2 * 1024 * 1024, - }); - }); - } - - async release() { - const ciDir = getCiFolder(); - const distDir = path.resolve(ciDir, 'dist'); - const distContentDir = path.resolve(distDir, getPluginId()); - const pluginJsonFile = path.resolve(distContentDir, 'plugin.json'); - const pluginInfo = getPluginJson(pluginJsonFile).info; - const PUBLISH_DIR = path.resolve(getCiFolder(), 'packages'); - const commitHash = this.commitHash || pluginInfo.build?.hash; - - try { - const latestRelease: AxiosResponse = await this.git.client.get(`releases/tags/v${pluginInfo.version}`); - - // Re-release if the version is the same as an existing release - if (latestRelease.data.tag_name === `v${pluginInfo.version}`) { - await this.git.client.delete(`releases/${latestRelease.data.id}`); - } - } catch (reason: any) { - if (reason.response.status !== 404) { - // 404 just means no release found. Not an error. Anything else though, re throw the error - throw reason; - } - } - - try { - // Now make the release - const newReleaseResponse = await this.git.client.post('releases', { - tag_name: `v${pluginInfo.version}`, - target_commitish: commitHash, - name: `v${pluginInfo.version}`, - body: this.releaseNotes, - draft: false, - prerelease: false, - }); - - const publishPromises = this.publishAssets( - PUBLISH_DIR, - `https://uploads.github.com/repos/${this.username}/${this.repository}/releases/${newReleaseResponse.data.id}/assets` - ); - await Promise.all(publishPromises); - } catch (reason: any) { - console.error(reason.data?.message ?? reason.response.data ?? reason); - // Rethrow the error so that we can trigger a non-zero exit code to circle-ci - throw reason; - } - } -} - -export { GitHubRelease }; diff --git a/packages/grafana-toolkit/src/cli/utils/pascalCase.ts b/packages/grafana-toolkit/src/cli/utils/pascalCase.ts deleted file mode 100644 index 17694ef55a0..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/pascalCase.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { flow, camelCase, upperFirst } from 'lodash'; - -export const pascalCase = flow([camelCase, upperFirst]); diff --git a/packages/grafana-toolkit/src/cli/utils/prompt.ts b/packages/grafana-toolkit/src/cli/utils/prompt.ts deleted file mode 100644 index 6a5a478e137..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/prompt.ts +++ /dev/null @@ -1,79 +0,0 @@ -import inquirer, { - Question, - InputQuestion, - CheckboxQuestion, - NumberQuestion, - PasswordQuestion, - EditorQuestion, - ConfirmQuestion, - ListQuestion, - ChoiceOptions, -} from 'inquirer'; - -type QuestionWithValidation = - | InputQuestion - | CheckboxQuestion - | NumberQuestion - | PasswordQuestion - | EditorQuestion; - -export const answerRequired = (question: QuestionWithValidation): Question => { - return { - ...question, - validate: (answer: A) => answer.trim() !== '' || `${question.name} is required`, - }; -}; - -export const promptInput = ( - name: string, - message: string | ((answers: A) => string), - required = false, - def: any = undefined, - when: boolean | ((answers: A) => boolean | Promise) = true -) => { - const model: InputQuestion = { - type: 'input', - name, - message, - default: def, - when, - }; - - return required ? answerRequired(model) : model; -}; - -export const promptList = ( - name: string, - message: string | ((answers: A) => string), - choices: () => ChoiceOptions[], - def: any = undefined, - when: boolean | ((answers: A) => boolean | Promise) = true -) => { - const model: ListQuestion = { - type: 'list', - name, - message, - choices, - default: def, - when, - }; - - return model; -}; - -export const promptConfirm = ( - name: string, - message: string | ((answers: A) => string), - def: any = undefined, - when: boolean | ((answers: A) => boolean | Promise) = true -) => { - const model: ConfirmQuestion = { - type: 'confirm', - name, - message, - default: def, - when, - }; - - return model; -}; diff --git a/packages/grafana-toolkit/src/cli/utils/rmdir.ts b/packages/grafana-toolkit/src/cli/utils/rmdir.ts deleted file mode 100644 index f1065628cdb..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/rmdir.ts +++ /dev/null @@ -1,23 +0,0 @@ -import fs = require('fs'); -import path = require('path'); - -/** - * Remove directory recursively - * Ref https://stackoverflow.com/a/42505874 - */ -export const rmdir = (dirPath: string) => { - if (!fs.existsSync(dirPath)) { - return; - } - - fs.readdirSync(dirPath).forEach((entry) => { - const entryPath = path.join(dirPath, entry); - if (fs.lstatSync(entryPath).isDirectory()) { - rmdir(entryPath); - } else { - fs.unlinkSync(entryPath); - } - }); - - fs.rmdirSync(dirPath); -}; diff --git a/packages/grafana-toolkit/src/cli/utils/useSpinner.ts b/packages/grafana-toolkit/src/cli/utils/useSpinner.ts deleted file mode 100644 index 6ba94e330c2..00000000000 --- a/packages/grafana-toolkit/src/cli/utils/useSpinner.ts +++ /dev/null @@ -1,23 +0,0 @@ -import ora from 'ora'; - -export const useSpinner = async (label: string, fn: () => Promise, killProcess = true) => { - const spinner = ora(label); - spinner.start(); - try { - await fn(); - spinner.succeed(); - } catch (err: any) { - spinner.fail(err.message || err); - - if (err.stdout) { - console.error(err.stdout); - } else if (err.message) { - // Return stack trace if error object - console.trace(err); // eslint-disable-line no-console - } - - if (killProcess) { - process.exit(1); - } - } -}; diff --git a/packages/grafana-toolkit/src/config/eslint.plugin.js b/packages/grafana-toolkit/src/config/eslint.plugin.js deleted file mode 100644 index 8881050af33..00000000000 --- a/packages/grafana-toolkit/src/config/eslint.plugin.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: ['@grafana/eslint-config'], - rules: { - 'react/prop-types': 'off', - }, -}; diff --git a/packages/grafana-toolkit/src/config/index.ts b/packages/grafana-toolkit/src/config/index.ts deleted file mode 100644 index ccb2e12e1b5..00000000000 --- a/packages/grafana-toolkit/src/config/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { CustomWebpackConfigurationGetter, WebpackConfigurationOptions } from './webpack.plugin.config'; diff --git a/packages/grafana-toolkit/src/config/jest.babel.config.js b/packages/grafana-toolkit/src/config/jest.babel.config.js deleted file mode 100644 index 58098923886..00000000000 --- a/packages/grafana-toolkit/src/config/jest.babel.config.js +++ /dev/null @@ -1,2 +0,0 @@ -// Transform es modules to prevent `SyntaxError: Cannot use import statement outside a module` -module.exports = { presets: [['@babel/preset-env', { targets: { esmodules: false, node: 'current' } }]] }; diff --git a/packages/grafana-toolkit/src/config/jest.plugin.config.local.js b/packages/grafana-toolkit/src/config/jest.plugin.config.local.js deleted file mode 100644 index bcf17c9087d..00000000000 --- a/packages/grafana-toolkit/src/config/jest.plugin.config.local.js +++ /dev/null @@ -1,8 +0,0 @@ -// This file is needed because it is used by vscode and other tools that -// call `jest` directly. However, unless you are doing anything special -// do not edit this file - -const standard = require('@grafana/toolkit/src/config/jest.plugin.config'); - -// This process will use the same config that `yarn test` is using -module.exports = standard.jestConfig(); diff --git a/packages/grafana-toolkit/src/config/jest.plugin.config.test.ts b/packages/grafana-toolkit/src/config/jest.plugin.config.test.ts deleted file mode 100644 index 021d5c37ca1..00000000000 --- a/packages/grafana-toolkit/src/config/jest.plugin.config.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { jestConfig, allowedJestConfigOverrides } from './jest.plugin.config'; - -describe('Jest config', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should throw if not supported overrides provided', () => { - // Do not show console error,log when running test - jest.spyOn(console, 'error').mockImplementation(); - jest.spyOn(console, 'log').mockImplementation(); - const getConfig = () => jestConfig(`${__dirname}/mocks/jestSetup/unsupportedOverrides`); - - expect(getConfig).toThrow('Provided Jest config is not supported'); - }); - - it(`should allow ${allowedJestConfigOverrides} settings overrides`, () => { - const config = jestConfig(`${__dirname}/mocks/jestSetup/overrides`); - const configKeys = Object.keys(config); - - for (const whitelistedOption of allowedJestConfigOverrides) { - expect(configKeys).toContain(whitelistedOption); - } - }); - - describe('stylesheets support', () => { - it('should provide module name mapper for stylesheets by default', () => { - const config = jestConfig(`${__dirname}/mocks/jestSetup/noOverrides`); - expect(config.moduleNameMapper).toBeDefined(); - expect(Object.keys(config.moduleNameMapper)).toContain('\\.(css|sass|scss)$'); - }); - - it('should preserve mapping for stylesheets when moduleNameMapper overrides provided', () => { - const config = jestConfig(`${__dirname}/mocks/jestSetup/overrides`); - expect(config.moduleNameMapper).toBeDefined(); - expect(Object.keys(config.moduleNameMapper)).toContain('\\.(css|sass|scss)$'); - expect(Object.keys(config.moduleNameMapper)).toContain('someOverride'); - }); - }); -}); diff --git a/packages/grafana-toolkit/src/config/jest.plugin.config.ts b/packages/grafana-toolkit/src/config/jest.plugin.config.ts deleted file mode 100644 index e5809e0aaec..00000000000 --- a/packages/grafana-toolkit/src/config/jest.plugin.config.ts +++ /dev/null @@ -1,117 +0,0 @@ -import fs from 'fs'; -import path = require('path'); - -export const allowedJestConfigOverrides = [ - 'snapshotSerializers', - 'moduleNameMapper', - 'globalSetup', - 'globalTeardown', - 'testEnvironment', -]; - -interface EnabledJestConfigOverrides { - snapshotSerializers: string[]; - moduleNameMapper: { [key: string]: string }; -} - -const getSetupFile = (filePath: string) => { - if (fs.existsSync(`${filePath}.js`)) { - return `${filePath}.js`; - } - if (fs.existsSync(`${filePath}.ts`)) { - return `${filePath}.ts`; - } - return undefined; -}; - -export const jestConfig = (baseDir: string = process.cwd()) => { - const jestConfigOverrides = (require(path.resolve(baseDir, 'package.json')).jest || {}) as EnabledJestConfigOverrides; - - const deniedOverrides = jestConfigOverrides - ? Object.keys(jestConfigOverrides).filter((override) => allowedJestConfigOverrides.indexOf(override) === -1) - : []; - - if (deniedOverrides.length > 0) { - console.error("\ngrafana-toolkit doesn't support following Jest options: ", deniedOverrides); - console.log('Supported Jest options are: ', JSON.stringify(allowedJestConfigOverrides)); - throw new Error('Provided Jest config is not supported'); - } - - const shimsFilePath = path.resolve(baseDir, 'config/jest-shim'); - const setupFilePath = path.resolve(baseDir, 'config/jest-setup'); - - // Mock css imports for tests. Otherwise Jest will have troubles understanding SASS/CSS imports - const { moduleNameMapper, ...otherOverrides } = jestConfigOverrides; - const moduleNameMapperConfig = { - '\\.(css|sass|scss)$': `${__dirname}/styles.mock.js`, - 'react-inlinesvg': `${__dirname}/react-inlinesvg.tsx`, - ...moduleNameMapper, - }; - - const setupFile = getSetupFile(setupFilePath); - const shimsFile = getSetupFile(shimsFilePath); - - const setupFiles = [setupFile, shimsFile, `${__dirname}/matchMedia.js`, require.resolve('jest-canvas-mock')].filter( - (f) => f - ); - const defaultJestConfig = { - verbose: false, - moduleDirectories: ['node_modules', 'src'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], - setupFiles, - globals: { - 'ts-jest': { - isolatedModules: true, - tsconfig: path.resolve(baseDir, 'tsconfig.json'), - }, - }, - coverageReporters: ['json-summary', 'text', 'lcov'], - collectCoverageFrom: ['src/**/*.{ts,tsx}', '!**/node_modules/**', '!**/vendor/**'], - reporters: [ - 'default', - [ - require.resolve('jest-junit'), - { - outputDirectory: 'coverage', - }, - ], - ], - testEnvironment: 'jsdom', - testMatch: [ - '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', - '/src/**/*.{spec,test,jest}.{js,jsx,ts,tsx}', - '/spec/**/*.{spec,test,jest}.{js,jsx,ts,tsx}', - ], - transform: { - '^.+\\.(js|jsx|mjs)$': [ - require.resolve('babel-jest'), - { configFile: path.resolve(__dirname, './jest.babel.config.js') }, - ], - '^.+\\.tsx?$': require.resolve('ts-jest'), - }, - transformIgnorePatterns: [ - '[/\\\\\\\\]node_modules[/\\\\\\\\].+\\\\.(js|jsx|ts|tsx)$', - '^.+\\\\.module\\\\.(css|sass|scss)$', - ], - moduleNameMapper: moduleNameMapperConfig, - }; - - return { - ...defaultJestConfig, - ...otherOverrides, - }; -}; - -/** - * This will load the existing just setup, or use the default if it exists - */ -export const loadJestPluginConfig = (baseDir: string = process.cwd()) => { - const cfgpath = path.resolve(baseDir, 'jest.config.js'); - if (!fs.existsSync(cfgpath)) { - const toolkitDir = path.dirname(require.resolve(`@grafana/toolkit/package.json`)); - const src = path.join(toolkitDir, 'src/config/jest.plugin.config.local.js'); - fs.copyFileSync(src, cfgpath); - console.log('Using standard jest plugin config', src); - } - return require(cfgpath); -}; diff --git a/packages/grafana-toolkit/src/config/matchMedia.js b/packages/grafana-toolkit/src/config/matchMedia.js deleted file mode 100644 index f4489dec143..00000000000 --- a/packages/grafana-toolkit/src/config/matchMedia.js +++ /dev/null @@ -1,14 +0,0 @@ -// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom -Object.defineProperty(global, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((query) => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // deprecated - removeListener: jest.fn(), // deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}); diff --git a/packages/grafana-toolkit/src/config/mocks/jestSetup/noOverrides/package.json b/packages/grafana-toolkit/src/config/mocks/jestSetup/noOverrides/package.json deleted file mode 100644 index 0967ef424bc..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/jestSetup/noOverrides/package.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/grafana-toolkit/src/config/mocks/jestSetup/overrides/package.json b/packages/grafana-toolkit/src/config/mocks/jestSetup/overrides/package.json deleted file mode 100644 index 0b7f4df4d03..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/jestSetup/overrides/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "jest": { - "moduleNameMapper": { - "someOverride": "somePath" - }, - "snapshotSerializers": "serializers", - "globalSetup": "path", - "globalTeardown": "path", - "testEnvironment": "node" - } -} diff --git a/packages/grafana-toolkit/src/config/mocks/jestSetup/unsupportedOverrides/package.json b/packages/grafana-toolkit/src/config/mocks/jestSetup/unsupportedOverrides/package.json deleted file mode 100644 index 8450f881628..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/jestSetup/unsupportedOverrides/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "jest": { - "runner": "some-runner" - } -} diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/duplicates/src/styles/dark.css b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/duplicates/src/styles/dark.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/duplicates/src/styles/dark.scss b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/duplicates/src/styles/dark.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/duplicates/src/styles/light.css b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/duplicates/src/styles/light.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/missing-theme-file/src/styles/light.css b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/missing-theme-file/src/styles/light.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/no-theme-files/src/styles/whatever.css b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/no-theme-files/src/styles/whatever.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/ok/src/styles/dark.css b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/ok/src/styles/dark.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/ok/src/styles/light.css b/packages/grafana-toolkit/src/config/mocks/stylesheetsSupport/ok/src/styles/light.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/noOverride/package.json b/packages/grafana-toolkit/src/config/mocks/webpack/noOverride/package.json deleted file mode 100644 index 4efb0bb15e1..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/noOverride/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "Testversion" -} diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/overrides/package.json b/packages/grafana-toolkit/src/config/mocks/webpack/overrides/package.json deleted file mode 100644 index 4efb0bb15e1..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/overrides/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "Testversion" -} diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/overrides/webpack.config.js b/packages/grafana-toolkit/src/config/mocks/webpack/overrides/webpack.config.js deleted file mode 100644 index e96ceeed1a9..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/overrides/webpack.config.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; -const { cloneDeep } = require('lodash'); - -const overrideWebpackConfig = (originalConfig, options) => { - const config = cloneDeep(originalConfig); - config.name = 'customConfig'; - return config; -}; - -module.exports = overrideWebpackConfig; diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/overridesNamedExport/package.json b/packages/grafana-toolkit/src/config/mocks/webpack/overridesNamedExport/package.json deleted file mode 100644 index 4efb0bb15e1..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/overridesNamedExport/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "Testversion" -} diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/overridesNamedExport/webpack.config.js b/packages/grafana-toolkit/src/config/mocks/webpack/overridesNamedExport/webpack.config.js deleted file mode 100644 index d2e086e5f00..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/overridesNamedExport/webpack.config.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; -const { cloneDeep } = require('lodash'); - -module.exports.getWebpackConfig = (originalConfig, options) => { - const config = cloneDeep(originalConfig); - config.name = 'customConfig'; - return config; -}; diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/unsupportedOverride/package.json b/packages/grafana-toolkit/src/config/mocks/webpack/unsupportedOverride/package.json deleted file mode 100644 index 4efb0bb15e1..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/unsupportedOverride/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "Testversion" -} diff --git a/packages/grafana-toolkit/src/config/mocks/webpack/unsupportedOverride/webpack.config.js b/packages/grafana-toolkit/src/config/mocks/webpack/unsupportedOverride/webpack.config.js deleted file mode 100644 index f62e5e8d0b4..00000000000 --- a/packages/grafana-toolkit/src/config/mocks/webpack/unsupportedOverride/webpack.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/* WRONG CONFIG ON PURPOSE - DO NOT COPY THIS */ - -module.exports.config = { - name: 'test', -}; diff --git a/packages/grafana-toolkit/src/config/prettier.plugin.config.json b/packages/grafana-toolkit/src/config/prettier.plugin.config.json deleted file mode 100644 index 49bc44f91c6..00000000000 --- a/packages/grafana-toolkit/src/config/prettier.plugin.config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 120 -} diff --git a/packages/grafana-toolkit/src/config/prettier.plugin.rc.js b/packages/grafana-toolkit/src/config/prettier.plugin.rc.js deleted file mode 100644 index c14684b0835..00000000000 --- a/packages/grafana-toolkit/src/config/prettier.plugin.rc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('@grafana/toolkit/src/config/prettier.plugin.config.json'), -}; diff --git a/packages/grafana-toolkit/src/config/react-inlinesvg.tsx b/packages/grafana-toolkit/src/config/react-inlinesvg.tsx deleted file mode 100644 index d540f3aabf8..00000000000 --- a/packages/grafana-toolkit/src/config/react-inlinesvg.tsx +++ /dev/null @@ -1,25 +0,0 @@ -// Due to the grafana/ui Icon component making fetch requests to -// `/public/img/icon/.svg` we need to mock react-inlinesvg to prevent -// the failed fetch requests from displaying errors in console. - -import React from 'react'; - -type Callback = (...args: any[]) => void; - -export interface StorageItem { - content: string; - queue: Callback[]; - status: string; -} - -export const cacheStore: { [key: string]: StorageItem } = Object.create(null); - -const SVG_FILE_NAME_REGEX = /(.+)\/(.+)\.svg$/; - -const InlineSVG = ({ src }: { src: string }) => { - // testId will be the file name without extension (e.g. `public/img/icons/angle-double-down.svg` -> `angle-double-down`) - const testId = src.replace(SVG_FILE_NAME_REGEX, '$2'); - return ; -}; - -export default InlineSVG; diff --git a/packages/grafana-toolkit/src/config/styles.mock.js b/packages/grafana-toolkit/src/config/styles.mock.js deleted file mode 100644 index ebea82fe270..00000000000 --- a/packages/grafana-toolkit/src/config/styles.mock.js +++ /dev/null @@ -1,4 +0,0 @@ -// Mock for handling stylesheet file imports in tests -// https://jestjs.io/docs/en/webpack.html#handling-static-assets - -module.exports = {}; diff --git a/packages/grafana-toolkit/src/config/tsconfig.plugin.json b/packages/grafana-toolkit/src/config/tsconfig.plugin.json deleted file mode 100644 index e2948cce30f..00000000000 --- a/packages/grafana-toolkit/src/config/tsconfig.plugin.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "alwaysStrict": true, - "declaration": false - }, - "extends": "@grafana/tsconfig" -} diff --git a/packages/grafana-toolkit/src/config/tsconfig.plugin.local.json b/packages/grafana-toolkit/src/config/tsconfig.plugin.local.json deleted file mode 100644 index dcc552c6d9a..00000000000 --- a/packages/grafana-toolkit/src/config/tsconfig.plugin.local.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "rootDir": "./src", - "baseUrl": "./src", - "jsx": "react" - }, - "extends": "@grafana/toolkit/src/config/tsconfig.plugin.json", - "include": ["src", "types"] -} diff --git a/packages/grafana-toolkit/src/config/utils/getPluginId.ts b/packages/grafana-toolkit/src/config/utils/getPluginId.ts deleted file mode 100644 index 5acf8090688..00000000000 --- a/packages/grafana-toolkit/src/config/utils/getPluginId.ts +++ /dev/null @@ -1,11 +0,0 @@ -import path from 'path'; - -let PLUGIN_ID: string; - -export const getPluginId = () => { - if (!PLUGIN_ID) { - const pluginJson = require(path.resolve(process.cwd(), 'src/plugin.json')); - PLUGIN_ID = pluginJson.id; - } - return PLUGIN_ID; -}; diff --git a/packages/grafana-toolkit/src/config/utils/pluginValidation.test.ts b/packages/grafana-toolkit/src/config/utils/pluginValidation.test.ts deleted file mode 100644 index 337057108eb..00000000000 --- a/packages/grafana-toolkit/src/config/utils/pluginValidation.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getPluginJson, validatePluginJson } from './pluginValidation'; - -describe('pluginValidation', () => { - describe('plugin.json', () => { - test('missing plugin.json file', () => { - expect(() => getPluginJson(`${__dirname}/mocks/missing-plugin.json`)).toThrowError(); - }); - }); - - describe('validatePluginJson', () => { - test('missing plugin.json file', () => { - expect(() => validatePluginJson({})).toThrow('Plugin id is missing in plugin.json'); - }); - }); -}); diff --git a/packages/grafana-toolkit/src/config/utils/pluginValidation.ts b/packages/grafana-toolkit/src/config/utils/pluginValidation.ts deleted file mode 100644 index 80b88ce8f13..00000000000 --- a/packages/grafana-toolkit/src/config/utils/pluginValidation.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { PluginMeta } from '@grafana/data'; - -export const validatePluginJson = (pluginJson: any) => { - if (!pluginJson.id) { - throw new Error('Plugin id is missing in plugin.json'); - } - - if (!pluginJson.info) { - throw new Error('Plugin info node is missing in plugin.json'); - } - - if (!pluginJson.info.version) { - throw new Error('Plugin info.version is missing in plugin.json'); - } - - const types = ['panel', 'datasource', 'app']; - const type = pluginJson.type; - if (!types.includes(type)) { - throw new Error('Invalid plugin type in plugin.json: ' + type); - } - - if (!pluginJson.id.endsWith('-' + type)) { - throw new Error('[plugin.json] id should end with: -' + type); - } -}; - -export const getPluginJson = (path: string): PluginMeta => { - let pluginJson; - try { - pluginJson = require(path); - } catch (e) { - throw new Error('Unable to find: ' + path); - } - - validatePluginJson(pluginJson); - - return pluginJson as PluginMeta; -}; - -export const assertRootUrlIsValid = (rootUrl: string) => { - try { - new URL(rootUrl); - } catch (err) { - throw new Error(`${rootUrl} is not a valid URL`); - } -}; diff --git a/packages/grafana-toolkit/src/config/webpack.plugin.config.test.ts b/packages/grafana-toolkit/src/config/webpack.plugin.config.test.ts deleted file mode 100644 index 5791f5920f7..00000000000 --- a/packages/grafana-toolkit/src/config/webpack.plugin.config.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import fs, { BigIntStats } from 'fs'; - -import { findModuleFiles, loadWebpackConfig } from './webpack.plugin.config'; -// eslint-disable-next-line no-duplicate-imports -import * as webpackConfig from './webpack.plugin.config'; - -jest.mock('./webpack/loaders', () => ({ - getFileLoaders: (): Array<{}> => [], - getStylesheetEntries: () => ({}), - getStyleLoaders: (): Array<{}> => [], -})); - -const modulePathsMock = [ - 'some/path/module.ts', - 'some/path/module.ts.whatever', - 'some/path/module.tsx', - 'some/path/module.tsx.whatever', - 'some/path/anotherFile.ts', - 'some/path/anotherFile.tsx', -]; - -describe('Plugin webpack config', () => { - describe('findModuleTs', () => { - beforeAll(() => { - jest.spyOn(fs, 'statSync').mockReturnValue({ - isDirectory: () => false, - } as BigIntStats); - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - it('finds module.ts and module.tsx files', async () => { - const moduleFiles = await findModuleFiles('/', modulePathsMock); - expect(moduleFiles.length).toBe(2); - // normalize windows path - \\ -> / - expect(moduleFiles.map((p) => p.replace(/\\/g, '/'))).toEqual(['/some/path/module.ts', '/some/path/module.tsx']); - }); - }); - - describe('loadWebpackConfig', () => { - beforeAll(() => { - jest.spyOn(webpackConfig, 'findModuleFiles').mockReturnValue(new Promise((res, _) => res([]))); - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - it('uses default config if no override exists', async () => { - const spy = jest.spyOn(process, 'cwd'); - spy.mockReturnValue(`${__dirname}/mocks/webpack/noOverride/`); - await loadWebpackConfig({}); - }); - - it('calls customConfig if it exists', async () => { - const spy = jest.spyOn(process, 'cwd'); - spy.mockReturnValue(`${__dirname}/mocks/webpack/overrides/`); - const config = await loadWebpackConfig({}); - expect(config.name).toBe('customConfig'); - }); - - it('loads export named getWebpackConfiguration', async () => { - const spy = jest.spyOn(process, 'cwd'); - spy.mockReturnValue(`${__dirname}/mocks/webpack/overridesNamedExport/`); - const config = await loadWebpackConfig({}); - expect(config.name).toBe('customConfig'); - }); - - it('throws an error if module does not export function', async () => { - const spy = jest.spyOn(process, 'cwd'); - spy.mockReturnValue(`${__dirname}/mocks/webpack/unsupportedOverride/`); - await expect(loadWebpackConfig({})).rejects.toThrowError(); - }); - }); -}); diff --git a/packages/grafana-toolkit/src/config/webpack.plugin.config.ts b/packages/grafana-toolkit/src/config/webpack.plugin.config.ts deleted file mode 100644 index 382c1e0b0a9..00000000000 --- a/packages/grafana-toolkit/src/config/webpack.plugin.config.ts +++ /dev/null @@ -1,289 +0,0 @@ -import * as webpack from 'webpack'; - -import { getStyleLoaders, getStylesheetEntries, getFileLoaders } from './webpack/loaders'; - -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); -const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); -const fs = require('fs'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const path = require('path'); -const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const util = require('util'); - -const readdirPromise = util.promisify(fs.readdir); -const accessPromise = util.promisify(fs.access); - -export interface WebpackConfigurationOptions { - watch?: boolean; - production?: boolean; - preserveConsole?: boolean; -} - -type WebpackConfigurationGetter = (options: WebpackConfigurationOptions) => Promise; - -export type CustomWebpackConfigurationGetter = ( - originalConfig: webpack.Configuration, - options: WebpackConfigurationOptions -) => webpack.Configuration; - -export const findModuleFiles = async (base: string, files?: string[], result?: string[]) => { - files = files || (await readdirPromise(base)); - result = result || []; - - if (files) { - await Promise.all( - files.map(async (file) => { - const newbase = path.join(base, file); - if (fs.statSync(newbase).isDirectory()) { - result = await findModuleFiles(newbase, await readdirPromise(newbase), result); - } else { - const filename = path.basename(file); - if (/^module.(t|j)sx?$/.exec(filename)) { - // @ts-ignore - result.push(newbase); - } - } - }) - ); - } - return result; -}; - -const getModuleFiles = () => { - return findModuleFiles(path.resolve(process.cwd(), 'src')); -}; - -const getManualChunk = (id: string) => { - if (id.endsWith('module.ts') || id.endsWith('module.js') || id.endsWith('module.tsx')) { - const idx = id.lastIndexOf(path.sep + 'src' + path.sep); - if (idx > 0) { - const name = id.substring(idx + 5, id.lastIndexOf('.')); - - return { - name, - module: id, - }; - } - } - return null; -}; - -const getEntries = async () => { - const entries: { [key: string]: string } = {}; - const modules = await getModuleFiles(); - - modules.forEach((modFile) => { - const mod = getManualChunk(modFile); - // @ts-ignore - entries[mod.name] = mod.module; - }); - return { - ...entries, - ...getStylesheetEntries(), - }; -}; - -const getCommonPlugins = (options: WebpackConfigurationOptions) => { - const hasREADME = fs.existsSync(path.resolve(process.cwd(), 'src', 'README.md')); - const packageJson = require(path.resolve(process.cwd(), 'package.json')); - return [ - new MiniCssExtractPlugin({ - // both options are optional - filename: 'styles/[name].css', - }), - new CopyWebpackPlugin({ - patterns: [ - // If src/README.md exists use it; otherwise the root README - { from: hasREADME ? 'README.md' : '../README.md', to: '.', force: true, priority: 1, noErrorOnMissing: true }, - { from: 'plugin.json', to: '.', noErrorOnMissing: true }, - { from: '**/README.md', to: '[path]README.md', priority: 0, noErrorOnMissing: true }, - { from: '../LICENSE', to: '.', noErrorOnMissing: true }, - { from: '../CHANGELOG.md', to: '.', force: true, noErrorOnMissing: true }, - { from: '**/*.{json,svg,png,html}', to: '.', noErrorOnMissing: true }, - { from: 'img/**/*', to: '.', noErrorOnMissing: true }, - { from: 'libs/**/*', to: '.', noErrorOnMissing: true }, - { from: 'static/**/*', to: '.', noErrorOnMissing: true }, - ], - }), - - new ReplaceInFileWebpackPlugin([ - { - dir: 'dist', - files: ['plugin.json', 'README.md'], - rules: [ - { - search: '%VERSION%', - replace: packageJson.version, - }, - { - search: '%TODAY%', - replace: new Date().toISOString().substring(0, 10), - }, - ], - }, - ]), - new ForkTsCheckerWebpackPlugin({ - typescript: { configFile: path.join(process.cwd(), 'tsconfig.json') }, - issue: { - include: [{ file: '**/*.{ts,tsx}' }], - }, - }), - ]; -}; - -const getBaseWebpackConfig: WebpackConfigurationGetter = async (options) => { - const plugins = getCommonPlugins(options); - const optimization: { [key: string]: any } = {}; - - if (options.production) { - const compressOptions = { drop_console: !options.preserveConsole, drop_debugger: true }; - optimization.minimizer = [ - new TerserPlugin({ terserOptions: { compress: compressOptions } }), - new CssMinimizerPlugin(), - ]; - optimization.chunkIds = 'total-size'; - optimization.moduleIds = 'size'; - } else if (options.watch) { - plugins.push(new HtmlWebpackPlugin()); - } - - return { - mode: options.production ? 'production' : 'development', - target: 'web', - context: path.join(process.cwd(), 'src'), - devtool: 'source-map', - entry: await getEntries(), - output: { - filename: '[name].js', - path: path.join(process.cwd(), 'dist'), - libraryTarget: 'amd', - publicPath: '/', - }, - performance: { hints: false }, - externals: [ - 'lodash', - 'jquery', - 'moment', - 'slate', - 'emotion', - '@emotion/react', - '@emotion/css', - 'prismjs', - 'slate-plain-serializer', - 'slate-react', - 'react', - 'react-dom', - 'react-redux', - 'redux', - 'rxjs', - 'react-router', - 'react-router-dom', - 'd3', - 'angular', - '@grafana/ui', - '@grafana/runtime', - '@grafana/data', - ({ request }, callback) => { - const prefix = 'grafana/'; - if (request?.indexOf(prefix) === 0) { - return callback(undefined, request.slice(prefix.length)); - } - - callback(); - }, - ], - plugins, - resolve: { - extensions: ['.ts', '.tsx', '.js'], - modules: [path.resolve(process.cwd(), 'src'), 'node_modules'], - fallback: { - buffer: false, - fs: false, - stream: false, - http: false, - https: false, - string_decoder: false, - os: false, - timers: false, - }, - }, - module: { - rules: [ - { - test: /\.[tj]sx?$/, - use: { - loader: require.resolve('babel-loader'), - options: { - cacheDirectory: true, - cacheCompression: false, - presets: [ - [require.resolve('@babel/preset-env'), { modules: false }], - [ - require.resolve('@babel/preset-typescript'), - { - allowNamespaces: true, - allowDeclareFields: true, - }, - ], - [require.resolve('@babel/preset-react')], - ], - plugins: [ - [ - require.resolve('@babel/plugin-transform-typescript'), - { - allowNamespaces: true, - allowDeclareFields: true, - }, - ], - require.resolve('@babel/plugin-proposal-class-properties'), - [require.resolve('@babel/plugin-proposal-object-rest-spread'), { loose: true }], - require.resolve('@babel/plugin-transform-react-constant-elements'), - require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), - require.resolve('@babel/plugin-proposal-optional-chaining'), - require.resolve('@babel/plugin-syntax-dynamic-import'), - require.resolve('babel-plugin-angularjs-annotate'), - ], - }, - }, - exclude: /node_modules/, - }, - ...getStyleLoaders(), - { - test: /\.html$/, - exclude: [/node_modules/], - use: { - loader: require.resolve('html-loader'), - }, - }, - ...getFileLoaders(), - ], - }, - optimization, - }; -}; - -export const loadWebpackConfig: WebpackConfigurationGetter = async (options) => { - const baseConfig = await getBaseWebpackConfig(options); - const customWebpackPath = path.resolve(process.cwd(), 'webpack.config.js'); - - try { - await accessPromise(customWebpackPath); - const customConfig = require(customWebpackPath); - const configGetter = customConfig.getWebpackConfig || customConfig; - if (typeof configGetter !== 'function') { - throw Error( - 'Custom webpack config needs to export a function implementing CustomWebpackConfigurationGetter. Function needs to be ' + - 'module export or named "getWebpackConfig"' - ); - } - return (configGetter as CustomWebpackConfigurationGetter)(baseConfig, options); - } catch (err: any) { - if (err.code === 'ENOENT') { - return baseConfig; - } - throw err; - } -}; diff --git a/packages/grafana-toolkit/src/config/webpack/loaders.test.ts b/packages/grafana-toolkit/src/config/webpack/loaders.test.ts deleted file mode 100644 index 4d63f47e1a2..00000000000 --- a/packages/grafana-toolkit/src/config/webpack/loaders.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { getStylesheetEntries } from './loaders'; - -describe('Loaders', () => { - describe('stylesheet helpers', () => { - beforeEach(() => { - jest.spyOn(console, 'log').mockImplementation(); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('getStylesheetEntries', () => { - it('returns entries for dark and light theme', () => { - const result = getStylesheetEntries(`${__dirname}/../mocks/stylesheetsSupport/ok`); - expect(Object.keys(result)).toHaveLength(2); - }); - it('throws on theme files duplicates', () => { - const result = () => { - getStylesheetEntries(`${__dirname}/../mocks/stylesheetsSupport/duplicates`); - }; - expect(result).toThrow(); - }); - }); - }); -}); diff --git a/packages/grafana-toolkit/src/config/webpack/loaders.ts b/packages/grafana-toolkit/src/config/webpack/loaders.ts deleted file mode 100644 index f4b46d45458..00000000000 --- a/packages/grafana-toolkit/src/config/webpack/loaders.ts +++ /dev/null @@ -1,129 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import { getPluginId } from '../utils/getPluginId'; - -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); - -const supportedExtensions = ['css', 'scss', 'less', 'sass']; - -const getStylesheetPaths = (root: string = process.cwd()) => { - return [`${root}/src/styles/light`, `${root}/src/styles/dark`]; -}; - -export const getStylesheetEntries = (root: string = process.cwd()) => { - const stylesheetsPaths = getStylesheetPaths(root); - const entries: { [key: string]: string } = {}; - supportedExtensions.forEach((e) => { - stylesheetsPaths.forEach((p) => { - const entryName = p.split('/').slice(-1)[0]; - if (fs.existsSync(`${p}.${e}`)) { - if (entries[entryName]) { - console.log(`\nSeems like you have multiple files for ${entryName} theme:`); - console.log(entries[entryName]); - console.log(`${p}.${e}`); - throw new Error('Duplicated stylesheet'); - } else { - entries[entryName] = `${p}.${e}`; - } - } - }); - }); - - return entries; -}; - -export const getStyleLoaders = () => { - const extractionLoader = { - loader: MiniCssExtractPlugin.loader, - options: { - publicPath: '../', - }, - }; - - const cssLoaders = [ - { - loader: require.resolve('css-loader'), - options: { - importLoaders: 1, - sourceMap: true, - }, - }, - { - loader: require.resolve('postcss-loader'), - options: { - postcssOptions: { - plugins: () => [ - require('postcss-flexbugs-fixes'), - require('postcss-preset-env')({ - autoprefixer: { flexbox: 'no-2009', grid: true }, - }), - ], - }, - }, - }, - ]; - - const styleDir = path.resolve(process.cwd(), 'src', 'styles') + path.sep; - const rules = [ - { - test: /(dark|light)\.css$/, - use: [extractionLoader, ...cssLoaders], - }, - { - test: /(dark|light)\.scss$/, - use: [extractionLoader, ...cssLoaders, require.resolve('sass-loader')], - }, - { - test: /\.css$/, - use: ['style-loader', ...cssLoaders, require.resolve('sass-loader')], - exclude: [`${styleDir}light.css`, `${styleDir}dark.css`], - }, - { - test: /\.s[ac]ss$/, - use: ['style-loader', ...cssLoaders, require.resolve('sass-loader')], - exclude: [`${styleDir}light.scss`, `${styleDir}dark.scss`], - }, - { - test: /\.less$/, - use: [ - { - loader: require.resolve('style-loader'), - }, - ...cssLoaders, - { - loader: require.resolve('less-loader'), - options: { - lessOptions: { - javascriptEnabled: true, - }, - }, - }, - ], - exclude: [`${styleDir}light.less`, `${styleDir}dark.less`], - }, - ]; - - return rules; -}; - -export const getFileLoaders = () => { - return [ - { - test: /\.(png|jpe?g|gif|svg)$/, - type: 'asset/resource', - generator: { - publicPath: `public/plugins/${getPluginId()}/img/`, - outputPath: 'img/', - }, - }, - { - test: /\.(woff|woff2|eot|ttf|otf)(\?v=\d+\.\d+\.\d+)?$/, - type: 'asset/resource', - generator: { - publicPath: `public/plugins/${getPluginId()}/fonts/`, - outputPath: 'fonts/', - }, - }, - ]; -}; diff --git a/packages/grafana-toolkit/src/plugins/env.ts b/packages/grafana-toolkit/src/plugins/env.ts deleted file mode 100644 index f79b68d449d..00000000000 --- a/packages/grafana-toolkit/src/plugins/env.ts +++ /dev/null @@ -1,147 +0,0 @@ -import execa from 'execa'; -import fs from 'fs'; -import path from 'path'; - -import { PluginBuildInfo } from '@grafana/data'; - -import { JobInfo } from './types'; - -const getJobFromProcessArgv = () => { - const arg = process.argv[2]; - if (arg && arg.startsWith('plugin:ci-')) { - const task = arg.substring('plugin:ci-'.length); - if ('build' === task) { - if ('--backend' === process.argv[3] && process.argv[4]) { - return task + '_' + process.argv[4]; - } - return 'build_plugin'; - } - return task; - } - return 'unknown_job'; -}; - -export const job = - (process.env.DRONE_STEP_NAME ? process.env.DRONE_STEP_NAME : process.env.CIRCLE_JOB) || getJobFromProcessArgv(); - -export const getPluginBuildInfo = async (): Promise => { - if (process.env.CI === 'true') { - let repo: string | undefined; - let branch: string | undefined; - let hash: string | undefined; - let build: number | undefined; - let pr: number | undefined; - if (process.env.DRONE === 'true') { - repo = process.env.DRONE_REPO_LINK; - branch = process.env.DRONE_BRANCH; - hash = process.env.DRONE_COMMIT_SHA; - build = parseInt(process.env.DRONE_BUILD_NUMBER || '', 10); - pr = parseInt(process.env.DRONE_PULL_REQUEST || '', 10); - } else if (process.env.CIRCLECI === 'true') { - repo = process.env.CIRCLE_REPOSITORY_URL; - branch = process.env.CIRCLE_BRANCH; - hash = process.env.CIRCLE_SHA1; - build = parseInt(process.env.CIRCLE_BUILD_NUM || '', 10); - const url = process.env.CIRCLE_PULL_REQUEST || ''; - const idx = url.lastIndexOf('/') + 1; - pr = parseInt(url.substring(idx), 10); - } - - const info: PluginBuildInfo = { - time: Date.now(), - repo, - branch, - hash, - }; - if (pr) { - info.pr = pr; - } - if (build) { - info.number = build; - } - return info; - } - - const branch = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD']); - const hash = await execa('git', ['rev-parse', 'HEAD']); - return { - time: Date.now(), - branch: branch.stdout, - hash: hash.stdout, - }; -}; - -export const getBuildNumber = (): number | undefined => { - if (process.env.DRONE === 'true') { - return parseInt(process.env.DRONE_BUILD_NUMBER || '', 10); - } else if (process.env.CIRCLECI === 'true') { - return parseInt(process.env.CIRCLE_BUILD_NUM || '', 10); - } - - return undefined; -}; - -export const getPullRequestNumber = (): number | undefined => { - if (process.env.DRONE === 'true') { - return parseInt(process.env.DRONE_PULL_REQUEST || '', 10); - } else if (process.env.CIRCLECI === 'true') { - const url = process.env.CIRCLE_PULL_REQUEST || ''; - const idx = url.lastIndexOf('/') + 1; - return parseInt(url.substring(idx), 10); - } - - return undefined; -}; - -export const getJobFolder = () => { - const dir = path.resolve(process.cwd(), 'ci', 'jobs', job); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - return dir; -}; - -export const getCiFolder = () => { - const dir = path.resolve(process.cwd(), 'ci'); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - return dir; -}; - -export const writeJobStats = (startTime: number, workDir: string) => { - const endTime = Date.now(); - const stats: JobInfo = { - job, - startTime, - endTime, - elapsed: endTime - startTime, - buildNumber: getBuildNumber(), - }; - const f = path.resolve(workDir, 'job.json'); - fs.writeFile(f, JSON.stringify(stats, null, 2), (err) => { - if (err) { - throw new Error('Unable to stats: ' + f); - } - }); -}; - -// https://circleci.com/api/v1.1/project/github/NatelEnergy/grafana-discrete-panel/latest/artifacts -export async function getCircleDownloadBaseURL(): Promise { - try { - const axios = require('axios'); - const repo = process.env.CIRCLE_PROJECT_REPONAME; - const user = process.env.CIRCLE_PROJECT_USERNAME; - let url = `https://circleci.com/api/v1.1/project/github/${user}/${repo}/latest/artifacts`; - const rsp = await axios.get(url); - for (const s of rsp.data) { - const { path, url } = s; - if (url && path && path.endsWith('report.json')) { - return url.substring(url.length - 'report.json'.length); - } - } - } catch (e) { - console.log('Error reading CircleCI artifact URL', e); - } - return undefined; -} diff --git a/packages/grafana-toolkit/src/plugins/index.ts b/packages/grafana-toolkit/src/plugins/index.ts deleted file mode 100644 index c302e909020..00000000000 --- a/packages/grafana-toolkit/src/plugins/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './env'; -export * from './utils'; -export * from './workflow'; -export * from './types'; diff --git a/packages/grafana-toolkit/src/plugins/manifest.ts b/packages/grafana-toolkit/src/plugins/manifest.ts deleted file mode 100644 index 17c1f052170..00000000000 --- a/packages/grafana-toolkit/src/plugins/manifest.ts +++ /dev/null @@ -1,91 +0,0 @@ -import crypto from 'crypto'; -import fs from 'fs'; -import path from 'path'; - -import { ManifestInfo } from './types'; - -const MANIFEST_FILE = 'MANIFEST.txt'; - -async function* walk(dir: string, baseDir: string): AsyncGenerator { - for await (const d of await (fs.promises as any).opendir(dir)) { - const entry = path.posix.join(dir, d.name); - if (d.isDirectory()) { - yield* await walk(entry, baseDir); - } else if (d.isFile()) { - yield path.posix.relative(baseDir, entry); - } else if (d.isSymbolicLink()) { - const realPath = await (fs.promises as any).realpath(entry); - if (!realPath.startsWith(baseDir)) { - throw new Error( - `symbolic link ${path.posix.relative( - baseDir, - entry - )} targets a file outside of the base directory: ${baseDir}` - ); - } - // if resolved symlink target is a file include it in the manifest - const stats = await (fs.promises as any).stat(realPath); - if (stats.isFile()) { - yield path.posix.relative(baseDir, entry); - } - } - } -} - -export async function buildManifest(dir: string): Promise { - const pluginJson = JSON.parse(fs.readFileSync(path.join(dir, 'plugin.json'), { encoding: 'utf8' })); - - const manifest = { - plugin: pluginJson.id, - version: pluginJson.info.version, - files: {}, - } as ManifestInfo; - - for await (const p of await walk(dir, dir)) { - if (p === MANIFEST_FILE) { - continue; - } - - manifest.files[p] = crypto - .createHash('sha256') - .update(fs.readFileSync(path.join(dir, p))) - .digest('hex'); - } - - return manifest; -} - -export async function signManifest(manifest: ManifestInfo): Promise { - const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY; - if (!GRAFANA_API_KEY) { - throw new Error('You must enter a GRAFANA_API_KEY to sign the plugin manifest'); - } - - const GRAFANA_COM_URL = process.env.GRAFANA_COM_URL || 'https://grafana.com/api'; - const url = GRAFANA_COM_URL + '/plugins/ci/sign'; - - const axios = require('axios'); - - try { - const info = await axios.post(url, manifest, { - headers: { Authorization: 'Bearer ' + GRAFANA_API_KEY }, - }); - if (info.status !== 200) { - console.warn('Error: ', info); - throw new Error('Error signing manifest'); - } - - return info.data; - } catch (err: any) { - if (err.response?.data?.message) { - throw new Error('Error signing manifest: ' + err.response.data.message); - } - - throw new Error('Error signing manifest: ' + err.message); - } -} - -export async function saveManifest(dir: string, signedManifest: string): Promise { - fs.writeFileSync(path.join(dir, MANIFEST_FILE), signedManifest); - return true; -} diff --git a/packages/grafana-toolkit/src/plugins/types.ts b/packages/grafana-toolkit/src/plugins/types.ts deleted file mode 100644 index 296cbc8252a..00000000000 --- a/packages/grafana-toolkit/src/plugins/types.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { PluginMeta, KeyValue } from '@grafana/data'; - -export interface PluginPackageDetails { - plugin: ZipFileInfo; - docs?: ZipFileInfo; -} - -export interface PluginBuildReport { - plugin: PluginMeta; - packages: PluginPackageDetails; - workflow: WorkflowInfo; - coverage: CoverageInfo[]; - tests: TestResultsInfo[]; - git?: GitLogInfo; - pullRequest?: number; - artifactsBaseURL?: string; - grafanaVersion?: KeyValue; -} - -export interface JobInfo { - job?: string; - startTime: number; - endTime: number; - elapsed: number; - status?: string; - buildNumber?: number; -} - -export interface WorkflowInfo extends JobInfo { - workflowId?: string; - jobs: JobInfo[]; - user?: string; - repo?: string; -} - -export interface CoverageDetails { - total: number; - covered: number; - skipped: number; - pct: number; -} - -export interface CoverageInfo { - job: string; - summary: { [key: string]: CoverageDetails }; - report?: string; // path to report -} - -export interface TestResultsInfo { - job: string; - grafana?: any; - error?: string; - passed: number; - failed: number; - screenshots: string[]; -} - -export interface CountAndSize { - count: number; - bytes: number; -} - -export interface ExtensionSize { - [key: string]: CountAndSize; -} - -export interface ZipFileInfo { - name: string; - size: number; - contents: ExtensionSize; - sha1?: string; - md5?: string; -} - -interface UserInfo { - name: string; - email: string; - time?: number; -} - -export interface GitLogInfo { - commit: string; - tree: string; - subject: string; - body?: string; - notes?: string; - author: UserInfo; - commiter: UserInfo; -} - -export interface ManifestInfo { - // time: number; << filled in by the server - // keyId: string; << filled in by the server - // signedByOrg: string; << filled in by the server - // signedByOrgName: string; << filled in by the server - signatureType?: string; // filled in by the server if not specified - rootUrls?: string[]; // for private signatures - plugin: string; - version: string; - files: Record; - toolkit?: { - version: string; - }; -} diff --git a/packages/grafana-toolkit/src/plugins/utils.ts b/packages/grafana-toolkit/src/plugins/utils.ts deleted file mode 100644 index e968fc31fd0..00000000000 --- a/packages/grafana-toolkit/src/plugins/utils.ts +++ /dev/null @@ -1,132 +0,0 @@ -import execa from 'execa'; -import fs from 'fs'; -import path from 'path'; - -import { KeyValue } from '@grafana/data'; - -import { ExtensionSize, ZipFileInfo, GitLogInfo } from './types'; - -const md5File = require('md5-file'); - -export function getGrafanaVersions(): KeyValue { - const dir = path.resolve(process.cwd(), 'node_modules', '@grafana'); - const versions: KeyValue = {}; - try { - fs.readdirSync(dir).forEach((file) => { - const json = require(path.resolve(dir, file, 'package.json')); - versions[file] = json.version; - }); - } catch (err) { - console.warn('Error reading toolkit versions', err); - } - return versions; -} - -export function getFileSizeReportInFolder(dir: string, info?: ExtensionSize): ExtensionSize { - const acc: ExtensionSize = info ? info : {}; - - const files = fs.readdirSync(dir); - if (files) { - files.forEach((file) => { - const newbase = path.join(dir, file); - const stat = fs.statSync(newbase); - if (stat.isDirectory()) { - getFileSizeReportInFolder(newbase, info); - } else { - let ext = '_none_'; - const idx = file.lastIndexOf('.'); - if (idx > 0) { - ext = file.substring(idx + 1).toLowerCase(); - } - const current = acc[ext]; - if (current) { - current.count += 1; - current.bytes += stat.size; - } else { - acc[ext] = { bytes: stat.size, count: 1 }; - } - } - }); - } - return acc; -} - -export async function getPackageDetails(zipFile: string, zipSrc: string, writeChecksum = true): Promise { - const zipStats = fs.statSync(zipFile); - if (zipStats.size < 100) { - throw new Error('Invalid zip file: ' + zipFile); - } - const info: ZipFileInfo = { - name: path.basename(zipFile), - size: zipStats.size, - contents: getFileSizeReportInFolder(zipSrc), - }; - try { - const exe = await execa('shasum', [zipFile]); - const idx = exe.stdout.indexOf(' '); - const sha1 = exe.stdout.substring(0, idx); - if (writeChecksum) { - fs.writeFile(zipFile + '.sha1', sha1, (err) => {}); - } - info.sha1 = sha1; - } catch { - console.warn('Unable to read SHA1 Checksum'); - } - try { - info.md5 = md5File.sync(zipFile); - } catch { - console.warn('Unable to read MD5 Checksum'); - } - return info; -} - -export function findImagesInFolder(dir: string, prefix = '', append?: string[]): string[] { - const imgs = append || []; - - const files = fs.readdirSync(dir); - if (files) { - files.forEach((file) => { - if (file.endsWith('.png')) { - imgs.push(file); - } - }); - } - - return imgs; -} - -export async function readGitLog(): Promise { - try { - let exe = await execa('git', [ - 'log', - '-1', // last line - '--pretty=format:{%n "commit": "%H",%n "tree": "%T",%n "subject": "%s",%n "author": {%n "name": "%aN",%n "email": "%aE",%n "time":"%at" },%n "commiter": {%n "name": "%cN",%n "email": "%cE",%n "time":"%ct" }%n}', - ]); - const info = JSON.parse(exe.stdout) as GitLogInfo; - - // Read the body - exe = await execa('git', [ - 'log', - '-1', // last line - '--pretty=format:%b', // Just the body (with newlines!) - ]); - if (exe.stdout && exe.stdout.length) { - info.body = exe.stdout.trim(); - } - - // Read any commit notes - exe = await execa('git', [ - 'log', - '-1', // last line - '--pretty=format:%N', // commit notes (with newlines!) - ]); - if (exe.stdout && exe.stdout.length) { - info.notes = exe.stdout.trim(); - } - - return info; - } catch (err) { - console.warn('Error REading Git log info', err); - } - return undefined; -} diff --git a/packages/grafana-toolkit/src/plugins/workflow.ts b/packages/grafana-toolkit/src/plugins/workflow.ts deleted file mode 100644 index ea653836841..00000000000 --- a/packages/grafana-toolkit/src/plugins/workflow.ts +++ /dev/null @@ -1,100 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import { getBuildNumber, getCiFolder } from './env'; -import { JobInfo, WorkflowInfo, CoverageInfo, TestResultsInfo } from './types'; - -export const agregateWorkflowInfo = (): WorkflowInfo => { - const now = Date.now(); - const workflow: WorkflowInfo = { - jobs: [], - startTime: now, - endTime: now, - workflowId: process.env.CIRCLE_WORKFLOW_ID, - repo: process.env.CIRCLE_PROJECT_REPONAME, - user: process.env.CIRCLE_PROJECT_USERNAME, - buildNumber: getBuildNumber(), - elapsed: 0, - }; - - const jobsFolder = path.resolve(getCiFolder(), 'jobs'); - if (fs.existsSync(jobsFolder)) { - const files = fs.readdirSync(jobsFolder); - if (files && files.length) { - files.forEach((file) => { - const p = path.resolve(jobsFolder, file, 'job.json'); - if (fs.existsSync(p)) { - const job = require(p) as JobInfo; - workflow.jobs.push(job); - if (job.startTime < workflow.startTime) { - workflow.startTime = job.startTime; - } - if (job.endTime > workflow.endTime) { - workflow.endTime = job.endTime; - } - } else { - console.log('Missing Job info: ', p); - } - }); - } else { - console.log('NO JOBS IN: ', jobsFolder); - } - } - - workflow.elapsed = workflow.endTime - workflow.startTime; - return workflow; -}; - -export const agregateCoverageInfo = (): CoverageInfo[] => { - const coverage: CoverageInfo[] = []; - const ciDir = getCiFolder(); - const jobsFolder = path.resolve(ciDir, 'jobs'); - if (fs.existsSync(jobsFolder)) { - const files = fs.readdirSync(jobsFolder); - if (files && files.length) { - files.forEach((file) => { - const dir = path.resolve(jobsFolder, file, 'coverage'); - if (fs.existsSync(dir)) { - const s = path.resolve(dir, 'coverage-summary.json'); - const r = path.resolve(dir, 'lcov-report', 'index.html'); - if (fs.existsSync(s)) { - const raw = require(s); - const info: CoverageInfo = { - job: file, - summary: raw.total, - }; - if (fs.existsSync(r)) { - info.report = r.substring(ciDir.length); - } - coverage.push(info); - } - } - }); - } else { - console.log('NO JOBS IN: ', jobsFolder); - } - } - return coverage; -}; - -export const agregateTestInfo = (): TestResultsInfo[] => { - const tests: TestResultsInfo[] = []; - const ciDir = getCiFolder(); - const jobsFolder = path.resolve(ciDir, 'jobs'); - if (fs.existsSync(jobsFolder)) { - const files = fs.readdirSync(jobsFolder); - if (files && files.length) { - files.forEach((file) => { - if (file.startsWith('test')) { - const summary = path.resolve(jobsFolder, file, 'results.json'); - if (fs.existsSync(summary)) { - tests.push(require(summary) as TestResultsInfo); - } - } - }); - } else { - console.log('NO Jobs IN: ', jobsFolder); - } - } - return tests; -}; diff --git a/packages/grafana-toolkit/tsconfig.json b/packages/grafana-toolkit/tsconfig.json index cbfacdd8a00..9252b4591f4 100644 --- a/packages/grafana-toolkit/tsconfig.json +++ b/packages/grafana-toolkit/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "declarationDir": "dist/src", + "declarationDir": "dist/src/cli", "module": "commonjs", - "outDir": "dist/src", + "outDir": "dist/src/cli", "rootDirs": ["."], "paths": { "@grafana/toolkit": ["."] diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 09febdfebd2..1acc87faa7e 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -2,7 +2,7 @@ "author": "Grafana Labs", "license": "Apache-2.0", "name": "@grafana/ui", - "version": "10.0.0-pre", + "version": "10.1.0-pre", "description": "Grafana Components Library", "keywords": [ "grafana", @@ -49,10 +49,10 @@ "dependencies": { "@emotion/css": "11.10.6", "@emotion/react": "11.10.6", - "@grafana/data": "10.0.0-pre", - "@grafana/e2e-selectors": "10.0.0-pre", + "@grafana/data": "10.1.0-pre", + "@grafana/e2e-selectors": "10.1.0-pre", "@grafana/faro-web-sdk": "1.0.2", - "@grafana/schema": "10.0.0-pre", + "@grafana/schema": "10.1.0-pre", "@leeoniya/ufuzzy": "1.0.6", "@monaco-editor/react": "4.4.6", "@popperjs/core": "2.11.6", @@ -63,7 +63,6 @@ "@react-aria/overlays": "3.10.1", "@react-aria/utils": "3.13.1", "@react-stately/menu": "3.4.1", - "@sentry/browser": "6.19.7", "ansicolor": "1.1.100", "calculate-size": "1.1.1", "classnames": "2.3.2", @@ -81,7 +80,7 @@ "monaco-editor": "0.34.0", "ol": "7.2.2", "prismjs": "1.29.0", - "rc-cascader": "3.8.0", + "rc-cascader": "3.10.2", "rc-drawer": "6.1.3", "rc-slider": "10.1.1", "rc-time-picker": "^3.7.3", @@ -97,7 +96,7 @@ "react-inlinesvg": "3.0.2", "react-popper": "2.3.0", "react-popper-tooltip": "4.4.2", - "react-router-dom": "^5.2.0", + "react-router-dom": "5.3.3", "react-select": "5.7.0", "react-select-event": "^5.1.0", "react-table": "7.8.0", @@ -134,10 +133,9 @@ "@storybook/preset-scss": "1.0.3", "@storybook/react": "6.5.16", "@storybook/theming": "6.5.16", - "@testing-library/dom": "8.20.0", + "@testing-library/dom": "9.0.1", "@testing-library/jest-dom": "5.16.5", - "@testing-library/react": "12.1.4", - "@testing-library/react-hooks": "8.0.1", + "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.4.3", "@types/common-tags": "^1.8.0", "@types/d3": "7.4.0", @@ -149,15 +147,15 @@ "@types/mock-raf": "1.0.3", "@types/node": "18.14.6", "@types/prismjs": "1.26.0", - "@types/react": "17.0.42", + "@types/react": "18.0.28", "@types/react-beautiful-dnd": "13.1.3", "@types/react-calendar": "3.9.0", "@types/react-color": "3.0.6", - "@types/react-dom": "17.0.14", + "@types/react-dom": "18.0.11", "@types/react-highlight-words": "0.16.4", "@types/react-router-dom": "5.3.3", "@types/react-table": "7.7.14", - "@types/react-test-renderer": "17.0.1", + "@types/react-test-renderer": "18.0.0", "@types/react-transition-group": "4.4.5", "@types/react-window": "1.8.5", "@types/slate": "0.47.11", @@ -173,9 +171,9 @@ "expose-loader": "4.0.0", "mock-raf": "1.0.1", "process": "^0.11.10", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-test-renderer": "17.0.2", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-test-renderer": "18.2.0", "rimraf": "4.4.0", "rollup": "2.79.1", "rollup-plugin-dts": "^5.0.0", @@ -190,7 +188,7 @@ "webpack": "5.76.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" } } diff --git a/packages/grafana-ui/src/components/Alert/Alert.tsx b/packages/grafana-ui/src/components/Alert/Alert.tsx index 4da17e027ca..e13dd28a294 100644 --- a/packages/grafana-ui/src/components/Alert/Alert.tsx +++ b/packages/grafana-ui/src/components/Alert/Alert.tsx @@ -1,5 +1,6 @@ import { css, cx } from '@emotion/css'; import React, { AriaRole, HTMLAttributes, ReactNode } from 'react'; +import tinycolor2 from 'tinycolor2'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -8,7 +9,6 @@ import { useTheme2 } from '../../themes'; import { IconName } from '../../types/icon'; import { Button } from '../Button/Button'; import { Icon } from '../Icon/Icon'; -import { IconButton } from '../IconButton/IconButton'; export type AlertVariant = 'success' | 'warning' | 'error' | 'info'; @@ -73,7 +73,14 @@ export const Alert = React.forwardRef( {/* If onRemove is specified, giving preference to onRemove */} {onRemove && !buttonContent && (
    - +
    )} @@ -113,6 +120,7 @@ const getStyles = ( ) => { const color = theme.colors[severity]; const borderRadius = theme.shape.borderRadius(); + const borderColor = tinycolor2(color.border).setAlpha(0.2).toString(); return { alert: css` @@ -122,8 +130,10 @@ const getStyles = ( display: flex; flex-direction: row; align-items: stretch; - background: ${theme.colors.background.secondary}; - box-shadow: ${elevated ? theme.shadows.z3 : theme.shadows.z1}; + background: ${color.transparent}; + box-shadow: ${elevated ? theme.shadows.z3 : 'none'}; + padding: ${theme.spacing(1, 2)}; + border: 1px solid ${borderColor}; margin-bottom: ${theme.spacing(bottomSpacing ?? 2)}; margin-top: ${theme.spacing(topSpacing ?? 0)}; @@ -139,21 +149,15 @@ const getStyles = ( } `, icon: css` - padding: ${theme.spacing(2, 3)}; - background: ${color.main}; - border-radius: ${borderRadius} 0 0 ${borderRadius}; - color: ${color.contrastText}; + padding: ${theme.spacing(1, 2, 0, 0)}; + color: ${color.text}; display: flex; - align-items: center; - justify-content: center; - `, - title: css` - font-weight: ${theme.typography.fontWeightMedium}; - color: ${theme.colors.text.primary}; `, + title: css({ + fontWeight: theme.typography.fontWeightMedium, + }), body: css` - color: ${theme.colors.text.secondary}; - padding: ${theme.spacing(2)}; + padding: ${theme.spacing(1, 0)}; flex-grow: 1; display: flex; flex-direction: column; @@ -162,21 +166,23 @@ const getStyles = ( word-break: break-word; `, content: css` - color: ${theme.colors.text.secondary}; - padding-top: ${hasTitle ? theme.spacing(1) : 0}; + padding-top: ${hasTitle ? theme.spacing(0.5) : 0}; max-height: 50vh; overflow-y: auto; `, buttonWrapper: css` - padding: ${theme.spacing(1)}; - background: none; + margin-left: ${theme.spacing(1)}; display: flex; align-items: center; + align-self: center; `, close: css` - padding: ${theme.spacing(2, 1)}; + position: relative; + color: ${theme.colors.text.secondary}; background: none; display: flex; + top: -6px; + right: -14px; `, }; }; diff --git a/packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx b/packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx index a9f45c3cfe6..4faeeacbd3c 100644 --- a/packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx +++ b/packages/grafana-ui/src/components/Alert/InlineBanner.story.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { Alert, AlertVariant, VerticalGroup } from '@grafana/ui'; +import { StoryExample } from '../../utils/storybook/StoryExample'; import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory'; import mdx from '../Alert/Alert.mdx'; @@ -60,4 +61,33 @@ WithActions.args = { buttonContent: 'Close', }; +export const Examples: ComponentStory = () => { + return ( + + + Close} + onRemove={action('Remove button clicked')} + > + Child content that includes some alert details, like maybe what actually happened + + + + + + + + {severities.map((severity) => ( + + Child content + + ))} + + + + ); +}; + export default meta; diff --git a/packages/grafana-ui/src/components/Alert/Toast.story.tsx b/packages/grafana-ui/src/components/Alert/Toast.story.tsx index 852242eead5..90cb66fd439 100644 --- a/packages/grafana-ui/src/components/Alert/Toast.story.tsx +++ b/packages/grafana-ui/src/components/Alert/Toast.story.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { Alert, AlertVariant, VerticalGroup } from '@grafana/ui'; +import { StoryExample } from '../../utils/storybook/StoryExample'; import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory'; import mdx from '../Alert/Alert.mdx'; @@ -29,7 +30,7 @@ const meta: ComponentMeta = { }, }; -export const Toast: ComponentStory = (args) => { +export const Basic: ComponentStory = (args) => { return (
    @@ -41,4 +42,91 @@ export const Toast: ComponentStory = (args) => { ); }; +export function Examples() { + return ( + + + + {severities.map((severity) => ( + + ))} + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam metus urna, aliquam eu scelerisque non, + facilisis eget est. Morbi eleifend egestas massa id vulputate. Fusce dignissim magna lacus, ut molestie odio + feugiat sed. Cras fringilla justo sit amet turpis scelerisque, a volutpat purus iaculis. Nunc sagittis + molestie faucibus. Curabitur at neque luctus, pellentesque urna eget, posuere urna. Nunc malesuada elit in + ipsum dictum egestas. Praesent convallis mauris massa, porta mattis ex gravida ut. Proin consectetur ultrices + tortor sit amet efficitur. Suspendisse nec turpis dapibus mauris venenatis maximus quis eget orci. Ut semper + enim magna, ullamcorper elementum sapien pharetra vitae. Vivamus at nulla ut metus bibendum ornare et ut leo. + Proin ante turpis, ornare a malesuada et, rutrum nec lorem. Maecenas vestibulum orci vel nibh convallis + eleifend. Quisque vitae consectetur massa, vitae elementum mauris. Pellentesque sit amet ligula lorem. Fusce + sit amet lorem non augue rutrum varius. Donec sed imperdiet libero, eget venenatis elit. Fusce porttitor + dapibus urna. Duis fringilla ante vel tempor tincidunt. In euismod vestibulum odio sit amet iaculis. Donec vel + dapibus libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi lacinia commodo lectus. Aenean + in magna eget lectus luctus suscipit et vitae erat. Pellentesque quis ligula id lorem egestas sollicitudin sit + amet sed sem. Nullam et nibh a odio rhoncus efficitur sed nec est. Sed commodo lacus vitae sem congue, + accumsan dignissim metus iaculis. Praesent in dignissim nisl. Aliquam facilisis, sapien eget porttitor + ultrices, massa libero bibendum odio, at ornare diam arcu ac massa. Vestibulum egestas leo eget lorem congue + condimentum. Praesent egestas, neque id gravida vehicula, augue ex scelerisque lectus, finibus pellentesque + enim dolor vel ante. Cras convallis, sem at malesuada tincidunt, diam urna auctor leo, sed laoreet est ex in + libero. Ut condimentum ante eget ex gravida, id tempus metus ultricies. Pellentesque placerat, massa id + laoreet molestie, justo nisl varius metus, maximus vehicula erat libero vitae nulla. Mauris rhoncus ligula + vitae volutpat auctor. Suspendisse potenti. Quisque quis orci faucibus, ullamcorper dolor eget, mollis massa. + Etiam eu molestie ipsum. Sed laoreet diam metus, luctus maximus erat viverra quis. Ut eu felis dictum, + tincidunt erat sit amet, scelerisque neque. Orci varius natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. Phasellus sit amet est tristique, fermentum massa ut, viverra metus. Interdum et + malesuada fames ac ante ipsum primis in faucibus. Nunc iaculis nunc elit, ut feugiat ipsum egestas eget. + Vestibulum pulvinar ligula mi, quis lacinia diam suscipit eget. Etiam consectetur vel nunc at hendrerit. + Pellentesque blandit eleifend aliquam. Etiam et malesuada purus, et bibendum sapien. Phasellus tincidunt + consequat eros consequat sodales. Vestibulum quis viverra neque. Integer sit amet lacinia nunc. Ut cursus, + elit id faucibus elementum, elit nunc dapibus tellus, non ornare nisi sapien et eros. Nunc sit amet suscipit + arcu. Nulla ut nunc tempor, auctor massa sed, consectetur orci. Pellentesque erat ante, placerat eget dictum + elementum, dapibus et ipsum. Nunc sit amet nulla gravida, finibus felis vel, tempus sem. In urna purus, + accumsan quis aliquam et, condimentum ac urna. Nullam volutpat ullamcorper sapien, quis ultricies purus + dignissim aliquam. Mauris quis enim ante. Etiam vulputate faucibus placerat. Ut pellentesque, purus vitae + euismod cursus, lacus enim vulputate sapien, in porttitor erat dui eu lectus. Duis eleifend, massa vel + vehicula gravida, magna urna rutrum ligula, vitae mollis ipsum neque id enim. Donec varius tristique nisi, et + vestibulum dolor efficitur eget. Cras mauris leo, bibendum eget pretium a, tincidunt faucibus massa. + Vestibulum hendrerit arcu magna, vel consequat est euismod nec. Vestibulum non lacus porttitor, congue tortor + ut, venenatis elit. Duis at lectus arcu. Nunc quis sapien eu ipsum rutrum accumsan. Orci varius natoque + penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus quis sapien luctus, volutpat nulla + eget, gravida nunc. Aenean placerat a felis quis imperdiet. Sed sapien tellus, ultrices non ipsum eget, + pretium rhoncus quam. Aliquam erat volutpat. Maecenas at interdum turpis, eu mattis ligula. Class aptent + taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In lobortis felis a leo + ultricies, venenatis mollis felis lobortis. Suspendisse placerat vel ante vel euismod. Aenean sit amet + ullamcorper mauris, id consectetur est. Ut ultricies enim non quam condimentum, et congue arcu commodo. + Praesent convallis eleifend turpis, vitae feugiat turpis imperdiet sit amet. Class aptent taciti sociosqu ad + litora torquent per conubia nostra, per inceptos himenaeos. Quisque vulputate porttitor mattis. Pellentesque + sed ullamcorper lectus. Suspendisse velit tortor, viverra eget facilisis condimentum, accumsan sit amet felis. + Cras lobortis mi fermentum ligula consectetur, vitae tincidunt mauris scelerisque. Aenean ac condimentum erat, + quis lacinia lacus. Ut magna nibh, tempor et ligula suscipit, placerat laoreet ipsum. In semper semper nisl. + Donec risus lorem, tempor sed sollicitudin vitae, fringilla et mi. Vivamus pulvinar quam nisl, et tincidunt + justo tempus quis. Duis semper magna nunc, vitae faucibus lectus facilisis sed. Phasellus consequat arcu vel + interdum fermentum. In condimentum euismod neque, sed aliquet mauris posuere nec. Etiam metus eros, + pellentesque eget scelerisque id, porttitor at ligula. Curabitur eget nibh maximus enim lobortis sodales. + Etiam vulputate ligula lobortis vestibulum pulvinar. Curabitur eros justo, accumsan sed elit ac, mattis + lacinia nisi. Suspendisse ullamcorper lectus sit amet tellus condimentum porttitor. Duis cursus, neque et + aliquam congue, odio lectus porta elit, id lacinia dolor justo non leo. Aliquam vehicula at tellus ullamcorper + tincidunt. Phasellus neque nibh, convallis sit amet arcu sit amet, convallis egestas tortor. Etiam sit amet + vehicula quam. Praesent id consequat lacus, ac facilisis quam. Integer tristique lorem eros, id consequat + lorem lobortis vitae. Aliquam luctus purus eget sem molestie iaculis. Duis nisl risus, sodales sit amet nunc + vitae, volutpat cursus augue. Pellentesque congue massa eu metus pellentesque consectetur at vel neque. Donec + bibendum hendrerit erat, vitae dictum enim lobortis a. Quisque ac dapibus tellus, sit amet facilisis orci. + Cras pretium tortor non condimentum semper. Phasellus mollis condimentum blandit. Pellentesque at arcu risus. + Vivamus sit amet dui semper, suscipit est nec, elementum arcu. Praesent ante turpis, convallis ac leo eget, + + + + ); +} + export default meta; diff --git a/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.mdx b/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.mdx new file mode 100644 index 00000000000..11b660493e0 --- /dev/null +++ b/packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.mdx @@ -0,0 +1,283 @@ +import { Props, Preview, ArgsTable } from '@storybook/addon-docs/blocks'; +import { AutoSaveField } from './AutoSaveField'; +import { Basic } from './AutoSaveField.story.tsx'; + +# AutoSaveField + +Used for form inputs that should save its content automatically. + +--- + +In this documentation you can find: + +1. [Usage](#usage) +1. [Variants](#variants) +1. [Accessibility](#accessibility) +1. [Content](#content) +1. [Formatting](#formatting) +1. [Behaviours](#behaviours) +1. [Props table](#propstable) +1. [Related](#related) + +
    +
    + +##
    Usage + +
    + +### **Do's** + +- Use in **low-impact** areas within Grafana (examples: team name, display name) +- Include helper text to tell users of the new behaviour for recently modified elements + +
    + +### **Dont's** + +- Do not use if high-impact situations (e.g. saving/changing passwords) +
    +
    + +##
    Variants + +### 1. Text: + +- The `Input` component allows saving the value of a string. +- This is the main component thought to work as an `AutoSaveField` child. + +```jsx + + {(onChange) => ( + { + onChange(e.currentTarget.value); + //Complete code if needed + }} + /> + )} + +``` + +### 2. TextArea: + +The `TextArea` component allows saving a multi-line text. + +```jsx + + {(onChange) => ( +
    {{ .Name }} - {{ .Value }} - {{ .Value }}
    {{ .Name }} - {{ .Value }} - {{ .Value }}