diff --git a/.betterer.eslint.config.js b/.betterer.eslint.config.js index d07e461ea2a..8d742ae5187 100644 --- a/.betterer.eslint.config.js +++ b/.betterer.eslint.config.js @@ -10,6 +10,7 @@ const testingLibraryPlugin = require('eslint-plugin-testing-library'); const grafanaConfig = require('@grafana/eslint-config/flat'); const grafanaPlugin = require('@grafana/eslint-plugin'); +const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin'); // Include the Grafana config and remove the rules, // as we just want to pull in all of the necessary configuration but not run the rules @@ -65,6 +66,7 @@ module.exports = [ 'no-barrel-files': barrelPlugin, '@grafana': grafanaPlugin, 'testing-library': testingLibraryPlugin, + '@grafana/i18n': grafanaI18nPlugin, }, linterOptions: { // This reports unused disable directives that we can clean up but @@ -108,6 +110,41 @@ module.exports = [ '@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }], }, }, + { + files: ['**/*.{js,jsx,ts,tsx}'], + ignores: [ + '**/*.{test,spec}.{ts,tsx}', + '**/__mocks__/**', + '**/public/test/**', + '**/mocks.{ts,tsx}', + '**/spec/**/*.{ts,tsx}', + ], + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: 'Identifier[name=localStorage]', + message: 'Direct usage of localStorage is not allowed. import store from @grafana/data instead', + }, + { + selector: 'MemberExpression[object.name=localStorage]', + message: 'Direct usage of localStorage is not allowed. import store from @grafana/data instead', + }, + { + selector: + 'Program:has(ImportDeclaration[source.value="@grafana/ui"] ImportSpecifier[imported.name="Card"]) JSXOpeningElement[name.name="Card"]:not(:has(JSXAttribute[name.name="noMargin"]))', + message: + 'Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.', + }, + { + selector: + 'Program:has(ImportDeclaration[source.value="@grafana/ui"] ImportSpecifier[imported.name="Field"]) JSXOpeningElement[name.name="Field"]:not(:has(JSXAttribute[name.name="noMargin"]))', + message: + 'Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.', + }, + ], + }, + }, { files: ['public/app/**/*.{ts,tsx}'], rules: { diff --git a/.betterer.results b/.betterer.results index 2518e0548be..a9efbf6fe5e 100644 --- a/.betterer.results +++ b/.betterer.results @@ -11,9 +11,17 @@ exports[`better eslint`] = { "apps/dashboard/tshack/v1alpha1_spec_gen.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "e2e/old-arch/utils/support/localStorage.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], "e2e/old-arch/utils/support/types.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "e2e/utils/support/localStorage.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], "e2e/utils/support/types.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -360,6 +368,14 @@ exports[`better eslint`] = { "packages/grafana-data/src/utils/location.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "packages/grafana-data/src/utils/store.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"] + ], "packages/grafana-data/src/utils/url.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -404,6 +420,14 @@ exports[`better eslint`] = { "packages/grafana-prometheus/src/components/PromQueryField.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "packages/grafana-prometheus/src/components/metrics-browser/useMetricsLabelsValues.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"] + ], "packages/grafana-prometheus/src/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -419,6 +443,9 @@ exports[`better eslint`] = { "packages/grafana-prometheus/src/language_utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "packages/grafana-prometheus/src/querybuilder/QueryPattern.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "packages/grafana-prometheus/src/querybuilder/components/LabelFilterItem.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -452,11 +479,12 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "packages/grafana-runtime/src/config.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"] + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], "packages/grafana-runtime/src/services/EchoSrv.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -501,6 +529,20 @@ exports[`better eslint`] = { "packages/grafana-runtime/src/utils/queryResponse.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "packages/grafana-runtime/src/utils/userStorage.tsx:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "6"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "7"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "8"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "9"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "10"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "11"] + ], "packages/grafana-schema/src/veneer/common.types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -513,9 +555,23 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "5"], [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], + "packages/grafana-sql/src/components/configuration/ConnectionLimits.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "packages/grafana-sql/src/components/configuration/TLSSecretsConfig.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "packages/grafana-sql/src/components/query-editor-raw/QueryToolbox.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], + "packages/grafana-sql/src/components/visual-query-builder/Preview.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -588,9 +644,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "4"], [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], - "packages/grafana-ui/src/components/Select/ValueContainer.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "packages/grafana-ui/src/components/Select/resetSelectStyles.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -649,11 +702,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"] - ], "packages/grafana-ui/src/components/Table/TableNG/utils.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -719,10 +767,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "packages/grafana-ui/src/components/index.ts:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'./Layout/Layout\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "\'VerticalGroup\' import from \'./Layout/Layout\' is restricted from being used by a pattern. Use Stack component instead.", "1"] - ], "packages/grafana-ui/src/components/uPlot/Plot.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] @@ -788,6 +832,9 @@ exports[`better eslint`] = { "packages/grafana-ui/src/utils/dom.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "packages/grafana-ui/src/utils/logger.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"] + ], "packages/grafana-ui/src/utils/useAsyncDependency.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -804,9 +851,23 @@ exports[`better eslint`] = { "public/app/core/components/AccessControl/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], + "public/app/core/components/AppChrome/History/HistoryWrapper.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/core/components/AppNotifications/StoredNotificationItem.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/core/components/DynamicImports/SafeDynamicImport.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/core/components/ForgottenPassword/ChangePassword.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/core/components/ForgottenPassword/ForgottenPassword.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/core/components/GraphNG/GraphNG.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -818,6 +879,53 @@ exports[`better eslint`] = { "public/app/core/components/LocalStorageValueProvider/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./LocalStorageValueProvider\`)", "0"] ], + "public/app/core/components/Login/LoginForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/core/components/Login/PasswordlessConfirmationForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/core/components/Login/PasswordlessLoginForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/core/components/NavLandingPage/NavLandingPageCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/core/components/OptionsUI/NumberInput.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/core/components/OptionsUI/fieldColor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/core/components/Page/EditableTitle.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/core/components/RolePickerDrawer/RolePickerDrawer.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/core/components/SharedPreferences/SharedPreferences.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"] + ], + "public/app/core/components/Signup/SignupPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"] + ], + "public/app/core/components/Signup/VerifyEmail.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/core/components/TagFilter/TagFilter.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -843,6 +951,11 @@ exports[`better eslint`] = { "public/app/core/navigation/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/core/reducers/appNotification.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"] + ], "public/app/core/services/ResponseQueue.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -867,6 +980,9 @@ exports[`better eslint`] = { "public/app/core/specs/backend_srv.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/core/store.ts:5381": [ + [0, 0, 0, "Do not re-export imported variable (\`store\`)", "0"] + ], "public/app/core/time_series2.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -932,9 +1048,75 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], + "public/app/features/actions/ActionEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"] + ], "public/app/features/actions/ParamsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/admin/AdminEditOrgPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/admin/ServerStatsCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/admin/UserCreatePage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/admin/UserOrgs.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/admin/ldap/LdapDrawer.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "10"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "11"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "12"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "13"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "14"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "15"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "16"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "17"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "18"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "19"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "20"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "21"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "22"] + ], + "public/app/features/admin/ldap/LdapGroupMapping.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/admin/ldap/LdapPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/admin/ldap/LdapSettingsPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], "public/app/features/alerting/routes.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -964,6 +1146,15 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "public/app/features/alerting/unified/AlertsFolderView.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/RedirectToRuleViewer.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/AlertLabelDropdown.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/alerting/unified/components/AnnotationDetailsField.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -981,23 +1172,88 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/components/alert-groups/GroupBy.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/alerting/unified/components/alert-groups/MatcherFilter.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/contact-points/components/ContactPointsFilter.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/create-folder/CreateNewFolder.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/import-to-gma/NamespaceAndGroupFilter.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/alerting/unified/components/mute-timings/MuteTimingForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/mute-timings/MuteTimingTimeInterval.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], + "public/app/features/alerting/unified/components/mute-timings/MuteTimingTimeRange.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], + "public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "10"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "11"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "12"] + ], + "public/app/features/alerting/unified/components/notification-policies/Filters.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/alerting/unified/components/receivers/form/ChannelOptions.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "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"] + ], + "public/app/features/alerting/unified/components/receivers/form/ChannelSubForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/receivers/form/CloudCommonChannelSettings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/receivers/form/GrafanaCommonChannelSettings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "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"] ], "public/app/features/alerting/unified/components/receivers/form/fields/OptionField.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Unexpected any. Specify a different type.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"] + [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], "public/app/features/alerting/unified/components/receivers/form/fields/SubformArrayField.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -1007,26 +1263,136 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "public/app/features/alerting/unified/components/rule-editor/AlertRuleNameInput.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/features/alerting/unified/components/rule-editor/AnnotationKeyInput.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/CloudEvaluationBehavior.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/alerting/unified/components/rule-editor/ExpressionEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/alerting/unified/components/rule-editor/FolderSelector.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"] + ], + "public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/alerting/unified/components/rule-editor/PreviewRule.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/alerting/unified/components/rule-editor/RuleInspector.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "6"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "7"] + ], + "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/contactPoint/ContactPointSelector.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/ActiveTimingFields.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/RouteSettings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/RouteTimings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/alerting/unified/components/rule-editor/rule-types/RuleType.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/rules/state-history/StateHistory.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/settings/AlertmanagerCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/alerting/unified/components/silences/MatchersField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], + "public/app/features/alerting/unified/components/silences/SilencePeriod.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/alerting/unified/components/silences/SilencesEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"] ], "public/app/features/alerting/unified/components/silences/SilencesFilter.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] + ], + "public/app/features/alerting/unified/group-details/GroupEditPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] ], "public/app/features/alerting/unified/hooks/useAlertmanagerConfig.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -1037,9 +1403,22 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/insights/InsightsMenuButton.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/alerting/unified/mocks.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], + "public/app/features/alerting/unified/rule-editor/formDefaults.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"] + ], + "public/app/features/alerting/unified/rule-list/FilterViewStatus.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/alerting/unified/types/receiver-form.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/alerting/unified/utils/misc.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -1076,12 +1455,41 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], + "public/app/features/auth-config/FieldRenderer.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], + "public/app/features/auth-config/ProviderConfigForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/auth-config/components/ServerDiscoveryModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/auth-config/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], "public/app/features/auth-config/utils/data.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/browse-dashboards/api/useRecentlyDeletedStateManager.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], + "public/app/features/browse-dashboards/components/BrowseActions/MoveModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/browse-dashboards/components/NewFolderForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/browse-dashboards/components/NewProvisionedFolderForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], "public/app/features/browse-dashboards/state/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"], [0, 0, 0, "Do not use export all (\`export * from ...\`)", "1"], @@ -1098,6 +1506,9 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./EditDataSourcePage\`)", "4"], [0, 0, 0, "Do not re-export imported variable (\`./NewDataSourcePage\`)", "5"] ], + "public/app/features/connections/tabs/ConnectData/CardGrid/CardGrid.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/connections/tabs/ConnectData/CardGrid/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], @@ -1105,7 +1516,11 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], "public/app/features/connections/tabs/ConnectData/ConnectData.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"] ], "public/app/features/connections/tabs/ConnectData/NoAccessModal/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] @@ -1116,9 +1531,33 @@ exports[`better eslint`] = { "public/app/features/connections/tabs/ConnectData/index.tsx:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], + "public/app/features/correlations/Forms/ConfigureCorrelationBasicInfoForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/correlations/Forms/ConfigureCorrelationSourceForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/features/correlations/Forms/ConfigureCorrelationTargetForm.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, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"] + ], + "public/app/features/correlations/Forms/QueryEditorField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/correlations/Forms/TransformationEditorRow.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/correlations/components/EmptyCorrelationsCTA.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/correlations/components/Wizard/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"], @@ -1128,12 +1567,36 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], - "public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx:5381": [ + "public/app/features/dashboard-scene/addToDashboard/AddToDashboardForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingGroupCondition.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingGroupVisibility.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingTimeRangeSize.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/edit-pane/DashboardEditPaneRenderer.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/dashboard-scene/edit-pane/DashboardOutline.tsx:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], + "public/app/features/dashboard-scene/inspect/HelpWizard/HelpWizard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/features/dashboard-scene/inspect/HelpWizard/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dashboard-scene/pages/DashboardScenePage.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -1148,6 +1611,21 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.test.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/panel-edit/PanelVizTypePicker.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/dashboard-scene/saving/SaveDashboardForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dashboard-scene/saving/getDashboardChanges.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -1184,6 +1662,22 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"] ], + "public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutManagerEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/dashboard-scene/scene/layout-default/row-actions/RowOptionsForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dashboard-scene/serialization/angularMigration.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -1207,12 +1701,69 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/serialization/transformToV1TypesUtils.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"] + ], + "public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsEdit.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"] + ], "public/app/features/dashboard-scene/settings/annotations/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsEdit\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsList\`)", "1"] ], + "public/app/features/dashboard-scene/settings/links/DashboardLinkForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"] + ], + "public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/VariableSetEditableElement.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/components/AdHocVariableForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/components/GroupByVariableForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/components/QueryVariableForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/components/VariableHideSelect.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dashboard-scene/settings/variables/components/VariableSelectField.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] + ], + "public/app/features/dashboard-scene/settings/variables/components/VariableTextAreaField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/components/VariableTextField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/settings/variables/editors/QueryVariableEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/dashboard-scene/settings/variables/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -1225,6 +1776,34 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./VersionHistoryHeader\`)", "3"], [0, 0, 0, "Do not re-export imported variable (\`./VersionHistoryTable\`)", "4"] ], + "public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/ConfigEmailSharing/ConfigEmailSharing.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/ConfigEmailSharing/EmailListConfiguration.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/sharing/ShareButton/share-snapshot/UpsertSnapshot.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/dashboard-scene/sharing/ShareExportTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard-scene/sharing/ShareLinkTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/dashboard-scene/sharing/ShareSnapshotTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/dashboard-scene/sharing/panel-share/SharePanelPreview.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -1232,10 +1811,13 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/dashboard-scene/v2schema/ImportDashboardFormV2.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], "public/app/features/dashboard-scene/v2schema/ImportDashboardOverviewV2.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -1271,7 +1853,13 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./AddLibraryPanelWidget\`)", "0"] ], "public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsEdit.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] + [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"] ], "public/app/features/dashboard/components/AnnotationSettings/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsEdit\`)", "0"], @@ -1327,12 +1915,35 @@ exports[`better eslint`] = { "public/app/features/dashboard/components/DashboardRow/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./DashboardRow\`)", "0"] ], + "public/app/features/dashboard/components/DashboardSettings/AutoRefreshIntervals.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/dashboard/components/DashboardSettings/GeneralSettings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"] + ], + "public/app/features/dashboard/components/DashboardSettings/TimePickerSettings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], "public/app/features/dashboard/components/DashboardSettings/VersionsSettings.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], "public/app/features/dashboard/components/DashboardSettings/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./DashboardSettings\`)", "0"] ], + "public/app/features/dashboard/components/HelpWizard/HelpWizard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/features/dashboard/components/Inspector/PanelInspector.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -1341,11 +1952,13 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./LinkSettingsList\`)", "1"] ], "public/app/features/dashboard/components/PanelEditor/DynamicConfigValueEditor.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] + [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] ], "public/app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"] ], "public/app/features/dashboard/components/PanelEditor/OverrideCategoryTitle.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] @@ -1355,7 +1968,8 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/dashboard/components/PanelEditor/VisualizationSelectPane.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"] ], "public/app/features/dashboard/components/PanelEditor/getVisualizationOptions.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -1368,10 +1982,20 @@ 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/dashboard/components/RowOptions/RowOptionsForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/dashboard/components/SaveDashboard/SaveDashboardButton.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/dashboard/components/SaveDashboard/forms/SaveDashboardAsForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], "public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.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"], @@ -1383,25 +2007,58 @@ exports[`better eslint`] = { "public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/dashboard/components/ShareModal/ShareExport.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] + ], + "public/app/features/dashboard/components/ShareModal/ShareLink.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] ], "public/app/features/dashboard/components/ShareModal/SharePublicDashboard/ConfigPublicDashboard/ConfigPublicDashboard.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] + [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] ], "public/app/features/dashboard/components/ShareModal/SharePublicDashboard/ConfigPublicDashboard/Configuration.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], + "public/app/features/dashboard/components/ShareModal/SharePublicDashboard/ConfigPublicDashboard/EmailSharingConfiguration.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/dashboard/components/ShareModal/SharePublicDashboard/CreatePublicDashboard/AcknowledgeCheckboxes.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "1"] ], + "public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/dashboard/components/ShareModal/ThemePicker.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "public/app/features/dashboard/components/TransformationsEditor/TransformationFilter.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dashboard/components/TransformationsEditor/TransformationPicker.tsx:5381": [ - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] + [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/dashboard/components/TransformationsEditor/TransformationPickerNg.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -1571,7 +2228,15 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use export all (\`export * from ...\`)", "1"] ], "public/app/features/datasources/components/DataSourceTypeCard.tsx:5381": [ - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"] + ], + "public/app/features/datasources/components/DataSourcesListCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/datasources/components/picker/DataSourceCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/datasources/state/actions.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -1601,8 +2266,13 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], + "public/app/features/dimensions/editors/FileUploader.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/dimensions/editors/FolderPickerTab.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"] ], "public/app/features/dimensions/editors/ResourceDimensionEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -1614,6 +2284,10 @@ exports[`better eslint`] = { "public/app/features/dimensions/editors/ThresholdsEditor/ThresholdsEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/dimensions/editors/URLPickerTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingEditRow.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -1647,13 +2321,30 @@ exports[`better eslint`] = { "public/app/features/dimensions/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/explore/CorrelationHelper.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/explore/CorrelationTransformationAddModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/explore/Logs/LogsColumnSearch.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/explore/PrometheusListView/RawListContainer.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/explore/RichHistory/RichHistorySettingsTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/explore/TraceView/TraceView.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], - "public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/features/explore/TraceView/components/TracePageHeader/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./TracePageHeader\`)", "0"] ], @@ -1754,11 +2445,18 @@ exports[`better eslint`] = { "public/app/features/inspector/InspectDataOptions.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Do not use any type assertions.", "6"] ], "public/app/features/inspector/InspectDataTab.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], + "public/app/features/inspector/InspectJSONTab.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/inspector/InspectStatsTab.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], @@ -1767,6 +2465,16 @@ exports[`better eslint`] = { [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"] ], + "public/app/features/invites/SignupInvited.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -1783,10 +2491,31 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/logs/utils.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"], + [0, 0, 0, "Do not use any type assertions.", "6"] + ], + "public/app/features/manage-dashboards/DashboardImportPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/manage-dashboards/components/ImportDashboardForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] ], "public/app/features/manage-dashboards/components/ImportDashboardLibraryPanelsList.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] + ], + "public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/manage-dashboards/state/actions.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -1808,10 +2537,16 @@ exports[`better eslint`] = { "public/app/features/migrate-to-cloud/api/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"] ], + "public/app/features/migrate-to-cloud/cloud/MigrationTokenPane/CreateTokenModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/migrate-to-cloud/onprem/ConfigureSnapshot.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], + "public/app/features/migrate-to-cloud/onprem/EmptyState/CallToAction/ConnectModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/migrate-to-cloud/onprem/resourceDependency.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] @@ -1819,6 +2554,18 @@ exports[`better eslint`] = { "public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/org/NewOrgPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/org/OrgProfile.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/org/UserInviteForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], "public/app/features/org/state/reducers.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -1828,9 +2575,27 @@ exports[`better eslint`] = { "public/app/features/panel/panellinks/linkSuppliers.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/playlist/PlaylistCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/features/playlist/PlaylistForm.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, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"], + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "5"] + ], + "public/app/features/playlist/ShareModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/playlist/StartModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] ], "public/app/features/plugins/admin/__mocks__/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./localPlugin.mock\`)", "0"], @@ -1861,8 +2626,12 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/plugins/admin/pages/Browse.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, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"] ], "public/app/features/plugins/admin/state/actions.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -1904,10 +2673,62 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Do not use any type assertions.", "3"] ], + "public/app/features/profile/ChangePasswordForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/profile/UserProfileEditForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/provisioning/Config/ConfigForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "10"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "11"] + ], + "public/app/features/provisioning/Config/ConfigFormGithubCollapse.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/features/provisioning/File/FileHistoryPage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/provisioning/File/FileStatusPage.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], + "public/app/features/provisioning/GettingStarted/SidebarItem.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/provisioning/Job/RecentJobs.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/provisioning/Repository/RepositoryCard.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/provisioning/Repository/RepositoryOverview.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/provisioning/Wizard/FinishStep.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], "public/app/features/provisioning/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -1969,17 +2790,62 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/search/state/SearchStateManager.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "6"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "7"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "8"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "9"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "10"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "11"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "12"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "13"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "14"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "15"], + [0, 0, 0, "Do not use any type assertions.", "16"] ], "public/app/features/search/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/search/utils.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"] + ], + "public/app/features/serviceaccounts/ServiceAccountCreatePage.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], + "public/app/features/serviceaccounts/components/CreateTokenModal.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] ], "public/app/features/serviceaccounts/state/reducers.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/support-bundles/SupportBundlesCreate.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/features/teams/CreateTeam.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/features/teams/TeamSettings.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], "public/app/features/teams/state/reducers.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -2064,6 +2930,9 @@ exports[`better eslint`] = { "public/app/features/transformers/editors/GroupByTransformerEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/transformers/editors/GroupToNestedTableTransformerEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/transformers/editors/ReduceTransformerEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -2178,6 +3047,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "public/app/features/variables/query/QueryVariableRefreshSelect.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/features/variables/query/VariableQueryRunner.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -2271,6 +3143,33 @@ exports[`better eslint`] = { "public/app/plugins/datasource/azuremonitor/components/ArgQueryEditor/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./ArgQueryEditor\`)", "0"] ], + "public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AppRegistrationCredentials.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], + "public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AzureCredentialsForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/datasource/azuremonitor/components/ConfigEditor/BasicLogsToggle.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/datasource/azuremonitor/components/ConfigEditor/ConfigEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/plugins/datasource/azuremonitor/components/ConfigEditor/DefaultSubscription.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./LogsQueryEditor\`)", "0"] ], @@ -2295,12 +3194,24 @@ exports[`better eslint`] = { "public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filter.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], + "public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./TracesQueryEditor\`)", "0"] ], "public/app/plugins/datasource/azuremonitor/components/VariableEditor/VariableEditor.test.tsx:5381": [ [0, 0, 0, "* import is invalid because \'Layout,HorizontalGroup,VerticalGroup\' from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], + "public/app/plugins/datasource/azuremonitor/components/VariableEditor/VariableEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"] + ], "public/app/plugins/datasource/azuremonitor/types/index.ts:5381": [ [0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"], [0, 0, 0, "Do not use export all (\`export * from ...\`)", "1"], @@ -2331,6 +3242,9 @@ exports[`better eslint`] = { "public/app/plugins/datasource/cloud-monitoring/components/Aggregation.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/plugins/datasource/cloud-monitoring/components/Fields.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/datasource/cloud-monitoring/components/GraphPeriod.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -2408,6 +3322,17 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`./thirdArgAfterSearchQuery\`)", "4"], [0, 0, 0, "Do not re-export imported variable (\`./withinStringQuery\`)", "5"] ], + "public/app/plugins/datasource/cloudwatch/components/ConfigEditor/ConfigEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/plugins/datasource/cloudwatch/components/ConfigEditor/SecureSocksProxySettingsNewStyling.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/datasource/cloudwatch/components/ConfigEditor/XrayLinkConfig.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/MetricsQueryEditor.test.tsx:5381": [ [0, 0, 0, "* import is invalid because \'Layout,HorizontalGroup,VerticalGroup\' from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -2448,6 +3373,12 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"] ], + "public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] + ], "public/app/plugins/datasource/dashboard/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -2552,6 +3483,23 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`ElasticsearchQuery\`)", "1"], [0, 0, 0, "Do not use export all (\`export * from ...\`)", "2"] ], + "public/app/plugins/datasource/grafana-postgresql-datasource/configuration/ConfigurationEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "10"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "11"] + ], + "public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/EditorField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -2587,13 +3535,26 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`config\`)", "0"] ], "public/app/plugins/datasource/grafana/components/AnnotationQueryEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"] ], "public/app/plugins/datasource/grafana/components/QueryEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], + "public/app/plugins/datasource/grafana/components/TimeRegionEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"] + ], "public/app/plugins/datasource/grafana/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], @@ -2605,6 +3566,11 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "7"], [0, 0, 0, "Unexpected any. Specify a different type.", "8"] ], + "public/app/plugins/datasource/graphite/configuration/ConfigEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/plugins/datasource/graphite/datasource.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -2681,7 +3647,20 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/plugins/datasource/influxdb/components/editor/config/ConfigEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] + ], + "public/app/plugins/datasource/influxdb/components/editor/config/InfluxInfluxQLConfig.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"] + ], + "public/app/plugins/datasource/influxdb/components/editor/config/InfluxSQLConfig.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] ], "public/app/plugins/datasource/influxdb/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -2737,9 +3716,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/jaeger/_importedDependencies/types/index.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./trace\`)", "0"] ], - "public/app/plugins/datasource/jaeger/components/QueryEditor.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/plugins/datasource/jaeger/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -2747,9 +3723,37 @@ exports[`better eslint`] = { "public/app/plugins/datasource/loki/LanguageProvider.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/plugins/datasource/loki/LogContextProvider.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], + "public/app/plugins/datasource/loki/components/LokiContextUi.tsx:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "2"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "3"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "4"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "5"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "6"] + ], + "public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], "public/app/plugins/datasource/loki/configuration/ConfigEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/plugins/datasource/loki/configuration/DerivedField.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"] + ], "public/app/plugins/datasource/loki/datasource.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -2757,6 +3761,17 @@ exports[`better eslint`] = { "public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/plugins/datasource/loki/querybuilder/components/QueryPattern.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/datasource/loki/querybuilder/state.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], + "public/app/plugins/datasource/loki/shardQuerySplitting.ts:5381": [ + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], + [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"] + ], "public/app/plugins/datasource/loki/types.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`LokiQueryDirection\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`LokiQueryType\`)", "1"], @@ -2766,6 +3781,58 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`Datasource\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`MixedDatasource\`)", "1"] ], + "public/app/plugins/datasource/mssql/azureauth/AzureCredentialsForm.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"] + ], + "public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "10"] + ], + "public/app/plugins/datasource/mssql/configuration/Kerberos.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"] + ], + "public/app/plugins/datasource/mysql/configuration/ConfigurationEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"] + ], + "public/app/plugins/datasource/opentsdb/components/OpenTsdbDetails.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], "public/app/plugins/datasource/opentsdb/datasource.d.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -2789,21 +3856,12 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "16"], [0, 0, 0, "Unexpected any. Specify a different type.", "17"] ], + "public/app/plugins/datasource/parca/QueryEditor/QueryOptions.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/datasource/parca/webpack.config.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`config\`)", "0"] ], - "public/app/plugins/datasource/tempo/QueryField.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], - "public/app/plugins/datasource/tempo/SearchTraceQLEditor/DurationInput.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], - "public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], - "public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/plugins/datasource/tempo/ServiceGraphSection.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], @@ -2839,9 +3897,8 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`config\`)", "0"] ], "public/app/plugins/datasource/zipkin/QueryField.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "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.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/plugins/datasource/zipkin/datasource.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -2855,6 +3912,9 @@ exports[`better eslint`] = { "public/app/plugins/panel/annolist/AnnoListPanel.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/plugins/panel/annolist/AnnotationListItem.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Card components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/panel/barchart/TickSpacingEditor.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], @@ -2878,6 +3938,18 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`VizDisplayMode\`)", "6"], [0, 0, 0, "Do not re-export imported variable (\`defaultCandlestickColors\`)", "7"] ], + "public/app/plugins/panel/canvas/editor/LineStyleEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/panel/canvas/editor/element/APIEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/plugins/panel/canvas/editor/element/PlacementEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/plugins/panel/debug/CursorView.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -2886,6 +3958,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "public/app/plugins/panel/debug/StateView.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/panel/gauge/GaugeMigrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -2902,18 +3977,32 @@ exports[`better eslint`] = { ], "public/app/plugins/panel/geomap/editor/StyleEditor.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Do not use any type assertions.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Do not use any type assertions.", "5"], - [0, 0, 0, "Do not use any type assertions.", "6"], - [0, 0, 0, "Do not use any type assertions.", "7"], - [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Do not use any type assertions.", "9"], - [0, 0, 0, "Do not use any type assertions.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"], - [0, 0, 0, "Do not use any type assertions.", "12"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "7"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "8"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "9"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "10"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "11"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "12"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "13"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "14"], + [0, 0, 0, "Do not use any type assertions.", "15"], + [0, 0, 0, "Do not use any type assertions.", "16"], + [0, 0, 0, "Do not use any type assertions.", "17"], + [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, "Do not use any type assertions.", "22"], + [0, 0, 0, "Do not use any type assertions.", "23"], + [0, 0, 0, "Do not use any type assertions.", "24"], + [0, 0, 0, "Do not use any type assertions.", "25"], + [0, 0, 0, "Do not use any type assertions.", "26"] ], "public/app/plugins/panel/geomap/editor/StyleRuleEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -3003,9 +4092,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "3"] ], "public/app/plugins/panel/nodeGraph/ViewControls.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/plugins/panel/nodeGraph/index.ts:5381": [ [0, 0, 0, "Do not re-export imported variable (\`./NodeGraph\`)", "0"] @@ -3027,8 +4114,28 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "public/app/plugins/panel/table/TableCellOptionEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/panel/table/cells/AutoCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/panel/table/cells/BarGaugeCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/plugins/panel/table/cells/ColorBackgroundCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/plugins/panel/table/cells/ImageCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/plugins/panel/table/cells/SparklineCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] + [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] ], "public/app/plugins/panel/table/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -3036,8 +4143,28 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], + "public/app/plugins/panel/table/table-new/TableCellOptionEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/panel/table/table-new/cells/AutoCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], + "public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], + "public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + ], + "public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] + ], "public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] + [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] ], "public/app/plugins/panel/table/table-new/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], @@ -3073,7 +4200,9 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationEditor2.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], "public/app/plugins/panel/timeseries/plugins/annotations2/AnnotationMarker2.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -3083,10 +4212,15 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/plugins/panel/xychart/SeriesEditor.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Do not use any type assertions.", "1"], - [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Do not use any type assertions.", "3"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"], + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"], + [0, 0, 0, "Do not use any type assertions.", "6"], + [0, 0, 0, "Do not use any type assertions.", "7"], + [0, 0, 0, "Do not use any type assertions.", "8"] ], "public/app/plugins/panel/xychart/migrations.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] diff --git a/.citools/README.md b/.citools/README.md new file mode 100644 index 00000000000..fad45076926 --- /dev/null +++ b/.citools/README.md @@ -0,0 +1,41 @@ +## API + +### Adding and Upgrading Tools + +To add a new tool, execute the installation script: + +```bash +install.sh +``` + +#### Example + +The following command will add `lefthook` to the tracked tools if it is not already installed, or update its version: + +```bash +install.sh github.com/evilmartians/lefthook@v1.11.10 +``` + +Behind the scenes, the script performs a few simple steps: + +- Creates a Go module under the `.citools/src/` directory to track the tool version and its dependencies. +- Creates a reference to the tool binary in the `.citools/Variables.mk` file. + +### Using Tools in the Makefile + +Our Makefile imports `.citools/Variables.mk`, so you can call a tool binary using standard Make syntax. + +#### Example + +```make +run: + $(bra) run +``` + +### Using Tracked Tools Without the Makefile + +If you want to use a tool outside of the Makefile, you can locate the tool binary by executing the following command: + +```bash +GOWORK=off go tool -n -modfile= +``` diff --git a/.citools/Variables.mk b/.citools/Variables.mk new file mode 100644 index 00000000000..54f102dba73 --- /dev/null +++ b/.citools/Variables.mk @@ -0,0 +1,34 @@ +# Generated tool paths +tools_dir := $(shell cd $(dir $(lastword $(MAKEFILE_LIST))) && pwd) +src_dir := $(tools_dir)/src + +# Due to a race condition, after initial call to `go tool` golang may report a wrong binary location pointing to the invalid `/tmp/go-buildXXX` directory +define compile_tool +$(shell \ + (cd $(src_dir)/$(1) \ + && GOWORK=off go tool -n $(2) > /dev/null \ + && GOWORK=off go tool -n $(2)) | sed 's/^[[:space:]]*//g'; \ +) +endef + + +# Tool: "bra" +bra = "$(call compile_tool,bra,github.com/unknwon/bra)" + +# Tool: "cog" +cog = "$(call compile_tool,cog,github.com/grafana/cog/cmd/cli)" + +# Tool: "cue" +cue = "$(call compile_tool,cue,cuelang.org/go/cmd/cue)" + +# Tool: "golangci-lint" +golangci-lint = "$(call compile_tool,golangci-lint,github.com/golangci/golangci-lint/v2/cmd/golangci-lint)" + +# Tool: "jb" +jb = "$(call compile_tool,jb,github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb)" + +# Tool: "lefthook" +lefthook = "$(call compile_tool,lefthook,github.com/evilmartians/lefthook)" + +# Tool: "swagger" +swagger = "$(call compile_tool,swagger,github.com/go-swagger/go-swagger/cmd/swagger)" diff --git a/.citools/generate.sh b/.citools/generate.sh new file mode 100755 index 00000000000..7bbdd7e8067 --- /dev/null +++ b/.citools/generate.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -euo pipefail + +TOOLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +TOOLS_SRC_DIR="$TOOLS_DIR/src" +TOOLS_MK="$TOOLS_DIR/Variables.mk" + +echo "# Generated tool paths" > "$TOOLS_MK" + +cat <<'EOL' >> "$TOOLS_MK" +tools_dir := $(shell cd $(dir $(lastword $(MAKEFILE_LIST))) && pwd) +src_dir := $(tools_dir)/src + +# Due to a race condition, after initial call to `go tool` golang may report a wrong binary location pointing to the invalid `/tmp/go-buildXXX` directory +define compile_tool +$(shell \ + (cd $(src_dir)/$(1) \ + && GOWORK=off go tool -n $(2) > /dev/null \ + && GOWORK=off go tool -n $(2)) | sed 's/^[[:space:]]*//g'; \ +) +endef + +EOL + +for tooldir in "$TOOLS_SRC_DIR"/*; do + [ -d "$tooldir" ] || continue + tool=$(basename "$tooldir") + fqtn=$(awk '/^tool / { print $2 }' "$tooldir/go.mod") + + cat <> "$TOOLS_MK" + +# Tool: "$tool" +${tool} = "\$(call compile_tool,${tool},${fqtn})" +EOL +done diff --git a/.citools/install.sh b/.citools/install.sh new file mode 100755 index 00000000000..f56b87bca4b --- /dev/null +++ b/.citools/install.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -euo pipefail + +TOOLS_BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TOOLS_SRC_DIR="$TOOLS_BASE_DIR/src" + +IMPORT_PATH_WITH_VERSION="$1" + +if [[ "$IMPORT_PATH_WITH_VERSION" != *"@"* ]]; then + echo "Error: tool version must be specified (e.g., github.com/foo/bar@v1.2.3)" + exit 1 +fi + +TOOL_PATH="${IMPORT_PATH_WITH_VERSION%@*}" +TOOL_NAME="${TOOL_PATH##*/}" + +TOOL_DIR="$TOOLS_SRC_DIR/$TOOL_NAME" +MOD_FILE="$TOOL_DIR/go.mod" + +mkdir -p "$TOOL_DIR" +cd "$TOOL_DIR" + + # Create a new module if go.mod doesn't exist +if [ ! -f go.mod ]; then + go mod init "$TOOL_NAME" +fi + +go get -tool --modfile="$MOD_FILE" "$IMPORT_PATH_WITH_VERSION" +echo "Installed $TOOL_NAME" +echo " Directory: $TOOL_DIR" +echo " Modfile: $MOD_FILE" + +exec "$TOOLS_BASE_DIR/generate.sh" diff --git a/.citools/bra/go.mod b/.citools/src/bra/go.mod similarity index 94% rename from .citools/bra/go.mod rename to .citools/src/bra/go.mod index 6a67dbaddee..8a75cdfef83 100644 --- a/.citools/bra/go.mod +++ b/.citools/src/bra/go.mod @@ -1,6 +1,6 @@ module bra -go 1.24.1 +go 1.24.3 tool github.com/unknwon/bra @@ -17,6 +17,6 @@ require ( github.com/unknwon/com v1.0.1 // indirect github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect github.com/urfave/cli v1.22.16 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/sys v0.33.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect ) diff --git a/.citools/bra/go.sum b/.citools/src/bra/go.sum similarity index 97% rename from .citools/bra/go.sum rename to .citools/src/bra/go.sum index e9a6788ad88..601cd7a8f3f 100644 --- a/.citools/bra/go.sum +++ b/.citools/src/bra/go.sum @@ -56,8 +56,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/cog/go.mod b/.citools/src/cog/go.mod similarity index 88% rename from .citools/cog/go.mod rename to .citools/src/cog/go.mod index 99587abf4c9..1258685195e 100644 --- a/.citools/cog/go.mod +++ b/.citools/src/cog/go.mod @@ -1,6 +1,6 @@ module cog -go 1.24.1 +go 1.24.3 tool github.com/grafana/cog/cmd/cli @@ -11,13 +11,13 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/proto v1.13.2 // indirect github.com/expr-lang/expr v1.17.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // indirect - github.com/grafana/cog v0.0.28 // indirect + github.com/grafana/cog v0.0.34 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect @@ -41,10 +41,10 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/yalue/merged_fs v1.3.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/tools v0.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/cog/go.sum b/.citools/src/cog/go.sum similarity index 88% rename from .citools/cog/go.sum rename to .citools/src/cog/go.sum index 94b74898809..4a69777748f 100644 --- a/.citools/cog/go.sum +++ b/.citools/src/cog/go.sum @@ -11,8 +11,8 @@ github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/expr-lang/expr v1.17.0 h1:+vpszOyzKLQXC9VF+wA8cVA0tlA984/Wabc/1hF9Whg= github.com/expr-lang/expr v1.17.0/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= -github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= -github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= @@ -27,8 +27,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= -github.com/grafana/cog v0.0.28 h1:0+FNuxyfNWm1OBZPPjgBIno3nzR4gOpSxsVe34hdN7g= -github.com/grafana/cog v0.0.28/go.mod h1:wZWsTLV7uX0jCbGpqvjawQ7JbaDVT9oW+PQhHwqanHc= +github.com/grafana/cog v0.0.34 h1:tXtjIB0A0Y6jeZ5iAQBJIAz5vkAJsV0iEFfX94PH/+Q= +github.com/grafana/cog v0.0.34/go.mod h1:UDstzYqMdgIROmbfkHL8fB9XWQO2lnf5z+4W/eJo4Dc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -87,18 +87,18 @@ github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.citools/cue/go.mod b/.citools/src/cue/go.mod similarity index 86% rename from .citools/cue/go.mod rename to .citools/src/cue/go.mod index 8cc38afcfed..31cb9301000 100644 --- a/.citools/cue/go.mod +++ b/.citools/src/cue/go.mod @@ -1,6 +1,6 @@ module cue -go 1.24.1 +go 1.24.3 tool cuelang.org/go/cmd/cue @@ -26,12 +26,12 @@ require ( github.com/stretchr/testify v1.10.0 // indirect github.com/tetratelabs/wazero v1.6.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/tools v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/cue/go.sum b/.citools/src/cue/go.sum similarity index 88% rename from .citools/cue/go.sum rename to .citools/src/cue/go.sum index 6ee8fc180e6..58868acedb9 100644 --- a/.citools/cue/go.sum +++ b/.citools/src/cue/go.sum @@ -55,18 +55,18 @@ github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzl github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.citools/golangci-lint/go.mod b/.citools/src/golangci-lint/go.mod similarity index 92% rename from .citools/golangci-lint/go.mod rename to .citools/src/golangci-lint/go.mod index 8b1e2dddb27..9f2b7babff3 100644 --- a/.citools/golangci-lint/go.mod +++ b/.citools/src/golangci-lint/go.mod @@ -1,6 +1,6 @@ module golangci-lint -go 1.24.1 +go 1.24.3 tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint @@ -79,6 +79,7 @@ require ( github.com/golangci/revgrep v0.8.0 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.5.0 // indirect @@ -87,7 +88,6 @@ require ( github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jgautheron/goconst v1.7.1 // indirect @@ -97,7 +97,6 @@ require ( github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect github.com/kisielk/errcheck v1.9.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect - github.com/klauspost/compress v1.18.0 // indirect github.com/kulti/thelper v0.6.3 // indirect github.com/kunwardeep/paralleltest v1.0.10 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect @@ -109,7 +108,6 @@ require ( github.com/leonklingele/grouper v1.1.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/macabu/inamedparam v0.2.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v1.1.0 // indirect @@ -119,7 +117,6 @@ require ( github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgechev/revive v1.7.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/moricho/tparallel v0.3.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -131,10 +128,10 @@ require ( github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.7.1 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.0 // indirect github.com/quasilyte/go-ruleguard v0.4.4 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect @@ -145,8 +142,7 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/ryancurrah/gomodguard v1.4.1 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect @@ -159,10 +155,10 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -189,15 +185,14 @@ require ( go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/tools v0.33.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect diff --git a/.citools/golangci-lint/go.sum b/.citools/src/golangci-lint/go.sum similarity index 94% rename from .citools/golangci-lint/go.sum rename to .citools/src/golangci-lint/go.sum index faf1ae88539..6d1048df76f 100644 --- a/.citools/golangci-lint/go.sum +++ b/.citools/src/golangci-lint/go.sum @@ -175,8 +175,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= @@ -201,8 +201,6 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -221,8 +219,6 @@ github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3 github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -252,8 +248,6 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= @@ -274,8 +268,6 @@ github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= -github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= @@ -312,14 +304,14 @@ github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= +github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= github.com/quasilyte/go-ruleguard v0.4.4 h1:53DncefIeLX3qEpjzlS1lyUmQoUEeOWPFWqaTJq9eAQ= github.com/quasilyte/go-ruleguard v0.4.4/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= @@ -342,10 +334,8 @@ github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= @@ -372,15 +362,15 @@ github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCp github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4= @@ -463,8 +453,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= @@ -497,8 +487,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -508,8 +498,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -531,8 +521,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -551,8 +541,8 @@ 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/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= @@ -571,8 +561,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -583,8 +573,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/.citools/jb/go.mod b/.citools/src/jb/go.mod similarity index 93% rename from .citools/jb/go.mod rename to .citools/src/jb/go.mod index 71037e9e239..9e0ba331ae7 100644 --- a/.citools/jb/go.mod +++ b/.citools/src/jb/go.mod @@ -1,6 +1,6 @@ module jb -go 1.24.1 +go 1.24.3 tool github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb @@ -15,6 +15,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/stretchr/testify v1.10.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/sys v0.33.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect ) diff --git a/.citools/jb/go.sum b/.citools/src/jb/go.sum similarity index 97% rename from .citools/jb/go.sum rename to .citools/src/jb/go.sum index cc459f28ba4..90354357864 100644 --- a/.citools/jb/go.sum +++ b/.citools/src/jb/go.sum @@ -54,8 +54,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/lefthook/go.mod b/.citools/src/lefthook/go.mod similarity index 75% rename from .citools/lefthook/go.mod rename to .citools/src/lefthook/go.mod index a72a562d2a4..2cc1154daf6 100644 --- a/.citools/lefthook/go.mod +++ b/.citools/src/lefthook/go.mod @@ -1,6 +1,6 @@ module lefthook -go 1.24.1 +go 1.24.3 tool github.com/evilmartians/lefthook @@ -14,42 +14,40 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/creack/pty v1.1.18 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/evilmartians/lefthook v1.4.8 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/tools v0.33.0 // indirect gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/lefthook/go.sum b/.citools/src/lefthook/go.sum similarity index 80% rename from .citools/lefthook/go.sum rename to .citools/src/lefthook/go.sum index 02b0b3f039a..b6db57ac497 100644 --- a/.citools/lefthook/go.sum +++ b/.citools/src/lefthook/go.sum @@ -19,7 +19,6 @@ github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNE github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/evilmartians/lefthook v1.4.8 h1:8FmXWtfFiEZw3w18JbhVrp3g+Iy/j2XEo6gcC25+4KA= @@ -30,12 +29,12 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -47,8 +46,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -69,22 +66,20 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -93,23 +88,21 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 h1:8ajkpB4hXVftY5ko905id+dOnmorcS2CHNxxHLLDcFM= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61/go.mod h1:IfMagxm39Ys4ybJrDb7W3Ob8RwxftP0Yy+or/NVz1O8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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/.citools/swagger/go.mod b/.citools/src/swagger/go.mod similarity index 78% rename from .citools/swagger/go.mod rename to .citools/src/swagger/go.mod index 5dd0a6e61a8..5c4f8438646 100644 --- a/.citools/swagger/go.mod +++ b/.citools/src/swagger/go.mod @@ -1,6 +1,6 @@ module swagger -go 1.24.1 +go 1.24.3 tool github.com/go-swagger/go-swagger/cmd/swagger @@ -24,16 +24,15 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect @@ -41,27 +40,23 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/toqueteos/webbrowser v1.2.0 // indirect go.mongodb.org/mongo-driver v1.16.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/crypto v0.38.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.32.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/tools v0.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/swagger/go.sum b/.citools/src/swagger/go.sum similarity index 81% rename from .citools/swagger/go.sum rename to .citools/src/swagger/go.sum index 5d6f6a6eb63..dd473c80d87 100644 --- a/.citools/swagger/go.sum +++ b/.citools/src/swagger/go.sum @@ -9,7 +9,6 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -42,14 +41,14 @@ github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3Bum github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 h1:KFcZmKdZmapAog2+eL1buervAYrYolBZk7fMecPPDmo= github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= @@ -60,8 +59,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -80,22 +77,20 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -106,26 +101,22 @@ go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4 go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/.drone.yml b/.drone.yml index d27ffd886ad..298efd04173 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,7 +25,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - ./bin/build verify-drone @@ -75,7 +75,7 @@ steps: - go install github.com/bazelbuild/buildtools/buildifier@latest - buildifier --lint=warn -mode=check -r . depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: lint-starlark trigger: event: @@ -219,7 +219,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -228,21 +228,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend - commands: - apk add --update build-base @@ -251,7 +251,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend-integration trigger: event: @@ -306,7 +306,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - echo $(/usr/bin/github-app-external-token) > /github-app/token @@ -351,16 +351,16 @@ steps: - apk add --update make - make gen-go depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: validate-openapi-spec trigger: event: @@ -437,7 +437,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -447,7 +447,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -456,7 +456,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - yarn install --immutable || yarn install --immutable @@ -488,24 +488,26 @@ steps: token: from_secret: drone_token - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 + - go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.2 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 - --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ - .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD - > packages.txt + -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER + --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base + }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID + }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i depends_on: - yarn-install environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-package pull: always volumes: @@ -520,10 +522,8 @@ steps: from_secret: docker_password DOCKER_USER: from_secret: docker_username - GITHUB_APP_ID: - from_secret: delivery-bot-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: delivery-bot-app-installation-id + GITHUB_APP_ID: "329617" + GITHUB_APP_INSTALLATION_ID: "37346161" GITHUB_APP_PRIVATE_KEY: from_secret: delivery-bot-app-private-key failure: ignore @@ -642,7 +642,7 @@ steps: from_secret: azure_tenant CYPRESS_CI: "true" HOST: grafana-server - image: us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0 + image: us-docker.pkg.dev/grafanalabs-dev/docker-oss-plugin-partnerships-dev/e2e-14.3.2:1.0.0 name: end-to-end-tests-cloud-plugins-suite-azure volumes: - name: github-app @@ -892,7 +892,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -906,7 +906,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -915,14 +915,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -943,7 +943,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -964,7 +964,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -980,7 +980,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -996,7 +996,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -1012,7 +1012,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: remote-alertmanager-integration-tests trigger: event: @@ -1094,7 +1094,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue trigger: event: @@ -1215,7 +1215,7 @@ steps: && return 1; fi depends_on: - clone-enterprise - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: swagger-gen trigger: event: @@ -1320,7 +1320,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1331,7 +1331,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - clone-enterprise - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1341,14 +1341,14 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - clone-enterprise - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - apk add --update build-base @@ -1356,7 +1356,7 @@ steps: - go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES} depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: sqlite-benchmark-integration-tests - commands: - apk add --update build-base @@ -1368,7 +1368,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: postgres-benchmark-integration-tests - commands: - apk add --update build-base @@ -1379,7 +1379,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: mysql-8.0-benchmark-integration-tests trigger: event: @@ -1451,7 +1451,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue trigger: branch: main @@ -1500,7 +1500,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1509,21 +1509,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend - commands: - apk add --update build-base @@ -1532,7 +1532,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend-integration trigger: branch: main @@ -1577,22 +1577,22 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - apk add --update make - make gen-go depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: validate-openapi-spec - commands: - ./bin/build verify-drone @@ -1724,7 +1724,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1734,7 +1734,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1743,7 +1743,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - yarn install --immutable || yarn install --immutable @@ -1774,24 +1774,26 @@ steps: image: node:22.11.0-alpine name: build-frontend-packages - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 + - go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.2 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 - --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ - .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD - > packages.txt + -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER + --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base + }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID + }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i depends_on: - update-package-json-version environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-package pull: always volumes: @@ -1808,10 +1810,8 @@ steps: from_secret: docker_username GCP_KEY: from_secret: gcp_grafanauploads - GITHUB_APP_ID: - from_secret: delivery-bot-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: delivery-bot-app-installation-id + GITHUB_APP_ID: "329617" + GITHUB_APP_INSTALLATION_ID: "37346161" GITHUB_APP_PRIVATE_KEY: from_secret: delivery-bot-app-private-key image: google/cloud-sdk:431.0.0 @@ -1932,7 +1932,7 @@ steps: from_secret: azure_tenant CYPRESS_CI: "true" HOST: grafana-server - image: us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0 + image: us-docker.pkg.dev/grafanalabs-dev/docker-oss-plugin-partnerships-dev/e2e-14.3.2:1.0.0 name: end-to-end-tests-cloud-plugins-suite-azure volumes: - name: github-app @@ -2112,10 +2112,8 @@ steps: from_secret: docker_username GCP_KEY: from_secret: gcp_grafanauploads - GITHUB_APP_ID: - from_secret: delivery-bot-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: delivery-bot-app-installation-id + GITHUB_APP_ID: "329617" + GITHUB_APP_INSTALLATION_ID: "37346161" GITHUB_APP_PRIVATE_KEY: from_secret: delivery-bot-app-private-key image: google/cloud-sdk:431.0.0 @@ -2255,7 +2253,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -2269,7 +2267,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2278,14 +2276,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -2306,7 +2304,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -2327,7 +2325,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -2343,7 +2341,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -2359,7 +2357,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -2375,7 +2373,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: remote-alertmanager-integration-tests trigger: branch: main @@ -2643,7 +2641,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2652,21 +2650,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend - commands: - apk add --update build-base @@ -2675,7 +2673,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend-integration trigger: branch: @@ -2718,22 +2716,22 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - apk add --update make - make gen-go depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: validate-openapi-spec trigger: branch: @@ -2812,7 +2810,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -2826,7 +2824,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2835,14 +2833,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -2863,7 +2861,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -2884,7 +2882,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -2900,7 +2898,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -2916,7 +2914,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -2932,7 +2930,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: remote-alertmanager-integration-tests trigger: branch: @@ -3032,7 +3030,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3110,10 +3108,8 @@ steps: from_secret: docker_username GCP_KEY: from_secret: gcp_grafanauploads - GITHUB_APP_ID: - from_secret: delivery-bot-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: delivery-bot-app-installation-id + GITHUB_APP_ID: "329617" + GITHUB_APP_INSTALLATION_ID: "37346161" GITHUB_APP_PRIVATE_KEY: from_secret: delivery-bot-app-private-key image: google/cloud-sdk:431.0.0 @@ -3164,7 +3160,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3305,7 +3301,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - ./bin/build artifacts packages --artifacts-editions=oss --tag $${DRONE_TAG} --src-bucket @@ -3397,7 +3393,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - yarn install --immutable || yarn install --immutable @@ -3497,7 +3493,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - depends_on: - compile-build-cmd @@ -3594,7 +3590,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: compile-build-cmd - commands: - ./bin/build publish grafana-com --edition oss ${DRONE_TAG} @@ -3634,9 +3630,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_main.sh + - ./pkg/build/daggerbuild/scripts/drone_build_main.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -3655,7 +3654,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.2 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -3667,7 +3665,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-build pull: always volumes: @@ -3709,9 +3707,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_tag_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -3730,7 +3731,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.2 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -3742,7 +3742,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-build pull: always volumes: @@ -3826,9 +3826,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_tag_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -3847,7 +3850,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.2 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -3859,7 +3861,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-build pull: always volumes: @@ -3998,7 +4000,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4007,21 +4009,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend - commands: - apk add --update build-base @@ -4030,7 +4032,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: test-backend-integration trigger: cron: @@ -4063,9 +4065,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_nightly_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -4084,7 +4089,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.2 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4096,7 +4100,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-build pull: always volumes: @@ -4205,9 +4209,12 @@ steps: image: google/cloud-sdk:alpine name: rgm-copy - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_publish_nightly_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh depends_on: - rgm-copy environment: @@ -4228,7 +4235,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.2 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4240,7 +4246,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-publish pull: always volumes: @@ -4332,10 +4338,13 @@ steps: - name: github-app path: /github-app - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GITHUB_TOKEN=$(cat /github-app/token) - - dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} - --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} - --go-version=1.24.2 + - dagger run --silent go run ./pkg/build/cmd artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} + --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --build-id=$${DRONE_BUILD_NUMBER} + --version=$${VERSION} depends_on: - github-app-generate-token environment: @@ -4356,7 +4365,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.2 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4368,7 +4376,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.3-alpine name: rgm-build pull: always volumes: @@ -4494,7 +4502,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4503,14 +4511,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -4531,7 +4539,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -4552,7 +4560,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -4568,7 +4576,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -4584,7 +4592,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -4600,7 +4608,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.24.2-alpine + image: golang:1.24.3-alpine name: remote-alertmanager-integration-tests trigger: event: @@ -4903,7 +4911,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:27-cli - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine/git:2.40.1 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.2-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.3-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.11.0-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 @@ -4941,7 +4949,7 @@ steps: - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL docker:27-cli - trivy --exit-code 1 --severity HIGH,CRITICAL alpine/git:2.40.1 - - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.2-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.3-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:22.11.0-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 @@ -5061,13 +5069,13 @@ name: prerelease_bucket --- get: name: username - path: infra/data/ci/grafanaci-docker-hub + path: ci/data/common/dockerhub kind: secret name: docker_username --- get: name: password - path: infra/data/ci/grafanaci-docker-hub + path: ci/data/common/dockerhub kind: secret name: docker_password --- @@ -5186,20 +5194,8 @@ kind: secret name: dagger_token --- get: - name: app-id - path: infra/data/ci/grafana-release-eng/grafana-delivery-bot -kind: secret -name: delivery-bot-app-id ---- -get: - name: app-installation-id - path: infra/data/ci/grafana-release-eng/grafana-delivery-bot -kind: secret -name: delivery-bot-app-installation-id ---- -get: - name: app-private-key - path: infra/data/ci/grafana-release-eng/grafana-delivery-bot + name: PRIVATE_KEY + path: ci/data/repo/grafana/grafana/delivery-bot-app kind: secret name: delivery-bot-app-private-key --- @@ -5210,6 +5206,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: 16029e3922ae0a13a31233717aa172c06bf0e6fc8cf01f5148de62147c259ac8 +hmac: e430c51190ba46dbd426d13f47c13c275f4d05b6b1a26d1d9f67b5a8f09ecb41 ... diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c8fdc572b60..7e90f5e6e6a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -49,6 +49,7 @@ /docs/sources/developers/plugins/ @grafana/plugins-platform-frontend @grafana/plugins-platform-backend /docs/sources/panels-visualizations/query-transform-data/transform-data/index.md @imatwawana @baldm0mma +/docs/sources/panels-visualizations/query-transform-data/sql-expressions/index.md @lwandz13 @irenerl24 # END Technical documentation # Backend code @@ -65,6 +66,7 @@ /build.go @grafana/grafana-backend-services-squad /scripts/modowners/ @grafana/grafana-backend-services-squad /scripts/go-workspace @grafana/grafana-app-platform-squad +/scripts/ci/backend-tests @grafana/grafana-operator-experience-squad /hack/ @grafana/grafana-app-platform-squad /pkg/apis/provisioning @grafana/grafana-git-ui-sync-team @@ -77,6 +79,7 @@ /apps/playlist/ @grafana/grafana-app-platform-squad /apps/investigations/ @fcjack @matryer @svennergr /apps/advisor/ @grafana/plugins-platform-backend +/apps/iam/ @grafana/access-squad /pkg/api/ @grafana/grafana-backend-group /pkg/apis/ @grafana/grafana-app-platform-squad /pkg/apis/query @grafana/grafana-datasources-core-services @@ -132,7 +135,7 @@ /pkg/services/apikey/ @grafana/identity-squad /pkg/services/cleanup/ @grafana/grafana-backend-group /pkg/services/contexthandler/ @grafana/grafana-backend-group @grafana/grafana-app-platform-squad -/pkg/services/correlations/ @grafana/dataviz-squad +/pkg/services/correlations/ @grafana/datapro /pkg/services/dashboardimport/ @grafana/grafana-backend-group /pkg/services/dashboards/ @grafana/grafana-app-platform-squad /pkg/services/dashboardversion/ @grafana/grafana-backend-group @@ -165,7 +168,7 @@ /pkg/services/tag/ @grafana/grafana-search-and-storage /pkg/services/team/ @grafana/access-squad /pkg/services/temp_user/ @grafana/grafana-backend-group -/pkg/services/updatechecker/ @grafana/grafana-backend-group +/pkg/services/updatemanager/ @grafana/grafana-backend-group /pkg/services/user/ @grafana/access-squad /pkg/services/validations/ @grafana/grafana-backend-group /pkg/setting/ @grafana/grafana-backend-services-squad @@ -173,7 +176,8 @@ /pkg/tests/apis/ @grafana/grafana-app-platform-squad /pkg/tests/apis/query @grafana/grafana-datasources-core-services /pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend -/pkg/tests/api/correlations/ @grafana/dataviz-squad +/pkg/tests/api/correlations/ @grafana/datapro +/pkg/tests/apis/secret/ @grafana/grafana-operator-experience-squad /pkg/tsdb/grafanads/ @grafana/grafana-backend-group /pkg/tsdb/opentsdb/ @grafana/partner-datasources /pkg/util/ @grafana/grafana-backend-group @@ -187,8 +191,8 @@ /devenv/docker/blocks/auth/ @grafana/identity-access-team # Logs code, developers environment -/devenv/docker/blocks/loki* @grafana/observability-logs -/devenv/docker/blocks/elastic* @grafana/aws-datasources +/devenv/docker/blocks/loki* @grafana/oss-big-tent +/devenv/docker/blocks/elastic* @grafana/partner-datasources /devenv/docker/blocks/self-instrumentation* @grafana/oss-big-tent /devenv/bulk-dashboards/ @grafana/dashboards-squad @@ -225,7 +229,7 @@ /devenv/dev-dashboards/dashboards.go @grafana/dataviz-squad /devenv/dev-dashboards/home.json @grafana/dataviz-squad -/devenv/dev-dashboards/datasource-elasticsearch/ @grafana/aws-datasources +/devenv/dev-dashboards/datasource-elasticsearch/ @grafana/partner-datasources /devenv/dev-dashboards/datasource-opentsdb/ @grafana/partner-datasources /devenv/dev-dashboards/datasource-influxdb/ @grafana/partner-datasources /devenv/dev-dashboards/datasource-mssql/ @grafana/partner-datasources @@ -255,7 +259,6 @@ /devenv/docker/blocks/etcd @grafana/grafana-app-platform-squad /devenv/docker/blocks/grafana/ @grafana/grafana-as-code /devenv/docker/blocks/graphite/ @grafana/partner-datasources -/devenv/docker/blocks/graphite09/ @grafana/partner-datasources /devenv/docker/blocks/graphite1/ @grafana/partner-datasources /devenv/docker/blocks/influxdb/ @grafana/partner-datasources /devenv/docker/blocks/influxdb1/ @grafana/partner-datasources @@ -329,8 +332,8 @@ # Observability backend code /pkg/tsdb/prometheus/ @grafana/oss-big-tent -/pkg/tsdb/elasticsearch/ @grafana/aws-datasources -/pkg/tsdb/loki/ @grafana/observability-logs +/pkg/tsdb/elasticsearch/ @grafana/partner-datasources +/pkg/tsdb/loki/ @grafana/oss-big-tent /pkg/tsdb/tempo/ @grafana/observability-traces-and-profiling /pkg/tsdb/grafana-pyroscope-datasource/ @grafana/observability-traces-and-profiling /pkg/tsdb/parca/ @grafana/oss-big-tent @@ -400,7 +403,7 @@ # Packages /packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend /packages/grafana-data/src/**/*logs* @grafana/observability-logs -/packages/grafana-data/src/transformations/ @grafana/dataviz-squad +/packages/grafana-data/src/transformations/ @grafana/datapro /packages/grafana-e2e-selectors/ @grafana/grafana-frontend-platform /packages/grafana-flamegraph/ @grafana/observability-traces-and-profiling /packages/grafana-o11y-ds-frontend/ @grafana/observability-logs @@ -437,6 +440,8 @@ /packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad /packages/grafana-ui/src/utils/storybook/ @grafana/grafana-frontend-platform /packages/grafana-alerting/ @grafana/alerting-frontend +/packages/grafana-i18n/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend +/packages/grafana-test-utils @grafana/grafana-frontend-platform # root files, mostly frontend /.browserslistrc @grafana/frontend-ops @@ -464,6 +469,7 @@ /stylelint.config.js @grafana/frontend-ops /tools/ @grafana/frontend-ops /lefthook.yml @grafana/frontend-ops +/lefthook.rc @grafana/frontend-ops /.husky/pre-commit @grafana/frontend-ops /cypress.config.js @grafana/grafana-frontend-platform /.levignore.js @grafana/plugins-platform-frontend @@ -479,7 +485,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/core/components/TimelineChart/ @grafana/dataviz-squad /public/app/core/components/Form/ @grafana/grafana-frontend-platform /public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad - +/public/app/dev-utils.ts @grafana/grafana-frontend-platform /public/app/core/history/ @grafana/observability-traces-and-profiling /public/app/features/admin/ @grafana/identity-access-team @@ -490,15 +496,14 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/actions/ @grafana/dataviz-squad /public/app/features/auth-config/ @grafana/identity-squad /public/app/features/annotations/ @grafana/dashboards-squad -/public/app/features/api-keys/ @grafana/identity-squad /public/app/features/canvas/ @grafana/dataviz-squad /public/app/features/geo/ @grafana/dataviz-squad /public/app/features/visualization/data-hover/ @grafana/dataviz-squad /public/app/features/commandPalette/ @grafana/grafana-frontend-platform /public/app/features/connections/ @grafana/plugins-platform-frontend -/public/app/features/correlations/ @grafana/dataviz-squad +/public/app/features/correlations/ @grafana/datapro /public/app/features/dashboard/ @grafana/dashboards-squad -/public/app/features/dashboard/components/TransformationsEditor/ @grafana/dataviz-squad +/public/app/features/dashboard/components/TransformationsEditor/ @grafana/datapro /public/app/features/dashboard-scene/ @grafana/dashboards-squad /public/app/features/scopes/ @grafana/dashboards-squad /public/app/features/datasources/ @grafana/plugins-platform-frontend @@ -529,7 +534,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/teams/ @grafana/access-squad /public/app/features/templating/ @grafana/dashboards-squad /public/app/features/trails/ @grafana/observability-metrics -/public/app/features/transformers/ @grafana/dataviz-squad +/public/app/features/transformers/ @grafana/datapro /public/app/features/transformers/timeSeriesTable/ @grafana/dataviz-squad @grafana/app-o11y-visualizations /public/app/features/users/ @grafana/access-squad /public/app/features/variables/ @grafana/dashboards-squad @@ -604,6 +609,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/dev.ts @grafana/frontend-ops /public/app/core/utils/metrics.ts @grafana/plugins-platform-frontend /public/app/index.ts @grafana/frontend-ops +/public/app/initApp.ts @grafana/frontend-ops /public/app/AppWrapper.tsx @grafana/frontend-ops /public/app/partials/ @grafana/grafana-frontend-platform @@ -613,7 +619,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/circle-* @grafana/grafana-developer-enablement-squad /scripts/publish-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend /scripts/validate-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend -/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad +/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad @grafana/datapro /scripts/cli/ @grafana/grafana-frontend-platform /scripts/clean-git-or-error.sh @grafana/grafana-as-code /scripts/grafana-server/ @grafana/grafana-frontend-platform @@ -636,8 +642,9 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/levitate-parse-json-report.js @grafana/plugins-platform-frontend /scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend /scripts/codemods/explicit-barrel-imports.cjs @grafana/frontend-ops +/scripts/rtk-client-generator/ @grafana/grafana-frontend-platform -/scripts/**/generate-transformations* @grafana/dataviz-squad +/scripts/**/generate-transformations* @grafana/datapro /scripts/webpack/ @grafana/frontend-ops /scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform .pa11yci.conf.js @grafana/grafana-frontend-platform @@ -651,14 +658,14 @@ playwright.config.ts @grafana/plugins-platform-frontend # Core datasources /public/app/plugins/datasource/dashboard/ @grafana/dashboards-squad /public/app/plugins/datasource/cloudwatch/ @grafana/aws-datasources -/public/app/plugins/datasource/elasticsearch/ @grafana/aws-datasources +/public/app/plugins/datasource/elasticsearch/ @grafana/partner-datasources /public/app/plugins/datasource/grafana/ @grafana/grafana-frontend-platform /public/app/plugins/datasource/grafana-testdata-datasource/ @grafana/plugins-platform-frontend /public/app/plugins/datasource/azuremonitor/ @grafana/partner-datasources /public/app/plugins/datasource/graphite/ @grafana/partner-datasources /public/app/plugins/datasource/influxdb/ @grafana/partner-datasources /public/app/plugins/datasource/jaeger/ @grafana/oss-big-tent -/public/app/plugins/datasource/loki/ @grafana/observability-logs +/public/app/plugins/datasource/loki/ @grafana/oss-big-tent /public/app/plugins/datasource/mixed/ @grafana/dashboards-squad /public/app/plugins/datasource/mssql/ @grafana/partner-datasources /public/app/plugins/datasource/mysql/ @grafana/oss-big-tent @@ -699,7 +706,6 @@ playwright.config.ts @grafana/plugins-platform-frontend /pkg/services/signingkeys/ @grafana/identity-squad /pkg/services/dashboards/accesscontrol.go @grafana/access-squad /pkg/services/datasources/guardian/ @grafana/access-squad -/pkg/services/guardian/ @grafana/access-squad /pkg/services/ldap/ @grafana/identity-squad /pkg/services/login/ @grafana/identity-squad /pkg/services/loginattempt/ @grafana/identity-squad @@ -732,11 +738,11 @@ embed.go @grafana/grafana-as-code /pkg/kinds/ @grafana/grafana-as-code /pkg/registry/ @grafana/grafana-as-code /pkg/registry/apis/ @grafana/grafana-app-platform-squad -/pkg/registry/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend /pkg/registry/apis/query @grafana/grafana-datasources-core-services /pkg/registry/apis/secret @grafana/grafana-operator-experience-squad /pkg/registry/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend /pkg/registry/apps/advisor @grafana/plugins-platform-backend +/pkg/registry/apps/alerting @grafana/alerting-backend /pkg/codegen/ @grafana/grafana-as-code /pkg/codegen/generators @grafana/grafana-as-code /pkg/kinds/*/*_gen.go @grafana/grafana-as-code @@ -752,13 +758,13 @@ embed.go @grafana/grafana-as-code /.github/commands.json @torkelo /.github/dependabot.yml @grafana/frontend-ops /.github/issue-opened.json @grafana/grafana-community-support -/.github/metrics-collector.json @torkelo /.github/pr-checks.json @tolzhabayev /.github/pr-commands.json @tolzhabayev /.github/renovate.json5 @grafana/frontend-ops /.github/actions/setup-enterprise/action.yml @grafana/grafana-backend-group -/.github/actions/test-coverage-processor/action.yml @grafana/grafana-backend-group /.github/actions/setup-grafana-bench/ @Proximyst +/.github/workflows/actionlint-format.txt @grafana/grafana-developer-enablement-squad +/.github/workflows/actionlint.yml @grafana/grafana-developer-enablement-squad /.github/workflows/add-to-whats-new.yml @grafana/docs-tooling /.github/workflows/auto-triager/ @grafana/plugins-platform-frontend /.github/workflows/alerting-swagger-gen.yml @grafana/alerting-backend @@ -766,13 +772,14 @@ embed.go @grafana/grafana-as-code /.github/workflows/auto-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/backend-code-checks.yml @grafana/grafana-backend-group /.github/workflows/backend-unit-tests.yml @grafana/grafana-backend-group -/.github/workflows/backport.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/backport-trigger.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/backport-workflow.yml @grafana/grafana-developer-enablement-squad /.github/workflows/bump-version.yml @grafana/grafana-developer-enablement-squad -/.github/workflows/close-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/release-pr.yml @grafana/grafana-developer-enablement-squad /.github/workflows/release-comms.yml @grafana/grafana-developer-enablement-squad /.github/workflows/migrate-prs.yml @grafana/grafana-developer-enablement-squad /.github/workflows/create-next-release-branch.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/create-security-branch.yml @grafana/grafana-developer-enablement-squad /.github/workflows/codeowners-validator.yml @tolzhabayev /.github/workflows/codeql-analysis.yml @DanCech /.github/workflows/commands.yml @torkelo @@ -780,57 +787,59 @@ embed.go @grafana/grafana-as-code /.github/workflows/detect-breaking-changes-* @grafana/plugins-platform-frontend /.github/workflows/documentation-ci.yml @grafana/docs-tooling /.github/workflows/deploy-pr-preview.yml @grafana/docs-tooling -/.github/workflows/epic-add-to-platform-ux-parent-project.yml @meanmina /.github/workflows/feature-toggles-ci.yml @grafana/docs-tooling /.github/workflows/github-release.yml @grafana/grafana-developer-enablement-squad /.github/workflows/issue-opened.yml @grafana/grafana-community-support /.github/workflows/lint-build-docs.yml @grafana/docs-tooling -/.github/workflows/metrics-collector.yml @torkelo -/.github/workflows/milestone.yml @tolzhabayev /.github/workflows/pr-checks.yml @tolzhabayev /.github/workflows/pr-codeql-analysis-javascript.yml @DanCech /.github/workflows/pr-codeql-analysis-python.yml @DanCech /.github/workflows/pr-commands.yml @tolzhabayev +/.github/workflows/pr-external-labelling.yml @Proximyst /.github/workflows/pr-patch-check-event.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/pr-patch-check.yml @grafana/grafana-developer-enablement-squad /.github/workflows/pr-test-integration.yml @grafana/grafana-backend-group -/.github/workflows/pr-backend-coverage.yml @grafana/grafana-backend-group +/.github/workflows/reject-gh-secrets.yml @grafana/grafana-developer-enablement-squad /.github/workflows/sync-mirror-event.yml @grafana/grafana-developer-enablement-squad /.github/workflows/publish-technical-documentation-next.yml @grafana/docs-tooling /.github/workflows/publish-technical-documentation-release.yml @grafana/docs-tooling -/.github/workflows/remove-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend /.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad /.github/workflows/storybook-verification.yml @grafana/grafana-frontend-platform -/.github/workflows/update-changelog.yml @grafana/grafana-developer-enablement-squad /.github/workflows/update-make-docs.yml @grafana/docs-tooling /.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring +/.github/workflows/scripts/create-security-branch/create-security-branch.sh @grafana/grafana-developer-enablement-squad /.github/workflows/publish-kinds-next.yml @grafana/platform-monitoring /.github/workflows/publish-kinds-release.yml @grafana/platform-monitoring /.github/workflows/verify-kinds.yml @grafana/platform-monitoring /.github/workflows/dashboards-issue-add-label.yml @grafana/dashboards-squad /.github/workflows/run-schema-v2-e2e.yml @grafana/dashboards-squad +/.github/workflows/e2e-dashboard-new-layouts.yml @grafana/dashboards-squad /.github/workflows/run-dashboard-search-e2e.yml @grafana/grafana-search-and-storage /.github/workflows/trigger-dashboard-search-e2e.yml @grafana/grafana-search-and-storage -/.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-backend-services-squad +/.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-operator-experience-squad /.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-developer-enablement-squad /.github/workflows/core-plugins-build-and-release.yml @grafana/plugins-platform-frontend @grafana/plugins-platform-backend /.github/workflows/i18n-crowdin-upload.yml @grafana/grafana-frontend-platform /.github/workflows/i18n-crowdin-download.yml @grafana/grafana-frontend-platform /.github/workflows/i18n-crowdin-create-tasks.yml @grafana/grafana-frontend-platform -/.github/workflows/scripts/crowdin/create-tasks.js @grafana/grafana-frontend-platform +/.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform /.github/workflows/pr-go-workspace-check.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-dependabot-update-go-workspace.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-k8s-codegen-check.yml @grafana/grafana-app-platform-squad /.github/workflows/go-lint.yml @grafana/grafana-backend-services-squad /.github/workflows/trivy-scan.yml @grafana/grafana-backend-services-squad +/.github/workflows/trufflehog.yml @Proximyst /.github/workflows/changelog.yml @zserge -/.github/workflows/actions/changelog @zserge +/.github/actions/changelog @zserge /.github/workflows/pr-frontend-unit-tests.yml @grafana/grafana-frontend-platform /.github/workflows/frontend-lint.yml @grafana/grafana-frontend-platform /.github/workflows/analytics-events-report.yml @grafana/grafana-frontend-platform /.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad /.github/workflows/run-e2e-suite.yml @grafana/grafana-developer-enablement-squad /.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform +/.github/zizmor.yml @grafana/grafana-developer-enablement-squad +/.github/license_finder.yaml @bergquist # Generated files not requiring owner approval /packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot diff --git a/.github/workflows/actions/changelog/action.yml b/.github/actions/changelog/action.yml similarity index 100% rename from .github/workflows/actions/changelog/action.yml rename to .github/actions/changelog/action.yml diff --git a/.github/workflows/actions/changelog/index.js b/.github/actions/changelog/index.js similarity index 79% rename from .github/workflows/actions/changelog/index.js rename to .github/actions/changelog/index.js index b658caf349c..7bfcfc82148 100644 --- a/.github/workflows/actions/changelog/index.js +++ b/.github/actions/changelog/index.js @@ -1,6 +1,7 @@ -import { appendFileSync, writeFileSync } from 'fs'; -import { exec as execCallback } from 'node:child_process'; -import { promisify } from 'node:util'; +import {appendFileSync, writeFileSync} from 'fs'; +import {exec as execCallback} from 'node:child_process'; +import {promisify} from 'node:util'; +import {findPreviousVersion, semverParse} from "./semver.js"; // // Github Action core utils: logging (notice + debug log levels), must escape @@ -9,35 +10,6 @@ import { promisify } from 'node:util'; const escapeData = (s) => s.replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A'); const LOG = (msg) => console.log(`::notice::${escapeData(msg)}`); -// -// Semver utils: parse, compare, sort etc (using official regexp) -// https://regex101.com/r/Ly7O1x/3/ -// -const semverRegExp = - /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; - -const semverParse = (tag) => { - const m = tag.match(semverRegExp); - if (!m) { - return; - } - const [_, major, minor, patch, prerelease] = m; - return [+major, +minor, +patch, prerelease, tag]; -}; - -// semverCompare takes two parsed semver tags and comparest them more or less -// according to the semver specs -const semverCompare = (a, b) => { - for (let i = 0; i < 3; i++) { - if (a[i] !== b[i]) { - return a[i] < b[i] ? 1 : -1; - } - } - if (a[3] !== b[3]) { - return a[3] < b[3] ? 1 : -1; - } - return 0; -}; // Using `git tag -l` output find the tag (version) that goes semantically // right before the given version. This might not work correctly with some @@ -45,29 +17,32 @@ const semverCompare = (a, b) => { // into this action explicitly to avoid this step. const getPreviousVersion = async (version) => { const exec = promisify(execCallback); - const { stdout } = await exec('git tag -l'); - const prev = stdout + const {stdout} = await exec('git for-each-ref --sort=-creatordate --format \'%(refname:short)\' refs/tags'); + + const parsedTags = stdout .split('\n') .map(semverParse) - .filter((tag) => tag) - .sort(semverCompare) - .find((tag) => semverCompare(tag, semverParse(version)) > 0); + .filter(Boolean); + + const parsedVersion = semverParse(version); + const prev = findPreviousVersion(parsedTags, parsedVersion); if (!prev) { throw `Could not find previous git tag for ${version}`; } - return prev[4]; + return prev[5]; }; + // A helper for Github GraphQL API endpoint const graphql = async (ghtoken, query, variables) => { - const { env } = process; + const {env} = process; const results = await fetch('https://api.github.com/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${ghtoken}`, }, - body: JSON.stringify({ query, variables }), + body: JSON.stringify({query, variables}), }); const res = await results.json(); @@ -100,7 +75,7 @@ const getCommitishDate = async (name, owner, target) => { } } `, - { name, owner, target } + {name, owner, target} ); return result.repository.object.committedDate; }; @@ -160,7 +135,7 @@ const getHistory = async (name, owner, from, to) => { let cursor; let nodes = []; - for (;;) { + for (; ;) { const result = await graphql(ghtoken, query, { name, owner, @@ -170,7 +145,7 @@ const getHistory = async (name, owner, from, to) => { }); LOG(`GraphQL: ${JSON.stringify(result)}`); nodes = [...nodes, ...result.repository.ref.compare.commits.nodes]; - const { hasNextPage, endCursor } = result.repository.ref.compare.commits.pageInfo; + const {hasNextPage, endCursor} = result.repository.ref.compare.commits.pageInfo; if (!hasNextPage) { break; } @@ -186,7 +161,7 @@ const getHistory = async (name, owner, from, to) => { // PR grouping relies on Github labels only, not on the PR contents. const getChangeLogItems = async (name, owner, from, to) => { // check if a node contains a certain label - const hasLabel = ({ labels }, label) => labels.nodes.some(({ name }) => name === label); + const hasLabel = ({labels}, label) => labels.nodes.some(({name}) => name === label); // get all the PRs between the two "commitish" items const history = await getHistory(name, owner, from, to); @@ -197,17 +172,17 @@ const getChangeLogItems = async (name, owner, from, to) => { return []; } const item = changes[0]; - const { number, url, labels } = item; + const {number, url, labels} = item; const title = item.title.replace(/^\[[^\]]+\]:?\s*/, ''); // for changelog PRs try to find a suitable category. // Note that we can not detect "deprecation notices" like that // as there is no suitable label yet. - const isBug = /fix/i.test(title) || hasLabel({ labels }, 'type/bug'); - const isBreaking = hasLabel({ labels }, 'breaking change'); + const isBug = /fix/i.test(title) || hasLabel({labels}, 'type/bug'); + const isBreaking = hasLabel({labels}, 'breaking change'); const isPlugin = - hasLabel({ labels }, 'area/grafana/ui') || - hasLabel({ labels }, 'area/grafana/toolkit') || - hasLabel({ labels }, 'area/grafana/runtime'); + hasLabel({labels}, 'area/grafana/ui') || + hasLabel({labels}, 'area/grafana/toolkit') || + hasLabel({labels}, 'area/grafana/runtime'); const author = item.commits.nodes[0].commit.author.user?.login; return { repo: name, @@ -227,7 +202,7 @@ const getChangeLogItems = async (name, owner, from, to) => { // ====================================================== LOG(`Changelog action started`); - +console.log(process.argv); const ghtoken = process.env.GITHUB_TOKEN || process.env.INPUT_GITHUB_TOKEN; if (!ghtoken) { throw 'GITHUB_TOKEN is not set and "github_token" input is empty'; @@ -286,15 +261,15 @@ const markdown = (changelog) => { : `### ${title} ${items - .map( - (item) => - `- ${item.title.replace(/^([^:]*:)/gm, '**$1**')} ${ - item.repo === 'grafana-enterprise' - ? '(Enterprise)' - : `${pullRequestLink(item.number)}${item.author ? ', ' + userLink(item.author) : ''}` - }` - ) - .join('\n')} + .map( + (item) => + `- ${item.title.replace(/^([^:]*:)/gm, '**$1**')} ${ + item.repo === 'grafana-enterprise' + ? '(Enterprise)' + : `${pullRequestLink(item.number)}${item.author ? ', ' + userLink(item.author) : ''}` + }` + ) + .join('\n')} `; // Render all present sections for the given changelog diff --git a/.github/workflows/actions/changelog/package.json b/.github/actions/changelog/package.json similarity index 100% rename from .github/workflows/actions/changelog/package.json rename to .github/actions/changelog/package.json diff --git a/.github/actions/changelog/semver.js b/.github/actions/changelog/semver.js new file mode 100644 index 00000000000..461ce5195c8 --- /dev/null +++ b/.github/actions/changelog/semver.js @@ -0,0 +1,92 @@ +// +// Semver utils: parse, compare, sort etc (using official regexp) +// https://regex101.com/r/Ly7O1x/3/ +// +const semverRegExp = + /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; + +export function semverParse(tag) { + const m = tag.match(semverRegExp); + if (!m) { + return; + } + const [_, major, minor, patch, prerelease, build] = m; + return [+major, +minor, +patch, prerelease, build, tag]; +}; + +// semverCompare takes two parsed semver tags and comparest them more or less +// according to the semver specs +export function semverCompare(a, b) { + for (let i = 0; i < 3; i++) { + if (a[i] !== b[i]) { + return a[i] < b[i] ? 1 : -1; + } + } + if (a[3] !== b[3]) { + return a[3] < b[3] ? 1 : -1; + } + return 0; +}; + + +// Finds the highest version that is lower than the target version. +// +// This function relies on the following invariant: versions are sorted by the release date. +// It will produce wrong result if invariant doesn't hold. +export const findPreviousVersion = (versionByDate, target) => { + let prev = null; + + for (let i = 0; i < versionByDate.length; i++) { + const version = versionByDate[i]; + + // version is greater than the target + if (semverCompare(target, version) > 0) { + continue; + } + + // we came across the target version, all versions seen previously have greater release date. + if (semverCompare(target, version) === 0 && target[4] === version[4]) { + prev = null; + continue; + } + + if (prev == null) { + prev = version; + continue; + } + + if (semverCompare(prev, version) > 0) { + prev = version; + } + } + + return prev; +}; + + +const versionsByDate = [ + "v10.4.19", "v12.0.1", "v11.6.2", "v11.5.5", "v11.4.5", "v11.3.7", "v11.2.10", "v12.0.0+security-01", "v11.2.9+security-01", "v11.3.6+security-01", + "v11.6.1+security-01", "v11.4.4+security-01", "v11.5.4+security-01", "v10.4.18+security-01", "v12.0.0", "v11.6.1", + "v11.5.4", "v11.4.4", "v11.3.6", "v11.2.9", "v10.4.18", "v11.6.0+security-01", "v11.5.3+security-01", "v11.4.3+security-01", + "v11.3.5+security-01", "v11.2.8+security-01", "v10.4.17+security-01", "v11.2.8", "v11.6.0", "v11.5.2", "v11.4.2", + "v11.3.4", "v11.2.7", "v11.1.12", "v11.0.11", "v10.4.16", "v11.5.1", "v11.5.0", "v11.3.3", "v11.1.11", "v11.2.6", + "v11.0.10", "v10.4.15", "v11.4.1", "v11.4.0", "v11.3.2", "v11.2.5", "v11.1.10", "v11.0.9", "v10.4.14", "v11.3.1", + "v11.2.4", "v11.1.9", "v11.0.8", "v10.4.13", "v11.0.2", "v10.4.6", "v10.3.8", "v10.2.9", "v11.1.0", "v11.0.1", + "v10.4.5", "v10.3.7", "v10.2.8", "v9.5.20", "v10.4.4", "v9.5.19", "v10.1.10", "v10.2.7", "v10.3.6", "v10.4.3", + "v11.0.0", "v10.4.2", "v11.0.0-preview", "v10.1.9", "v10.0.13", "v9.2.0", "v9.1.8", +].map(semverParse); + +function test(version, expected) { + const v1 = semverParse(version); + const prev = findPreviousVersion(versionsByDate, v1); + + const failureMessage = `FAIILED. Expected ${expected}, but was ${prev[5]}`; + + console.log(`Test ${version}, ${prev[5] === expected ? 'PASSED' : failureMessage}`); +} + +test("v11.5.4+security-01", "v11.5.4"); +test("v11.5.4", "v11.5.3+security-01"); +test("v12.0.0", "v11.6.1"); +test("v12.0.0+security-01", "v12.0.0"); +test("v11.0.0", "v11.0.0-preview"); diff --git a/.github/actions/setup-enterprise/action.yml b/.github/actions/setup-enterprise/action.yml index e87d3d7bae9..37c09911fc8 100644 --- a/.github/actions/setup-enterprise/action.yml +++ b/.github/actions/setup-enterprise/action.yml @@ -12,7 +12,7 @@ runs: steps: - name: Retrieve GitHub App secrets id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 + uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 # zizmor: ignore[unpinned-uses] with: repo_secrets: | APP_ID=${{ inputs.github-app-name }}:app-id diff --git a/.github/actions/setup-grafana-bench/action.yml b/.github/actions/setup-grafana-bench/action.yml index 624ad243b7b..f30cc37221d 100644 --- a/.github/actions/setup-grafana-bench/action.yml +++ b/.github/actions/setup-grafana-bench/action.yml @@ -16,7 +16,7 @@ runs: steps: - name: Retrieve GitHub App secrets id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 + uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 # zizmor: ignore[unpinned-uses] with: repo_secrets: | APP_ID=${{ inputs.github-app-name }}:app-id @@ -36,9 +36,10 @@ runs: shell: bash env: GH_TOKEN: ${{ steps.generate_token.outputs.token }} + BRANCH: ${{ inputs.branch }} run: | git clone https://x-access-token:${GH_TOKEN}@github.com/grafana/grafana-bench.git ../grafana-bench cd ../grafana-bench - git switch ${{ inputs.branch }} + git switch "$BRANCH" go install . diff --git a/.github/actions/test-coverage-processor/action.yml b/.github/actions/test-coverage-processor/action.yml deleted file mode 100644 index 7560031e846..00000000000 --- a/.github/actions/test-coverage-processor/action.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: 'Go Coverage Processor' -description: 'Process Go test coverage files and generate reports' - -inputs: - test-type: - description: 'Type of test (e.g., be-unit, be-integration)' - required: true - type: string - coverage-file: - description: 'Path to the Go coverage file (.cov)' - required: true - type: string - codecov-token: - description: 'Token for CodeCov (required for CodeCov reporting)' - required: false - default: '' - codecov-flag: - description: 'Flag to categorize the upload to CodeCov' - required: false - default: '' - codecov-name: - description: 'Custom name for the upload to CodeCov' - required: false - default: '' - -runs: - using: 'composite' - steps: - - name: Process Go coverage output - shell: bash - run: | - # Ensure valid coverage file even if empty - if [ ! -s ${{ inputs.coverage-file }} ]; then - echo "Coverage file is empty, creating a minimal valid file" - echo "mode: set" > ${{ inputs.coverage-file }} - fi - - - name: Report coverage to CodeCov - uses: codecov/codecov-action@v5 - if: inputs.codecov-token != '' - with: - files: ${{ inputs.coverage-file }} - flags: ${{ inputs.codecov-flag || inputs.test-type }} - name: ${{ inputs.codecov-name || inputs.test-type }} - slug: grafana/grafana - # This URL doesn't use the Google auth, but is much more locked down. As such, it requires OIDC or a CodeCov-provided token to do anything. - url: https://codecov-webhook.grafana-dev.net - token: ${{ inputs.codecov-token }} diff --git a/.github/commands.json b/.github/commands.json index f92e29a215b..b26f0a37719 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -128,7 +128,7 @@ "name": "datasource/Loki", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/203" + "url": "https://github.com/orgs/grafana/projects/457" } }, { @@ -160,7 +160,7 @@ "name": "datasource/Elasticsearch", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/97" + "url": "https://github.com/orgs/grafana/projects/190" } }, { @@ -488,7 +488,23 @@ "name": "area/transformations", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/56" + "url": "https://github.com/orgs/grafana/projects/908" + } + }, + { + "type": "label", + "name": "area/correlations", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/908" + } + }, + { + "type": "label", + "name": "area/area/expressions/sql", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/908" } }, { @@ -1194,5 +1210,13 @@ "addToProject": { "url": "https://github.com/orgs/grafana/projects/699" } + }, + { + "type": "label", + "name": "type/docs", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/69" + } } ] diff --git a/.github/license_finder.yaml b/.github/license_finder.yaml new file mode 100644 index 00000000000..f2fc8a144c4 --- /dev/null +++ b/.github/license_finder.yaml @@ -0,0 +1,128 @@ +--- +- - :permit + - MIT + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:11:50.696368005 Z +- - :permit + - Apache 2.0 + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - New BSD + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - Simplified BSD + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - Mozilla Public License 2.0 + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - ISC + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 +- - :license + - github.com/grafana/alerting + - GNU Affero GPL + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/advisor + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/alerting/notifications + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/dashboard + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/folder + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/investigations + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/playlist + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/aggregator + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/apimachinery + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/apis/secret + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/apiserver + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/promlib + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/semconv + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z \ No newline at end of file diff --git a/.github/metrics-collector.json b/.github/metrics-collector.json deleted file mode 100644 index fc717a74908..00000000000 --- a/.github/metrics-collector.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "queries": [ - { - "name": "type_bug", - "query": "label:\"type/bug\" is:issue is:open" - }, - { - "name": "type_docs", - "query": "label:\"type/docs\" is:issue is:open" - }, - { - "name": "needs_investigation", - "query": "label:\"needs investigation\" is:issue is:open" - }, - { - "name": "needs_more_info", - "query": "label:\"needs more info\" is:issue is:open" - }, - { - "name": "triage_needs_confirmation", - "query": "label:\"triage/needs-confirmation\" is:issue is:open" - }, - { - "name": "unlabeled", - "query": "is:open is:issue no:label" - }, - { - "name": "open_prs", - "query": "is:open is:pull-request" - } - ] -} \ No newline at end of file diff --git a/.github/pr-commands.json b/.github/pr-commands.json index c8117bd6577..f9934555782 100644 --- a/.github/pr-commands.json +++ b/.github/pr-commands.json @@ -253,38 +253,6 @@ "action": "updateLabel", "addLabel": "area/alerting" }, - { - "type": "label", - "name": "area/alerting", - "action": "addToProject", - "addToProject": { - "url": "https://github.com/orgs/grafana/projects/52" - } - }, - { - "type": "author", - "name": "pr/external", - "notMemberOf": { - "org": "grafana" - }, - "ignoreList": [ - "renovate[bot]", - "dependabot[bot]", - "grafana-delivery-bot[bot]", - "grafanabot", - "alerting-team[bot]" - ], - "action": "updateLabel", - "addLabel": "pr/external" - }, - { - "type": "label", - "name": "type/docs", - "action": "addToProject", - "addToProject": { - "url": "https://github.com/orgs/grafana/projects/69" - } - }, { "type": "changedfiles", "matches": [ diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 13edbbb0943..aa28c25a240 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,6 +2,15 @@ extends: ["config:recommended"], enabledManagers: ["npm"], ignoreDeps: [ + // ignoring these until we can upgrade to react 19 + // see epic here: https://github.com/grafana/grafana/issues/98813 + '@types/react', + '@types/react-dom', + 'eslint-plugin-react-hooks', + 'react', + 'react-dom', + 'react-refresh', + "@types/history", // this can be removed entirely when we upgrade history since v5 exposes types directly "history", // we should bump this together with react-router-dom (see https://github.com/grafana/grafana/issues/76744) "react-router", // we should bump this together with history and react-router-dom diff --git a/.github/workflows/actionlint-format.txt b/.github/workflows/actionlint-format.txt new file mode 100644 index 00000000000..6e60cd50399 --- /dev/null +++ b/.github/workflows/actionlint-format.txt @@ -0,0 +1,66 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "GitHub Actions lint", + "version": {{ getVersion | json }}, + "informationUri": "https://github.com/rhysd/actionlint", + "rules": [ + {{$first := true}} + {{range $ := allKinds }} + {{if $first}}{{$first = false}}{{else}},{{end}} + { + "id": {{json $.Name}}, + "name": {{$.Name | toPascalCase | json}}, + "defaultConfiguration": { + "level": "error" + }, + "properties": { + "description": {{json $.Description}}, + "queryURI": "https://github.com/rhysd/actionlint/blob/main/docs/checks.md" + }, + "fullDescription": { + "text": {{json $.Description}} + }, + "helpUri": "https://github.com/rhysd/actionlint/blob/main/docs/checks.md" + } + {{end}} + ] + } + }, + "results": [ + {{$first := true}} + {{range $ := .}} + {{if $first}}{{$first = false}}{{else}},{{end}} + { + "ruleId": {{json $.Kind}}, + "message": { + "text": {{json $.Message}} + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": {{json $.Filepath}}, + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": {{$.Line}}, + "startColumn": {{$.Column}}, + "endColumn": {{$.EndColumn}}, + "snippet": { + "text": {{json $.Snippet}} + } + } + } + } + ] + } + {{end}} + ] + } + ] +} \ No newline at end of file diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 00000000000..de1051ef729 --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,60 @@ +# This workflow depends on the ./actionlint-format.txt file. It is MIT licensed (thanks, rhysd!): https://github.com/rhysd/actionlint/blob/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/format/sarif_template.txt +name: Actionlint + +on: + push: + branches: + - main + - release-* + pull_request: + types: + - opened + - synchronize + - reopened + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + run-actionlint: + name: Lint GitHub Actions files + runs-on: ubuntu-latest + permissions: + contents: read # to check out the code + actions: read # to read the workflow files + security-events: write # for uploading the SARIF report + + env: + ACTIONLINT_VERSION: 1.7.7 + # curl -LXGET https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_checksums.txt | grep linux_amd64 + CHECKSUM: 023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757 + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + # GitHub Actions only runs x86_64. This will break if that assumption changes. + - name: Download Actionlint + run: | + set -euo pipefail + curl -OLXGET https://github.com/rhysd/actionlint/releases/download/v"${ACTIONLINT_VERSION}"/actionlint_"${ACTIONLINT_VERSION}"_linux_amd64.tar.gz + echo "${CHECKSUM} actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | sha256sum -c - + tar xzf actionlint_"${ACTIONLINT_VERSION}"_linux_amd64.tar.gz + test -f actionlint + chmod +x actionlint + + - name: Run Actionlint + run: ./actionlint -format "$(cat .github/workflows/actionlint-format.txt)" | tee results.sarif + + - name: Upload to GitHub security events + if: success() || failure() + # If there are security problems, GitHub will automatically comment on the PR for us. + uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 + with: + sarif_file: results.sarif + category: actionlint diff --git a/.github/workflows/alerting-swagger-gen.yml b/.github/workflows/alerting-swagger-gen.yml index 7c3af87837f..c06304b38a3 100644 --- a/.github/workflows/alerting-swagger-gen.yml +++ b/.github/workflows/alerting-swagger-gen.yml @@ -13,15 +13,16 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod - name: Build swagger run: | make -C pkg/services/ngalert/api/tooling post.json api.json - name: Open Pull Request - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@4e1beaa7521e8b457b572c090b25bd3db56bf1c5 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "chore: update alerting swagger spec" @@ -34,4 +35,3 @@ jobs: labels: 'area/alerting,type/docs,no-changelog' team-reviewers: 'grafana/alerting-backend' draft: false - diff --git a/.github/workflows/alerting-update-module.yml b/.github/workflows/alerting-update-module.yml index 213b243f9b1..dce80d065e4 100644 --- a/.github/workflows/alerting-update-module.yml +++ b/.github/workflows/alerting-update-module.yml @@ -17,8 +17,9 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 - + uses: actions/checkout@v4 # 4.2.2 + with: + persist-credentials: false - name: Check if update branch exists run: | if git ls-remote --heads origin update-alerting-module | grep -q 'update-alerting-module'; then @@ -36,7 +37,7 @@ jobs: id: current-commit run: | FROM_COMMIT=$(go list -m -json github.com/grafana/alerting | jq -r '.Version' | grep -oP '(?<=-)[a-f0-9]+$') - echo "from_commit=$FROM_COMMIT" >> $GITHUB_OUTPUT + echo "from_commit=$FROM_COMMIT" >> "$GITHUB_OUTPUT" - name: Get current branch name id: current-branch-name @@ -46,14 +47,14 @@ jobs: id: latest-commit env: GH_TOKEN: ${{ github.token }} + BRANCH: ${{ steps.current-branch-name.outputs.name }} run: | - BRANCH="${{ steps.current-branch-name.outputs.name }}" - TO_COMMIT=$(gh api repos/grafana/alerting/commits/$BRANCH --jq '.sha') + TO_COMMIT="$(gh api repos/grafana/alerting/commits/"$BRANCH" --jq '.sha')" if [ -z "$TO_COMMIT" ]; then echo "Branch $BRANCH not found in alerting repo, falling back to main branch" exit 1 fi - echo "to_commit=$TO_COMMIT" >> $GITHUB_OUTPUT + echo "to_commit=$TO_COMMIT" >> "$GITHUB_OUTPUT" - name: Compare commit hashes run: | @@ -73,30 +74,35 @@ jobs: id: check-commits env: GH_TOKEN: ${{ github.token }} + FROM_COMMIT: ${{ steps.current-commit.outputs.from_commit }} + TO_COMMIT: ${{ steps.latest-commit.outputs.to_commit }} run: | - # get all commits that contains 'Alerting:' in the message - ALERTING_COMMITS=$(gh api repos/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} \ - --jq '.commits[].commit.message | split("\n")[0]') || true - + # get all commits that contains 'Alerting:' in the message + ALERTING_COMMITS="$(gh api repos/grafana/alerting/compare/"$FROM_COMMIT"..."$TO_COMMIT" \ + --jq '.commits[].commit.message | split("\n")[0]')" || true + # Use printf instead of echo -e for better multiline handling printf "%s\n" "$ALERTING_COMMITS" - + # make the list for markdown and replace PR numbers with links - ALERTING_COMMITS_FORMATTED=$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done) - - echo "alerting_commits<> $GITHUB_OUTPUT - echo "$ALERTING_COMMITS_FORMATTED" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + ALERTING_COMMITS_FORMATTED="$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done)" + + { + echo "alerting_commits<> "$GITHUB_OUTPUT" - name: Update alerting module env: GOSUMDB: off + PINNED_COMMIT: ${{ steps.latest-commit.outputs.to_commit }} run: | - go get github.com/grafana/alerting@${{ steps.latest-commit.outputs.to_commit }} + go get github.com/grafana/alerting@"$PINNED_COMMIT" make update-workspace - id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # 1.1.0 + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: repo_secrets: | GITHUB_APP_ID=alerting-team:app-id @@ -123,7 +129,7 @@ jobs: Compare changes: https://github.com/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }}
Commits - + ${{ steps.check-commits.outputs.alerting_commits }}
@@ -131,6 +137,10 @@ jobs: Created by: [GitHub Action Job](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) - name: Add PR URL to Summary if: steps.create-pr.outputs.pull-request-url != '' + env: + PR_URL: ${{ steps.create-pr.outputs.pull-request-url }} run: | - echo "## Pull Request Created" >> $GITHUB_STEP_SUMMARY - echo "🔗 [View Pull Request](${{ steps.create-pr.outputs.pull-request-url }})" >> $GITHUB_STEP_SUMMARY + { + echo "## Pull Request Created" + echo "🔗 [View Pull Request]($PR_URL)" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/analytics-events-report.yml b/.github/workflows/analytics-events-report.yml index 9d4d6907256..af265157337 100644 --- a/.github/workflows/analytics-events-report.yml +++ b/.github/workflows/analytics-events-report.yml @@ -3,12 +3,18 @@ name: Analytics Events Report on: workflow_dispatch: +permissions: {} + jobs: generate-report: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/backend-code-checks.yml b/.github/workflows/backend-code-checks.yml index 75249775bad..b95257d99c3 100644 --- a/.github/workflows/backend-code-checks.yml +++ b/.github/workflows/backend-code-checks.yml @@ -1,5 +1,4 @@ name: Backend Code Checks -description: Validate go.mod and OpenAPI specifications on: pull_request: @@ -25,7 +24,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/backend-unit-tests.yml b/.github/workflows/backend-unit-tests.yml index f1dccf53779..e53be4d1d00 100644 --- a/.github/workflows/backend-unit-tests.yml +++ b/.github/workflows/backend-unit-tests.yml @@ -17,21 +17,32 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} -permissions: - contents: read - id-token: write +permissions: {} jobs: grafana: - # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # Run this workflow only for PRs from forks # the `pr-backend-unit-tests-enterprise` workflow will run instead if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true - name: Grafana + strategy: + matrix: + shard: [ + 1/8, 2/8, 3/8, 4/8, + 5/8, 6/8, 7/8, 8/8, + ] + fail-fast: false + + name: Grafana (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores continue-on-error: true + permissions: + contents: read + id-token: write steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: @@ -39,16 +50,34 @@ jobs: - name: Generate Go code run: make gen-go - name: Run unit tests - run: make test-go-unit + env: + SHARD: ${{ matrix.shard }} + run: | + readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")" + go test -short -timeout=30m "${PACKAGES[@]}" grafana-enterprise: # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) + # If it gets merged into `main` or `release-*`, then a junit test result is uploaded to GCS. if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false - name: Grafana Enterprise + strategy: + matrix: + shard: [ + 1/8, 2/8, 3/8, 4/8, + 5/8, 6/8, 7/8, 8/8, + ] + fail-fast: false + + name: Grafana Enterprise (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores + permissions: + contents: read + id-token: write steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: @@ -57,7 +86,34 @@ jobs: uses: ./.github/actions/setup-enterprise with: github-app-name: 'grafana-ci-bot' + - run: echo "RESULTS_FILE=$(date --rfc-3339=seconds --utc | sed -s 's/ /-/g')_${SHARD/\//_}.xml" >> "$GITHUB_ENV" + env: + SHARD: ${{ matrix.shard }} + - run: go install github.com/jstemmer/go-junit-report/v2@85bf4716ac1f025f2925510a9f5e9f5bb347c009 - name: Generate Go code run: make gen-go - name: Run unit tests - run: make test-go-unit + env: + SHARD: ${{ matrix.shard }} + run: | + readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")" + go test -short -v -timeout=30m "${PACKAGES[@]}" | tee >(go-junit-report -set-exit-code > "$RESULTS_FILE") + - name: Log in to GCS + if: github.repository == 'grafana/grafana' && (success() || failure()) + uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs-v0.2.0 + with: + service_account: github-junit-uploader@grafanalabs-workload-identity.iam.gserviceaccount.com + bucket: grafana-test-results + - if: github.repository == 'grafana/grafana' && (success() || failure()) + run: echo "BUCKET_PATH=go-unit-tests/$(echo ${REF_NAME} | sed 's/\//-/g')" >> "$GITHUB_ENV" + env: + REF_NAME: ${{ github.ref_name }} + - name: Upload test results + if: github.repository == 'grafana/grafana' && (success() || failure()) + uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0 + with: + bucket: grafana-test-results + service_account: github-junit-uploader@grafanalabs-workload-identity.iam.gserviceaccount.com + environment: "dev" # Can be dev/prod (defaults to dev) + path: ${{ env.RESULTS_FILE }} + bucket_path: ${{ env.BUCKET_PATH }} diff --git a/.github/workflows/backport-trigger.yml b/.github/workflows/backport-trigger.yml new file mode 100644 index 00000000000..3b3758c573b --- /dev/null +++ b/.github/workflows/backport-trigger.yml @@ -0,0 +1,47 @@ +# We need secrets to backport, but they're not available for actions ran by forks. +# So this workflow is used as a 'trigger', which the backport-workflow.yml will with +# via workflow_run + +name: Backport (trigger) +on: + pull_request: + types: + - closed + - labeled + +permissions: {} + +jobs: + trigger: + # Only run this job if the PR has been merged and has a label containing "backport v" + if: | + github.repository == 'grafana/grafana' && + github.event.pull_request.merged == true && + contains(join(github.event.pull_request.labels.*.name, ','), 'backport v') + runs-on: ubuntu-latest + steps: + # TODO: save this as job summary instead? + - name: Trigger + run: | + echo "Triggering workflow" + echo "See https://github.com/${{ github.repository }}/actions/workflows/workflow_run.yml for progress" + + # Create a JSON artifact with details of this PR to pass to the backport workflow. + # The { action: 'labelled', label: 'backport-1.23.x' } can only be determined from this event payload, + # and is needed to do a backport after a PR has been merged + # + # Important that we don't run *anything* from the PR which could modify the backport_data.json file + - name: Create action data + run: | + jq '{ + action: .action, + label: .label.name, + pr_number: .number, + }' "$GITHUB_EVENT_PATH" > /tmp/pr_info.json + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: pr_info + path: /tmp/pr_info.json + retention-days: 1 diff --git a/.github/workflows/backport-workflow.yml b/.github/workflows/backport-workflow.yml new file mode 100644 index 00000000000..925c6c54bb3 --- /dev/null +++ b/.github/workflows/backport-workflow.yml @@ -0,0 +1,88 @@ +# Runs the actual backport, after being triggered by the backport-trigger.yml workflow. + +name: Backport (workflow) +run-name: "Backport for ${{ github.event.workflow_run.head_branch }} #${{ github.event.workflow_run.run_number }}" +on: + workflow_run: # zizmor: ignore[dangerous-triggers] backport-trigger.yml does not run any user code + workflows: ["Backport (trigger)"] + types: + - completed + +permissions: {} + +jobs: + backport: + # Only run this job if the triggering workflow was not skipped (and on grafana repo) + if: github.repository == 'grafana/grafana' && github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + permissions: + id-token: write + actions: read + steps: + - name: Get vault secrets + id: secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + export_env: false + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + APP_PEM=delivery-bot-app:PRIVATE_KEY + + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ fromJSON(steps.secrets.outputs.secrets).APP_PEM }} + + - name: Download PR info artifact + uses: actions/download-artifact@v4 + id: download-pr-info + with: + github-token: ${{ github.token }} + run-id: ${{ github.event.workflow_run.id }} + name: pr_info + + - name: Get PR info + id: pr-info + env: + PR_INFO_FILE: ${{ steps.download-pr-info.outputs.download-path }}/pr_info.json + # jq-magic to convert the JSON object into a list of key=value pairs for $GITHUB_OUTPUT + run: + jq -r 'to_entries[] | select(.value | type != "object") | "\(.key)=\(.value)"' "$PR_INFO_FILE" >> "$GITHUB_OUTPUT" + + - name: Print PR info + env: + PR_ACTION: ${{ steps.pr-info.outputs.action }} + PR_LABEL: ${{ steps.pr-info.outputs.label }} + PR_NUMBER: ${{ steps.pr-info.outputs.pr_number }} + run: | + echo "PR action: $PR_ACTION" + echo "PR label: $PR_LABEL" + echo "PR number: $PR_NUMBER" + + - name: Checkout Grafana + uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + fetch-depth: 2 + fetch-tags: false + token: ${{ steps.generate_token.outputs.token }} + persist-credentials: true + + - name: Configure git user + run: | + git config --local user.name "github-actions[bot]" + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local --add --bool push.autoSetupRemote true + + - name: Run backport + uses: grafana/grafana-github-actions-go/backport@dev + with: + token: ${{ steps.generate_token.outputs.token }} + # If triggered by being labelled, only backport that label. + # Otherwise, the action will backport all labels. + pr_label: ${{ steps.pr-info.outputs.action == 'labeled' && steps.pr-info.outputs.label || '' }} + pr_number: ${{ steps.pr-info.outputs.pr_number }} + repo_owner: ${{ github.repository_owner }} + repo_name: ${{ github.event.repository.name }} diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml deleted file mode 100644 index fca35029cd6..00000000000 --- a/.github/workflows/backport.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Backport PR Creator -on: - pull_request_target: - types: - - closed - - labeled - -jobs: - main: - if: github.repository == 'grafana/grafana' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 - with: - persist-credentials: false - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - run: git config --global user.email '132647405+grafana-delivery-bot[bot]@users.noreply.github.com' - - run: git config --global user.name 'grafana-delivery-bot[bot]' - - name: Set remote URL - env: - GIT_TOKEN: ${{ steps.generate_token.outputs.token }} - run: | - git remote set-url origin "https://grafana-delivery-bot:$GIT_TOKEN@github.com/grafana/grafana.git" - - name: Run backport - uses: grafana/grafana-github-actions-go/backport@d4c452f92ed826d515dccf1f62923e537953acd8 # main - with: - token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index 5f0b7cf3020..e9c902fe484 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -11,33 +11,37 @@ on: dry_run: default: false required: false + +permissions: + contents: write + pull-requests: write + jobs: - main: + bump-version: runs-on: ubuntu-latest steps: - name: Checkout Grafana uses: actions/checkout@v4 + with: + persist-credentials: false - name: Update package.json versions uses: ./pkg/build/actions/bump-version with: version: ${{ inputs.version }} - - if: ${{ inputs.push }} - name: Generate token - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - if: ${{ inputs.push }} name: Push & Create PR + env: + VERSION: ${{ inputs.version }} + DRY_RUN: ${{ inputs.dry_run }} + REF_NAME: ${{ github.ref_name }} + RUN_ID: ${{ github.run_id }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git config --local user.name "github-actions[bot]" git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - git checkout -b "bump-version/${{ github.run_id }}/${{ inputs.version }}" + git checkout -b "bump-version/${RUN_ID}/${VERSION}" git add . - git commit -m "bump version ${{ inputs.version }}" + git commit -m "bump version ${VERSION}" git push - gh pr create --dry-run=${{ inputs.dry_run }} -l "type/ci" -l "no-changelog" -B "${{ github.ref_name }}" --title "Release: Bump version to ${{ inputs.version }}" --body "Updated version to ${{ inputs.version }}" - env: - GH_TOKEN: ${{ steps.generate_token.outputs.token }} + gh pr create --dry-run="$DRY_RUN" -l "type/ci" -l "no-changelog" -B "$REF_NAME" --title "Release: Bump version to ${VERSION}" --body "Updated version to ${VERSION}" diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index d23ca18e612..e83ba74642e 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -22,11 +22,10 @@ on: required: false default: false type: boolean - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true + work_branch: + required: false + type: string + description: "Use specific branch for changelog" workflow_dispatch: inputs: @@ -50,29 +49,47 @@ on: required: false default: false type: boolean + work_branch: + required: false + type: string + description: "Use specific branch for changelog" -permissions: - contents: write - pull-requests: write +permissions: {} jobs: main: + env: + RUN_ID: ${{ github.run_id }} + VERSION: ${{ inputs.version }} + PREVIOUS_VERISON: ${{ inputs.previous_version }} + TARGET: ${{ inputs.target }} + DRY_RUN: ${{ inputs.dry_run }} runs-on: ubuntu-latest permissions: + id-token: write contents: write + pull-requests: write steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Checkout Grafana repo" uses: "actions/checkout@v4" with: ref: main sparse-checkout: | .github/workflows + .github/actions CHANGELOG.md .nvmrc .prettierignore @@ -89,10 +106,23 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - name: "Create branch" - run: git checkout -b "changelog/${{ github.run_id }}/${{ inputs.version }}" + run: | + if [[ "$WORK_BRANCH" == '' ]]; then + git switch -c "changelog/${RUN_ID}/${VERSION}" + exit 0 + fi + + # Checkout the changelog branch if exists, otherwise create a new one + if git show-ref --verify --quiet "refs/remotes/origin/$WORK_BRANCH"; then + git switch --track "origin/$WORK_BRANCH" + else + git switch -c "$WORK_BRANCH" + fi + env: + WORK_BRANCH: ${{ inputs.work_branch }} - name: "Generate changelog" id: changelog - uses: ./.github/workflows/actions/changelog + uses: ./.github/actions/changelog with: previous: ${{ inputs.previous_version }} github_token: ${{ steps.generate_token.outputs.token }} @@ -103,24 +133,24 @@ jobs: # Prepare CHANGELOG.md content with version delimiters ( echo - echo "# ${{ inputs.version}} ($(date '+%F'))" + echo "# ${VERSION} ($(date '+%F'))" echo cat changelog_items.md ) > CHANGELOG.part # Check if a version exists in the changelog - if grep -q "" + echo "" cat CHANGELOG.part - echo "" + echo "" cat CHANGELOG.md ) > CHANGELOG.tmp mv CHANGELOG.tmp CHANGELOG.md @@ -133,16 +163,29 @@ jobs: - name: "Commit changelog changes" run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md - name: "git push" - if: ${{ inputs.dry_run }} != true + if: inputs.dry_run != true run: git push - name: "Create changelog PR" - run: > - gh pr create \ - --dry-run=${{ inputs.dry_run }} \ - --label "no-backport" \ - --label "no-changelog" \ - -B "${{ inputs.target }}" \ - --title "Release: update changelog for ${{ inputs.version }}" \ - --body "Changelog changes for release ${{ inputs.version }}" + run: | + if gh pr view &>/dev/null; then + echo "Changelog pr has already been created" + else + + gh pr create \ + --dry-run="${DRY_RUN}" \ + --label "no-backport" \ + --label "no-changelog" \ + -B "${TARGET}" \ + --title "Release: update changelog for ${TARGET}" \ + --body "Changelog changes for release versions:" + fi env: - GH_TOKEN: ${{ steps.generate_token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: "Add release version to PR description" + if: inputs.dry_run != true + run: | + gh pr view --json body --jq .body > pr_body.md + echo " - ${VERSION}" >> pr_body.md + gh pr edit --body-file pr_body.md + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/close-milestone.yml b/.github/workflows/close-milestone.yml deleted file mode 100644 index 11613b5fab9..00000000000 --- a/.github/workflows/close-milestone.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Close milestone -on: - workflow_dispatch: - inputs: - version: - required: true - description: Needs to match, exactly, the name of a milestone - workflow_call: - inputs: - version_call: - description: Needs to match, exactly, the name of a milestone - required: true - type: string - -jobs: - main: - if: github.repository == 'grafana/grafana' - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Close milestone (manually invoked) - if: ${{ github.event.inputs.version != '' }} - uses: ./actions/close-milestone - with: - token: ${{ steps.generate_token.outputs.token }} - - name: Close milestone (workflow invoked) - if: ${{ inputs.version_call != '' }} - uses: ./actions/close-milestone - with: - version_call: ${{ inputs.version_call }} - token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/codeowners-validator.yml b/.github/workflows/codeowners-validator.yml index 12184b2f680..da01ffcd3c4 100644 --- a/.github/workflows/codeowners-validator.yml +++ b/.github/workflows/codeowners-validator.yml @@ -4,14 +4,20 @@ on: pull_request: branches: [ main ] +permissions: {} + jobs: codeowners-validator: runs-on: ubuntu-latest + permissions: + contents: read steps: # Checks-out your repository, which is validated in the next step - uses: actions/checkout@v4 + with: + persist-credentials: false - name: GitHub CODEOWNERS Validator - uses: mszostok/codeowners-validator@v0.7.4 + uses: mszostok/codeowners-validator@7f3f5e28c6d7b8dfae5731e54ce2272ca384592f # input parameters with: # ==== GitHub Auth ==== @@ -21,7 +27,7 @@ jobs: # "The comma-separated list of experimental checks that should be executed. By default, all experimental checks are turned off. Possible values: notowned,avoid-shadowing" experimental_checks: "notowned,avoid-shadowing" - + # The repository path in which CODEOWNERS file should be validated." repository_path: "." @@ -35,4 +41,4 @@ jobs: owner_checker_allow_unowned_patterns: "false" # Specifies whether only teams are allowed as owners of files. - owner_checker_owners_must_be_teams: "false" + owner_checker_owners_must_be_teams: "false" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d65cc42baf2..a07cb222898 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,8 +33,9 @@ jobs: fail-fast: false matrix: # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['javascript', 'go', 'python'] + # Supported options are listed here + # https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#changing-the-languages-that-are-analyzed + language: ['actions', 'javascript', 'go'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection @@ -45,10 +46,11 @@ jobs: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 + persist-credentials: false - if: matrix.language == 'go' name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index f733b2f244d..2c0f8586132 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -12,9 +12,7 @@ on: concurrency: group: issue-commands-${{ github.event.issue.number }} -permissions: - contents: read - id-token: write +permissions: {} jobs: config: @@ -26,7 +24,7 @@ jobs: id: check shell: bash run: | - if [ "${{ github.repository }}" == "grafana/grafana" ] && [ -n "${{ secrets.GRAFANA_MISC_STATS_API_KEY }}" ]; then + if [ "${{ github.repository }}" == "grafana/grafana" ]; then echo "has-secrets=1" >> "$GITHUB_OUTPUT" fi @@ -34,35 +32,39 @@ jobs: needs: config if: needs.config.outputs.has-secrets runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@v4 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands uses: ./actions/commands with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} + metricsWriteAPIKey: "" token: ${{ steps.generate_token.outputs.token }} configPath: commands diff --git a/.github/workflows/community-release.yml b/.github/workflows/community-release.yml index 86e7703e5c4..bdf079f2217 100644 --- a/.github/workflows/community-release.yml +++ b/.github/workflows/community-release.yml @@ -11,11 +11,6 @@ on: required: false default: false description: When enabled, this workflow will print a preview instead of creating an actual post. - secrets: - GRAFANA_MISC_STATS_API_KEY: - required: true - GRAFANABOT_FORUM_KEY: - required: true workflow_dispatch: inputs: version: @@ -30,17 +25,25 @@ on: permissions: contents: read + id-token: write jobs: main: runs-on: ubuntu-latest steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/repo/grafana/grafana/community_release path in Vault + repo_secrets: | + GRAFANABOT_FORUM_KEY=community_release:GRAFANABOT_FORUM_KEY + - name: Run community-release (manually invoked) uses: grafana/grafana-github-actions-go/community-release@main with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ inputs.version }} - metrics_api_key: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} - community_api_key: ${{ secrets.GRAFANABOT_FORUM_KEY }} + community_api_key: ${{ env.GRAFANABOT_FORUM_KEY }} community_api_username: grafanabot dry_run: ${{ inputs.dry_run }} diff --git a/.github/workflows/core-plugins-build-and-release.yml b/.github/workflows/core-plugins-build-and-release.yml index e4803a2208f..6b05b9863c8 100644 --- a/.github/workflows/core-plugins-build-and-release.yml +++ b/.github/workflows/core-plugins-build-and-release.yml @@ -33,6 +33,8 @@ permissions: jobs: build-and-publish: + env: + PLUGIN_ID: ${{ inputs.plugin_id }} name: Build and publish ${{ inputs.plugin_id }} runs-on: ubuntu-latest outputs: @@ -42,11 +44,13 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Verify inputs run: | - if [ -z ${{ inputs.plugin_id }} ]; then echo "Missing plugin ID"; exit 1; fi + if [ -z "$PLUGIN_ID" ]; then echo "Missing plugin ID"; exit 1; fi - id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana// path in Vault repo_secrets: | @@ -54,11 +58,11 @@ jobs: PLUGINS_GRAFANA_API_KEY=core-plugins-build-and-release:PLUGINS_GRAFANA_API_KEY PLUGINS_GCOM_TOKEN=core-plugins-build-and-release:PLUGINS_GCOM_TOKEN - name: 'Authenticate to Google Cloud' - uses: 'google-github-actions/auth@v2' + uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' with: credentials_json: '${{ env.PLUGINS_GOOGLE_CREDENTIALS }}' - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v2' + uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a' - name: Setup nodejs environment uses: actions/setup-node@v4 with: @@ -68,13 +72,13 @@ jobs: shell: bash id: get_dir run: | - dir=$(dirname \ - $(egrep -lir --include=plugin.json --exclude-dir=dist \ - '"id": "${{ inputs.plugin_id }}"' \ + dir="$(dirname \ + "$(grep -Elir --include=plugin.json --exclude-dir=dist \ + '"id": "'"${PLUGIN_ID}"'"' \ public/app/plugins \ - ) \ - ) - echo "dir=${dir}" >> $GITHUB_OUTPUT + )" \ + )" + echo "dir=${dir}" >> "$GITHUB_OUTPUT" - name: Install frontend dependencies shell: bash working-directory: ${{ steps.get_dir.outputs.dir }} @@ -84,20 +88,20 @@ jobs: shell: sh working-directory: ${{ steps.get_dir.outputs.dir }} run: | - [ ! -d ./bin ] && mkdir -pv ./bin || true - curl -fL -o ./bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${{ env.GRABPL_VERSION }}/grabpl + mkdir -pv ./bin + curl -fL -o ./bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v"$GRABPL_VERSION"/grabpl chmod 0755 ./bin/grabpl - name: Check backend id: check_backend shell: bash run: | - if egrep -qr --include=main.go 'datasource.Manage\("${{ inputs.plugin_id }}"' pkg/tsdb; then - echo "has_backend=true" >> $GITHUB_OUTPUT + if grep -Eqr --include=main.go 'datasource.Manage\('"$PLUGIN_ID" pkg/tsdb; then + echo "has_backend=true" >> "$GITHUB_OUTPUT" else - echo "has_backend=false" >> $GITHUB_OUTPUT + echo "has_backend=false" >> "$GITHUB_OUTPUT" fi - name: Setup golang environment - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 if: steps.check_backend.outputs.has_backend == 'true' with: go-version-file: go.mod @@ -145,22 +149,24 @@ jobs: - name: build:frontend shell: bash id: build_frontend + env: + OUTPUT_DIR: ${{ steps.get_dir.outputs.dir }} run: | command="plugin:build:commit" if [ "$GITHUB_REF" != "refs/heads/main" ]; then # Release branch, do not add commit hash to version command="plugin:build" fi - yarn $command --scope="@grafana-plugins/${{ inputs.plugin_id }}" - version=$(cat ${{ steps.get_dir.outputs.dir }}/dist/plugin.json | jq -r .info.version) - echo "version=${version}" >> $GITHUB_OUTPUT + yarn $command --scope="@grafana-plugins/$PLUGIN_ID" + version="$(jq -r .info.version "$OUTPUT_DIR"/dist/plugin.json)" + echo "version=${version}" >> "$GITHUB_OUTPUT" - name: build:backend if: steps.check_backend.outputs.has_backend == 'true' shell: bash env: VERSION: ${{ steps.build_frontend.outputs.version }} run: | - make build-plugin-go PLUGIN_ID=${{ inputs.plugin_id }} + make build-plugin-go PLUGIN_ID="$PLUGIN_ID" - name: package working-directory: ${{ steps.get_dir.outputs.dir }} run: | @@ -173,16 +179,17 @@ jobs: env: GCOM_TOKEN: ${{ env.PLUGINS_GCOM_TOKEN }} VERSION: ${{ steps.build_frontend.outputs.version }} + GCOM_API: ${{ env.GCOM_API }} run: | - api_res=$(curl -X 'GET' -H "Authorization: Bearer $GCOM_TOKEN" \ - '${{ env.GCOM_API}}/api/plugins/${{ inputs.plugin_id }}?version=$VERSION' \ - -H 'accept: application/json') - api_res_code=$(echo $api_res | jq -r .code) + api_res="$(curl -X 'GET' -H "Authorization: Bearer $GCOM_TOKEN" \ + "$GCOM_API/api/plugins/$PLUGIN_ID?version=$VERSION" \ + -H 'accept: application/json')" + api_res_code="$(echo "$api_res" | jq -r .code)" if [ "$api_res_code" = "NotFound" ]; then echo "No existing release found" else echo "Expecting a missing release, got:" - echo $api_res + echo "$api_res" exit 1 fi - name: store build artifacts @@ -193,55 +200,46 @@ jobs: - name: Publish release to Google Cloud Storage working-directory: ${{ steps.get_dir.outputs.dir }} env: - VERSION: ${{ steps.build_frontend.outputs.version }} + VERSION: ${{ steps.build_frontend.outputs.version }} + GCP_BUCKET: ${{ env.GCP_BUCKET }} run: | echo "Publish release to Google Cloud Storage:" + set -x touch ci/packages/windows ci/packages/darwin ci/packages/linux ci/packages/any - gsutil -m cp -r ci/packages/*windows* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/windows - gsutil -m cp -r ci/packages/*linux* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux - gsutil -m cp -r ci/packages/*darwin* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/darwin - gsutil -m cp -r ci/packages/*any* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/any + gsutil -m cp -r ci/packages/*windows* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/windows" + gsutil -m cp -r ci/packages/*linux* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/linux" + gsutil -m cp -r ci/packages/*darwin* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/darwin" + gsutil -m cp -r ci/packages/*any* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/any" - name: Publish new plugin version on grafana.com if: steps.check_backend.outputs.has_backend == 'true' working-directory: ${{ steps.get_dir.outputs.dir }} env: GCOM_TOKEN: ${{ env.PLUGINS_GCOM_TOKEN }} VERSION: ${{ steps.build_frontend.outputs.version }} + GCP_BUCKET: ${{ env.GCP_BUCKET }} + OUTPUT_DIR: ${{ steps.get_dir.outputs.dir }} + GCOM_API: ${{ env.GCOM_API }} run: | echo "Publish new plugin version on grafana.com:" echo "Plugin version: ${VERSION}" - result=`curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" ${{ env.GCOM_API}}/api/plugins -d "{ - \"url\": \"https://github.com/grafana/grafana/tree/main/${{ steps.get_dir.outputs.dir }}\", - \"download\": { - \"linux-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux/${{ inputs.plugin_id }}-${VERSION}.linux_amd64.zip\", - \"md5\": \"$(cat ci/packages/info-linux_amd64.json | jq -r .plugin.md5)\" - }, - \"linux-arm64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux/${{ inputs.plugin_id }}-${VERSION}.linux_arm64.zip\", - \"md5\": \"$(cat ci/packages/info-linux_arm64.json | jq -r .plugin.md5)\" - }, - \"linux-arm\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux/${{ inputs.plugin_id }}-${VERSION}.linux_arm.zip\", - \"md5\": \"$(cat ci/packages/info-linux_arm.json | jq -r .plugin.md5)\" - }, - \"windows-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/windows/${{ inputs.plugin_id }}-${VERSION}.windows_amd64.zip\", - \"md5\": \"$(cat ci/packages/info-windows_amd64.json | jq -r .plugin.md5)\" - }, - \"darwin-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/darwin/${{ inputs.plugin_id }}-${VERSION}.darwin_amd64.zip\", - \"md5\": \"$(cat ci/packages/info-darwin_amd64.json | jq -r .plugin.md5)\" - }, - \"darwin-arm64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/darwin/${{ inputs.plugin_id }}-${VERSION}.darwin_arm64.zip\", - \"md5\": \"$(cat ci/packages/info-darwin_arm64.json | jq -r .plugin.md5)\" - } - } - }"` - if [[ "$(echo $result | jq -r .version)" == "null" ]]; then + + OUTPUT_URL="https://github.com/grafana/grafana/tree/$OUTPUT_DIR" \ + jq -n '{"url": env.OUTPUT_URL}' > body.json + osarchs=(linux_amd64 linux_arm64 linux_arm windows_amd64 darwin_amd64 darwin_arm64) + for osarch in "${osarchs[@]}"; do + echo "Processing $osarch" + KEY="${osarch//_/-}" \ + OSARCH="$osarch" \ + jq -s '. as $i | .[0] | .download[env.KEY] = { + "url": "https://storage.googleapis.com/\(env.GCP_BUCKET)/\(env.PLUGIN_ID)/release/\(env.VERSION)/linux/\(env.PLUGIN_ID)-\(env.VERSION).\(env.OSARCH).zip", + "md5": $i[1].plugin.md5 + }' body.json ci/packages/info-"$osarch".json > tmp.json && mv tmp.json body.json + done + + result="$(curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" "$GCOM_API"/api/plugins --data-binary '@body.json')" + if [[ "$(echo "$result" | jq -r .version)" == "null" ]]; then echo "Failed to publish plugin version. Got:" - echo $result + echo "$result" exit 1 fi - name: Publish new plugin version on grafana.com (frontend only) @@ -250,20 +248,29 @@ jobs: env: GCOM_TOKEN: ${{ env.PLUGINS_GCOM_TOKEN }} VERSION: ${{ steps.build_frontend.outputs.version }} + GCOM_API: ${{ env.GCOM_API }} + OUTPUT_DIR: ${{ steps.get_dir.outputs.dir }} + GCP_BUCKET: ${{ env.GCP_BUCKET }} run: | echo "Publish new plugin version on grafana.com:" echo "Plugin version: ${VERSION}" - result=`curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" ${{ env.GCOM_API}}/api/plugins -d "{ - \"url\": \"https://github.com/grafana/grafana/tree/main/${{ steps.get_dir.outputs.dir }}\", - \"download\": { - \"any\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/any/${{ inputs.plugin_id }}-${VERSION}.any.zip\", - \"md5\": \"$(cat ci/packages/info-any.json | jq -r .plugin.md5)\" + + OUTPUT_URL="https://github.com/grafana/grafana/tree/$OUTPUT_DIR" \ + DOWNLOAD_URL="https://storage.googleapis.com/$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/any/$PLUGIN_ID-${VERSION}.any.zip" \ + MD5_CHECKSUM="$(jq -r '.plugin.md5' ci/packages/info-any.json)" \ + jq -rn '{ + "url": env.OUTPUT_URL, + "download": { + "any": { + "url": env.DOWNLOAD_URL, + "md5": env.MD5_CHECKSUM } } - }"` - if [[ "$(echo $result | jq -r .version)" == "null" ]]; then + }' > body.json + + result="$(curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" "$GCOM_API"/api/plugins --data-binary '@body.json')" + if [[ "$(echo "$result" | jq -r .version)" == "null" ]]; then echo "Failed to publish plugin version. Got:" - echo $result + echo "$result" exit 1 fi diff --git a/.github/workflows/create-next-release-branch.yml b/.github/workflows/create-next-release-branch.yml index 8fc01cd442d..5f537a5418a 100644 --- a/.github/workflows/create-next-release-branch.yml +++ b/.github/workflows/create-next-release-branch.yml @@ -10,11 +10,6 @@ on: description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created) type: string required: true - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true outputs: branch: description: The new branch that was created @@ -27,26 +22,35 @@ on: description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created) type: string required: true - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true + +permissions: + contents: read + id-token: write + jobs: main: runs-on: ubuntu-latest outputs: branch: ${{ steps.branch.outputs.branch }} steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + repositories: "[\"grafana\", \"grafana-enterprise\"]" + permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}" - name: Create release branch id: branch - uses: grafana/grafana-github-actions-go/bump-release@main + uses: grafana/grafana-github-actions-go/bump-release@main # zizmor: ignore[unpinned-uses] with: ownerRepo: ${{ inputs.ownerRepo }} source: ${{ inputs.source }} diff --git a/.github/workflows/create-security-branch.yml b/.github/workflows/create-security-branch.yml new file mode 100644 index 00000000000..98ff8267380 --- /dev/null +++ b/.github/workflows/create-security-branch.yml @@ -0,0 +1,79 @@ +name: Create security branch +on: + workflow_call: + inputs: + release_branch: + type: string + description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.3+security-01` being created) + required: true + security_branch_number: + type: string + description: 'The security branch number (e.g., 01)' + required: false + default: '01' + repository: + type: string + description: 'The repository to create the security branch in (e.g., grafana/grafana-security-mirror)' + required: true + outputs: + branch: + description: The new security branch that was created + value: ${{ jobs.main.outputs.branch }} + workflow_dispatch: + inputs: + release_branch: + type: string + description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.3+security-01` being created) + required: true + security_branch_number: + type: string + description: 'The security branch number (e.g., 01)' + required: false + default: '01' + repository: + type: string + description: 'The repository to create the security branch in (e.g., grafana/grafana-security-mirror)' + required: true + +permissions: + contents: write + id-token: write + +jobs: + main: + runs-on: ubuntu-latest + outputs: + branch: ${{ steps.branch.outputs.branch }} + steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ steps.generate_token.outputs.token }} + repository: ${{ inputs.repository }} + ref: ${{ inputs.release_branch }} + + - name: Create security branch + id: branch + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + INPUT_RELEASE_BRANCH: ${{ inputs.release_branch }} + INPUT_SECURITY_BRANCH_NUMBER: ${{ inputs.security_branch_number }} + INPUT_REPOSITORY: ${{ inputs.repository }} + run: | + chmod +x .github/workflows/scripts/create-security-branch/create-security-branch.sh + .github/workflows/scripts/create-security-branch/create-security-branch.sh diff --git a/.github/workflows/create-security-patch-from-security-mirror.yml b/.github/workflows/create-security-patch-from-security-mirror.yml index f85239241ad..7499d925236 100644 --- a/.github/workflows/create-security-patch-from-security-mirror.yml +++ b/.github/workflows/create-security-patch-from-security-mirror.yml @@ -17,7 +17,7 @@ on: jobs: trigger_downstream_create_security_patch: concurrency: create-patch-${{ github.ref_name }} - uses: grafana/security-patch-actions/.github/workflows/create-patch.yml@main + uses: grafana/security-patch-actions/.github/workflows/create-patch.yml@main # zizmor: ignore[unpinned-uses] if: github.repository == 'grafana/grafana-security-mirror' with: repo: "${{ github.repository }}" @@ -25,5 +25,4 @@ jobs: patch_ref: "${{ github.base_ref }}" # this is the target branch name, Ex: "main" patch_repo: "grafana/grafana-security-patches" patch_prefix: "${{ github.event.pull_request.number }}" - secrets: inherit - + secrets: inherit # zizmor: ignore[secrets-inherit] diff --git a/.github/workflows/dashboards-issue-add-label.yml b/.github/workflows/dashboards-issue-add-label.yml index 95abce4355b..2ea89c7a441 100644 --- a/.github/workflows/dashboards-issue-add-label.yml +++ b/.github/workflows/dashboards-issue-add-label.yml @@ -11,7 +11,7 @@ env: ORGANIZATION: ${{ github.repository_owner }} REPO: ${{ github.event.repository.name }} TARGET_PROJECT: 202 - LABEL_IDs: "LA_kwDOAOaWjc8AAAABT38U-A" + LABEL_IDS: "LA_kwDOAOaWjc8AAAABT38U-A" concurrency: group: issue-label-when-in-project-${{ github.event.number }} @@ -22,27 +22,30 @@ jobs: steps: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Check if issue is in target project env: GH_TOKEN: ${{ steps.generate_token.outputs.token }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + TARGET_PROJECT: ${{ env.TARGET_PROJECT }} run: | + # shellcheck disable=SC2016 # we don't want the $s to be expanded gh api graphql -f query=' - query($org: String!, $repo: String!) { + query($org: String!, $repo: String!, $issueNumber: Int!) { repository(name: $repo, owner: $org) { - issue (number: ${{ github.event.issue.number }}) { + issue (number: $issueNumber) { id projectItems(first:20) { nodes { @@ -53,14 +56,19 @@ jobs: } } } - }' -f org=$ORGANIZATION -f repo=$REPO > projects_data.json + }' -f org="$ORGANIZATION" -f repo="$REPO" -F issueNumber="$ISSUE_NUMBER" > projects_data.json - echo 'IN_TARGET_PROJ='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.TARGET_PROJECT }}) | .project != null' projects_data.json) >> $GITHUB_ENV - echo 'ITEM_ID='$(jq '.data.repository.issue.id' projects_data.json) >> $GITHUB_ENV + { + echo "IN_TARGET_PROJ=$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number=='"$TARGET_PROJECT"') | .project != null' projects_data.json)" + echo "ITEM_ID=$(jq '.data.repository.issue.id' projects_data.json)" + } >> "$GITHUB_ENV" - name: Set up label array if: env.IN_TARGET_PROJ + env: + LABEL_IDS: ${{ env.LABEL_IDS }} run: | - IFS=',' read -ra LABEL_IDs <<< "${{ env.LABEL_IDs }}" + # shellcheck disable=SC2153 # we define the variable on the line above in 'read' + IFS=',' read -ra LABEL_IDs <<< "$LABEL_IDS" for item in "${LABEL_IDs[@]}"; do echo "Item: $item" done @@ -68,7 +76,9 @@ jobs: if: env.IN_TARGET_PROJ env: GH_TOKEN: ${{ steps.generate_token.outputs.token }} + LABEL_IDS: ${{ env.LABEL_IDS }} run: | + # shellcheck disable=SC2016 # we don't want the $s to be expanded gh api graphql -f query=' mutation ($labelableId: ID!, $labelIds: [ID!]!) { addLabelsToLabelable( @@ -76,4 +86,4 @@ jobs: ) { clientMutationId } - }' -f labelableId=$ITEM_ID -f labelIds=${{ env.LABEL_IDs }} + }' -f labelableId="$ITEM_ID" -f labelIds="$LABEL_IDS" diff --git a/.github/workflows/deploy-pr-preview.yml b/.github/workflows/deploy-pr-preview.yml index bfad92ebf73..0a957ffae25 100644 --- a/.github/workflows/deploy-pr-preview.yml +++ b/.github/workflows/deploy-pr-preview.yml @@ -9,10 +9,17 @@ on: paths: - "docs/sources/**" +permissions: {} + jobs: deploy-pr-preview: + permissions: + contents: read # Clone repositories. + id-token: write # Fetch Vault secrets. + pull-requests: write # Create or update PR comments. + statuses: write # Update GitHub status check with deploy preview link. if: "!github.event.pull_request.head.repo.fork" - uses: grafana/writers-toolkit/.github/workflows/deploy-preview.yml@main + uses: grafana/writers-toolkit/.github/workflows/deploy-preview.yml@main # zizmor: ignore[unpinned-uses] with: branch: ${{ github.head_ref }} event_number: ${{ github.event.number }} diff --git a/.github/workflows/detect-breaking-changes-levitate.yml b/.github/workflows/detect-breaking-changes-levitate.yml index 70c823483d9..61999c718a9 100644 --- a/.github/workflows/detect-breaking-changes-levitate.yml +++ b/.github/workflows/detect-breaking-changes-levitate.yml @@ -6,9 +6,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -permissions: - contents: read - id-token: write +permissions: {} on: pull_request: @@ -24,11 +22,16 @@ jobs: defaults: run: working-directory: './pr' + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v4 with: path: './pr' + persist-credentials: false + - uses: actions/setup-node@v4 with: node-version: 22.11.0 @@ -67,6 +70,9 @@ jobs: buildBase: name: Build Base packages artifacts runs-on: ubuntu-latest + permissions: + contents: read + id-token: write defaults: run: working-directory: './base' @@ -76,6 +82,7 @@ jobs: with: path: './base' ref: ${{ github.event.pull_request.base.ref }} + persist-credentials: false - uses: actions/setup-node@v4 with: @@ -124,6 +131,9 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-node@v4 with: node-version: 22.11.0 @@ -145,14 +155,14 @@ jobs: run: unzip -j base_built_packages.zip -d ./base && rm base_built_packages.zip - id: 'auth' - uses: 'google-github-actions/auth@v2' + uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' with: - workload_identity_provider: ${{ secrets.WIF_PROVIDER }} - service_account: ${{ secrets.LEVITATE_SA }} + workload_identity_provider: projects/304398677251/locations/global/workloadIdentityPools/github/providers/github-provider + service_account: github-plugins-data-levitate@grafanalabs-workload-identity.iam.gserviceaccount.com project_id: 'grafanalabs-global' - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v2' + uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a' with: version: '>= 363.0.0' project_id: 'grafanalabs-global' @@ -167,7 +177,11 @@ jobs: - name: Persisting the check output run: | mkdir -p ./levitate - echo "{ \"exit_code\": ${{ steps.breaking-changes.outputs.is_breaking }}, \"message\": \"${{ steps.breaking-changes.outputs.message }}\", \"pr_number\": \"${{ github.event.pull_request.number }}\" }" > ./levitate/result.json + echo "{ \"exit_code\": ${IS_BREAKING}, \"message\": \"${MESSAGE}\", \"pr_number\": \"${PR_NUMBER}\" }" > ./levitate/result.json + env: + IS_BREAKING: ${{ steps.breaking-changes.outputs.is_breaking }} + MESSAGE: ${{ steps.breaking-changes.outputs.message }} + PR_NUMBER: ${{ github.event.pull_request.number }} - name: Upload check output as artifact uses: actions/upload-artifact@v4 @@ -180,16 +194,29 @@ jobs: name: Report breaking changes in PR comment runs-on: ubuntu-latest needs: ['Detect'] + permissions: + contents: read + id-token: write steps: - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + - id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # get-vault-secrets-v1.1.0 with: - app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} - private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} + # Secrets placed in the ci/repo/grafana/grafana in vault + repo_secrets: | + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem + + - name: Generate token + id: generate_token + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + with: + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - uses: actions/checkout@v4 + with: + persist-credentials: false - name: 'Download artifact' uses: actions/download-artifact@v4 @@ -197,7 +224,7 @@ jobs: name: levitate - name: Parsing levitate result - uses: actions/github-script@v6 + uses: actions/github-script@v7 id: levitate-run with: script: | @@ -208,7 +235,7 @@ jobs: # Check if label exists - name: Check if "levitate breaking change" label exists id: does-label-exist - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ github.event.pull_request.number }} with: @@ -238,7 +265,7 @@ jobs: # Comment on the PR - name: Comment on PR if: steps.levitate-run.outputs.exit_code == 1 - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 with: header: levitate-breaking-change-comment number: ${{ github.event.pull_request.number }} @@ -255,7 +282,7 @@ jobs: # Remove comment from the PR (no more breaking changes) - name: Remove comment from PR if: steps.levitate-run.outputs.exit_code == 0 - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 with: header: levitate-breaking-change-comment number: ${{ github.event.pull_request.number }} @@ -301,7 +328,7 @@ jobs: # Add the label - name: Add "levitate breaking change" label if: steps.levitate-run.outputs.exit_code == 1 && steps.does-label-exist.outputs.result == 0 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -317,7 +344,7 @@ jobs: # Remove label (no more breaking changes) - name: Remove "levitate breaking change" label if: steps.levitate-run.outputs.exit_code == 0 && steps.does-label-exist.outputs.result == 1 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -335,7 +362,7 @@ jobs: # Related issue: https://github.com/renovatebot/renovate/issues/1908 - name: Add "grafana/plugins-platform-frontend" as a reviewer if: steps.levitate-run.outputs.exit_code == 1 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -352,7 +379,7 @@ jobs: # Remove reviewers (no more breaking changes) - name: Remove "grafana/plugins-platform-frontend" from the list of reviewers if: steps.levitate-run.outputs.exit_code == 0 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -368,9 +395,11 @@ jobs: - name: Exit run: | - if [ "${{ steps.levitate-run.outputs.exit_code }}" -ne 0 ]; then + if [ "${LV_EXIT_CODE}" -ne 0 ]; then echo "Breaking changes detected. Please check the levitate report in your pull request. This workflow won't block merging." fi - exit ${{ steps.levitate-run.outputs.exit_code }} + exit "${LV_EXIT_CODE}" shell: bash + env: + LV_EXIT_CODE: ${{ steps.levitate-run.outputs.exit_code }} diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml index d27f16c9298..3dfde5d37b1 100644 --- a/.github/workflows/documentation-ci.yml +++ b/.github/workflows/documentation-ci.yml @@ -4,16 +4,23 @@ on: branches: ["main"] paths: ["docs/sources/**"] workflow_dispatch: + +permissions: {} + jobs: vale: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + security-events: write container: - image: grafana/vale:latest + image: grafana/vale:latest # zizmor: ignore[unpinned-images] steps: - uses: actions/checkout@v4 with: persist-credentials: false - - uses: grafana/writers-toolkit/vale-action@vale-action/v1 + - uses: grafana/writers-toolkit/vale-action@vale-action/v1 # zizmor: ignore[unpinned-uses] with: filter: '.Name in ["Grafana.GrafanaCom", "Grafana.WordList", "Grafana.Spelling", "Grafana.ProductPossessives"]' token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/e2e-dashboard-new-layouts.yml b/.github/workflows/e2e-dashboard-new-layouts.yml new file mode 100644 index 00000000000..a93ef052666 --- /dev/null +++ b/.github/workflows/e2e-dashboard-new-layouts.yml @@ -0,0 +1,42 @@ +name: Run e2e for dashboardNewLayouts + +on: + pull_request: + branches: + - '**' + paths: + - 'e2e/dashboard-new-layouts/**' + - 'public/app/features/dashboard-scene/**' + +env: + ARCH: linux-amd64 + +jobs: + dashboard-new-layouts-e2e: + runs-on: ubuntu-latest + continue-on-error: true + if: github.event.pull_request.draft == false + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Pin Go version to mod file + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - run: go version + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'yarn' + - name: Install dependencies + run: yarn install --immutable + - name: Build grafana + run: make build + - name: Install Cypress dependencies + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f + with: + runTests: false + - name: Run dashboardNewLayouts e2e + run: yarn e2e:dashboard-new-layouts diff --git a/.github/workflows/ephemeral-instances-pr-comment.yml b/.github/workflows/ephemeral-instances-pr-comment.yml index be07b7ef6ac..78160190fa0 100644 --- a/.github/workflows/ephemeral-instances-pr-comment.yml +++ b/.github/workflows/ephemeral-instances-pr-comment.yml @@ -1,61 +1,63 @@ -name: 'Ephemeral instances' +name: "Ephemeral instances" + on: issue_comment: types: [created] pull_request: types: [closed] -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.EI_APP_ID != '' && - secrets.EI_APP_PRIVATE_KEY != '' && - secrets.EI_GCOM_HOST != '' && - secrets.EI_GCOM_TOKEN != '' && - secrets.EI_EPHEMERAL_INSTANCES_REGISTRY != '' && - secrets.EI_GCP_SERVICE_ACCOUNT_KEY_BASE64 != '' && - secrets.EI_EPHEMERAL_ORG_ID != '' - ) || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - handle-pull-request-event: - needs: config - if: needs.config.outputs.has-secrets && - ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/deploy-to-hg') || github.event.action == 'closed') }} +permissions: {} + +jobs: + handle-ephemeral-instances: + if: ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/deploy-to-hg') || github.event.action == 'closed') && github.repository_owner == 'grafana' }} runs-on: labels: ubuntu-latest-8-cores continue-on-error: true + permissions: + # For commenting. + pull-requests: write + # No contents permission is needed because we will impersonate an app to create the PR instead. + id-token: write # required for vault access + steps: + - name: Get vault secrets + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in ci/repo/grafana/grafana/ + repo_secrets: | + APP_ID=ephemeral-instances-bot:app-id + APP_PEM=ephemeral-instances-bot:app-private-key + GCOM_HOST=ephemeral-instances-bot:gcom-host + GCOM_TOKEN=ephemeral-instances-bot:gcom-token + REGISTRY=ephemeral-instances-bot:registry + GCP_SA_ACCOUNT_KEY_BASE64=ephemeral-instances-bot:sa-key + - name: Generate a GitHub app installation token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 # v1.8.0 with: - app_id: ${{ secrets.EI_APP_ID }} - private_key: ${{ secrets.EI_APP_PRIVATE_KEY }} + app_id: ${{ env.APP_ID }} + private_key: ${{ env.APP_PEM }} - name: Checkout ephemeral instances repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: repository: grafana/ephemeral-grafana-instances-github-action token: ${{ steps.generate_token.outputs.token }} ref: main path: ephemeral + persist-credentials: false - name: build and deploy ephemeral instance uses: ./ephemeral with: - github-token: ${{ steps.generate_token.outputs.token }} - gcom-host: ${{ secrets.EI_GCOM_HOST }} - gcom-token: ${{ secrets.EI_GCOM_TOKEN }} - registry: "${{ secrets.EI_EPHEMERAL_INSTANCES_REGISTRY }}" - gcp-service-account-key: "${{ secrets.EI_GCP_SERVICE_ACCOUNT_KEY_BASE64 }}" - ephemeral-org-id: "${{ secrets.EI_EPHEMERAL_ORG_ID }}" + github-token: ${{ steps.generate_token.outputs.token }} + gcom-host: ${{ env.GCOM_HOST }} + gcom-token: ${{ env.GCOM_TOKEN }} + registry: "${{ env.REGISTRY }}" + gcp-service-account-key: ${{ env.GCP_SA_ACCOUNT_KEY_BASE64 }} + ephemeral-org-id: ephemeral oss-or-enterprise: oss verbose: true diff --git a/.github/workflows/feature-toggles-ci.yml b/.github/workflows/feature-toggles-ci.yml index a6c9f5c52dc..ab1aa9b2dca 100644 --- a/.github/workflows/feature-toggles-ci.yml +++ b/.github/workflows/feature-toggles-ci.yml @@ -7,9 +7,15 @@ on: - 'pkg/services/featuremgmt/registry.go' - 'docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md' +permissions: {} + jobs: test: runs-on: ubuntu-latest + + permissions: + contents: read + steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/frontend-lint.yml b/.github/workflows/frontend-lint.yml index 148838465fa..29910d256f0 100644 --- a/.github/workflows/frontend-lint.yml +++ b/.github/workflows/frontend-lint.yml @@ -6,16 +6,19 @@ on: - main - release-*.*.* -permissions: - contents: read - id-token: write +permissions: {} jobs: lint-frontend-verify-i18n: name: Verify i18n runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -23,17 +26,21 @@ jobs: cache-dependency-path: 'yarn.lock' - run: yarn install --immutable --check-cache - run: | + # shellcheck disable=SC2102,SC2016,SC2125 # this is just a string. we _want_ all the bash features to be disabled. extract_error_message='::error::Extraction failed. Make sure that you have no dynamic translation phrases, such as "t(`preferences.theme.{themeID}`, themeName)" and that no translation key is used twice. Search the output for '[warning]' to find the offending file.' make i18n-extract || (echo "${extract_error_message}" && false) - run: | uncommited_error_message="::error::Translation extraction has not been committed. Please run 'make i18n-extract', commit the changes and push again." file_diff=$(git diff --dirstat public/locales) if [ -n "$file_diff" ]; then - echo $file_diff + echo "$file_diff" echo "${uncommited_error_message}" exit 1 fi lint-frontend-prettier: + permissions: + contents: read + id-token: write # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, # the `lint-frontend-prettier-enterprise` workflow will run instead if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true @@ -41,6 +48,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -50,12 +59,17 @@ jobs: - run: yarn run prettier:check - run: yarn run lint lint-frontend-prettier-enterprise: + permissions: + contents: read + id-token: write # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -69,6 +83,9 @@ jobs: - run: yarn run prettier:check - run: yarn run lint lint-frontend-typecheck: + permissions: + contents: read + id-token: write # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, # the `lint-frontend-typecheck-enterprise` workflow will run instead if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true @@ -76,6 +93,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -84,12 +103,17 @@ jobs: - run: yarn install --immutable --check-cache - run: yarn run typecheck lint-frontend-typecheck-enterprise: + permissions: + contents: read + id-token: write # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false name: Typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -102,10 +126,15 @@ jobs: - run: yarn install --immutable --check-cache - run: yarn run typecheck lint-frontend-betterer: + permissions: + contents: read + id-token: write name: Betterer runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 8cbe3030ffe..bd8eab8c176 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -34,16 +34,16 @@ on: permissions: # contents: write allows the action(s) to create github releases contents: write + id-token: write jobs: main: runs-on: ubuntu-latest steps: - name: Create GitHub release (manually invoked) - uses: grafana/grafana-github-actions-go/github-release@main + uses: grafana/grafana-github-actions-go/github-release@main # zizmor: ignore[unpinned-uses] with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ inputs.version }} - metrics_api_key: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} latest: ${{ inputs.latest }} dry_run: ${{ inputs.dry_run }} diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index d6ae5ee85ee..cdc84874d01 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -17,12 +17,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-go@v5 with: go-version-file: ./go.mod - run: make gen-go - name: golangci-lint - uses: golangci/golangci-lint-action@v7 + uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd with: version: v2.0.2 args: | diff --git a/.github/workflows/i18n-crowdin-create-tasks.yml b/.github/workflows/i18n-crowdin-create-tasks.yml index f12ca3ff7a2..6ecaa883e01 100644 --- a/.github/workflows/i18n-crowdin-create-tasks.yml +++ b/.github/workflows/i18n-crowdin-create-tasks.yml @@ -1,25 +1,14 @@ -name: Crowdin Create Tasks +name: Crowdin automatic task management on: workflow_dispatch: + # once a week on Sunday at midnight + # TODO enable once we're ready to create tasks automatically # schedule: - # - cron: "0 0 * * *" + # - cron: "0 0 * * 0" jobs: create-tasks-in-crowdin: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - - - name: Create tasks - env: - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} - run: node ./.github/workflows/scripts/crowdin/create-tasks.js + uses: grafana/grafana-github-actions/.github/workflows/crowdin-create-tasks.yml@main + with: + crowdin_project_id: 5 diff --git a/.github/workflows/i18n-crowdin-download.yml b/.github/workflows/i18n-crowdin-download.yml index e0c3c50b9bb..46b456ddebb 100644 --- a/.github/workflows/i18n-crowdin-download.yml +++ b/.github/workflows/i18n-crowdin-download.yml @@ -7,152 +7,9 @@ on: jobs: download-sources-from-crowdin: - runs-on: ubuntu-latest - - permissions: - contents: write # needed to commit changes into the PR - pull-requests: write # needed to update PR description, labels, etc - id-token: write # needed to get vault secrets - - steps: - - name: Generate token - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} - private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - token: ${{ steps.generate_token.outputs.token }} - - - name: Download sources - id: crowdin-download - uses: crowdin/github-action@v2 - with: - upload_sources: false - upload_translations: false - download_sources: false - download_translations: true - export_only_approved: true - localization_branch_name: i18n_crowdin_translations - create_pull_request: true - pull_request_title: 'I18n: Download translations from Crowdin' - pull_request_body: | - :robot: Automatic download of translations from Crowdin. - - This runs once per day and will merge automatically if all the required checks pass. - - If there's a conflict, close the pull request and **delete the branch**. - You can then either wait for the schedule to trigger a new PR, or rerun the action manually. - pull_request_labels: 'area/frontend, area/internationalization, no-changelog, no-backport' - pull_request_base_branch_name: 'main' - base_url: 'https://grafana.api.crowdin.com' - config: 'crowdin.yml' - source: 'public/locales/en-US/grafana.json' - translation: 'public/locales/%locale%/%original_file_name%' - # Magic details of the github-actions bot user, to pass CLA checks - github_user_name: "github-actions[bot]" - github_user_email: "41898282+github-actions[bot]@users.noreply.github.com" - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} - - - name: Get pull request ID - if: steps.crowdin-download.outputs.pull_request_url - shell: bash - # Crowdin action returns us the URL of the pull request, but we need an ID for the GraphQL API - # that looks like 'PR_kwDOAOaWjc5mP_GU' - run: | - pr_id=$(gh pr view ${{ steps.crowdin-download.outputs.pull_request_url }} --json id -q .id) - echo "PULL_REQUEST_ID=$pr_id" >> "$GITHUB_ENV" - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Get project board ID - uses: octokit/graphql-action@v2.x - id: get-project-id - if: steps.crowdin-download.outputs.pull_request_url - with: - # Frontend Platform project - https://github.com/orgs/grafana/projects/78 - org: grafana - project_number: 78 - query: | - query getProjectId($org: String!, $project_number: Int!){ - organization(login: $org) { - projectV2(number: $project_number) { - title - id - } - } - } - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Add to project board - uses: octokit/graphql-action@v2.x - if: steps.crowdin-download.outputs.pull_request_url - with: - projectid: ${{ fromJson(steps.get-project-id.outputs.data).organization.projectV2.id }} - prid: ${{ env.PULL_REQUEST_ID }} - query: | - mutation addPullRequestToProject($projectid: ID!, $prid: ID!){ - addProjectV2ItemById(input: {projectId: $projectid, contentId: $prid}) { - item { - id - } - } - } - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Run auto-milestone - uses: grafana/grafana-github-actions-go/auto-milestone@main - if: steps.crowdin-download.outputs.pull_request_url - with: - pr: ${{ steps.crowdin-download.outputs.pull_request_number }} - token: ${{ steps.generate_token.outputs.token }} - - - name: Get vault secrets - id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main - with: - # Secrets placed in ci/repo/grafana/grafana/grafana-pr-approver - repo_secrets: | - GRAFANA_PR_APPROVER_APP_ID=grafana-pr-approver:app-id - GRAFANA_PR_APPROVER_APP_PEM=grafana-pr-approver:private-key - - - name: Generate approver token - if: steps.crowdin-download.outputs.pull_request_url - id: generate_approver_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ env.GRAFANA_PR_APPROVER_APP_ID }} - private_key: ${{ env.GRAFANA_PR_APPROVER_APP_PEM }} - - - name: Approve and automerge PR - if: steps.crowdin-download.outputs.pull_request_url - shell: bash - # Only approve if: - # - the PR does not modify files other than json files under the public/locales/ directory - # - the PR does not modify the en-US locale - run: | - filesChanged=$(gh pr diff --name-only ${{ steps.crowdin-download.outputs.pull_request_url }}) - - if [[ $(echo $filesChanged | grep -v 'public/locales/[a-zA-Z\-]*/grafana.json' | wc -l) -ne 0 ]]; then - echo "Non-i18n changes detected, not approving" - exit 1 - fi - - if [[ $(echo $filesChanged | grep "public/locales/en-US" | wc -l) -ne 0 ]]; then - echo "public/locales/en-US changes detected, not approving" - exit 1 - fi - - echo "Approving and enabling automerge" - gh pr review ${{ steps.crowdin-download.outputs.pull_request_url }} --approve - gh pr merge --auto --squash ${{ steps.crowdin-download.outputs.pull_request_url }} - env: - GITHUB_TOKEN: ${{ steps.generate_approver_token.outputs.token }} + uses: grafana/grafana-github-actions/.github/workflows/crowdin-download.yml@main + with: + crowdin_project_id: 5 + pr_labels: 'area/frontend, area/internationalization, no-changelog, no-backport' + github_board_id: 78 # Frontend Platform project + en_paths: public/locales/en-US/grafana.json, public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json, public/app/plugins/datasource/mssql/locales/en-US/mssql.json diff --git a/.github/workflows/i18n-crowdin-upload.yml b/.github/workflows/i18n-crowdin-upload.yml index 39a89c5aad2..97638769526 100644 --- a/.github/workflows/i18n-crowdin-upload.yml +++ b/.github/workflows/i18n-crowdin-upload.yml @@ -5,29 +5,13 @@ on: push: paths: - 'public/locales/en-US/grafana.json' + - 'public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json' + - 'public/app/plugins/datasource/mssql/locales/en-US/mssql.json' branches: - main jobs: upload-sources-to-crowdin: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Upload sources - uses: crowdin/github-action@v2 - with: - upload_sources: true - upload_sources_args: '--dest=public/locales/en-US/grafana.json' - upload_translations: false - download_translations: false - create_pull_request: false - base_url: 'https://grafana.api.crowdin.com' - config: 'crowdin.yml' - source: 'public/locales/en-US/grafana.json' - translation: 'public/locales/%locale%/%original_file_name%' - env: - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + uses: grafana/grafana-github-actions/.github/workflows/crowdin-upload.yml@main + with: + crowdin_project_id: 5 diff --git a/.github/workflows/issue-opened.yml b/.github/workflows/issue-opened.yml index 4de94070072..97694bd2d04 100644 --- a/.github/workflows/issue-opened.yml +++ b/.github/workflows/issue-opened.yml @@ -10,22 +10,24 @@ on: concurrency: group: issue-opened-${{ github.event.issue.number }} -permissions: - contents: read - id-token: write +permissions: {} jobs: main: runs-on: ubuntu-latest if: github.repository == 'grafana/grafana' + permissions: + contents: read + id-token: write steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@v4 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions @@ -37,57 +39,61 @@ jobs: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Run Commands uses: ./actions/commands with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} token: ${{ steps.generate_token.outputs.token }} configPath: "issue-opened" auto-triage: needs: [main] + permissions: + contents: read + id-token: write if: github.repository == 'grafana/grafana' && github.event.issue.author_association != 'MEMBER' && github.event.issue.author_association != 'OWNER' runs-on: ubuntu-latest steps: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_triager path in Vault repo_secrets: | AUTOTRIAGER_OPENAI_API_KEY=plugins_platform_issue_triager:AUTOTRIAGER_OPENAI_API_KEY AUTOTRIAGER_SLACK_WEBHOOK_URL=plugins_platform_issue_triager:AUTOTRIAGER_SLACK_WEBHOOK_URL - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=plugins_platform_issue_triager_github_bot:app_id + GITHUB_APP_PRIVATE_KEY=plugins_platform_issue_triager_github_bot:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v4 # v4.2.2 + with: + persist-credentials: false - name: Send issue to the auto triager action id: auto_triage - uses: grafana/auto-triager@main + uses: grafana/auto-triager@main # zizmor: ignore[unpinned-uses] with: token: ${{ steps.generate_token.outputs.token }} issue_number: ${{ github.event.issue.number }} @@ -99,7 +105,7 @@ jobs: - name: "Send Slack notification" if: ${{ steps.auto_triage.outputs.triage_labels != '' }} - uses: slackapi/slack-github-action@v1.27.0 + uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0 with: payload: > { diff --git a/.github/workflows/lint-build-docs.yml b/.github/workflows/lint-build-docs.yml index ecd6151e228..667a8215565 100644 --- a/.github/workflows/lint-build-docs.yml +++ b/.github/workflows/lint-build-docs.yml @@ -16,13 +16,21 @@ on: - 'packages/**/*.md' - 'latest.json' +permissions: {} + jobs: docs: name: Build & Verify Docs runs-on: ubuntu-latest + + permissions: + contents: read + steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 @@ -43,18 +51,18 @@ jobs: run: | # Create and start a container from the docs-base image in detached mode docker run -d --name docs-builder grafana/docs-base:latest tail -f /dev/null - + # Create the directory structure inside the container docker exec docs-builder mkdir -p /hugo/content/docs/grafana/latest - + # Create the _index.md file docker exec docs-builder /bin/sh -c "echo -e '---\nredirectURL: /docs/grafana/latest/\ntype: redirect\nversioned: true\n---\n' > /hugo/content/docs/grafana/_index.md" - + # Copy the docs sources from the host to the container docker cp docs/sources/. docs-builder:/hugo/content/docs/grafana/latest/ - + # Run the make prod command inside the container docker exec -w /hugo docs-builder make prod || echo "Build completed with warnings" - + # Clean up the container docker rm -f docs-builder diff --git a/.github/workflows/metrics-collector.yml b/.github/workflows/metrics-collector.yml deleted file mode 100644 index 2e22a830a88..00000000000 --- a/.github/workflows/metrics-collector.yml +++ /dev/null @@ -1,50 +0,0 @@ -# -# When triggered by the cron job it will also collect metrics for: -# * number of issues without label -# * number of issues with "needs more info" -# * number of issues with "needs investigation" -# * number of issues with label type/bug -# * number of open issues in current milestone -# -# https://github.com/grafana/grafana-github-actions/blob/main/metrics-collector/index.ts -# -name: Github issue metrics collection -on: - schedule: - - cron: "*/10 * * * *" - issues: - types: [opened, closed] - -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_MISC_STATS_API_KEY != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - - name: Run metrics collector - uses: ./actions/metrics-collector - with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} - token: ${{secrets.GITHUB_TOKEN}} - configPath: "metrics-collector" diff --git a/.github/workflows/migrate-prs.yml b/.github/workflows/migrate-prs.yml index 31bb8f9f9da..d690245be19 100644 --- a/.github/workflows/migrate-prs.yml +++ b/.github/workflows/migrate-prs.yml @@ -15,11 +15,6 @@ on: description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') required: true type: string - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true workflow_dispatch: inputs: from: @@ -34,22 +29,28 @@ on: description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') required: true type: string - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true + +permissions: + contents: read + id-token: write jobs: main: runs-on: ubuntu-latest steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: Migrate PRs uses: grafana/grafana-github-actions-go/migrate-open-prs@main with: diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml deleted file mode 100644 index f686dee7d55..00000000000 --- a/.github/workflows/milestone.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Close Milestone -on: - workflow_dispatch: - inputs: - version_input: - description: 'The version to be released please respect: major.minor.patch, major.minor.patch-preview or major.minor.patch-preview format. example: 7.4.3, 7.4.3-preview or 7.4.3-preview1' - required: true -jobs: - call-remove-milestone: - uses: grafana/grafana/.github/workflows/remove-milestone.yml@main - with: - version_call: ${{ github.event.inputs.version_input }} - secrets: inherit - call-close-milestone: - uses: grafana/grafana/.github/workflows/close-milestone.yml@main - with: - version_call: ${{ github.event.inputs.version_input }} - secrets: inherit - needs: call-remove-milestone diff --git a/.github/workflows/pr-backend-coverage.yml b/.github/workflows/pr-backend-coverage.yml deleted file mode 100644 index b210abcbd91..00000000000 --- a/.github/workflows/pr-backend-coverage.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Coverage - -on: - workflow_dispatch: - push: - branches: - - main - paths-ignore: - - 'docs/**' - - '**/*.md' - -permissions: - contents: read - id-token: write - -env: - EDITION: 'oss' - WIRE_TAGS: 'oss' - -jobs: - main: - name: Backend Unit Tests - runs-on: ubuntu-latest-8-cores - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: true - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential shared-mime-info - go install github.com/mfridman/tparse@c1754a1f484ac5cd422697b0fec635177ddc8507 # v0.17.0 - - name: Generate Go code - run: make gen-go - - name: Run unit tests - run: COVER_OPTS="-coverprofile=be-unit.cov -coverpkg=github.com/grafana/grafana/..." GO_TEST_OUTPUT="/tmp/unit.log" make test-go-unit-cov - - name: Process and upload coverage - uses: ./.github/actions/test-coverage-processor - with: - test-type: 'be-unit' - # Needs to be named 'unit.cov' based on the Makefile command `make test-go-unit` - coverage-file: 'unit.cov' - codecov-token: ${{ secrets.CODECOV_TOKEN }} - codecov-flag: 'be-unit' - codecov-name: 'be-unit' - - - name: Install Grafana Bench - # We can't allow forks here, as we need secret access. - if: ${{ github.event_name != 'pull_request' }} - uses: ./.github/actions/setup-grafana-bench - - - name: Process output for Bench - if: ${{ github.event_name != 'pull_request' }} - run: | - grafana-bench report \ - --trigger pr-backend-unit-tests-oss \ - --report-input go \ - --report-output log \ - --grafana-version "$(git rev-parse HEAD)" \ - --suite-name grafana-oss-unit-tests \ - /tmp/unit.log || true - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index ae2b0898f73..cc8d2531bef 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -31,11 +31,12 @@ jobs: if: github.event.pull_request.draft == false steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@v4 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - name: Run PR Checks diff --git a/.github/workflows/pr-codeql-analysis-javascript.yml b/.github/workflows/pr-codeql-analysis-javascript.yml index d24b7db9671..885c6116f58 100644 --- a/.github/workflows/pr-codeql-analysis-javascript.yml +++ b/.github/workflows/pr-codeql-analysis-javascript.yml @@ -25,6 +25,7 @@ jobs: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/pr-codeql-analysis-python.yml b/.github/workflows/pr-codeql-analysis-python.yml index 4e8b1b14747..df9cbf18f45 100644 --- a/.github/workflows/pr-codeql-analysis-python.yml +++ b/.github/workflows/pr-codeql-analysis-python.yml @@ -23,12 +23,26 @@ jobs: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 + persist-credentials: false + + - name: Check for Python files + id: check-python + run: | + if [ -z "$(find . -name '*.py' -type f)" ]; then + echo "No Python files found, skipping analysis" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "Python files found, proceeding with analysis" + echo "skip=false" >> "$GITHUB_OUTPUT" + fi # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL + if: steps.check-python.outputs.skip != 'true' uses: github/codeql-action/init@v3 with: languages: "python" - name: Perform CodeQL Analysis + if: steps.check-python.outputs.skip != 'true' uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-commands.yml b/.github/workflows/pr-commands.yml index 51838dc7ae7..da9134ed3ae 100644 --- a/.github/workflows/pr-commands.yml +++ b/.github/workflows/pr-commands.yml @@ -5,47 +5,27 @@ on: - labeled - opened - synchronize +permissions: {} concurrency: group: pr-commands-${{ github.event.number }} jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_PR_AUTOMATION_APP_ID != '' && - secrets.GRAFANA_PR_AUTOMATION_APP_PEM != '' && - secrets.GRAFANA_MISC_STATS_API_KEY != '' - ) || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - main: - needs: config - if: needs.config.outputs.has-secrets + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@v4 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} - private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - name: Run Commands uses: ./actions/commands with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} - token: ${{ steps.generate_token.outputs.token }} + token: ${{ secrets.GITHUB_TOKEN }} configPath: pr-commands diff --git a/.github/workflows/pr-dependabot-update-go-workspace.yml b/.github/workflows/pr-dependabot-update-go-workspace.yml index 9fd36a20082..82169bd69ac 100644 --- a/.github/workflows/pr-dependabot-update-go-workspace.yml +++ b/.github/workflows/pr-dependabot-update-go-workspace.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Retrieve GitHub App secrets id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 + uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 # zizmor: ignore[unpinned-uses] with: repo_secrets: | APP_ID=grafana-go-workspace-bot:app-id @@ -42,9 +42,10 @@ jobs: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} token: ${{ steps.generate_token.outputs.token }} + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod @@ -64,5 +65,5 @@ jobs: if ! git diff --exit-code --quiet; then echo "Committing and pushing workspace changes" git commit -a -m "update workspace" - git push origin $BRANCH_NAME - fi \ No newline at end of file + git push origin "$BRANCH_NAME" + fi diff --git a/.github/workflows/pr-e2e-tests.yml b/.github/workflows/pr-e2e-tests.yml index b7c1f7c8b1b..c88813150c5 100644 --- a/.github/workflows/pr-e2e-tests.yml +++ b/.github/workflows/pr-e2e-tests.yml @@ -11,26 +11,26 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +permissions: {} + jobs: build-grafana: name: Build & Package Grafana runs-on: ubuntu-latest-16-cores + permissions: + contents: read outputs: artifact: ${{ steps.artifact.outputs.artifact }} steps: - - uses: actions/checkout@v4 - with: - repository: 'grafana/grafana-build' - ref: 'main' - uses: actions/checkout@v4 with: path: ./grafana - - run: echo "GRAFANA_GO_VERSION=$(grep "go 1." grafana/go.work | cut -d\ -f2)" >> "$GITHUB_ENV" - - uses: dagger/dagger-for-github@8.0.0 + persist-credentials: false + - uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: verb: run - args: go run ./cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir=grafana --go-version=${GRAFANA_GO_VERSION} > out.txt - - run: mv $(cat out.txt) grafana.tar.gz + args: go -C grafana run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}/grafana" > out.txt + - run: mv "$(cat out.txt)" grafana.tar.gz - run: echo "artifact=grafana-e2e-${{github.run_number}}" >> "$GITHUB_OUTPUT" id: artifact - uses: actions/upload-artifact@v4 @@ -40,8 +40,11 @@ jobs: name: ${{ steps.artifact.outputs.artifact }} path: grafana.tar.gz e2e-matrix: + permissions: + contents: read name: ${{ matrix.suite }} strategy: + fail-fast: false matrix: suite: - various-suite @@ -55,8 +58,11 @@ jobs: package: ${{ needs.build-grafana.outputs.artifact }} suite: ${{ matrix.suite }} e2e-matrix-old-arch: + permissions: + contents: read name: ${{ matrix.suite }} (old arch) strategy: + fail-fast: false matrix: suite: - old-arch/various-suite diff --git a/.github/workflows/pr-external-labelling.yml b/.github/workflows/pr-external-labelling.yml new file mode 100644 index 00000000000..62bd9655edd --- /dev/null +++ b/.github/workflows/pr-external-labelling.yml @@ -0,0 +1,25 @@ +name: External PR labelling + +on: + # We need "write" permissions on the PR to be able to add a label. + pull_request_target: # zizmor: ignore[dangerous-triggers] We need this to have labelling permissions. There are no user inputs here, so we should be fine. + types: + - opened + +permissions: {} + +jobs: + label-if-external: + name: Add 'pr/external' label if the PR is external + if: github.event.pull_request.author_association != 'MEMBER' && github.event.pull_request.author_association != 'OWNER' + runs-on: ubuntu-latest + permissions: + pull-requests: write # to write the label + + steps: + - name: Add the 'pr/external' label + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + echo "Adding 'pr/external' label to the PR" + gh pr edit "$PR_NUMBER" --add-label pr/external diff --git a/.github/workflows/pr-frontend-unit-tests.yml b/.github/workflows/pr-frontend-unit-tests.yml index beced0ac371..ef320ce66c5 100644 --- a/.github/workflows/pr-frontend-unit-tests.yml +++ b/.github/workflows/pr-frontend-unit-tests.yml @@ -6,12 +6,13 @@ on: - main - release-*.*.* -permissions: - contents: read - id-token: write +permissions: {} jobs: frontend-unit-tests: + permissions: + contents: read + id-token: write # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, # the `frontend-unit-tests-enterprise` workflow will run instead if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true @@ -23,6 +24,8 @@ jobs: chunk: [1, 2, 3, 4, 5, 6, 7, 8] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -36,6 +39,9 @@ jobs: TEST_SHARD_TOTAL: 8 frontend-unit-tests-enterprise: + permissions: + contents: read + id-token: write # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false runs-on: ubuntu-latest-8-cores @@ -46,6 +52,8 @@ jobs: chunk: [1, 2, 3, 4, 5, 6, 7, 8] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/pr-go-workspace-check.yml b/.github/workflows/pr-go-workspace-check.yml index f2151dde2fb..25e013b9484 100644 --- a/.github/workflows/pr-go-workspace-check.yml +++ b/.github/workflows/pr-go-workspace-check.yml @@ -22,9 +22,11 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: cache: false go-version-file: go.mod @@ -42,4 +44,4 @@ jobs: exit 1 fi - name: Ensure Dockerfile contains submodule COPY commands - run: ./scripts/go-workspace/validate-dockerfile.sh \ No newline at end of file + run: ./scripts/go-workspace/validate-dockerfile.sh diff --git a/.github/workflows/pr-k8s-codegen-check.yml b/.github/workflows/pr-k8s-codegen-check.yml index ae5b6d3893a..6c34674e5c9 100644 --- a/.github/workflows/pr-k8s-codegen-check.yml +++ b/.github/workflows/pr-k8s-codegen-check.yml @@ -20,9 +20,11 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod @@ -36,4 +38,4 @@ jobs: git diff echo "Please run './hack/update-codegen.sh' and commit the changes." exit 1 - fi \ No newline at end of file + fi diff --git a/.github/workflows/pr-patch-check-event.yml b/.github/workflows/pr-patch-check-event.yml index e1389fdac6a..2b30e0fa375 100644 --- a/.github/workflows/pr-patch-check-event.yml +++ b/.github/workflows/pr-patch-check-event.yml @@ -1,57 +1,27 @@ -# Owned by grafana-delivery-squad -# Intended to be dropped into the base repo Ex: grafana/grafana name: Dispatch check for patch conflicts -run-name: dispatch-check-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }} on: - pull_request_target: + pull_request: types: - opened - reopened - synchronize branches: - "main" - - "v*.*.*" - "release-*" +permissions: + id-token: write + contents: read + # Since this is run on a pull request, we want to apply the patches intended for the # target branch onto the source branch, to verify compatibility before merging. jobs: dispatch-job: - env: - HEAD_REF: ${{ github.head_ref }} - BASE_REF: ${{ github.base_ref }} - REPO: ${{ github.repository }} - SENDER: ${{ github.event.sender.login }} - SHA: ${{ github.sha }} - PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - runs-on: ubuntu-latest - steps: - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a - with: - # App needs Actions: Read/Write for the grafana/security-patch-actions repo - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: "Dispatch job" - uses: actions/github-script@v7 - with: - github-token: ${{ steps.generate_token.outputs.token }} - script: | - const {HEAD_REF, BASE_REF, REPO, SENDER, SHA, PR_COMMIT_SHA} = process.env; - - await github.rest.actions.createWorkflowDispatch({ - owner: 'grafana', - repo: 'security-patch-actions', - workflow_id: 'test-patches-event.yml', - ref: 'main', - inputs: { - src_repo: REPO, - src_ref: HEAD_REF, - src_merge_sha: SHA, - src_pr_commit_sha: PR_COMMIT_SHA, - patch_repo: REPO + '-security-patches', - patch_ref: BASE_REF, - triggering_github_handle: SENDER - } - }) + uses: grafana/grafana/.github/workflows/pr-patch-check.yml@main + with: + head_ref: ${{ github.head_ref }} + base_ref: ${{ github.base_ref }} + repo: ${{ github.repository }} + sender_login: ${{ github.event.sender.login }} + sha: ${{ github.sha }} + pr_commit_sha: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/pr-patch-check.yml b/.github/workflows/pr-patch-check.yml new file mode 100644 index 00000000000..5de7fcf89ed --- /dev/null +++ b/.github/workflows/pr-patch-check.yml @@ -0,0 +1,78 @@ +name: Dispatch check for patch conflicts +on: + workflow_call: + inputs: + head_ref: + type: string + required: true + base_ref: + type: string + required: true + repo: + type: string + required: true + sender_login: + type: string + required: true + sha: + type: string + required: true + pr_commit_sha: + type: string + required: true + +permissions: + id-token: write + contents: read + +# Since this is run on a pull request, we want to apply the patches intended for the +# target branch onto the source branch, to verify compatibility before merging. +jobs: + dispatch-job: + env: + HEAD_REF: ${{ inputs.head_ref }} + BASE_REF: ${{ github.base_ref }} + REPO: ${{ inputs.repo }} + SENDER: ${{ inputs.sender_login }} + SHA: ${{ inputs.sha }} + PR_COMMIT_SHA: ${{ inputs.pr_commit_sha }} + runs-on: ubuntu-latest + steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a + with: + # App needs Actions: Read/Write for the grafana/security-patch-actions repo + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + permissions: "{\"actions\": \"write\", \"workflows\": \"write\"}" + repositories: "[\"security-patch-actions\"]" + - name: "Dispatch job" + uses: actions/github-script@v7 + with: + github-token: ${{ steps.generate_token.outputs.token }} + script: | + const {HEAD_REF, BASE_REF, REPO, SENDER, SHA, PR_COMMIT_SHA} = process.env; + + await github.rest.actions.createWorkflowDispatch({ + owner: 'grafana', + repo: 'security-patch-actions', + workflow_id: 'test-patches-event.yml', + ref: 'main', + inputs: { + src_repo: REPO, + src_ref: HEAD_REF, + src_merge_sha: SHA, + src_pr_commit_sha: PR_COMMIT_SHA, + patch_repo: REPO + '-security-patches', + patch_ref: BASE_REF, + triggering_github_handle: SENDER + } + }) diff --git a/.github/workflows/pr-test-integration.yml b/.github/workflows/pr-test-integration.yml index 518e1150531..53138fd9cc7 100644 --- a/.github/workflows/pr-test-integration.yml +++ b/.github/workflows/pr-test-integration.yml @@ -6,29 +6,62 @@ on: - main - release-*.*.* pull_request: + types: + - opened + - synchronize + - reopened concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +permissions: {} + jobs: sqlite: - name: Sqlite + strategy: + matrix: + shard: [ + 1/8, 2/8, 3/8, 4/8, + 5/8, 6/8, 7/8, 8/8, + ] + fail-fast: false + + name: Sqlite (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores + permissions: + contents: read steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - - run: | - make gen-go - go test -tags=sqlite -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) + - name: Generate Go code + run: make gen-go + - name: Run tests + env: + SHARD: ${{ matrix.shard }} + run: | + readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)" + go test -tags=sqlite -timeout=5m -run '^TestIntegration' "${PACKAGES[@]}" mysql: - name: MySQL + strategy: + matrix: + shard: [ + 1/8, 2/8, 3/8, 4/8, + 5/8, 6/8, 7/8, 8/8, + ] + fail-fast: false + + name: MySQL (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores + permissions: + contents: read env: GRAFANA_TEST_DB: mysql MYSQL_HOST: 127.0.0.1 @@ -46,19 +79,40 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - - run: | - sudo apt-get update -yq && sudo apt-get install mariadb-client - cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h 127.0.0.1 -P 3306 -u root -prootpass --disable-ssl-verify-server-cert - make gen-go - go test -tags=mysql -p=1 -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) + - name: Setup MySQL devenv + run: mysql -h 127.0.0.1 -P 3306 -u root -prootpass < devenv/docker/blocks/mysql_tests/setup.sql + - name: Generate Go code + run: make gen-go + - name: Run tests + env: + SHARD: ${{ matrix.shard }} + run: | + readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)" + go test -p=1 -tags=mysql -timeout=5m -run '^TestIntegration' "${PACKAGES[@]}" postgres: - name: Postgres + strategy: + matrix: + shard: [ + 1/8, 2/8, 3/8, 4/8, + 5/8, 6/8, 7/8, 8/8, + ] + fail-fast: false + + name: Postgres (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores + permissions: + contents: read + env: + GRAFANA_TEST_DB: postgres + PGPASSWORD: grafanatest + POSTGRES_HOST: 127.0.0.1 services: postgres: image: postgres:12.3-alpine @@ -71,17 +125,20 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - - env: - GRAFANA_TEST_DB: postgres - PGPASSWORD: grafanatest - POSTGRES_HOST: 127.0.0.1 + - name: Setup Postgres devenv + run: psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql + - name: Generate Go code + run: make gen-go + - name: Run tests + env: + SHARD: ${{ matrix.shard }} run: | - sudo apt-get update -yq && sudo apt-get install postgresql-client - psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - make gen-go - go test -p=1 -tags=postgres -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) + readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)" + go test -p=1 -tags=postgres -timeout=5m -run '^TestIntegration' "${PACKAGES[@]}" diff --git a/.github/workflows/publish-kinds-next.yml b/.github/workflows/publish-kinds-next.yml index 4aed6cf36c3..495cb35abae 100644 --- a/.github/workflows/publish-kinds-next.yml +++ b/.github/workflows/publish-kinds-next.yml @@ -8,45 +8,46 @@ on: - '**/*.cue' workflow_dispatch: -jobs: - config: - runs-on: "ubuntu-latest" - if: github.repository == 'grafana/grafana' - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' &&secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi +permissions: {} +jobs: main: - needs: config - if: github.repository == 'grafana/grafana' && needs.config.outputs.has-secrets + if: github.repository == 'grafana/grafana' runs-on: "ubuntu-latest" + permissions: + contents: read # cloning repo + actions: read # reading .github/workflows/ dir + id-token: write # reading vault secrets + steps: - name: "Checkout Grafana repo" uses: "actions/checkout@v4" with: fetch-depth: 0 + persist-credentials: false - name: "Setup Go" - uses: "actions/setup-go@v4" + uses: "actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639" with: go-version-file: go.mod - name: "Verify kinds" run: go run .github/workflows/scripts/kinds/verify-kinds.go + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + # App needs Actions: Read/Write for the grafana/security-patch-actions repo + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Clone website-sync Action" run: "git clone --single-branch --no-tags --depth 1 -b master https://grafana-delivery-bot:${{ steps.generate_token.outputs.token }}@github.com/grafana/website-sync ./.github/actions/website-sync" diff --git a/.github/workflows/publish-kinds-release.yml b/.github/workflows/publish-kinds-release.yml index 691cdff3867..03873c27641 100644 --- a/.github/workflows/publish-kinds-release.yml +++ b/.github/workflows/publish-kinds-release.yml @@ -10,34 +10,27 @@ on: - '**/*.cue' workflow_dispatch: -jobs: - config: - runs-on: "ubuntu-latest" - if: github.repository == 'grafana/grafana' - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' && secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi +permissions: {} +jobs: main: - needs: config - if: github.repository == 'grafana/grafana' && needs.config.outputs.has-secrets + if: github.repository == 'grafana/grafana' runs-on: "ubuntu-latest" + permissions: + contents: read # cloning repo + actions: read # reading .github/workflows/ dir + id-token: write # reading vault secrets + steps: - name: "Checkout Grafana repo" uses: "actions/checkout@v4" with: # required for the `grafana/grafana-github-actions/has-matching-release-tag` action to work fetch-depth: 0 + persist-credentials: false - name: "Setup Go" - uses: "actions/setup-go@v4" + uses: "actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639" with: go-version-file: go.mod @@ -49,6 +42,7 @@ jobs: with: repository: "grafana/grafana-github-actions" path: "./actions" + persist-credentials: false - name: "Install Actions from library" run: "npm install --production --prefix ./actions" @@ -61,12 +55,20 @@ jobs: release_tag_regexp: "^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$" release_branch_regexp: "^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.x$" + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + # App needs Actions: Read/Write for the grafana/security-patch-actions repo + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Clone website-sync Action" if: "steps.has-matching-release-tag.outputs.bool == 'true'" diff --git a/.github/workflows/publish-technical-documentation-next.yml b/.github/workflows/publish-technical-documentation-next.yml index 6b2cd7489b3..0047e2992e0 100644 --- a/.github/workflows/publish-technical-documentation-next.yml +++ b/.github/workflows/publish-technical-documentation-next.yml @@ -16,6 +16,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 + with: + persist-credentials: false + - uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 # zizmor: ignore[unpinned-uses] with: website_directory: content/docs/grafana/next diff --git a/.github/workflows/publish-technical-documentation-release.yml b/.github/workflows/publish-technical-documentation-release.yml index 57d779660c5..52d7da0562f 100644 --- a/.github/workflows/publish-technical-documentation-release.yml +++ b/.github/workflows/publish-technical-documentation-release.yml @@ -20,7 +20,8 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: grafana/writers-toolkit/publish-technical-documentation-release@publish-technical-documentation-release/v2 + persist-credentials: false + - uses: grafana/writers-toolkit/publish-technical-documentation-release@publish-technical-documentation-release/v2 # zizmor: ignore[unpinned-uses] with: release_tag_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" release_branch_regexp: "^release-(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" diff --git a/.github/workflows/reject-gh-secrets.yml b/.github/workflows/reject-gh-secrets.yml new file mode 100644 index 00000000000..066b7353116 --- /dev/null +++ b/.github/workflows/reject-gh-secrets.yml @@ -0,0 +1,31 @@ +name: Reject GitHub secrets + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - main + - release-* + +permissions: {} + +jobs: + reject-gh-secrets: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + - name: Grep for secrets accesses + run: | + if grep -E '\$\{\{\s*secrets\s*\.\s*[a-zA-Z0-9_\-]+\s*\}\}' .github/workflows/*.yml | grep -vF 'secrets.GITHUB_TOKEN' | grep -vF '# nolint:reject-gh-secrets'; then + echo "Found secrets access in the codebase. Please remove it in favour of Vault secrets." + echo "If you are sure this is correct, add '# nolint:reject-gh-secrets' to the end of the line. Be VERY careful with this." + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/release-comms.yml b/.github/workflows/release-comms.yml index 008b6552883..7d149fad297 100644 --- a/.github/workflows/release-comms.yml +++ b/.github/workflows/release-comms.yml @@ -21,6 +21,11 @@ on: - 'main' - 'release-*.*.*' +permissions: + contents: write + pull-requests: write + id-token: write + jobs: setup: if: ${{ github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/')) }} @@ -30,58 +35,69 @@ jobs: release_branch: ${{ steps.output.outputs.release_branch }} dry_run: ${{ steps.output.outputs.dry_run }} latest: ${{ steps.output.outputs.latest }} + private_key: ${{ steps.output.outputs.delivery_bot_pem }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + env: + HEAD_REF: ${{ github.head_ref }} + DRY_RUN: ${{ inputs.dry_run }} + LATEST: ${{ inputs.latest && '1' || '0' }} + VERSION: ${{ inputs.version }} runs-on: ubuntu-latest steps: - # The github-release action expects a `LATEST` value of a string of either '1' or '0' - - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - echo setting up GITHUB_ENV for ${{ github.event_name }} - echo "VERSION=${{ inputs.version }}" >> $GITHUB_ENV - echo "DRY_RUN=${{ inputs.dry_run }}" >> $GITHUB_ENV - echo "LATEST=${{ inputs.latest && '1' || '0' }}" >> $GITHUB_ENV - if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') }} run: | - echo "VERSION=$(echo ${{ github.head_ref }} | sed -e 's/release\/.*\//v/g')" >> $GITHUB_ENV - echo "DRY_RUN=${{ contains(github.event.pull_request.labels.*.name, 'release/dry-run') }}" >> $GITHUB_ENV - echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') && '1' || '0' }}" >> $GITHUB_ENV + { + echo "VERSION=$(echo "${HEAD_REF}" | sed -e 's/release\/.*\//v/g')" + echo "DRY_RUN=${{ contains(github.event.pull_request.labels.*.name, 'release/dry-run') }}" + echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') && '1' || '0' }}" + } >> "$GITHUB_ENV" - id: output run: | echo "dry_run: $DRY_RUN" echo "latest: $LATEST" echo "version: $VERSION" - echo "release_branch=$(echo $VERSION | sed -s 's/^v/release-/g')" >> "$GITHUB_OUTPUT" - echo "dry_run=$DRY_RUN" >> "$GITHUB_OUTPUT" - echo "latest=$LATEST" >> "$GITHUB_OUTPUT" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" + { + echo "release_branch=$(echo "$VERSION" | sed -s 's/^v/release-/g')" + echo "dry_run=$DRY_RUN" + echo "latest=$LATEST" + echo "version=$VERSION" + } >> "$GITHUB_OUTPUT" create_next_release_branch_grafana: name: Create next release branch (Grafana) needs: setup - uses: ./.github/workflows/create-next-release-branch.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/create-next-release-branch.yml@main with: ownerRepo: 'grafana/grafana' source: ${{ needs.setup.outputs.release_branch }} create_next_release_branch_enterprise: name: Create next release branch (Grafana Enterprise) needs: setup - uses: ./.github/workflows/create-next-release-branch.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/create-next-release-branch.yml@main with: ownerRepo: 'grafana/grafana-enterprise' source: ${{ needs.setup.outputs.release_branch }} + create_security_branch_grafana: + name: Create security branch (Grafana Security Mirror) + needs: setup + uses: ./.github/workflows/create-security-branch.yml + with: + release_branch: ${{ needs.setup.outputs.release_branch }} + security_branch_number: "01" + repository: grafana/grafana-security-mirror + create_security_branch_enterprise: + name: Create security branch (Enterprise) + needs: setup + uses: ./.github/workflows/create-security-branch.yml + with: + release_branch: ${{ needs.setup.outputs.release_branch }} + security_branch_number: "01" + repository: grafana/grafana-enterprise migrate_prs_grafana: needs: - setup - create_next_release_branch_grafana - uses: ./.github/workflows/migrate-prs.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/migrate-prs.yml@main with: ownerRepo: 'grafana/grafana' from: ${{ needs.setup.outputs.release_branch }} @@ -90,20 +106,14 @@ jobs: needs: - setup - create_next_release_branch_enterprise - uses: ./.github/workflows/migrate-prs.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/migrate-prs.yml@main with: ownerRepo: 'grafana/grafana-enterprise' from: ${{ needs.setup.outputs.release_branch }} to: ${{ needs.create_next_release_branch_enterprise.outputs.branch }} post_changelog_on_forum: needs: setup - uses: ./.github/workflows/community-release.yml - secrets: - GRAFANA_MISC_STATS_API_KEY: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} - GRAFANABOT_FORUM_KEY: ${{ secrets.GRAFANABOT_FORUM_KEY }} + uses: grafana/grafana/.github/workflows/community-release.yml@main with: version: ${{ needs.setup.outputs.version }} dry_run: ${{ needs.setup.outputs.dry_run == 'true' }} @@ -112,7 +122,7 @@ jobs: # The github-release action retrieves the changelog using the /repos/grafana/grafana/contents/CHANGELOG.md API # endpoint. needs: setup - uses: ./.github/workflows/github-release.yml + uses: grafana/grafana/.github/workflows/github-release.yml@main with: version: ${{ needs.setup.outputs.version }} dry_run: ${{ needs.setup.outputs.dry_run == 'true' }} @@ -120,7 +130,10 @@ jobs: post_on_slack: needs: setup runs-on: ubuntu-latest + env: + DRY_RUN: ${{ needs.setup.outputs.dry_run }} + VERSION: ${{ needs.setup.outputs.version }} steps: - run: | - echo announce on slack that ${{ needs.setup.outputs.version }} has been released - echo dry run: ${{ needs.setup.outputs.dry_run }} + echo announce on slack that "$VERSION" has been released + echo dry run: "$DRY_RUN" diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 1234cdc75ef..c2f22c755fa 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -15,15 +15,15 @@ on: version: required: true type: string - description: The version of Grafana that is being released - target: - required: true - type: string - description: The release branch pattern (eg v9.5.x) that these changes are being merged into - backport: + description: The version of Grafana that is being released (without the `v` prefix)` + changelog: required: false - type: string - description: Branch to backport these changes to + type: boolean + default: true + bump: + required: false + type: boolean + default: true dry_run: required: false default: false @@ -32,99 +32,150 @@ on: required: false default: false type: boolean + release_date: + required: false + type: string + description: "Release date in format YYYY-MM-DD" permissions: - contents: write - pull-requests: write + contents: read jobs: + capture-date: + runs-on: ubuntu-latest + outputs: + release_date: ${{ steps.set_release_date.outputs.release_date }} + steps: + - name: compute_release_date + run: | + if [ -n "$DATE" ]; then + echo "release_date=$DATE" >> "$GITHUB_ENV" + exit 0 + fi + + echo "Fetching workflow run creation date..." + created_at=$(gh run view "$GITHUB_RUN_ID" --repo "$GH_REPO" --json createdAt -q .createdAt) + formatted_date=$(date -d "$created_at" +%Y-%m-%d) + echo "release_date=$formatted_date" >> "$GITHUB_ENV" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + DATE: ${{ inputs.release_date }} + + - id: set_release_date + run: echo "release_date=$release_date" >> "$GITHUB_OUTPUT" + push-changelog-to-main: + needs: capture-date + permissions: + contents: write + id-token: write + pull-requests: write + name: Create PR to main to update the changelog uses: ./.github/workflows/changelog.yml + concurrency: + group: grafana-release-pr-update-changelog-main + cancel-in-progress: false with: previous_version: ${{inputs.previous_version}} version: ${{ inputs.version }} latest: ${{ inputs.latest }} dry_run: ${{ inputs.dry_run }} target: main - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + work_branch: changelog/update-changelog-${{ needs.capture-date.outputs.release_date }} + create-prs: + permissions: + contents: write + id-token: write + pull-requests: write name: Create Release PR runs-on: ubuntu-latest if: github.repository == 'grafana/grafana' + env: + VERSION: ${{ inputs.version }} + LATEST: ${{ inputs.latest }} + DRY_RUN: ${{ inputs.dry_run }} steps: - - name: Generate bot token - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Get release branch - id: branch - uses: grafana/grafana-github-actions-go/latest-release-branch@main + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: Generate token + id: generate_changelog_token + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - token: ${{ steps.generate_token.outputs.token }} - ownerRepo: 'grafana/grafana' - pattern: ${{ inputs.target }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + repositories: "[\"grafana\", \"grafana-enterprise\"]" + permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}" + - run: echo "RELEASE_BRANCH=release-${VERSION}" >> "$GITHUB_ENV" - name: Checkout Grafana uses: actions/checkout@v4 with: - ref: ${{ steps.branch.outputs.branch }} - fetch-depth: 0 + token: ${{ steps.generate_changelog_token.outputs.token }} + ref: ${{ env.RELEASE_BRANCH }} fetch-tags: true - token: ${{ steps.generate_token.outputs.token }} + fetch-depth: 0 - name: Checkout Grafana (main) uses: actions/checkout@v4 with: + token: ${{ steps.generate_changelog_token.outputs.token }} ref: main fetch-depth: '0' - fetch-tags: 'false' path: .grafana-main - token: ${{ steps.generate_token.outputs.token }} + - name: Setup nodejs environment uses: actions/setup-node@v4 with: node-version-file: .nvmrc + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod - name: Configure git user run: | - git config --local user.name "github-actions[bot]" - git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "grafana-delivery-bot[bot]" + git config --local user.email "grafana-delivery-bot[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - name: Create branch - run: git checkout -b "release/${{ github.run_id }}/${{ inputs.version }}" + run: git checkout -b "release/${{ github.run_number }}/$VERSION" - name: Generate changelog id: changelog - uses: ./.grafana-main/.github/workflows/actions/changelog + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} + uses: ./.grafana-main/.github/actions/changelog with: - github_token: ${{ steps.generate_token.outputs.token }} - target: v${{ inputs.version }} + previous: ${{inputs.previous_version}} + github_token: ${{ steps.generate_changelog_token.outputs.token }} + target: v${{ env.VERSION }} output_file: changelog_items.md - name: Patch CHANGELOG.md + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} run: | # Prepare CHANGELOG.md content with version delimiters ( echo - echo "# ${{ inputs.version}} ($(date '+%F'))" + echo "# $VERSION ($(date '+%F'))" echo cat changelog_items.md ) > CHANGELOG.part # Check if a version exists in the changelog - if grep -q "" + echo "" cat CHANGELOG.part - echo "" + echo "" cat CHANGELOG.md ) > CHANGELOG.tmp mv CHANGELOG.tmp CHANGELOG.md @@ -134,48 +185,43 @@ jobs: git diff CHANGELOG.md - name: "Prettify CHANGELOG.md" + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} run: npx prettier --write CHANGELOG.md - name: Commit CHANGELOG.md changes + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md - - - name: Update package.json versions - uses: ./.grafana-main/pkg/build/actions/bump-version + - name: Bump versions + if: ${{ inputs.bump == true || inputs.bump == 'true' }} + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: - version: 'patch' + verb: run + args: go run -C .grafana-main ./pkg/build/actions/bump-version -version="patch" + - name: make gen-cue + shell: bash + run: make gen-cue - name: Add package.json changes + if: ${{ inputs.bump == true || inputs.bump == 'true' }} run: | git add package.json lerna.json yarn.lock packages public test -e e2e/test-plugins && git add e2e/test-plugins - git commit -m "Update version to ${{ inputs.version }}" + git commit -m "Update version to $VERSION" - name: Git push - if: ${{ inputs.dry_run }} != true - run: git push --set-upstream origin release/${{ github.run_id }}/${{ inputs.version }} - - - name: Create PR without backports - if: "${{ inputs.backport == '' }}" - run: > - gh pr create \ - $( [ "x${{ inputs.latest }}" == "xtrue" ] && printf %s '-l "release/latest"') \ - -l "no-changelog" \ - --dry-run=${{ inputs.dry_run }} \ - -B "${{ steps.branch.outputs.branch }}" \ - --title "Release: ${{ inputs.version }}" \ - --body "These code changes must be merged after a release is complete" + run: git push + - name: Create PR env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create PR with backports - if: "${{ inputs.backport != '' }}" - run: > + DRY_RUN: ${{ inputs.dry_run }} + run: | + LATEST_FLAG=() + if [ "$LATEST" = "true" ]; then + LATEST_FLAG=(-l "release/latest") + fi gh pr create \ - $( [ "x${{ inputs.latest }}" == "xtrue" ] && printf %s '-l "release/latest"') \ - -l "product-approved" \ + "${LATEST_FLAG[@]}" \ -l "no-changelog" \ - --dry-run=${{ inputs.dry_run }} \ - -B "${{ steps.branch.outputs.branch }}" \ - --title "Release: ${{ inputs.version }}" \ + --dry-run="$DRY_RUN" \ + -B "${RELEASE_BRANCH}" \ + --title "Release: $VERSION" \ --body "These code changes must be merged after a release is complete" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/remove-milestone.yml b/.github/workflows/remove-milestone.yml deleted file mode 100644 index d41b63f1f51..00000000000 --- a/.github/workflows/remove-milestone.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Remove milestone -on: - workflow_dispatch: - inputs: - version: - required: true - description: Needs to match, exactly, the name of a milestone - workflow_call: - inputs: - version_call: - description: Needs to match, exactly, the name of a milestone - required: true - type: string - -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' && secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets - permissions: - issues: write - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Remove milestone from open issues (manually invoked) - if: ${{ github.event.inputs.version != '' }} - uses: ./actions/remove-milestone - with: - token: ${{ steps.generate_token.outputs.token }} - - name: Remove milestone from open issues (workflow invoked) - if: ${{ inputs.version_call != '' }} - uses: ./actions/remove-milestone - with: - version_call: ${{ inputs.version_call }} - token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/run-dashboard-search-e2e.yml b/.github/workflows/run-dashboard-search-e2e.yml index 9eb9930cdd4..ca1d6d4f940 100644 --- a/.github/workflows/run-dashboard-search-e2e.yml +++ b/.github/workflows/run-dashboard-search-e2e.yml @@ -2,7 +2,7 @@ name: run-dashboard-search-e2e on: workflow_run: - workflows: + workflows: - trigger-dashboard-search-e2e types: - completed @@ -11,6 +11,8 @@ on: env: ARCH: linux-amd64 +permissions: {} + jobs: setup: runs-on: ubuntu-latest @@ -18,9 +20,14 @@ jobs: outputs: ini_files: ${{ steps.get_files.outputs.ini_files }} + permissions: + contents: read + id-token: write steps: - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Pin Go version to mod file uses: actions/setup-go@v5 with: @@ -33,7 +40,7 @@ jobs: cache: 'yarn' - name: Cache Node Modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -44,12 +51,12 @@ jobs: run: yarn install --immutable - name: Install Cypress dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f with: runTests: false - name: Cache Grafana Build and Dependencies id: cache-grafana - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | bin/ @@ -67,9 +74,11 @@ jobs: - name: Get list of .ini files id: get_files + env: + WORKSPACE: ${{ github.workspace }} run: | - INI_FILES=$(ls ${{ github.workspace }}/e2e/dashboards-search-suite/*.ini | jq -R -s -c 'split("\n")[:-1]') - echo "ini_files=$INI_FILES" >> $GITHUB_OUTPUT + INI_FILES="$(find "$WORKSPACE"/e2e/dashboards-search-suite/ -type f -name '*.ini' | jq -R -s -c 'split("\n")[:-1]')" + echo "ini_files=$INI_FILES" >> "$GITHUB_OUTPUT" shell: bash run_tests: @@ -81,12 +90,17 @@ jobs: matrix: ini_file: ${{ fromJson(needs.setup.outputs.ini_files) }} + permissions: + contents: read + id-token: write + steps: - name: Checkout repository uses: actions/checkout@v4 - + with: + persist-credentials: false - name: Restore Cached Node Modules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -94,7 +108,7 @@ jobs: key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} - name: Restore Cached Grafana Build and Dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | bin/ @@ -107,10 +121,15 @@ jobs: key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }} - name: Set the step name id: set_file_name + env: + INI_NAME: ${{ matrix.ini_file }} run: | - FILE_NAME=$(basename "${{ matrix.ini_file }}" .ini) - echo "FILE_NAME=$FILE_NAME" >> $GITHUB_ENV - - name: Run tests for ${{ env.FILE_NAME }} + FILE_NAME="$(basename "$INI_NAME" .ini)" + echo "FILE_NAME=$FILE_NAME" >> "$GITHUB_OUTPUT" + - name: Run tests for ${{ steps.set_file_name.outputs.FILE_NAME }} + env: + INI_NAME: ${{ matrix.ini_file }} + WORKSPACE: ${{ github.workspace }} run: | - cp -rf ${{ matrix.ini_file }} ${{ github.workspace }}/scripts/grafana-server/custom.ini + cp -rf "$INI_NAME" "$WORKSPACE"/scripts/grafana-server/custom.ini yarn e2e:dashboards-search || echo "Test failed but marking as success since unified search is behind a feature flag and should not block PRs" diff --git a/.github/workflows/run-e2e-suite.yml b/.github/workflows/run-e2e-suite.yml index 8c3c5851325..0aa6d292abc 100644 --- a/.github/workflows/run-e2e-suite.yml +++ b/.github/workflows/run-e2e-suite.yml @@ -15,18 +15,25 @@ jobs: runs-on: ubuntu-latest-8-cores steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/download-artifact@v4 with: name: ${{ inputs.package }} - - uses: dagger/dagger-for-github@8.0.0 - if: inputs.old-arch == false + - uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: verb: run args: go run ./pkg/build/e2e --package=grafana.tar.gz --suite=${{ inputs.suite }} - - run: echo "suite=$(echo ${{ inputs.suite }} | sed 's/\//-/g')" >> $GITHUB_ENV + - name: Set suite name + id: set-suite-name + if: success() || failure() + env: + SUITE: ${{ inputs.suite }} + run: | + echo "suite=$(echo "$SUITE" | sed 's/\//-/g')" >> "$GITHUB_OUTPUT" - uses: actions/upload-artifact@v4 - if: ${{ always() && inputs.old-arch != true }} + if: success() || failure() with: - name: e2e-${{ env.suite }}-${{github.run_number}} + name: e2e-${{ steps.set-suite-name.outputs.suite }}-${{github.run_number}} path: videos retention-days: 1 diff --git a/.github/workflows/run-schema-v2-e2e.yml b/.github/workflows/run-schema-v2-e2e.yml index 8b55aa4c430..aa8a11c4c7f 100644 --- a/.github/workflows/run-schema-v2-e2e.yml +++ b/.github/workflows/run-schema-v2-e2e.yml @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Pin Go version to mod file uses: actions/setup-go@v5 with: @@ -33,7 +35,7 @@ jobs: - name: Build grafana run: make build - name: Install Cypress dependencies - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f with: runTests: false - name: Run dashboard scenes e2e @@ -41,4 +43,4 @@ jobs: - name: Always succeed # This is a workaround to make the job pass even if the previous step fails if: failure() - run: exit 0 \ No newline at end of file + run: exit 0 diff --git a/.github/workflows/scripts/create-security-branch/create-security-branch.sh b/.github/workflows/scripts/create-security-branch/create-security-branch.sh new file mode 100644 index 00000000000..11a3a2f3808 --- /dev/null +++ b/.github/workflows/scripts/create-security-branch/create-security-branch.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Construct the security branch name +SECURITY_BRANCH="${INPUT_RELEASE_BRANCH}+security-${INPUT_SECURITY_BRANCH_NUMBER}" + +# Check if branch already exists +if git show-ref --verify --quiet "refs/heads/${SECURITY_BRANCH}"; then + echo "::error::Security branch ${SECURITY_BRANCH} already exists" + exit 1 +fi + +# Create and push the new branch from the release branch +git checkout "${INPUT_RELEASE_BRANCH}" +git checkout -b "${SECURITY_BRANCH}" +git push origin "${SECURITY_BRANCH}" + +# Output the branch name for the workflow +echo "branch=${SECURITY_BRANCH}" >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/scripts/crowdin/create-tasks.js b/.github/workflows/scripts/crowdin/create-tasks.js deleted file mode 100644 index d3085f8afa0..00000000000 --- a/.github/workflows/scripts/crowdin/create-tasks.js +++ /dev/null @@ -1,84 +0,0 @@ -const crowdin = require('@crowdin/crowdin-api-client'); -const TRANSLATED_CONNECTOR_DESCRIPTION = '{{tos_service_type: premium}}'; - -const API_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN; -if (!API_TOKEN) { - console.error('Error: CROWDIN_PERSONAL_TOKEN environment variable is not set'); - process.exit(1); -} - -const PROJECT_ID = process.env.CROWDIN_PROJECT_ID; -if (!PROJECT_ID) { - console.error('Error: CROWDIN_PROJECT_ID environment variable is not set'); - process.exit(1); -} - -const { tasksApi, projectsGroupsApi, sourceFilesApi } = new crowdin.default({ - token: API_TOKEN, - organization: 'grafana' -}); - -const languages = await getLanguages(); -const fileIds = await getFileIds(); -console.log('Languages: ', languages); -console.log('File IDs: ', fileIds); - -// for (const language of languages) { -// const { name, id } = language; -// await createTask(`Translate to ${name}`, id, fileIds); -// } - -async function getLanguages() { - try { - const project = await projectsGroupsApi.getProject(PROJECT_ID); - const languages = project.data.targetLanguages; - return languages; - } catch (error) { - console.error('Failed to fetch languages: ', error.message); - if (error.response && error.response.data) { - console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); - } - process.exit(1); - } -} - -async function getFileIds() { - try { - const response = await sourceFilesApi.listProjectFiles(PROJECT_ID); - const files = response.data; - const fileIds = files.map(file => file.data.id); - return fileIds; - } catch (error) { - console.error('Failed to fetch file IDs: ', error.message); - if (error.response && error.response.data) { - console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); - } - process.exit(1); - } -} - -async function createTask(title, languageId, fileIds) { - try { - const taskParams = { - title, - description: TRANSLATED_CONNECTOR_DESCRIPTION, - languageId, - type: 2, // Translation by vendor - workflowStepId: 78, // Translation step ID - skipAssignedStrings: true, - fileIds, - }; - - console.log(`Creating Crowdin task: "${title}" for language ${languageId}`); - - const response = await tasksApi.addTask(PROJECT_ID, taskParams); - console.log(`Task created successfully! Task ID: ${response.data.id}`); - return response.data; - } catch (error) { - console.error('Failed to create Crowdin task: ', error.message); - if (error.response && error.response.data) { - console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); - } - process.exit(1); - } -} diff --git a/.github/workflows/scripts/crowdin/create-tasks.ts b/.github/workflows/scripts/crowdin/create-tasks.ts new file mode 100644 index 00000000000..3a2535640c8 --- /dev/null +++ b/.github/workflows/scripts/crowdin/create-tasks.ts @@ -0,0 +1,110 @@ +import crowdinImport from '@crowdin/crowdin-api-client'; +const TRANSLATED_CONNECTOR_DESCRIPTION = '{{tos_service_type: premium}}'; +const TRANSLATE_BY_VENDOR_WORKFLOW_TYPE = 'TranslateByVendor' + +// TODO Remove this type assertion when https://github.com/crowdin/crowdin-api-client-js/issues/508 is fixed +// @ts-expect-error +const crowdin = crowdinImport.default as typeof crowdinImport; + +const API_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN; +if (!API_TOKEN) { + console.error('Error: CROWDIN_PERSONAL_TOKEN environment variable is not set'); + process.exit(1); +} + +const PROJECT_ID = process.env.CROWDIN_PROJECT_ID ? parseInt(process.env.CROWDIN_PROJECT_ID, 10) : undefined; +if (!PROJECT_ID) { + console.error('Error: CROWDIN_PROJECT_ID environment variable is not set'); + process.exit(1); +} + +const credentials = { + token: API_TOKEN, + organization: 'grafana' +}; + +const { tasksApi, projectsGroupsApi, sourceFilesApi, workflowsApi } = new crowdin(credentials); + +const languages = await getLanguages(PROJECT_ID); +const fileIds = await getFileIds(PROJECT_ID); +const workflowStepId = await getWorkflowStepId(PROJECT_ID); + +for (const language of languages) { + const { name, id } = language; + await createTask(PROJECT_ID, `Translate to ${name}`, id, fileIds, workflowStepId); +} + +async function getLanguages(projectId: number) { + try { + const project = await projectsGroupsApi.getProject(projectId); + const languages = project.data.targetLanguages; + console.log('Fetched languages successfully!'); + return languages; + } catch (error) { + console.error('Failed to fetch languages: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function getFileIds(projectId: number) { + try { + const response = await sourceFilesApi.listProjectFiles(projectId); + const files = response.data; + const fileIds = files.map(file => file.data.id); + console.log('Fetched file ids successfully!'); + return fileIds; + } catch (error) { + console.error('Failed to fetch file IDs: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function getWorkflowStepId(projectId: number) { + try { + const response = await workflowsApi.listWorkflowSteps(projectId); + const workflowSteps = response.data; + const workflowStepId = workflowSteps.find(step => step.data.type === TRANSLATE_BY_VENDOR_WORKFLOW_TYPE)?.data.id; + if (!workflowStepId) { + throw new Error(`Workflow step with type "${TRANSLATE_BY_VENDOR_WORKFLOW_TYPE}" not found`); + } + console.log('Fetched workflow step ID successfully!'); + return workflowStepId; + } catch (error) { + console.error('Failed to fetch workflow step ID: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function createTask(projectId: number, title: string, languageId: string, fileIds: number[], workflowStepId: number) { + try { + const taskParams = { + title, + description: TRANSLATED_CONNECTOR_DESCRIPTION, + languageId, + workflowStepId, + skipAssignedStrings: true, + fileIds, + }; + + console.log(`Creating Crowdin task: "${title}" for language ${languageId}`); + + const response = await tasksApi.addTask(projectId, taskParams); + console.log(`Task created successfully! Task ID: ${response.data.id}`); + return response.data; + } catch (error) { + console.error('Failed to create Crowdin task: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} diff --git a/.github/workflows/skye-add-to-project.yml b/.github/workflows/skye-add-to-project.yml index 5e8ccc8e557..7dbc08e3e85 100644 --- a/.github/workflows/skye-add-to-project.yml +++ b/.github/workflows/skye-add-to-project.yml @@ -1,15 +1,15 @@ -name: Add issues and PRs to Skye project board +name: Add issues to Skye project board on: workflow_dispatch: inputs: manual_issue_number: - description: 'Issue/PR number to add to project' + description: 'Issue to add to project' required: false type: number + # Ideally we could trigger this for PRs as well, but getting the secrets on that is tricky + # so we just won't bother for now issues: types: [opened] - pull_request: - types: [opened] permissions: contents: read @@ -30,14 +30,14 @@ jobs: steps: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Vault secret paths: - # - ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot + # - ci/repo/grafana/grafana/grafana_pr_automation_app # - ci/repo/grafana/grafana/frontend_platform_skye_usernames (comma separated list of usernames) repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GH_APP_ID=grafana_pr_automation_app:app_id + GH_APP_PEM=grafana_pr_automation_app:app_pem ALLOWED_USERS=frontend_platform_skye_usernames:allowed_users - name: Generate token @@ -47,28 +47,20 @@ jobs: app_id: ${{ env.GH_APP_ID }} private_key: ${{ env.GH_APP_PEM }} - # Check if the user is in the list from the secret + # We do the check in the Github Actions expression and then export it to the output + # to reuse it - name: Check if user is allowed id: check_user + env: + USER_IS_ALLOWED: ${{ contains(fromJSON(env.ALLOWED_USERS), github.event.sender.login) }} run: | - # Convert the comma-separated list to an array - IFS=',' read -ra ALLOWED_USERS <<< "${{ env.ALLOWED_USERS }}" - USERNAME="${{ github.event.sender.login }}" + echo "user_allowed=${USER_IS_ALLOWED}" >> "$GITHUB_OUTPUT" - # Check if user is in the allowed list - for allowed_user in "${ALLOWED_USERS[@]}"; do - if [ "$allowed_user" = "$USERNAME" ]; then - echo "user_allowed=true" >> $GITHUB_OUTPUT - exit 0 - fi - done - echo "user_allowed=false" >> $GITHUB_OUTPUT - - # Convert the issue/PR number to a node ID for the GraphQL API + # Convert the issue to a node ID for the GraphQL API - name: Get node ID for item if: steps.check_user.outputs.user_allowed == 'true' id: get_node_id - uses: octokit/graphql-action@v2.x + uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 with: query: | query getNodeId($owner: String!, $repo: String!, $number: Int!) { @@ -86,10 +78,10 @@ jobs: env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - # Finally, add the issue/PR to the project board + # Finally, add the issue to the project board - name: Add to project board if: steps.check_user.outputs.user_allowed == 'true' - uses: octokit/graphql-action@v2.x + uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 with: query: | mutation addItem($projectid: ID!, $itemid: ID!) { diff --git a/.github/workflows/storybook-verification.yml b/.github/workflows/storybook-verification.yml index efab7af6c4c..fa5b6dfd3ea 100644 --- a/.github/workflows/storybook-verification.yml +++ b/.github/workflows/storybook-verification.yml @@ -14,26 +14,32 @@ on: - '!docs/**' - '!*.md' +permissions: {} + jobs: verify-storybook: name: Verify Storybook runs-on: ubuntu-latest - + permissions: + contents: read + steps: - name: Checkout code uses: actions/checkout@v4 - + with: + persist-credentials: false + - name: Setup Node.js uses: actions/setup-node@v4 with: node-version-file: 'package.json' cache: 'yarn' - + - name: Install dependencies run: yarn install --immutable - + - name: Run Storybook and E2E tests - uses: cypress-io/github-action@v6 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f with: browser: chrome start: yarn storybook --quiet diff --git a/.github/workflows/sync-mirror-event.yml b/.github/workflows/sync-mirror-event.yml index b1a1466fdf9..4a073c9b2b3 100644 --- a/.github/workflows/sync-mirror-event.yml +++ b/.github/workflows/sync-mirror-event.yml @@ -10,34 +10,54 @@ on: - "v*.*.*" - "release-*" +permissions: {} + # This is run after the pull request has been merged, so we'll run against the target branch jobs: dispatch-job: runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + actions: write + env: + REF_NAME: ${{ github.ref_name }} + REPO: ${{ github.repository }} + SHA: ${{ github.sha }} steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: "Generate token" id: generate_token uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: # App needs Actions: Read/Write for the grafana/security-patch-actions repo - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - uses: actions/github-script@v7 if: github.repository == 'grafana/grafana' with: github-token: ${{ steps.generate_token.outputs.token }} script: | + const {REF_NAME, REPO, SHA} = process.env; + await github.rest.actions.createWorkflowDispatch({ owner: 'grafana', repo: 'security-patch-actions', workflow_id: 'mirror-branch-and-apply-patches-event.yml', ref: 'main', inputs: { - src_ref: "${{ github.ref_name }}", - src_repo: "${{ github.repository }}", - src_sha: "${{ github.sha }}", - dest_repo: "${{ github.repository }}-security-mirror", - patch_repo: "${{ github.repository }}-security-patches" + src_ref: REF_NAME, + src_repo: REPO, + src_sha: SHA, + dest_repo: REPO + "-security-mirror", + patch_repo: REPO + "-security-patches" } }) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 25104af8f29..85e2ed7f07f 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -17,8 +17,10 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install Trivy - uses: aquasecurity/setup-trivy@v0.2.2 + uses: aquasecurity/setup-trivy@9ea583eb67910444b1f64abf338bd2e105a0a93d with: version: v0.56.2 cache: true diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml new file mode 100644 index 00000000000..761916940fe --- /dev/null +++ b/.github/workflows/trufflehog.yml @@ -0,0 +1,35 @@ +name: Trufflehog + +on: + pull_request: + types: + - opened + - synchronize + - reopened + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + trufflehog: + name: Run Trufflehog + runs-on: ubuntu-latest + permissions: + contents: read # clone the repo + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + ref: ${{ github.head_ref }} + - name: Trufflehog + uses: trufflesecurity/trufflehog@90190deac64289cb10bb694894be8db9ead8790b # v3.88.29 + with: + base: ${{ github.event.pull_request.base.sha }} + head: ${{ github.event.pull_request.head.sha }} + extra_args: --results=verified diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml deleted file mode 100644 index db22ab1fb96..00000000000 --- a/.github/workflows/update-changelog.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Update changelog -on: - workflow_dispatch: - inputs: - version: - required: true - description: 'Needs to match, exactly, the name of a milestone. The version to be released please respect: major.minor.patch, major.minor.patch-preview or major.minor.patch-preview format. example: 7.4.3, 7.4.3-preview or 7.4.3-preview1' - skip_pr: - required: false - default: "0" - skip_community_post: - required: false - default: "0" -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' && - secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '' && - secrets.GRAFANA_MISC_STATS_API_KEY != '' && - secrets.GRAFANABOT_FORUM_KEY != '' - ) || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets - runs-on: ubuntu-latest - steps: - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Run update changelog (manually invoked) - uses: grafana/grafana-github-actions-go/update-changelog@main - with: - token: ${{ steps.generate_token.outputs.token }} - 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/.github/workflows/update-make-docs.yml b/.github/workflows/update-make-docs.yml index 49b64504bd0..aab2be84ad9 100644 --- a/.github/workflows/update-make-docs.yml +++ b/.github/workflows/update-make-docs.yml @@ -9,7 +9,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: grafana/writers-toolkit/update-make-docs@update-make-docs/v1 + with: + persist-credentials: false + - uses: grafana/writers-toolkit/update-make-docs@update-make-docs/v1 # zizmor: ignore[unpinned-uses] with: pr_options: > --label 'backport v10.1.x' diff --git a/.github/workflows/verify-kinds.yml b/.github/workflows/verify-kinds.yml index 88b45660d45..c793dcb2895 100644 --- a/.github/workflows/verify-kinds.yml +++ b/.github/workflows/verify-kinds.yml @@ -14,9 +14,10 @@ jobs: uses: "actions/checkout@v4" with: fetch-depth: 0 + persist-credentials: false - name: "Setup Go" - uses: "actions/setup-go@v4" + uses: "actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639" with: go-version-file: go.mod diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000000..eac536c3db5 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,34 @@ +rules: + unpinned-uses: + config: + policies: + "*": hash-pin + actions/*: any + github/*: any + grafana/*: any + forbidden-uses: + config: + deny: + # Policy-banned by our security team due to CVE-2025-30066 & CVE-2025-30154. + # https://www.cisa.gov/news-events/alerts/2025/03/18/supply-chain-compromise-third-party-tj-actionschanged-files-cve-2025-30066-and-reviewdogaction + # https://nvd.nist.gov/vuln/detail/cve-2025-30066 + # https://nvd.nist.gov/vuln/detail/cve-2025-30154 + - reviewdog/* + cache-poisoning: + ignore: + - backend-unit-tests.yml + - frontend-lint.yml + - pr-frontend-unit-tests.yml + - pr-test-integration.yml + - publish-kinds-release.yml + dangerous-triggers: + ignore: + - auto-milestone.yml + - backport.yml + - pr-checks.yml + - pr-commands.yml + - pr-patch-check-event.yml + - run-dashboard-search-e2e.yml + excessive-permissions: + ignore: + - release-comms.yml diff --git a/.gitignore b/.gitignore index 5ddb7c03ed1..9ffe2cf1e61 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ __debug_bin* # This is the new place of the block, but I leave the previous here for a while /devenv/docker/blocks/auth/saml-enterprise /devenv/docker/blocks/auth/signer -/devenv/docker/blocks/spanner_tests /devenv/docker/blocks/mt-db /tmp diff --git a/.golangci.yml b/.golangci.yml index 82ff07845f3..189670d6172 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -293,6 +293,7 @@ linters: - third_party$ - builtin$ - examples$ + - pkg/util/xorm issues: max-same-issues: 0 formatters: diff --git a/.ignore b/.ignore index 01e1bb8d6e2..e2253d1f69d 100644 --- a/.ignore +++ b/.ignore @@ -19,4 +19,3 @@ # This is the new place of the block, but I leave the previous here for a while !/devenv/docker/blocks/auth/saml-enterprise !/devenv/docker/blocks/auth/signer -!/devenv/docker/blocks/spanner_tests \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 650825ae9c9..8fd0b837b78 100644 --- a/.prettierignore +++ b/.prettierignore @@ -35,7 +35,8 @@ public/openapi3.json public/mockServiceWorker.js # Crowdin files -public/locales/**/*.json +# Ignore `locales` directory so we also catch enterprise files +**/locales/**/*.json /.nx/cache -/.nx/workspace-data \ No newline at end of file +/.nx/workspace-data diff --git a/.yarn/plugins/@yarnpkg/plugin-licenses.cjs b/.yarn/plugins/@yarnpkg/plugin-licenses.cjs new file mode 100644 index 00000000000..5c846c5d604 --- /dev/null +++ b/.yarn/plugins/@yarnpkg/plugin-licenses.cjs @@ -0,0 +1,49 @@ +/* eslint-disable */ +//prettier-ignore +module.exports = { +name: "@yarnpkg/plugin-licenses", +factory: function (require) { +"use strict";var plugin=(()=>{var ct=Object.create;var re=Object.defineProperty;var lt=Object.getOwnPropertyDescriptor;var ht=Object.getOwnPropertyNames;var dt=Object.getPrototypeOf,ft=Object.prototype.hasOwnProperty;var $=(i=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(i,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):i)(function(i){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+i+'" is not supported')});var te=(i,e)=>()=>(e||i((e={exports:{}}).exports,e),e.exports),oe=(i,e)=>{for(var t in e)re(i,t,{get:e[t],enumerable:!0})},Ee=(i,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ht(e))!ft.call(i,n)&&n!==t&&re(i,n,{get:()=>e[n],enumerable:!(s=lt(e,n))||s.enumerable});return i};var pt=(i,e,t)=>(t=i!=null?ct(dt(i)):{},Ee(e||!i||!i.__esModule?re(t,"default",{value:i,enumerable:!0}):t,i)),ut=i=>Ee(re({},"__esModule",{value:!0}),i);var Ie=(i,e,t)=>{if(!e.has(i))throw TypeError("Cannot "+t)};var C=(i,e,t)=>(Ie(i,e,"read from private field"),t?t.call(i):e.get(i)),ae=(i,e,t)=>{if(e.has(i))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(i):e.set(i,t)};var x=(i,e,t)=>(Ie(i,e,"access private method"),t);var je=te((Yt,We)=>{var ie=typeof performance=="object"&&performance&&typeof performance.now=="function"?performance:Date,mt=typeof AbortController=="function",ce=mt?AbortController:class{constructor(){this.signal=new Ce}abort(e=new Error("This operation was aborted")){this.signal.reason=this.signal.reason||e,this.signal.aborted=!0,this.signal.dispatchEvent({type:"abort",target:this.signal})}},gt=typeof AbortSignal=="function",wt=typeof ce.AbortSignal=="function",Ce=gt?AbortSignal:wt?ce.AbortController:class{constructor(){this.reason=void 0,this.aborted=!1,this._listeners=[]}dispatchEvent(e){e.type==="abort"&&(this.aborted=!0,this.onabort(e),this._listeners.forEach(t=>t(e),this))}onabort(){}addEventListener(e,t){e==="abort"&&this._listeners.push(t)}removeEventListener(e,t){e==="abort"&&(this._listeners=this._listeners.filter(s=>s!==t))}},ke=new Set,ye=(i,e)=>{let t=`LRU_CACHE_OPTION_${i}`;le(t)&&ve(t,`${i} option`,`options.${e}`,z)},be=(i,e)=>{let t=`LRU_CACHE_METHOD_${i}`;if(le(t)){let{prototype:s}=z,{get:n}=Object.getOwnPropertyDescriptor(s,i);ve(t,`${i} method`,`cache.${e}()`,n)}},yt=(i,e)=>{let t=`LRU_CACHE_PROPERTY_${i}`;if(le(t)){let{prototype:s}=z,{get:n}=Object.getOwnPropertyDescriptor(s,i);ve(t,`${i} property`,`cache.${e}`,n)}},Me=(...i)=>{typeof process=="object"&&process&&typeof process.emitWarning=="function"?process.emitWarning(...i):console.error(...i)},le=i=>!ke.has(i),ve=(i,e,t,s)=>{ke.add(i);let n=`The ${e} is deprecated. Please use ${t} instead.`;Me(n,"DeprecationWarning",i,s)},M=i=>i&&i===Math.floor(i)&&i>0&&isFinite(i),Ue=i=>M(i)?i<=Math.pow(2,8)?Uint8Array:i<=Math.pow(2,16)?Uint16Array:i<=Math.pow(2,32)?Uint32Array:i<=Number.MAX_SAFE_INTEGER?V:null:null,V=class extends Array{constructor(e){super(e),this.fill(0)}},$e=class{constructor(e){if(e===0)return[];let t=Ue(e);this.heap=new t(e),this.length=0}push(e){this.heap[this.length++]=e}pop(){return this.heap[--this.length]}},z=class{constructor(e={}){let{max:t=0,ttl:s,ttlResolution:n=1,ttlAutopurge:r,updateAgeOnGet:o,updateAgeOnHas:a,allowStale:f,dispose:c,disposeAfter:l,noDisposeOnSet:g,noUpdateTTL:h,maxSize:p=0,maxEntrySize:y=0,sizeCalculation:d,fetchMethod:u,fetchContext:P,noDeleteOnFetchRejection:F,noDeleteOnStaleGet:k,allowStaleOnFetchRejection:b,allowStaleOnFetchAbort:D,ignoreFetchAbort:E}=e,{length:I,maxAge:R,stale:ee}=e instanceof z?{}:e;if(t!==0&&!M(t))throw new TypeError("max option must be a nonnegative integer");let ge=t?Ue(t):Array;if(!ge)throw new Error("invalid max value: "+t);if(this.max=t,this.maxSize=p,this.maxEntrySize=y||this.maxSize,this.sizeCalculation=d||I,this.sizeCalculation){if(!this.maxSize&&!this.maxEntrySize)throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");if(typeof this.sizeCalculation!="function")throw new TypeError("sizeCalculation set to non-function")}if(this.fetchMethod=u||null,this.fetchMethod&&typeof this.fetchMethod!="function")throw new TypeError("fetchMethod must be a function if specified");if(this.fetchContext=P,!this.fetchMethod&&P!==void 0)throw new TypeError("cannot set fetchContext without fetchMethod");if(this.keyMap=new Map,this.keyList=new Array(t).fill(null),this.valList=new Array(t).fill(null),this.next=new ge(t),this.prev=new ge(t),this.head=0,this.tail=0,this.free=new $e(t),this.initialFill=1,this.size=0,typeof c=="function"&&(this.dispose=c),typeof l=="function"?(this.disposeAfter=l,this.disposed=[]):(this.disposeAfter=null,this.disposed=null),this.noDisposeOnSet=!!g,this.noUpdateTTL=!!h,this.noDeleteOnFetchRejection=!!F,this.allowStaleOnFetchRejection=!!b,this.allowStaleOnFetchAbort=!!D,this.ignoreFetchAbort=!!E,this.maxEntrySize!==0){if(this.maxSize!==0&&!M(this.maxSize))throw new TypeError("maxSize must be a positive integer if specified");if(!M(this.maxEntrySize))throw new TypeError("maxEntrySize must be a positive integer if specified");this.initializeSizeTracking()}if(this.allowStale=!!f||!!ee,this.noDeleteOnStaleGet=!!k,this.updateAgeOnGet=!!o,this.updateAgeOnHas=!!a,this.ttlResolution=M(n)||n===0?n:1,this.ttlAutopurge=!!r,this.ttl=s||R||0,this.ttl){if(!M(this.ttl))throw new TypeError("ttl must be a positive integer if specified");this.initializeTTLTracking()}if(this.max===0&&this.ttl===0&&this.maxSize===0)throw new TypeError("At least one of max, maxSize, or ttl is required");if(!this.ttlAutopurge&&!this.max&&!this.maxSize){let we="LRU_CACHE_UNBOUNDED";le(we)&&(ke.add(we),Me("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.","UnboundedCacheWarning",we,z))}ee&&ye("stale","allowStale"),R&&ye("maxAge","ttl"),I&&ye("length","sizeCalculation")}getRemainingTTL(e){return this.has(e,{updateAgeOnHas:!1})?1/0:0}initializeTTLTracking(){this.ttls=new V(this.max),this.starts=new V(this.max),this.setItemTTL=(s,n,r=ie.now())=>{if(this.starts[s]=n!==0?r:0,this.ttls[s]=n,n!==0&&this.ttlAutopurge){let o=setTimeout(()=>{this.isStale(s)&&this.delete(this.keyList[s])},n+1);o.unref&&o.unref()}},this.updateItemAge=s=>{this.starts[s]=this.ttls[s]!==0?ie.now():0},this.statusTTL=(s,n)=>{s&&(s.ttl=this.ttls[n],s.start=this.starts[n],s.now=e||t(),s.remainingTTL=s.now+s.ttl-s.start)};let e=0,t=()=>{let s=ie.now();if(this.ttlResolution>0){e=s;let n=setTimeout(()=>e=0,this.ttlResolution);n.unref&&n.unref()}return s};this.getRemainingTTL=s=>{let n=this.keyMap.get(s);return n===void 0?0:this.ttls[n]===0||this.starts[n]===0?1/0:this.starts[n]+this.ttls[n]-(e||t())},this.isStale=s=>this.ttls[s]!==0&&this.starts[s]!==0&&(e||t())-this.starts[s]>this.ttls[s]}updateItemAge(e){}statusTTL(e,t){}setItemTTL(e,t,s){}isStale(e){return!1}initializeSizeTracking(){this.calculatedSize=0,this.sizes=new V(this.max),this.removeItemSize=e=>{this.calculatedSize-=this.sizes[e],this.sizes[e]=0},this.requireSize=(e,t,s,n)=>{if(this.isBackgroundFetch(t))return 0;if(!M(s))if(n){if(typeof n!="function")throw new TypeError("sizeCalculation must be a function");if(s=n(t,e),!M(s))throw new TypeError("sizeCalculation return invalid (expect positive integer)")}else throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");return s},this.addItemSize=(e,t,s)=>{if(this.sizes[e]=t,this.maxSize){let n=this.maxSize-this.sizes[e];for(;this.calculatedSize>n;)this.evict(!0)}this.calculatedSize+=this.sizes[e],s&&(s.entrySize=t,s.totalCalculatedSize=this.calculatedSize)}}removeItemSize(e){}addItemSize(e,t){}requireSize(e,t,s,n){if(s||n)throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache")}*indexes({allowStale:e=this.allowStale}={}){if(this.size)for(let t=this.tail;!(!this.isValidIndex(t)||((e||!this.isStale(t))&&(yield t),t===this.head));)t=this.prev[t]}*rindexes({allowStale:e=this.allowStale}={}){if(this.size)for(let t=this.head;!(!this.isValidIndex(t)||((e||!this.isStale(t))&&(yield t),t===this.tail));)t=this.next[t]}isValidIndex(e){return e!==void 0&&this.keyMap.get(this.keyList[e])===e}*entries(){for(let e of this.indexes())this.valList[e]!==void 0&&this.keyList[e]!==void 0&&!this.isBackgroundFetch(this.valList[e])&&(yield[this.keyList[e],this.valList[e]])}*rentries(){for(let e of this.rindexes())this.valList[e]!==void 0&&this.keyList[e]!==void 0&&!this.isBackgroundFetch(this.valList[e])&&(yield[this.keyList[e],this.valList[e]])}*keys(){for(let e of this.indexes())this.keyList[e]!==void 0&&!this.isBackgroundFetch(this.valList[e])&&(yield this.keyList[e])}*rkeys(){for(let e of this.rindexes())this.keyList[e]!==void 0&&!this.isBackgroundFetch(this.valList[e])&&(yield this.keyList[e])}*values(){for(let e of this.indexes())this.valList[e]!==void 0&&!this.isBackgroundFetch(this.valList[e])&&(yield this.valList[e])}*rvalues(){for(let e of this.rindexes())this.valList[e]!==void 0&&!this.isBackgroundFetch(this.valList[e])&&(yield this.valList[e])}[Symbol.iterator](){return this.entries()}find(e,t){for(let s of this.indexes()){let n=this.valList[s],r=this.isBackgroundFetch(n)?n.__staleWhileFetching:n;if(r!==void 0&&e(r,this.keyList[s],this))return this.get(this.keyList[s],t)}}forEach(e,t=this){for(let s of this.indexes()){let n=this.valList[s],r=this.isBackgroundFetch(n)?n.__staleWhileFetching:n;r!==void 0&&e.call(t,r,this.keyList[s],this)}}rforEach(e,t=this){for(let s of this.rindexes()){let n=this.valList[s],r=this.isBackgroundFetch(n)?n.__staleWhileFetching:n;r!==void 0&&e.call(t,r,this.keyList[s],this)}}get prune(){return be("prune","purgeStale"),this.purgeStale}purgeStale(){let e=!1;for(let t of this.rindexes({allowStale:!0}))this.isStale(t)&&(this.delete(this.keyList[t]),e=!0);return e}dump(){let e=[];for(let t of this.indexes({allowStale:!0})){let s=this.keyList[t],n=this.valList[t],r=this.isBackgroundFetch(n)?n.__staleWhileFetching:n;if(r===void 0)continue;let o={value:r};if(this.ttls){o.ttl=this.ttls[t];let a=ie.now()-this.starts[t];o.start=Math.floor(Date.now()-a)}this.sizes&&(o.size=this.sizes[t]),e.unshift([s,o])}return e}load(e){this.clear();for(let[t,s]of e){if(s.start){let n=Date.now()-s.start;s.start=ie.now()-n}this.set(t,s.value,s)}}dispose(e,t,s){}set(e,t,{ttl:s=this.ttl,start:n,noDisposeOnSet:r=this.noDisposeOnSet,size:o=0,sizeCalculation:a=this.sizeCalculation,noUpdateTTL:f=this.noUpdateTTL,status:c}={}){if(o=this.requireSize(e,t,o,a),this.maxEntrySize&&o>this.maxEntrySize)return c&&(c.set="miss",c.maxEntrySizeExceeded=!0),this.delete(e),this;let l=this.size===0?void 0:this.keyMap.get(e);if(l===void 0)l=this.newIndex(),this.keyList[l]=e,this.valList[l]=t,this.keyMap.set(e,l),this.next[this.tail]=l,this.prev[l]=this.tail,this.tail=l,this.size++,this.addItemSize(l,o,c),c&&(c.set="add"),f=!1;else{this.moveToTail(l);let g=this.valList[l];if(t!==g){if(this.isBackgroundFetch(g)?g.__abortController.abort(new Error("replaced")):r||(this.dispose(g,e,"set"),this.disposeAfter&&this.disposed.push([g,e,"set"])),this.removeItemSize(l),this.valList[l]=t,this.addItemSize(l,o,c),c){c.set="replace";let h=g&&this.isBackgroundFetch(g)?g.__staleWhileFetching:g;h!==void 0&&(c.oldValue=h)}}else c&&(c.set="update")}if(s!==0&&this.ttl===0&&!this.ttls&&this.initializeTTLTracking(),f||this.setItemTTL(l,s,n),this.statusTTL(c,l),this.disposeAfter)for(;this.disposed.length;)this.disposeAfter(...this.disposed.shift());return this}newIndex(){return this.size===0?this.tail:this.size===this.max&&this.max!==0?this.evict(!1):this.free.length!==0?this.free.pop():this.initialFill++}pop(){if(this.size){let e=this.valList[this.head];return this.evict(!0),e}}evict(e){let t=this.head,s=this.keyList[t],n=this.valList[t];return this.isBackgroundFetch(n)?n.__abortController.abort(new Error("evicted")):(this.dispose(n,s,"evict"),this.disposeAfter&&this.disposed.push([n,s,"evict"])),this.removeItemSize(t),e&&(this.keyList[t]=null,this.valList[t]=null,this.free.push(t)),this.head=this.next[t],this.keyMap.delete(s),this.size--,t}has(e,{updateAgeOnHas:t=this.updateAgeOnHas,status:s}={}){let n=this.keyMap.get(e);if(n!==void 0)if(this.isStale(n))s&&(s.has="stale",this.statusTTL(s,n));else return t&&this.updateItemAge(n),s&&(s.has="hit"),this.statusTTL(s,n),!0;else s&&(s.has="miss");return!1}peek(e,{allowStale:t=this.allowStale}={}){let s=this.keyMap.get(e);if(s!==void 0&&(t||!this.isStale(s))){let n=this.valList[s];return this.isBackgroundFetch(n)?n.__staleWhileFetching:n}}backgroundFetch(e,t,s,n){let r=t===void 0?void 0:this.valList[t];if(this.isBackgroundFetch(r))return r;let o=new ce;s.signal&&s.signal.addEventListener("abort",()=>o.abort(s.signal.reason));let a={signal:o.signal,options:s,context:n},f=(p,y=!1)=>{let{aborted:d}=o.signal,u=s.ignoreFetchAbort&&p!==void 0;return s.status&&(d&&!y?(s.status.fetchAborted=!0,s.status.fetchError=o.signal.reason,u&&(s.status.fetchAbortIgnored=!0)):s.status.fetchResolved=!0),d&&!u&&!y?l(o.signal.reason):(this.valList[t]===h&&(p===void 0?h.__staleWhileFetching?this.valList[t]=h.__staleWhileFetching:this.delete(e):(s.status&&(s.status.fetchUpdated=!0),this.set(e,p,a.options))),p)},c=p=>(s.status&&(s.status.fetchRejected=!0,s.status.fetchError=p),l(p)),l=p=>{let{aborted:y}=o.signal,d=y&&s.allowStaleOnFetchAbort,u=d||s.allowStaleOnFetchRejection,P=u||s.noDeleteOnFetchRejection;if(this.valList[t]===h&&(!P||h.__staleWhileFetching===void 0?this.delete(e):d||(this.valList[t]=h.__staleWhileFetching)),u)return s.status&&h.__staleWhileFetching!==void 0&&(s.status.returnedStale=!0),h.__staleWhileFetching;if(h.__returned===h)throw p},g=(p,y)=>{this.fetchMethod(e,r,a).then(d=>p(d),y),o.signal.addEventListener("abort",()=>{(!s.ignoreFetchAbort||s.allowStaleOnFetchAbort)&&(p(),s.allowStaleOnFetchAbort&&(p=d=>f(d,!0)))})};s.status&&(s.status.fetchDispatched=!0);let h=new Promise(g).then(f,c);return h.__abortController=o,h.__staleWhileFetching=r,h.__returned=null,t===void 0?(this.set(e,h,{...a.options,status:void 0}),t=this.keyMap.get(e)):this.valList[t]=h,h}isBackgroundFetch(e){return e&&typeof e=="object"&&typeof e.then=="function"&&Object.prototype.hasOwnProperty.call(e,"__staleWhileFetching")&&Object.prototype.hasOwnProperty.call(e,"__returned")&&(e.__returned===e||e.__returned===null)}async fetch(e,{allowStale:t=this.allowStale,updateAgeOnGet:s=this.updateAgeOnGet,noDeleteOnStaleGet:n=this.noDeleteOnStaleGet,ttl:r=this.ttl,noDisposeOnSet:o=this.noDisposeOnSet,size:a=0,sizeCalculation:f=this.sizeCalculation,noUpdateTTL:c=this.noUpdateTTL,noDeleteOnFetchRejection:l=this.noDeleteOnFetchRejection,allowStaleOnFetchRejection:g=this.allowStaleOnFetchRejection,ignoreFetchAbort:h=this.ignoreFetchAbort,allowStaleOnFetchAbort:p=this.allowStaleOnFetchAbort,fetchContext:y=this.fetchContext,forceRefresh:d=!1,status:u,signal:P}={}){if(!this.fetchMethod)return u&&(u.fetch="get"),this.get(e,{allowStale:t,updateAgeOnGet:s,noDeleteOnStaleGet:n,status:u});let F={allowStale:t,updateAgeOnGet:s,noDeleteOnStaleGet:n,ttl:r,noDisposeOnSet:o,size:a,sizeCalculation:f,noUpdateTTL:c,noDeleteOnFetchRejection:l,allowStaleOnFetchRejection:g,allowStaleOnFetchAbort:p,ignoreFetchAbort:h,status:u,signal:P},k=this.keyMap.get(e);if(k===void 0){u&&(u.fetch="miss");let b=this.backgroundFetch(e,k,F,y);return b.__returned=b}else{let b=this.valList[k];if(this.isBackgroundFetch(b)){let ee=t&&b.__staleWhileFetching!==void 0;return u&&(u.fetch="inflight",ee&&(u.returnedStale=!0)),ee?b.__staleWhileFetching:b.__returned=b}let D=this.isStale(k);if(!d&&!D)return u&&(u.fetch="hit"),this.moveToTail(k),s&&this.updateItemAge(k),this.statusTTL(u,k),b;let E=this.backgroundFetch(e,k,F,y),I=E.__staleWhileFetching!==void 0,R=I&&t;return u&&(u.fetch=I&&D?"stale":"refresh",R&&D&&(u.returnedStale=!0)),R?E.__staleWhileFetching:E.__returned=E}}get(e,{allowStale:t=this.allowStale,updateAgeOnGet:s=this.updateAgeOnGet,noDeleteOnStaleGet:n=this.noDeleteOnStaleGet,status:r}={}){let o=this.keyMap.get(e);if(o!==void 0){let a=this.valList[o],f=this.isBackgroundFetch(a);return this.statusTTL(r,o),this.isStale(o)?(r&&(r.get="stale"),f?(r&&(r.returnedStale=t&&a.__staleWhileFetching!==void 0),t?a.__staleWhileFetching:void 0):(n||this.delete(e),r&&(r.returnedStale=t),t?a:void 0)):(r&&(r.get="hit"),f?a.__staleWhileFetching:(this.moveToTail(o),s&&this.updateItemAge(o),a))}else r&&(r.get="miss")}connect(e,t){this.prev[t]=e,this.next[e]=t}moveToTail(e){e!==this.tail&&(e===this.head?this.head=this.next[e]:this.connect(this.prev[e],this.next[e]),this.connect(this.tail,e),this.tail=e)}get del(){return be("del","delete"),this.delete}delete(e){let t=!1;if(this.size!==0){let s=this.keyMap.get(e);if(s!==void 0)if(t=!0,this.size===1)this.clear();else{this.removeItemSize(s);let n=this.valList[s];this.isBackgroundFetch(n)?n.__abortController.abort(new Error("deleted")):(this.dispose(n,e,"delete"),this.disposeAfter&&this.disposed.push([n,e,"delete"])),this.keyMap.delete(e),this.keyList[s]=null,this.valList[s]=null,s===this.tail?this.tail=this.prev[s]:s===this.head?this.head=this.next[s]:(this.next[this.prev[s]]=this.next[s],this.prev[this.next[s]]=this.prev[s]),this.size--,this.free.push(s)}}if(this.disposed)for(;this.disposed.length;)this.disposeAfter(...this.disposed.shift());return t}clear(){for(let e of this.rindexes({allowStale:!0})){let t=this.valList[e];if(this.isBackgroundFetch(t))t.__abortController.abort(new Error("deleted"));else{let s=this.keyList[e];this.dispose(t,s,"delete"),this.disposeAfter&&this.disposed.push([t,s,"delete"])}}if(this.keyMap.clear(),this.valList.fill(null),this.keyList.fill(null),this.ttls&&(this.ttls.fill(0),this.starts.fill(0)),this.sizes&&this.sizes.fill(0),this.head=0,this.tail=0,this.initialFill=1,this.free.length=0,this.calculatedSize=0,this.size=0,this.disposed)for(;this.disposed.length;)this.disposeAfter(...this.disposed.shift())}get reset(){return be("reset","clear"),this.clear}get length(){return yt("length","size"),this.size}static get AbortController(){return ce}static get AbortSignal(){return Ce}};We.exports=z});var Ne=te((Gt,Re)=>{"use strict";var m=(...i)=>i.every(e=>e)?i.join(""):"",v=i=>i?encodeURIComponent(i):"",De=i=>i.toLowerCase().replace(/^\W+|\/|\W+$/g,"").replace(/\W+/g,"-"),bt={sshtemplate:({domain:i,user:e,project:t,committish:s})=>`git@${i}:${e}/${t}.git${m("#",s)}`,sshurltemplate:({domain:i,user:e,project:t,committish:s})=>`git+ssh://git@${i}/${e}/${t}.git${m("#",s)}`,edittemplate:({domain:i,user:e,project:t,committish:s,editpath:n,path:r})=>`https://${i}/${e}/${t}${m("/",n,"/",v(s||"HEAD"),"/",r)}`,browsetemplate:({domain:i,user:e,project:t,committish:s,treepath:n})=>`https://${i}/${e}/${t}${m("/",n,"/",v(s))}`,browsetreetemplate:({domain:i,user:e,project:t,committish:s,treepath:n,path:r,fragment:o,hashformat:a})=>`https://${i}/${e}/${t}/${n}/${v(s||"HEAD")}/${r}${m("#",a(o||""))}`,browseblobtemplate:({domain:i,user:e,project:t,committish:s,blobpath:n,path:r,fragment:o,hashformat:a})=>`https://${i}/${e}/${t}/${n}/${v(s||"HEAD")}/${r}${m("#",a(o||""))}`,docstemplate:({domain:i,user:e,project:t,treepath:s,committish:n})=>`https://${i}/${e}/${t}${m("/",s,"/",v(n))}#readme`,httpstemplate:({auth:i,domain:e,user:t,project:s,committish:n})=>`git+https://${m(i,"@")}${e}/${t}/${s}.git${m("#",n)}`,filetemplate:({domain:i,user:e,project:t,committish:s,path:n})=>`https://${i}/${e}/${t}/raw/${v(s||"HEAD")}/${n}`,shortcuttemplate:({type:i,user:e,project:t,committish:s})=>`${i}:${e}/${t}${m("#",s)}`,pathtemplate:({user:i,project:e,committish:t})=>`${i}/${e}${m("#",t)}`,bugstemplate:({domain:i,user:e,project:t})=>`https://${i}/${e}/${t}/issues`,hashformat:De},U={};U.github={protocols:["git:","http:","git+ssh:","git+https:","ssh:","https:"],domain:"github.com",treepath:"tree",blobpath:"blob",editpath:"edit",filetemplate:({auth:i,user:e,project:t,committish:s,path:n})=>`https://${m(i,"@")}raw.githubusercontent.com/${e}/${t}/${v(s||"HEAD")}/${n}`,gittemplate:({auth:i,domain:e,user:t,project:s,committish:n})=>`git://${m(i,"@")}${e}/${t}/${s}.git${m("#",n)}`,tarballtemplate:({domain:i,user:e,project:t,committish:s})=>`https://codeload.${i}/${e}/${t}/tar.gz/${v(s||"HEAD")}`,extract:i=>{let[,e,t,s,n]=i.pathname.split("/",5);if(!(s&&s!=="tree")&&(s||(n=i.hash.slice(1)),t&&t.endsWith(".git")&&(t=t.slice(0,-4)),!(!e||!t)))return{user:e,project:t,committish:n}}};U.bitbucket={protocols:["git+ssh:","git+https:","ssh:","https:"],domain:"bitbucket.org",treepath:"src",blobpath:"src",editpath:"?mode=edit",edittemplate:({domain:i,user:e,project:t,committish:s,treepath:n,path:r,editpath:o})=>`https://${i}/${e}/${t}${m("/",n,"/",v(s||"HEAD"),"/",r,o)}`,tarballtemplate:({domain:i,user:e,project:t,committish:s})=>`https://${i}/${e}/${t}/get/${v(s||"HEAD")}.tar.gz`,extract:i=>{let[,e,t,s]=i.pathname.split("/",4);if(!["get"].includes(s)&&(t&&t.endsWith(".git")&&(t=t.slice(0,-4)),!(!e||!t)))return{user:e,project:t,committish:i.hash.slice(1)}}};U.gitlab={protocols:["git+ssh:","git+https:","ssh:","https:"],domain:"gitlab.com",treepath:"tree",blobpath:"tree",editpath:"-/edit",httpstemplate:({auth:i,domain:e,user:t,project:s,committish:n})=>`git+https://${m(i,"@")}${e}/${t}/${s}.git${m("#",n)}`,tarballtemplate:({domain:i,user:e,project:t,committish:s})=>`https://${i}/${e}/${t}/repository/archive.tar.gz?ref=${v(s||"HEAD")}`,extract:i=>{let e=i.pathname.slice(1);if(e.includes("/-/")||e.includes("/archive.tar.gz"))return;let t=e.split("/"),s=t.pop();s.endsWith(".git")&&(s=s.slice(0,-4));let n=t.join("/");if(!(!n||!s))return{user:n,project:s,committish:i.hash.slice(1)}}};U.gist={protocols:["git:","git+ssh:","git+https:","ssh:","https:"],domain:"gist.github.com",editpath:"edit",sshtemplate:({domain:i,project:e,committish:t})=>`git@${i}:${e}.git${m("#",t)}`,sshurltemplate:({domain:i,project:e,committish:t})=>`git+ssh://git@${i}/${e}.git${m("#",t)}`,edittemplate:({domain:i,user:e,project:t,committish:s,editpath:n})=>`https://${i}/${e}/${t}${m("/",v(s))}/${n}`,browsetemplate:({domain:i,project:e,committish:t})=>`https://${i}/${e}${m("/",v(t))}`,browsetreetemplate:({domain:i,project:e,committish:t,path:s,hashformat:n})=>`https://${i}/${e}${m("/",v(t))}${m("#",n(s))}`,browseblobtemplate:({domain:i,project:e,committish:t,path:s,hashformat:n})=>`https://${i}/${e}${m("/",v(t))}${m("#",n(s))}`,docstemplate:({domain:i,project:e,committish:t})=>`https://${i}/${e}${m("/",v(t))}`,httpstemplate:({domain:i,project:e,committish:t})=>`git+https://${i}/${e}.git${m("#",t)}`,filetemplate:({user:i,project:e,committish:t,path:s})=>`https://gist.githubusercontent.com/${i}/${e}/raw${m("/",v(t))}/${s}`,shortcuttemplate:({type:i,project:e,committish:t})=>`${i}:${e}${m("#",t)}`,pathtemplate:({project:i,committish:e})=>`${i}${m("#",e)}`,bugstemplate:({domain:i,project:e})=>`https://${i}/${e}`,gittemplate:({domain:i,project:e,committish:t})=>`git://${i}/${e}.git${m("#",t)}`,tarballtemplate:({project:i,committish:e})=>`https://codeload.github.com/gist/${i}/tar.gz/${v(e||"HEAD")}`,extract:i=>{let[,e,t,s]=i.pathname.split("/",4);if(s!=="raw"){if(!t){if(!e)return;t=e,e=null}return t.endsWith(".git")&&(t=t.slice(0,-4)),{user:e,project:t,committish:i.hash.slice(1)}}},hashformat:function(i){return i&&"file-"+De(i)}};U.sourcehut={protocols:["git+ssh:","https:"],domain:"git.sr.ht",treepath:"tree",blobpath:"tree",filetemplate:({domain:i,user:e,project:t,committish:s,path:n})=>`https://${i}/${e}/${t}/blob/${v(s)||"HEAD"}/${n}`,httpstemplate:({domain:i,user:e,project:t,committish:s})=>`https://${i}/${e}/${t}.git${m("#",s)}`,tarballtemplate:({domain:i,user:e,project:t,committish:s})=>`https://${i}/${e}/${t}/archive/${v(s)||"HEAD"}.tar.gz`,bugstemplate:({user:i,project:e})=>`https://todo.sr.ht/${i}/${e}`,extract:i=>{let[,e,t,s]=i.pathname.split("/",4);if(!["archive"].includes(s)&&(t&&t.endsWith(".git")&&(t=t.slice(0,-4)),!(!e||!t)))return{user:e,project:t,committish:i.hash.slice(1)}}};for(let[i,e]of Object.entries(U))U[i]=Object.assign({},bt,e);Re.exports=U});var xe=te((Jt,Be)=>{var $t=$("url"),Se=(i,e,t)=>{let s=i.indexOf(t);return i.lastIndexOf(e,s>-1?s:1/0)},He=i=>{try{return new $t.URL(i)}catch{}},kt=(i,e)=>{let t=i.indexOf(":"),s=i.slice(0,t+1);if(Object.prototype.hasOwnProperty.call(e,s))return i;let n=i.indexOf("@");return n>-1?n>t?`git+ssh://${i}`:i:i.indexOf("//")===t+1?i:`${i.slice(0,t+1)}//${i.slice(t+1)}`},vt=i=>{let e=Se(i,"@","#"),t=Se(i,":","#");return t>e&&(i=i.slice(0,t)+"/"+i.slice(t+1)),Se(i,":","#")===-1&&i.indexOf("//")===-1&&(i=`git+ssh://${i}`),i};Be.exports=(i,e)=>{let t=e?kt(i,e):i;return He(t)||He(vt(t))}});var qe=te((Kt,Ve)=>{"use strict";var St=xe(),xt=i=>{let e=i.indexOf("#"),t=i.indexOf("/"),s=i.indexOf("/",t+1),n=i.indexOf(":"),r=/\s/.exec(i),o=i.indexOf("@"),a=!r||e>-1&&r.index>e,f=o===-1||e>-1&&o>e,c=n===-1||e>-1&&n>e,l=s===-1||e>-1&&s>e,g=t>0,h=e>-1?i[e-1]!=="/":!i.endsWith("/"),p=!i.startsWith(".");return a&&g&&h&&p&&f&&c&&l};Ve.exports=(i,e,{gitHosts:t,protocols:s})=>{if(!i)return;let n=xt(i)?`github:${i}`:i,r=St(n,s);if(!r)return;let o=t.byShortcut[r.protocol],a=t.byDomain[r.hostname.startsWith("www.")?r.hostname.slice(4):r.hostname],f=o||a;if(!f)return;let c=t[o||a],l=null;s[r.protocol]?.auth&&(r.username||r.password)&&(l=`${r.username}${r.password?":"+r.password:""}`);let g=null,h=null,p=null,y=null;try{if(o){let d=r.pathname.startsWith("/")?r.pathname.slice(1):r.pathname,u=d.indexOf("@");u>-1&&(d=d.slice(u+1));let P=d.lastIndexOf("/");P>-1?(h=decodeURIComponent(d.slice(0,P)),h||(h=null),p=decodeURIComponent(d.slice(P+1))):p=decodeURIComponent(d),p.endsWith(".git")&&(p=p.slice(0,-4)),r.hash&&(g=decodeURIComponent(r.hash.slice(1))),y="shortcut"}else{if(!c.protocols.includes(r.protocol))return;let d=c.extract(r);if(!d)return;h=d.user&&decodeURIComponent(d.user),p=decodeURIComponent(d.project),g=decodeURIComponent(d.committish),y=s[r.protocol]?.name||r.protocol.slice(0,-1)}}catch(d){if(d instanceof URIError)return;throw d}return[f,h,l,p,g,y,e]}});var Ge=te((Xt,Ye)=>{"use strict";var Lt=je(),Pt=Ne(),Tt=qe(),At=xe(),Le=new Lt({max:1e3}),W,se,S,L,_=class{constructor(e,t,s,n,r,o,a={}){ae(this,S);Object.assign(this,C(_,W)[e],{type:e,user:t,auth:s,project:n,committish:r,default:o,opts:a})}static addHost(e,t){C(_,W)[e]=t,C(_,W).byDomain[t.domain]=e,C(_,W).byShortcut[`${e}:`]=e,C(_,se)[`${e}:`]={name:e}}static fromUrl(e,t){if(typeof e!="string")return;let s=e+JSON.stringify(t||{});if(!Le.has(s)){let n=Tt(e,t,{gitHosts:C(_,W),protocols:C(_,se)});Le.set(s,n?new _(...n):void 0)}return Le.get(s)}static parseUrl(e){return At(e)}hash(){return this.committish?`#${this.committish}`:""}ssh(e){return x(this,S,L).call(this,this.sshtemplate,e)}sshurl(e){return x(this,S,L).call(this,this.sshurltemplate,e)}browse(e,...t){return typeof e!="string"?x(this,S,L).call(this,this.browsetemplate,e):typeof t[0]!="string"?x(this,S,L).call(this,this.browsetreetemplate,{...t[0],path:e}):x(this,S,L).call(this,this.browsetreetemplate,{...t[1],fragment:t[0],path:e})}browseFile(e,...t){return typeof t[0]!="string"?x(this,S,L).call(this,this.browseblobtemplate,{...t[0],path:e}):x(this,S,L).call(this,this.browseblobtemplate,{...t[1],fragment:t[0],path:e})}docs(e){return x(this,S,L).call(this,this.docstemplate,e)}bugs(e){return x(this,S,L).call(this,this.bugstemplate,e)}https(e){return x(this,S,L).call(this,this.httpstemplate,e)}git(e){return x(this,S,L).call(this,this.gittemplate,e)}shortcut(e){return x(this,S,L).call(this,this.shortcuttemplate,e)}path(e){return x(this,S,L).call(this,this.pathtemplate,e)}tarball(e){return x(this,S,L).call(this,this.tarballtemplate,{...e,noCommittish:!1})}file(e,t){return x(this,S,L).call(this,this.filetemplate,{...t,path:e})}edit(e,t){return x(this,S,L).call(this,this.edittemplate,{...t,path:e})}getDefaultRepresentation(){return this.default}toString(e){return this.default&&typeof this[this.default]=="function"?this[this.default](e):this.sshurl(e)}},q=_;W=new WeakMap,se=new WeakMap,S=new WeakSet,L=function(e,t){if(typeof e!="function")return null;let s={...this,...this.opts,...t};s.path||(s.path=""),s.path.startsWith("/")&&(s.path=s.path.slice(1)),s.noCommittish&&(s.committish=null);let n=e(s);return s.noGitPlus&&n.startsWith("git+")?n.slice(4):n},ae(q,W,{byShortcut:{},byDomain:{}}),ae(q,se,{"git+ssh:":{name:"sshurl"},"ssh:":{name:"sshurl"},"git+https:":{name:"https",auth:!0},"git:":{auth:!0},"http:":{auth:!0},"https:":{auth:!0},"git+http:":{auth:!0}});for(let[i,e]of Object.entries(Pt))q.addHost(i,e);Ye.exports=q});var Nt={};oe(Nt,{default:()=>Rt});var rt=$("@yarnpkg/cli"),K=$("@yarnpkg/core"),O=$("clipanion");var w=$("@yarnpkg/core"),T=$("@yarnpkg/fslib"),et=pt(Ge());var Ae={};oe(Ae,{getFs:()=>zt,getPackagePath:()=>Ft});var Je=$("@yarnpkg/plugin-pnp"),he=$("@yarnpkg/core"),Ke=$("@yarnpkg/fslib"),de=$("@yarnpkg/libzip");var Y=()=>({os:[process.platform],cpu:[process.arch],libc:[]});var Ft=async(i,e)=>{if(_t(i),!he.structUtils.isPackageCompatible(e,Y()))return null;let t=he.structUtils.convertPackageToLocator(e),s={name:he.structUtils.stringifyIdent(t),reference:t.reference},n=Pe.getPackageInformation(s);if(!n)return null;let{packageLocation:r}=n;return r},Pe,_t=i=>{Pe||(Pe=module.require((0,Je.getPnpPath)(i).cjs))},zt=()=>(Ot(),Te),Te,Ot=()=>{Te||(Te=new Ke.VirtualFS({baseFs:new de.ZipOpenFS({libzip:(0,de.getLibzipSync)(),readOnlyArchives:!0})}))};var Fe={};oe(Fe,{_getYarnStateAliases:()=>Ze,getFs:()=>Ct,getPackagePath:()=>Et});var N=$("@yarnpkg/core"),Xe=$("@yarnpkg/parsers"),j=$("@yarnpkg/fslib");var Et=async(i,e)=>{if(await It(i),!N.structUtils.isPackageCompatible(e,Y()))return null;let t=N.structUtils.convertPackageToLocator(e),s=N.structUtils.stringifyLocator(t),n=fe[s]||Qe[s];if(!n)return null;let r=n.locations[0];return r?j.ppath.join(i.cwd,r):i.cwd},fe,Qe,It=async i=>{if(!fe){let e=j.ppath.join(i.configuration.projectCwd,j.Filename.nodeModules,".yarn-state.yml");fe=(0,Xe.parseSyml)(await j.xfs.readFilePromise(e,"utf8")),Qe=Ze(fe)}},Ct=()=>j.xfs,Ze=i=>Object.entries(i).reduce((e,[t,s])=>{if(!s.aliases)return e;let n=N.structUtils.parseLocator(t);for(let r of s.aliases){let o=N.structUtils.makeLocator(n,r),a=N.structUtils.stringifyLocator(o);e[a]=s}return e},{});var _e={};oe(_e,{getFs:()=>Ut,getPackagePath:()=>Mt});var ne=$("@yarnpkg/core"),H=$("@yarnpkg/fslib");var Mt=async(i,e)=>{if(!ne.structUtils.isPackageCompatible(e,Y()))return null;let t=ne.structUtils.convertPackageToLocator(e),s=ne.structUtils.slugifyLocator(t),n=ne.structUtils.stringifyIdent(t),r=i.tryWorkspaceByLocator(t);return r?r.cwd:H.ppath.join(i.configuration.projectCwd,H.Filename.nodeModules,".store",s,H.Filename.nodeModules,n)},Ut=()=>H.xfs;var ze=i=>{switch(i){case"pnp":return Ae;case"node-modules":return Fe;case"pnpm":return _e;default:throw new Error("Unsupported linker")}};var ki=T.npath.basename(__dirname)==="@yarnpkg"?T.ppath.join(T.npath.toPortablePath(__dirname),"../.."):T.ppath.join(T.npath.toPortablePath(__dirname),".."),ue=async(i,e,t,s,n)=>{let r={},o={children:r},a=await tt(i,t,s),f=ze(i.configuration.get("nodeLinker")),c=f.getFs();for(let[l,g]of a.entries()){let h=await f.getPackagePath(i,g);if(h===null)continue;let p=JSON.parse(await c.readFilePromise(T.ppath.join(h,T.Filename.manifest),"utf8")),{license:y,url:d,vendorName:u,vendorUrl:P}=jt(p);r[y]||(r[y]={value:w.formatUtils.tuple(w.formatUtils.Type.NO_HINT,y),children:{}});let F=w.structUtils.convertPackageToLocator(g),k=w.formatUtils.tuple(w.formatUtils.Type.DEPENDENT,{locator:F,descriptor:l}),b=n?{}:{...d?{url:{value:w.formatUtils.tuple(w.formatUtils.Type.NO_HINT,Oe("URL",d,e))}}:{},...u?{vendorName:{value:w.formatUtils.tuple(w.formatUtils.Type.NO_HINT,Oe("VendorName",u,e))}}:{},...P?{vendorUrl:{value:w.formatUtils.tuple(w.formatUtils.Type.NO_HINT,Oe("VendorUrl",P,e))}}:{}},D={value:k,children:b},E=w.structUtils.stringifyLocator(F),I=r[y];if(I){let R=I.children;R[E]=D}}return o},tt=async(i,e,t)=>{let s=new Map,n;if(e){if(t){for(let f of i.workspaces)f.manifest.devDependencies.clear();let a=await w.Cache.find(i.configuration);await i.resolveEverything({report:new w.ThrowReport,cache:a})}n=i.storedDescriptors.values()}else n=i.workspaces.flatMap(a=>{let f=[a.anchoredDescriptor];for(let[c,l]of a.anchoredPackage.dependencies.entries())t&&a.manifest.devDependencies.has(c)||f.push(l);return f});let r=w.miscUtils.sortMap(n,[a=>w.structUtils.stringifyIdent(a),a=>w.structUtils.isVirtualDescriptor(a)?"0":"1",a=>a.range]),o=new Set;for(let a of r.values()){let f=i.storedResolutions.get(a.descriptorHash);if(!f)continue;let c=i.storedPackages.get(f);if(!c)continue;let{descriptorHash:l}=w.structUtils.isVirtualDescriptor(a)?w.structUtils.devirtualizeDescriptor(a):a;o.has(l)||(o.add(l),s.set(a,c))}return s},G=async(i,e,t,s)=>{if(!e.length)return;let n=new Set(e.map(o=>i.getWorkspaceByIdent(w.structUtils.parseIdent(o))));if(t)for(let o of n)for(let a of s?["dependencies"]:w.Manifest.hardDependencies)for(let f of o.manifest.getForScope(a).values()){let c=i.tryWorkspaceByDescriptor(f);c!==null&&n.add(c)}for(let o of i.workspaces)n.has(o)||(o.manifest.installConfig=o.manifest.installConfig||{},o.manifest.installConfig.selfReferences=!1,o.manifest.dependencies.clear(),o.manifest.devDependencies.clear(),o.manifest.peerDependencies.clear());i.workspaces=[...n];let r=await w.Cache.find(i.configuration);await i.resolveEverything({report:new w.ThrowReport,cache:r})};function Wt(i){let e={},t=i.match(/^([^(<]+)/);if(t){let r=t[0].trim();r&&(e.name=r)}let s=i.match(/<([^>]+)>/);s&&(e.email=s[1]);let n=i.match(/\(([^)]+)\)/);return n&&(e.url=n[1]),e}var jt=i=>{let{license:e,licenses:t,repository:s,homepage:n,author:r}=i,o=typeof r=="string"?Wt(r):r;return{license:(()=>{if(e)return pe(e);if(t){if(!Array.isArray(t))return pe(t);if(t.length===1)return pe(t[0]);if(t.length>1)return`(${t.map(pe).join(" OR ")})`}return it})(),url:st(s)||n,vendorName:o?.name,vendorUrl:n||o?.url}},it="UNKNOWN",pe=i=>(typeof i!="string"?i.type:i)||it,st=i=>{let e=typeof i=="string"?i:i?.url;if(!e)return e;let t=et.fromUrl(e);return!t||t.getDefaultRepresentation()!=="shortcut"?e:t.https()},Oe=(i,e,t)=>t?e:`${i}: ${e}`,nt=async(i,e,t)=>{let s=await tt(i,e,t),n=ze(i.configuration.get("nodeLinker")),r=n.getFs(),o=new Map;for(let f of s.values()){let c=await n.getPackagePath(i,f);if(c===null)continue;let l=JSON.parse(await r.readFilePromise(T.ppath.join(c,T.Filename.manifest),"utf8")),h=(await r.readdirPromise(c,{withFileTypes:!0})).filter(k=>k.isFile()).map(({name:k})=>k),p=h.find(k=>{let b=k.toLowerCase();return b==="license"||b.startsWith("license.")||b==="unlicense"||b.startsWith("unlicense.")});if(!p)continue;let y=await r.readFilePromise(T.ppath.join(c,p),"utf8"),d=h.find(k=>{let b=k.toLowerCase();return b==="notice"||b.startsWith("notice.")}),u;d&&(u=await r.readFilePromise(T.ppath.join(c,d),"utf8"));let P=u?`${y} + +NOTICE + +${u}`:y,F=o.get(P);F?F.set(l.name,l):o.set(P,new Map([[l.name,l]]))}let a=`THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THE ${String(i.topLevelWorkspace.manifest.raw.name).toUpperCase().replace(/-/g," ")} PRODUCT. + +`;for(let[f,c]of o.entries()){a+=`----- + +`;let l=[],g=[];for(let{name:p,repository:y}of c.values()){l.push(p);let d=st(y);d&&g.push(c.size===1?d:`${d} (${p})`)}let h=[];h.push(`The following software may be included in this product: ${l.join(", ")}.`),g.length>0&&h.push(`A copy of the source code may be downloaded from ${g.join(", ")}.`),h.push("This software contains the following license and notice below:"),a+=`${h.join(" ")} + +`,a+=`${f.trim()} + +`}return a};var J=class extends O.Command{constructor(){super(...arguments);this.recursive=O.Option.Boolean("-R,--recursive",!1,{description:"Include transitive dependencies (dependencies of direct dependencies)"});this.production=O.Option.Boolean("--production",!1,{description:"Exclude development dependencies"});this.json=O.Option.Boolean("--json",!1,{description:"Format output as JSON"});this.excludeMetadata=O.Option.Boolean("--exclude-metadata",!1,{description:"Exclude dependency metadata from output"});this.focus=O.Option.Array("--focus",[],{description:"Focus on one or more workspaces"})}async execute(){let t=await K.Configuration.find(this.context.cwd,this.context.plugins),{project:s,workspace:n}=await K.Project.find(t,this.context.cwd);if(!n)throw new rt.WorkspaceRequiredError(s.cwd,this.context.cwd);await s.restoreInstallState(),await G(s,this.focus,this.recursive,this.production);let r=await ue(s,this.json,this.recursive,this.production,this.excludeMetadata);K.treeUtils.emitTree(r,{configuration:t,stdout:this.context.stdout,json:this.json,separators:1})}};J.paths=[["licenses","list"]],J.usage=O.Command.Usage({description:"display the licenses for all packages in the project",details:` + This command prints the license information for packages in the project. By default, only direct dependencies are listed. + + If \`-R,--recursive\` is set, the listing will include transitive dependencies (dependencies of direct dependencies). + + If \`--production\` is set, the listing will exclude development dependencies. + + If \`--focus\` is passed, the listing will only include dependencies of the specified workspaces. + `,examples:[["List all licenses of direct dependencies","$0 licenses list"],["List all licenses of direct and transitive dependencies","$0 licenses list --recursive"],["List all licenses of production dependencies only","$0 licenses list --production"],["List all licenses for specified workspaces only","$0 licenses list --focus --focus "]]});var ot=$("@yarnpkg/cli"),me=$("@yarnpkg/core"),B=$("clipanion");var X=class extends B.Command{constructor(){super(...arguments);this.recursive=B.Option.Boolean("-R,--recursive",!1,{description:"Include transitive dependencies (dependencies of direct dependencies)"});this.production=B.Option.Boolean("--production",!1,{description:"Exclude development dependencies"});this.focus=B.Option.Array("--focus",[],{description:"Focus on one or more workspaces"})}async execute(){let t=await me.Configuration.find(this.context.cwd,this.context.plugins),{project:s,workspace:n}=await me.Project.find(t,this.context.cwd);if(!n)throw new ot.WorkspaceRequiredError(s.cwd,this.context.cwd);await s.restoreInstallState(),await G(s,this.focus,this.recursive,this.production);let r=await nt(s,this.recursive,this.production);this.context.stdout.write(r)}};X.paths=[["licenses","generate-disclaimer"]],X.usage=B.Command.Usage({description:"display the license disclaimer including all packages in the project",details:` + This command prints the license disclaimer for packages in the project. By default, only direct dependencies are listed. + + If \`-R,--recursive\` is set, the disclaimer will include transitive dependencies (dependencies of direct dependencies). + + If \`--production\` is set, the disclaimer will exclude development dependencies. + + If \`--focus\` is passed, the disclaimer will only include dependencies of the specified workspaces. + `,examples:[["Include licenses of direct dependencies","$0 licenses generate-disclaimer"],["Include licenses of direct and transitive dependencies","$0 licenses generate-disclaimer --recursive"],["Include licenses of production dependencies only","$0 licenses generate-disclaimer --production"],["Include licenses for specified workspaces only","$0 licenses generate-disclaimer --focus --focus "]]});var at=$("@yarnpkg/cli"),Z=$("@yarnpkg/core"),A=$("clipanion");var Q=class extends A.Command{constructor(){super(...arguments);this.recursive=A.Option.Boolean("-R,--recursive",!1,{description:"Include transitive dependencies (dependencies of direct dependencies)"});this.production=A.Option.Boolean("--production",!1,{description:"Exclude development dependencies"});this.json=A.Option.Boolean("--json",!1,{description:"Format output as JSON"});this.excludeMetadata=A.Option.Boolean("--exclude-metadata",!1,{description:"Exclude dependency metadata from output"});this.focus=A.Option.Array("--focus",[],{description:"Focus on one or more workspaces"});this.allowedLicenses=A.Option.Array("--allowed",[],{description:"List of allowed licenses"});this.blockedLicenses=A.Option.Array("--blocked",[],{description:"List of blocked licenses"})}async execute(){if(this.allowedLicenses.length===0==(this.blockedLicenses.length===0))throw new A.UsageError("You must provide at least one of --allowed or --blocked, but not both");let t=await Z.Configuration.find(this.context.cwd,this.context.plugins),{project:s,workspace:n}=await Z.Project.find(t,this.context.cwd);if(!n)throw new at.WorkspaceRequiredError(s.cwd,this.context.cwd);await s.restoreInstallState(),await G(s,this.focus,this.recursive,this.production);let r=await ue(s,this.json,this.recursive,this.production,this.excludeMetadata),o=[];for(let[a,f]of Object.entries(r.children)){if(!f)continue;let c=this.allowedLicenses.length===0||this.allowedLicenses.includes(a),l=this.blockedLicenses.length!==0&&this.blockedLicenses.includes(a);(!c||l)&&o.push(f)}return Z.treeUtils.emitTree({children:o},{configuration:t,stdout:this.context.stdout,json:this.json,separators:1}),o.length>0?1:0}};Q.paths=[["licenses","audit"]],Q.usage=A.Command.Usage({description:"audits the licenses for all packages in the project",details:` + This command audits and prints the license information for packages in the project that violate the rules set. By default, only direct dependencies are audited. + + If \`-R,--recursive\` is set, the auditing will include transitive dependencies (dependencies of direct dependencies). + + If \`--production\` is set, the auditing will exclude development dependencies. + + If \`--focus\` is passed, the auditing will only include dependencies of the specified workspaces. + + If \`--allowed\` is passed, the auditing will accept dependencies with the specified allowed licenses. + + If \`--blocked\` is passed, the auditing will reject dependencies with the specified blocked licenses. + `,examples:[["Audit all licenses of direct dependencies","$0 licenses audit --blocked GPL-3.0"],["Audit all licenses of direct and transitive dependencies","$0 licenses audit --blocked GPL-3.0 --recursive"],["Audit all licenses of production dependencies only","$0 licenses audit --blocked GPL-3.0 --production"],["Audit all licenses for specified workspaces only","$0 licenses audit --blocked GPL-3.0 --focus --focus "]]});var Dt={commands:[J,X,Q]},Rt=Dt;return ut(Nt);})(); +return plugin; +} +}; diff --git a/.yarnrc.yml b/.yarnrc.yml index a5a0620502d..8852de3d25b 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -7,7 +7,7 @@ enableTelemetry: false nodeLinker: node-modules packageExtensions: - 'croact-css-styled@1.1.9': + croact-css-styled@1.1.9: dependencies: croact: 1.0.4 doctrine@3.0.0: @@ -20,13 +20,9 @@ packageExtensions: plugins: - path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs - spec: 'https://mskelton.dev/yarn-outdated/v2' + spec: "https://mskelton.dev/yarn-outdated/v2" + - checksum: 374f735053958b77583ef7b70e56a59540d1d9c681f04b8ab7a600f4325301d2e1fb67c51d34ba75bbbc4e71f8c003fbc52b1f22d7daa30dca655f1a1bf205a1 + path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs + spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.15.0/bundles/@yarnpkg/plugin-licenses.js" yarnPath: .yarn/releases/yarn-4.6.0.cjs -# Uncomment the following lines if you want to use Verdaccio local npm registry. Read more at packages/README.md -#npmScopes: -# grafana: -# npmRegistryServer: http://localhost:4873 -# -#unsafeHttpWhitelist: -# - 'localhost' diff --git a/CHANGELOG.md b/CHANGELOG.md index 7239f14008c..e0a269eb40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,334 @@ + + +# 12.0.1 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105101](https://github.com/grafana/grafana/pull/105101), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/openfga/openfga from v1.8.6 to v1.8.12 [#105368](https://github.com/grafana/grafana/pull/105368), [@macabu](https://github.com/macabu) +- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105249](https://github.com/grafana/grafana/pull/105249), [@macabu](https://github.com/macabu) +- **K8s:** Dashboards: Add fine grained access control checks to /apis [#104419](https://github.com/grafana/grafana/pull/104419), [@stephaniehingtgen](https://github.com/stephaniehingtgen) + +### Bug fixes + +- **Dashboard:** Fix Panel Explore link subpath duplication (#104952) [#105056](https://github.com/grafana/grafana/pull/105056), [@axelavargas](https://github.com/axelavargas) +- **Dashboard:** Fixes issue with row repeats and first row [#104469](https://github.com/grafana/grafana/pull/104469), [@torkelo](https://github.com/torkelo) +- **Graphite:** Ensure template variables are interpolated correctly [#105389](https://github.com/grafana/grafana/pull/105389), [@aangelisc](https://github.com/aangelisc) +- **Graphite:** Fix Graphite series interpolation [#104516](https://github.com/grafana/grafana/pull/104516), [@aangelisc](https://github.com/aangelisc) +- **InfluxDB:** Fix nested variable interpolation [#104176](https://github.com/grafana/grafana/pull/104176), [@aangelisc](https://github.com/aangelisc) +- **MetricsDrilldown:** Restore link to Metrics Drilldown from Explore [#104073](https://github.com/grafana/grafana/pull/104073), [@NWRichmond](https://github.com/NWRichmond) +- **NestedFolderPicker:** Fix scroll jumps back to top [#105769](https://github.com/grafana/grafana/pull/105769), [@samsch](https://github.com/samsch) +- **Preferences:** Disable the save button whilst saving preferences [#105612](https://github.com/grafana/grafana/pull/105612), [@ashharrison90](https://github.com/ashharrison90) +- **Prometheus:** Fix semver import path [#104945](https://github.com/grafana/grafana/pull/104945), [@jackw](https://github.com/jackw) +- **Themes:** Prevent duplicated API call in drawer [#105611](https://github.com/grafana/grafana/pull/105611), [@ashharrison90](https://github.com/ashharrison90) +- **XYChart:** Coerce threshold steps to numbers [#104492](https://github.com/grafana/grafana/pull/104492), [@leeoniya](https://github.com/leeoniya) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 11.6.2 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105103](https://github.com/grafana/grafana/pull/105103), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.4-git to v2.5.0 [#105443](https://github.com/grafana/grafana/pull/105443), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/openfga/openfga from v1.8.6 to v1.8.12 [#105369](https://github.com/grafana/grafana/pull/105369), [@macabu](https://github.com/macabu) +- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105251](https://github.com/grafana/grafana/pull/105251), [@macabu](https://github.com/macabu) + +### Bug fixes + +- **Dashboard:** Fixes issue with row repeats and first row [#104467](https://github.com/grafana/grafana/pull/104467), [@torkelo](https://github.com/torkelo) +- **Graphite:** Ensure template variables are interpolated correctly [#105388](https://github.com/grafana/grafana/pull/105388), [@aangelisc](https://github.com/aangelisc) +- **Graphite:** Fix Graphite series interpolation [#104568](https://github.com/grafana/grafana/pull/104568), [@aangelisc](https://github.com/aangelisc) +- **Prometheus:** Fix semver import path [#104943](https://github.com/grafana/grafana/pull/104943), [@jackw](https://github.com/jackw) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 11.5.5 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105109](https://github.com/grafana/grafana/pull/105109), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.3 to v2.5.0 [#105441](https://github.com/grafana/grafana/pull/105441), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105373](https://github.com/grafana/grafana/pull/105373), [@macabu](https://github.com/macabu) +- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105252](https://github.com/grafana/grafana/pull/105252), [@macabu](https://github.com/macabu) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 11.4.5 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105110](https://github.com/grafana/grafana/pull/105110), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.2 to v2.5.0 [#105445](https://github.com/grafana/grafana/pull/105445), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105375](https://github.com/grafana/grafana/pull/105375), [@macabu](https://github.com/macabu) +- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105253](https://github.com/grafana/grafana/pull/105253), [@macabu](https://github.com/macabu) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 11.3.7 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105112](https://github.com/grafana/grafana/pull/105112), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.2 to v2.5.0 [#105447](https://github.com/grafana/grafana/pull/105447), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105376](https://github.com/grafana/grafana/pull/105376), [@macabu](https://github.com/macabu) +- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105254](https://github.com/grafana/grafana/pull/105254), [@macabu](https://github.com/macabu) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 11.2.10 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105113](https://github.com/grafana/grafana/pull/105113), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105378](https://github.com/grafana/grafana/pull/105378), [@macabu](https://github.com/macabu) +- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105255](https://github.com/grafana/grafana/pull/105255), [@macabu](https://github.com/macabu) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 10.4.19 (2025-05-22) + +### Features and enhancements + +- **Chore:** Bump Go version to 1.24.3 [#105115](https://github.com/grafana/grafana/pull/105115), [@macabu](https://github.com/macabu) +- **Dependencies:** Bump golang.org/x/net from v0.37.0 to v0.40.0 [#105449](https://github.com/grafana/grafana/pull/105449), [@macabu](https://github.com/macabu) +- **Security:** Fix CVE-2025-4123 +- **Security:** Fix CVE-2025-3580 + + + + +# 12.0.0+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 11.6.1+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 11.5.4+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 11.4.4+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 11.3.6+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 11.2.9+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 10.4.18+security-01 (2025-05-21) + +### Bug fixes + +- **Security:** Fix CVE-2025-4123 + + + + +# 12.0.0 (2025-05-05) + +### Features and enhancements + +- **Alerting:** API to convert submitted Prometheus rules to GMA [#102231](https://github.com/grafana/grafana/pull/102231), [@fayzal-g](https://github.com/fayzal-g) +- **Alerting:** Add HMAC signature config to the webhook integration [#100960](https://github.com/grafana/grafana/pull/100960), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Add MissingSeriesEvalsToResolve to the APIs [#102150](https://github.com/grafana/grafana/pull/102150), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Add UI migration feature toggle [#102217](https://github.com/grafana/grafana/pull/102217), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Add backend support for keep_firing_for [#100750](https://github.com/grafana/grafana/pull/100750), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Add details and edit pages for groups [#100884](https://github.com/grafana/grafana/pull/100884), [@konrad147](https://github.com/konrad147) +- **Alerting:** Add keep_firing_for and Recovering state [#103248](https://github.com/grafana/grafana/pull/103248), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Add migration to clean up rule versions table [#102484](https://github.com/grafana/grafana/pull/102484), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Add missing_series_evals_to_resolve option to alert rule form [#102808](https://github.com/grafana/grafana/pull/102808), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Delete permanently deleted alert rules. [#102960](https://github.com/grafana/grafana/pull/102960), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Detect target folder rules and show warning [#103673](https://github.com/grafana/grafana/pull/103673), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Migration UI [#102010](https://github.com/grafana/grafana/pull/102010), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Recover deleted alert rules [#101869](https://github.com/grafana/grafana/pull/101869), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Remove constraints for uniqueness of rule title [#102067](https://github.com/grafana/grafana/pull/102067), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Remove feature flag `alertingNoDataErrorExecution` [#102156](https://github.com/grafana/grafana/pull/102156), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Sequential evaluation of rules in group [#98829](https://github.com/grafana/grafana/pull/98829), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Skip rules that are managed by plugins when importing datasource-managed rules [#103573](https://github.com/grafana/grafana/pull/103573), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Stop allowing manual editing/restore of internal AM config via settings [#103884](https://github.com/grafana/grafana/pull/103884), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Template preview enhancements [#103817](https://github.com/grafana/grafana/pull/103817), [@JacobsonMT](https://github.com/JacobsonMT) +- **Alerting:** Update alerting module to 58ba6c617ff05eb1d6f65c59d369a6a16923dff6 [#102812](https://github.com/grafana/grafana/pull/102812), [@yuri-tceretian](https://github.com/yuri-tceretian) +- **Alerting:** Use 'Grafana IRM' wording in alerting contact point [#102014](https://github.com/grafana/grafana/pull/102014), [@brojd](https://github.com/brojd) +- **Alerting:** Webhook Improvements - Templateable Payloads [#103818](https://github.com/grafana/grafana/pull/103818), [@JacobsonMT](https://github.com/JacobsonMT) +- **AppChrome:** Move kiosk button into profile menu [#103600](https://github.com/grafana/grafana/pull/103600), [@torkelo](https://github.com/torkelo) +- **AppPlatform:** Introduce experimental Github integration for dashboard configuration management [#96329](https://github.com/grafana/grafana/pull/96329), [@MissingRoberto](https://github.com/MissingRoberto) +- **Authorization:** Add group to role DisplayName to make filtered list more clear [#102950](https://github.com/grafana/grafana/pull/102950), [@forsethc](https://github.com/forsethc) +- **Azure Monitor:** Add logs query builder [#99055](https://github.com/grafana/grafana/pull/99055), [@alyssabull](https://github.com/alyssabull) +- **Azure:** Mark Azure Prometheus exemplars as GA and enable by default [#100595](https://github.com/grafana/grafana/pull/100595), [@aangelisc](https://github.com/aangelisc) +- **AzureMonitor:** Improve selection of Basic Logs tables in the query builder [#103820](https://github.com/grafana/grafana/pull/103820), [@aangelisc](https://github.com/aangelisc) +- **BrowseDashboards:** Switch to list view if sort is set [#102196](https://github.com/grafana/grafana/pull/102196), [@Clarity-89](https://github.com/Clarity-89) +- **Checkbox:** Add z-index to description [#103847](https://github.com/grafana/grafana/pull/103847), [@Clarity-89](https://github.com/Clarity-89) +- **Chore:** Promoting feature toggle pluginsSriChecks GA [#102212](https://github.com/grafana/grafana/pull/102212), [@tolzhabayev](https://github.com/tolzhabayev) +- **CloudMigrations:** Add sorting and error filtering to Snapshot Results backend [#102753](https://github.com/grafana/grafana/pull/102753), [@mmandrus](https://github.com/mmandrus) +- **CloudMigrations:** Change onPremToCloudMigrations feature toggle to GA [#103212](https://github.com/grafana/grafana/pull/103212), [@dana-axinte](https://github.com/dana-axinte) +- **CloudMigrations:** Enable high-level resource type selection [#103011](https://github.com/grafana/grafana/pull/103011), [@macabu](https://github.com/macabu) +- **CloudMigrations:** Implement table sorting in the UI [#103061](https://github.com/grafana/grafana/pull/103061), [@mmandrus](https://github.com/mmandrus) +- **CloudWatch:** Migrate to aws-sdk-go-v2 [#103106](https://github.com/grafana/grafana/pull/103106), [@njvrzm](https://github.com/njvrzm) +- **Cloudwatch:** Do not parse log query grouping field to float [#102244](https://github.com/grafana/grafana/pull/102244), [@iwysiu](https://github.com/iwysiu) +- **Cloudwatch:** Migrate to aws-sdk-go-v2 [#99643](https://github.com/grafana/grafana/pull/99643), [@njvrzm](https://github.com/njvrzm) +- **Cloudwatch:** Revert aws sdk go v2 [#103644](https://github.com/grafana/grafana/pull/103644), [@iwysiu](https://github.com/iwysiu) +- **Config:** Removes setting `viewers_can_edit` [#102275](https://github.com/grafana/grafana/pull/102275), [@eleijonmarck](https://github.com/eleijonmarck) +- **Dashboard Restore:** Remove experimental functionality under feature flag `dashboardRestore` for now - this will be reworked [#103204](https://github.com/grafana/grafana/pull/103204), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **Dashboards:** Add Dashboard Schema validation (1) [#103662](https://github.com/grafana/grafana/pull/103662), [@marcoabreu](https://github.com/marcoabreu) +- **Dashboards:** Add a config setting that limits the number of series that will be displayed in a panel. Users can opt in to render all series. [#103405](https://github.com/grafana/grafana/pull/103405), [@oscarkilhed](https://github.com/oscarkilhed) +- **Dashboards:** Prevent saving to a non-existent folder [#103503](https://github.com/grafana/grafana/pull/103503), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **Dashboards:** Prevent version restore to same data [#102665](https://github.com/grafana/grafana/pull/102665), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **Dependencies:** Bump github.com/redis/go-redis/v9 from 9.7.0 to 9.7.3 [#102555](https://github.com/grafana/grafana/pull/102555), [@dependabot[bot]](https://github.com/dependabot[bot]) +- **Docs:** Standard Datetime units limited to millisecond precision [#103610](https://github.com/grafana/grafana/pull/103610), [@axelavargas](https://github.com/axelavargas) +- **ElasticSearch:** Improve index pattern error messaging and docs [#103899](https://github.com/grafana/grafana/pull/103899), [@idastambuk](https://github.com/idastambuk) +- **ElasticSearch:** Make script field input a text area [#103708](https://github.com/grafana/grafana/pull/103708), [@idastambuk](https://github.com/idastambuk) +- **Extensions:** Expose new observable APIs for accessing components and links [#103063](https://github.com/grafana/grafana/pull/103063), [@leventebalogh](https://github.com/leventebalogh) +- **Feat:** Make expressions work with plugins that set `alerting:false` but `backend:true` in their `plugin.json` files [#102232](https://github.com/grafana/grafana/pull/102232), [@tolzhabayev](https://github.com/tolzhabayev) +- **FlameGraphPanel:** Add units to standard options (#89815) [#102720](https://github.com/grafana/grafana/pull/102720), [@snyderdan](https://github.com/snyderdan) +- **Frontend:** Remove Angular [#99760](https://github.com/grafana/grafana/pull/99760), [@jackw](https://github.com/jackw) +- **Go:** Bump to 1.24.2 [#103521](https://github.com/grafana/grafana/pull/103521), [@Proximyst](https://github.com/Proximyst) +- **Go:** Bump to 1.24.2 (Enterprise) +- **I18n:** Add 13 new languages for translations [#102971](https://github.com/grafana/grafana/pull/102971), [@joshhunt](https://github.com/joshhunt) +- **Influx:** Support PDC for Influx SQL [#103032](https://github.com/grafana/grafana/pull/103032), [@aangelisc](https://github.com/aangelisc) +- **JWT:** Add org role mapping support to the JWT provider [#101584](https://github.com/grafana/grafana/pull/101584), [@QuentinBisson](https://github.com/QuentinBisson) +- **K8s:** Dashboards: Add fine grained access control checks to /apis [#104418](https://github.com/grafana/grafana/pull/104418), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **K8s:** Enable kubernetesClientDashboardsFolders by default [#103843](https://github.com/grafana/grafana/pull/103843), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **LBAC for data sources:** PublicPreview and self serve enablement [#102276](https://github.com/grafana/grafana/pull/102276), [@eleijonmarck](https://github.com/eleijonmarck) +- **Live:** Remove queryOverLive and live-service-web-worker experimental feature flags [#103518](https://github.com/grafana/grafana/pull/103518), [@ryantxu](https://github.com/ryantxu) +- **Logs Panel:** Add ISO8601 date to log download files [#102932](https://github.com/grafana/grafana/pull/102932), [@gtk-grafana](https://github.com/gtk-grafana) +- **Logs Table:** Add new Controls component to Explore [#103467](https://github.com/grafana/grafana/pull/103467), [@matyax](https://github.com/matyax) +- **Logs:** Add new Controls component to Explore [#103401](https://github.com/grafana/grafana/pull/103401), [@matyax](https://github.com/matyax) +- **Logs:** Always keep displayed fields with changed queries [#102493](https://github.com/grafana/grafana/pull/102493), [@svennergr](https://github.com/svennergr) +- **Logs:** Clean up Explore meta information [#103801](https://github.com/grafana/grafana/pull/103801), [@matyax](https://github.com/matyax) +- **Logs:** Prevent automatic scrolling on refresh after changing scroll position [#102463](https://github.com/grafana/grafana/pull/102463), [@matyax](https://github.com/matyax) +- **MetricsDrilldown:** Advance `exploreMetricsUseExternalAppPlugin` feature toggle stage [#102137](https://github.com/grafana/grafana/pull/102137), [@NWRichmond](https://github.com/NWRichmond) +- **MetricsDrilldown:** Advance `exploreMetricsUseExternalAppPlugin` to GA [#103653](https://github.com/grafana/grafana/pull/103653), [@NWRichmond](https://github.com/NWRichmond) +- **MetricsDrilldown:** Mark `exploreMetricsUseExternalAppPlugin` as not frontend-only [#102942](https://github.com/grafana/grafana/pull/102942), [@NWRichmond](https://github.com/NWRichmond) +- **MetricsDrilldown:** Remove legacy Metrics Drilldown code paths [#103845](https://github.com/grafana/grafana/pull/103845), [@NWRichmond](https://github.com/NWRichmond) +- **MetricsDrilldown:** Restore link to Metrics Drilldown from Explore [#104075](https://github.com/grafana/grafana/pull/104075), [@NWRichmond](https://github.com/NWRichmond) +- **NodeGraph:** Add node graph algorithm layout option [#102760](https://github.com/grafana/grafana/pull/102760), [@joey-grafana](https://github.com/joey-grafana) +- **Plugins:** Remove plugin dependency version (Enterprise) +- **Plugins:** Remove sort by options from plugins catalog [#102862](https://github.com/grafana/grafana/pull/102862), [@oshirohugo](https://github.com/oshirohugo) +- **Plugins:** Remove support for secrets manager plugins [#101467](https://github.com/grafana/grafana/pull/101467), [@wbrowne](https://github.com/wbrowne) +- **Plugins:** Remove support for secrets manager plugins (Enterprise) +- **Plugins:** Remove userStorageAPI feature toggle [#102915](https://github.com/grafana/grafana/pull/102915), [@oshirohugo](https://github.com/oshirohugo) +- **Prometheus:** Add back @lezer/highlight to dev dependency [#102632](https://github.com/grafana/grafana/pull/102632), [@idastambuk](https://github.com/idastambuk) +- **Prometheus:** Add support for cloud partners Prometheus data sources [#103482](https://github.com/grafana/grafana/pull/103482), [@kevinwcyu](https://github.com/kevinwcyu) +- **Prometheus:** Enable Combobox metric select by default [#101045](https://github.com/grafana/grafana/pull/101045), [@joshhunt](https://github.com/joshhunt) +- **Prometheus:** Enable prometheusRunQueriesInParallel feature toggle by default [#102127](https://github.com/grafana/grafana/pull/102127), [@itsmylife](https://github.com/itsmylife) +- **RecordedQueries:** Deprecate recorded queries UI messaging (Enterprise) +- **Security:** Update JWT library (CVE-2025-30204) [#102715](https://github.com/grafana/grafana/pull/102715), [@Proximyst](https://github.com/Proximyst) +- **Tempo:** Add support for ad-hoc filters [#102448](https://github.com/grafana/grafana/pull/102448), [@ifrost](https://github.com/ifrost) +- **Tempo:** Remove aggregate by [#98474](https://github.com/grafana/grafana/pull/98474), [@joey-grafana](https://github.com/joey-grafana) +- **TraceView:** Add scope attributes to span details [#103173](https://github.com/grafana/grafana/pull/103173), [@joey-grafana](https://github.com/joey-grafana) +- **TraceView:** Render all links in span details [#101881](https://github.com/grafana/grafana/pull/101881), [@ifrost](https://github.com/ifrost) +- **Traces:** Preinstall Traces Drilldown app with Grafana [#102986](https://github.com/grafana/grafana/pull/102986), [@ifrost](https://github.com/ifrost) + +### Bug fixes + +- **Alerting:** Fix Simple condition threshold inputs with negative values. [#102976](https://github.com/grafana/grafana/pull/102976), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron) +- **Alerting:** Fix display of `Normal (Updated)` in alert history [#102476](https://github.com/grafana/grafana/pull/102476), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Fix rule instances table [#102290](https://github.com/grafana/grafana/pull/102290), [@konrad147](https://github.com/konrad147) +- **Alerting:** Make nested folders work in Alert List Panel [#103550](https://github.com/grafana/grafana/pull/103550), [@tomratcliffe](https://github.com/tomratcliffe) +- **Alerting:** Remove rule type switch for modified export mode [#102287](https://github.com/grafana/grafana/pull/102287), [@konrad147](https://github.com/konrad147) +- **Alerting:** Simplified alert rule toggle bug fixes [#102119](https://github.com/grafana/grafana/pull/102119), [@gillesdemey](https://github.com/gillesdemey) +- **Alertmanager:** Add Role-Based Access Control via reqAction Field [#101543](https://github.com/grafana/grafana/pull/101543), [@olegpixel](https://github.com/olegpixel) +- **App Platform:** Pin bleve to fix CVE-2022-31022 [#102513](https://github.com/grafana/grafana/pull/102513), [@Proximyst](https://github.com/Proximyst) +- **AppChrome/MegaMenu:** Fixes issue with default state being initialised to undocked [#103507](https://github.com/grafana/grafana/pull/103507), [@torkelo](https://github.com/torkelo) +- **AppTitle:** Fix overflowing text [#103583](https://github.com/grafana/grafana/pull/103583), [@tskarhed](https://github.com/tskarhed) +- **Azure:** Ensure basic logs queries are limited to a single resource [#103588](https://github.com/grafana/grafana/pull/103588), [@aangelisc](https://github.com/aangelisc) +- **CloudWatch:** Import new grafana-aws-sdk with PDC fix [#103249](https://github.com/grafana/grafana/pull/103249), [@njvrzm](https://github.com/njvrzm) +- **ColorPicker:** Fixed height when switching tabs [#103304](https://github.com/grafana/grafana/pull/103304), [@DanMPA](https://github.com/DanMPA) +- **Dashboard:** Fix Core Panel Migrations - table panel [#102146](https://github.com/grafana/grafana/pull/102146), [@axelavargas](https://github.com/axelavargas) +- **DashboardScenePage:** Correct slug in self referencing data links [#100048](https://github.com/grafana/grafana/pull/100048), [@Sergej-Vlasov](https://github.com/Sergej-Vlasov) +- **Dashboards:** Fix duplicate provisioning when errors occur on title-only based provisioning [#102249](https://github.com/grafana/grafana/pull/102249), [@stephaniehingtgen](https://github.com/stephaniehingtgen) +- **Dashboards:** Fix panel link to Grafana Metrics Drilldown [#103759](https://github.com/grafana/grafana/pull/103759), [@NWRichmond](https://github.com/NWRichmond) +- **Fix:** Change secure_json_data column data type to medium text only MYSQL [#102557](https://github.com/grafana/grafana/pull/102557), [@s4kh](https://github.com/s4kh) +- **GrafanaUI:** Prevent ToolbarButton from submitting form [#102228](https://github.com/grafana/grafana/pull/102228), [@kozhuhds](https://github.com/kozhuhds) +- **GrafanaUI:** Remove blurred background from overlay backdrops to improve performance [#103563](https://github.com/grafana/grafana/pull/103563), [@joshhunt](https://github.com/joshhunt) +- **LDAP test:** Fix page crash [#102587](https://github.com/grafana/grafana/pull/102587), [@ashharrison90](https://github.com/ashharrison90) +- **Navigation:** Fix bookmarks when Grafana is running under subpath [#102679](https://github.com/grafana/grafana/pull/102679), [@matejkubinec](https://github.com/matejkubinec) +- **PanelEdit:** Fixes suggestions not applying options or field config [#102675](https://github.com/grafana/grafana/pull/102675), [@torkelo](https://github.com/torkelo) +- **PluginProxy:** Fix nil pointer in OAuth forwarding [#103626](https://github.com/grafana/grafana/pull/103626), [@moustafab](https://github.com/moustafab) +- **Plugins:** Fix better UX for disabled Angular plugins [#101333](https://github.com/grafana/grafana/pull/101333), [@hugohaggmark](https://github.com/hugohaggmark) +- **Plugins:** Fix support for adhoc filters with raw queries in InfluxDB [#101966](https://github.com/grafana/grafana/pull/101966), [@beejeebus](https://github.com/beejeebus) +- **Renderer:** Fix regression on callback URL in plugin mode [#103787](https://github.com/grafana/grafana/pull/103787), [@AgnesToulet](https://github.com/AgnesToulet) +- **SQL:** Fix builder crashes when any in selected [#102871](https://github.com/grafana/grafana/pull/102871), [@zoltanbedi](https://github.com/zoltanbedi) +- **SSE:** Fix goroutine leak in math operation expression parsing [#102380](https://github.com/grafana/grafana/pull/102380), [@kylebrandt](https://github.com/kylebrandt) +- **Tempo:** Add fixes for broken exemplars [#103298](https://github.com/grafana/grafana/pull/103298), [@joey-grafana](https://github.com/joey-grafana) + +### Breaking changes + +- **Alerting:** Make $value return the query value in case when a single datasource is used [#102301](https://github.com/grafana/grafana/pull/102301), [@alexander-akhmetov](https://github.com/alexander-akhmetov) +- **Alerting:** Relax permissions for access a rule [#103664](https://github.com/grafana/grafana/pull/103664), [@moustafab](https://github.com/moustafab) +- **Alerting:** Remove feature toggles relating to Loki Alert State History [#103540](https://github.com/grafana/grafana/pull/103540), [@rwwiv](https://github.com/rwwiv) +- **Alerting:** Remove the POST endpoint for the internal Grafana Alertmanager config [#103819](https://github.com/grafana/grafana/pull/103819), [@rwwiv](https://github.com/rwwiv) +- **Anonymous:** Enforce org role Viewer setting [#102070](https://github.com/grafana/grafana/pull/102070), [@eleijonmarck](https://github.com/eleijonmarck) +- **Chore:** Enable Grafana version check when installing plugins [#103176](https://github.com/grafana/grafana/pull/103176), [@andresmgot](https://github.com/andresmgot) +- **Chore:** Enabling failWrongDSUID by default in Grafana 12 [#102192](https://github.com/grafana/grafana/pull/102192), [@tolzhabayev](https://github.com/tolzhabayev) +- **Config:** Removes setting `viewers_can_edit` [#101767](https://github.com/grafana/grafana/pull/101767), [@eleijonmarck](https://github.com/eleijonmarck) +- **Frontend:** Remove Angular (Enterprise) +- **Plugin Extensions:** Clean up the deprecated APIs [#102102](https://github.com/grafana/grafana/pull/102102), [@leventebalogh](https://github.com/leventebalogh) +- **Plugins:** Remove plugin dependency version [#103728](https://github.com/grafana/grafana/pull/103728), [@wbrowne](https://github.com/wbrowne) +- **Tempo:** Remove traceQLStreaming feature toggle [#103619](https://github.com/grafana/grafana/pull/103619), [@adrapereira](https://github.com/adrapereira) + +### Plugin development fixes & changes + +- **Combobox:** add grouping functionality [#100603](https://github.com/grafana/grafana/pull/100603), [@eledobleefe](https://github.com/eledobleefe) +- **Grafana UI:** Add `columnGap` + `rowGap` to `Stack`/`Grid` [#102883](https://github.com/grafana/grafana/pull/102883), [@ashharrison90](https://github.com/ashharrison90) +- **Grafana UI:** Clearly separate multiple warnings by using HTML tags [#97979](https://github.com/grafana/grafana/pull/97979), [@zenador](https://github.com/zenador) + + # 11.6.1 (2025-04-23) diff --git a/Dockerfile b/Dockerfile index 4ec06a8b9ea..966d1753ad3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ ARG BASE_IMAGE=alpine:3.21 ARG JS_IMAGE=node:22-alpine ARG JS_PLATFORM=linux/amd64 -ARG GO_IMAGE=golang:1.24.2-alpine +ARG GO_IMAGE=golang:1.24.3-alpine # Default to building locally ARG GO_SRC=go-builder @@ -60,13 +60,7 @@ WORKDIR /tmp/grafana COPY go.* ./ COPY .bingo .bingo -COPY .citools/bra .citools/bra -COPY .citools/cue .citools/cue -COPY .citools/cog .citools/cog -COPY .citools/lefthook .citools/lefthook -COPY .citools/jb .citools/jb -COPY .citools/golangci-lint .citools/golangci-lint -COPY .citools/swagger .citools/swagger +COPY .citools .citools # Include vendored dependencies COPY pkg/util/xorm pkg/util/xorm @@ -77,6 +71,7 @@ COPY pkg/build pkg/build COPY pkg/build/wire pkg/build/wire COPY pkg/promlib pkg/promlib COPY pkg/storage/unified/resource pkg/storage/unified/resource +COPY pkg/storage/unified/resourcepb pkg/storage/unified/resourcepb COPY pkg/storage/unified/apistore pkg/storage/unified/apistore COPY pkg/semconv pkg/semconv COPY pkg/aggregator pkg/aggregator @@ -85,6 +80,7 @@ COPY apps/investigations apps/investigations COPY apps/advisor apps/advisor COPY apps/dashboard apps/dashboard COPY apps/folder apps/folder +COPY apps/iam apps/iam COPY apps apps COPY kindsv2 kindsv2 COPY apps/alerting/notifications apps/alerting/notifications diff --git a/LICENSING.md b/LICENSING.md index 5239154d161..cfd239e7230 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -30,3 +30,13 @@ The following directories and their subdirectories are licensed under their orig ``` public/vendor/ ``` + +## MIT license + +The following files are licensed under MIT License: + +``` +.github/workflows/actionlint-format.txt + -> Vendored: https://github.com/rhysd/actionlint/blob/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/format/sarif_template.txt + -> The workflow that uses it is AGPL-3.0-only. +``` diff --git a/Makefile b/Makefile index 60763468e11..9be96ca3248 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,10 @@ WIRE_TAGS = "oss" -include local/Makefile include .bingo/Variables.mk +include .citools/Variables.mk GO = go -GO_VERSION = 1.24.2 +GO_VERSION = 1.24.3 GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh) GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh) SH_FILES ?= $(shell find ./scripts -name *.sh) @@ -18,18 +19,16 @@ GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev) GO_BUILD_FLAGS += $(if $(GO_BUILD_TAGS),-build-tags=$(GO_BUILD_TAGS)) GO_BUILD_FLAGS += $(GO_RACE_FLAG) GO_TEST_FLAGS += $(if $(GO_BUILD_TAGS),-tags=$(GO_BUILD_TAGS)) -GO_TEST_OUTPUT := $(shell [ -n "$(GO_TEST_OUTPUT)" ] && echo '-json | tee $(GO_TEST_OUTPUT) | tparse -all') -GO_UNIT_COVERAGE ?= true -GO_UNIT_COVER_PROFILE ?= unit.cov -GO_INTEGRATION_COVER_PROFILE ?= integration.cov GIT_BASE = remotes/origin/main # GNU xargs has flag -r, and BSD xargs (e.g. MacOS) has that behaviour by default XARGSR = $(shell xargs --version 2>&1 | grep -q GNU && echo xargs -r || echo xargs) -targets := $(shell echo '$(sources)' | tr "," " ") +# Test sharding to replicate CI behaviour locally. +SHARD ?= 1 +SHARDS ?= 1 -GO_INTEGRATION_TESTS := $(shell find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) +targets := $(shell echo '$(sources)' | tr "," " ") .PHONY: all all: deps build @@ -62,13 +61,13 @@ $(NGALERT_SPEC_TARGET): $(MERGED_SPEC_TARGET): swagger-oss-gen swagger-enterprise-gen $(NGALERT_SPEC_TARGET) ## Merge generated and ngalert API specs # known conflicts DsPermissionType, AddApiKeyCommand, Json, Duration (identical models referenced by both specs) - GODEBUG=gotypesalias=0 $(GO) tool swagger mixin -q $(SPEC_TARGET) $(ENTERPRISE_SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET) + GODEBUG=gotypesalias=0 $(swagger) mixin -q $(SPEC_TARGET) $(ENTERPRISE_SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET) .PHONY: swagger-oss-gen swagger-oss-gen: ## Generate API Swagger specification @echo "re-generating swagger for OSS" rm -f $(SPEC_TARGET) - SWAGGER_GENERATE_EXTENSION=false GODEBUG=gotypesalias=0 $(GO) tool swagger generate spec -q -m -w pkg/server -o $(SPEC_TARGET) \ + SWAGGER_GENERATE_EXTENSION=false GODEBUG=gotypesalias=0 $(swagger) generate spec -q -m -w pkg/server -o $(SPEC_TARGET) \ -x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \ -x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \ -x "github.com/prometheus/alertmanager" \ @@ -86,7 +85,7 @@ else swagger-enterprise-gen: ## Generate API Swagger specification @echo "re-generating swagger for enterprise" rm -f $(ENTERPRISE_SPEC_TARGET) - SWAGGER_GENERATE_EXTENSION=false GODEBUG=gotypesalias=0 $(GO) tool swagger generate spec -q -m -w pkg/server -o $(ENTERPRISE_SPEC_TARGET) \ + SWAGGER_GENERATE_EXTENSION=false GODEBUG=gotypesalias=0 $(swagger) generate spec -q -m -w pkg/server -o $(ENTERPRISE_SPEC_TARGET) \ -x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \ -x "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" \ -x "github.com/prometheus/alertmanager" \ @@ -100,7 +99,7 @@ swagger-gen: gen-go $(MERGED_SPEC_TARGET) swagger-validate .PHONY: swagger-validate swagger-validate: $(MERGED_SPEC_TARGET) # Validate API spec - GODEBUG=gotypesalias=0 $(GO) tool swagger validate --skip-warnings $(<) + GODEBUG=gotypesalias=0 $(swagger) validate --skip-warnings $(<) .PHONY: swagger-clean swagger-clean: @@ -112,11 +111,11 @@ cleanup-old-git-hooks: .PHONY: lefthook-install lefthook-install: cleanup-old-git-hooks # install lefthook for pre-commit hooks - $(GO) tool lefthook install -f + $(lefthook) install -f .PHONY: lefthook-uninstall lefthook-uninstall: - $(GO) tool lefthook uninstall + $(lefthook) uninstall ##@ OpenAPI 3 OAPI_SPEC_TARGET = public/openapi3.json @@ -141,6 +140,8 @@ endif i18n-extract: i18n-extract-enterprise @echo "Extracting i18n strings for OSS" yarn run i18next --config public/locales/i18next-parser.config.cjs + @echo "Extracting i18n strings for plugins" + yarn run plugin:i18n-extract ##@ Building .PHONY: gen-cue @@ -194,8 +195,8 @@ gen-go: .PHONY: fix-cue fix-cue: @echo "formatting cue files" - $(GO) tool cue fix kinds/**/*.cue - $(GO) tool cue fix public/app/plugins/**/**/*.cue + $(cue) fix kinds/**/*.cue + $(cue) fix public/app/plugins/**/**/*.cue .PHONY: gen-jsonnet gen-jsonnet: @@ -252,7 +253,7 @@ build: build-go build-js ## Build backend and frontend. .PHONY: run run: ## Build and run web server on filesystem changes. See /.bra.toml for configuration. - $(GO) tool bra run + $(bra) run .PHONY: run-go run-go: ## Build and run web server immediately. @@ -270,13 +271,9 @@ test-go: test-go-unit test-go-integration .PHONY: test-go-unit test-go-unit: ## Run unit tests for backend with flags. - @echo "backend unit tests" - $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -v -short -timeout=30m $(GO_TEST_FILES) $(GO_TEST_OUTPUT) - -.PHONY: test-go-unit-cov -test-go-unit-cov: ## Run unit tests for backend with flags and coverage - @echo "backend unit tests with coverage" - $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -v -short $(if $(filter true,$(GO_UNIT_COVERAGE)),-covermode=atomic -coverprofile=$(GO_UNIT_COVER_PROFILE) $(if $(GO_UNIT_TEST_COVERPKG),-coverpkg=$(GO_UNIT_TEST_COVERPKG)),) -timeout=30m $(GO_TEST_FILES) $(GO_TEST_OUTPUT) + @echo "backend unit tests ($(SHARD)/$(SHARDS))" + $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -v -short -timeout=30m \ + $(shell ./scripts/ci/backend-tests/shard.sh -n$(SHARD) -m$(SHARDS) -s) .PHONY: test-go-unit-pretty test-go-unit-pretty: check-tparse @@ -289,7 +286,8 @@ test-go-unit-pretty: check-tparse .PHONY: test-go-integration test-go-integration: ## Run integration tests for backend with flags. @echo "test backend integration tests" - $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -count=1 -run "^TestIntegration" -covermode=atomic -coverprofile=$(GO_INTEGRATION_COVER_PROFILE) -timeout=5m $(GO_INTEGRATION_TESTS) $(GO_TEST_OUTPUT) + $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -count=1 -run "^TestIntegration" -covermode=atomic -coverprofile=$(GO_INTEGRATION_COVER_PROFILE) -timeout=5m \ + $(shell ./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -n$(SHARD) -m$(SHARDS) -s) .PHONY: test-go-integration-alertmanager test-go-integration-alertmanager: ## Run integration tests for the remote alertmanager (config taken from the mimir_backend block). @@ -311,32 +309,29 @@ test-go-integration-postgres: devenv-postgres ## Run integration tests for postg @echo "test backend integration postgres tests" $(GO) clean -testcache GRAFANA_TEST_DB=postgres \ - $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -p=1 -count=1 -run "^TestIntegration" -covermode=atomic -timeout=10m $(GO_INTEGRATION_TESTS) + $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -p=1 -count=1 -run "^TestIntegration" -covermode=atomic -timeout=10m \ + $(shell ./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -n$(SHARD) -m$(SHARDS) -s) .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" GRAFANA_TEST_DB=mysql \ - $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -p=1 -count=1 -run "^TestIntegration" -covermode=atomic -timeout=10m $(GO_INTEGRATION_TESTS) + $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -p=1 -count=1 -run "^TestIntegration" -covermode=atomic -timeout=10m \ + $(shell ./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -n$(SHARD) -m$(SHARDS) -s) .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 $(GO_TEST_FLAGS) -run IntegrationRedis -covermode=atomic -timeout=2m $(GO_INTEGRATION_TESTS) + REDIS_URL=localhost:6379 $(GO) test $(GO_TEST_FLAGS) -run IntegrationRedis -covermode=atomic -timeout=2m \ + $(shell ./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -n$(SHARD) -m$(SHARDS) -s) .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 $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -run IntegrationMemcached -covermode=atomic -timeout=2m $(GO_INTEGRATION_TESTS) - -.PHONY: test-go-integration-spanner -test-go-integration-spanner: ## Run integration tests for Spanner backend with flags. Uses spanner-emulator on localhost:9010 and localhost:9020. - @if [ "${WIRE_TAGS}" != "enterprise" ]; then echo "Spanner integration test require enterprise setup"; exit 1; fi - @echo "test backend integration spanner tests" - GRAFANA_TEST_DB=spanner \ - $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -p=1 -count=1 -v -run "^TestIntegration" -covermode=atomic -timeout=2m $(GO_INTEGRATION_TESTS) + MEMCACHED_HOSTS=localhost:11211 $(GO) test $(GO_RACE_FLAG) $(GO_TEST_FLAGS) -run IntegrationMemcached -covermode=atomic -timeout=2m \ + $(shell ./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -n$(SHARD) -m$(SHARDS) -s) .PHONY: test-js test-js: ## Run tests for frontend. @@ -350,7 +345,7 @@ test: test-go test-js ## Run all tests. .PHONY: golangci-lint golangci-lint: @echo "lint via golangci-lint" - $(GO) tool golangci-lint run \ + $(golangci-lint) run \ --config .golangci.yml \ $(if $(GO_BUILD_TAGS),--build-tags $(GO_BUILD_TAGS)) \ $(GO_LINT_FILES) @@ -365,7 +360,7 @@ lint-go-diff: $(XARGSR) dirname | \ sort -u | \ sed 's,^,./,' | \ - $(XARGSR) $(GO) tool golangci-lint run --config .golangci.yml + $(XARGSR) $(golangci-lint) run --config .golangci.yml # with disabled SC1071 we are ignored some TCL,Expect `/usr/bin/env expect` scripts .PHONY: shellcheck @@ -458,11 +453,11 @@ devenv-mysql: .PHONY: protobuf protobuf: ## Compile protobuf definitions bash scripts/protobuf-check.sh - go install google.golang.org/protobuf/cmd/protoc-gen-go + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.5 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.4.0 buf generate pkg/plugins/backendplugin/pluginextensionv2 --template pkg/plugins/backendplugin/pluginextensionv2/buf.gen.yaml buf generate pkg/apis/secret/v0alpha1/decrypt --template pkg/apis/secret/v0alpha1/decrypt/buf.gen.yaml - buf generate pkg/storage/unified/resource --template pkg/storage/unified/resource/buf.gen.yaml + buf generate pkg/storage/unified/proto --template pkg/storage/unified/proto/buf.gen.yaml buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml buf generate pkg/services/ngalert/store/proto/v1 --template pkg/services/ngalert/store/proto/v1/buf.gen.yaml @@ -519,3 +514,9 @@ check-tparse: .PHONY: help help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +# check licenses of used dependencies (can be run using build image using +# container/check-licenses target) +check-licenses: + license_finder --decisions-file .github/license_finder.yaml + diff --git a/SECURITY.md b/SECURITY.md index 5442ba9fb14..42c58f9cd67 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,10 +1,16 @@ # Reporting security issues -This product is in scope for our Bug Bounty Program. To submit a vulnerability report, please visit [Grafana Labs Bug Bounty Policy page](https://github.com/grafana/bugbounty) and follow the instructions provided. Our security team will review your submission and get back to you as soon as possible. +If you think you have found a security vulnerability, we have two routes for reporting security issues. + +Important: Whichever route you choose, we ask you to not disclose the vulnerability before it has been fixed and announced, unless you received a response from the Grafana Labs security team that you can do so. + +[Full guidance on reporting a security issue can be found here](https://grafana.com/legal/report-a-security-issue/). + +This product is in scope for our Bug Bounty Program. To submit a vulnerability report, please visit [Grafana Labs Bug Bounty page](https://app.intigriti.com/programs/grafanalabs/grafanaossbbp/detail) and follow the instructions provided. Our security team will review your submission and get back to you as soon as possible. --- -For any other security issues, please send an email to security@grafana.com +For products and services outside the scope of our bug bounty program, or if you do not wish to receive a bounty, you can report issues directly to us via email at security@grafana.com. This address can be used for all of Grafana Labs’ open source and commercial products (including but not limited to Grafana, Grafana Cloud, Grafana Enterprise, and grafana.com). Please encrypt your message to us; please use our PGP key. The key fingerprint is: diff --git a/apps/advisor/README.md b/apps/advisor/README.md new file mode 100644 index 00000000000..27c44711f43 --- /dev/null +++ b/apps/advisor/README.md @@ -0,0 +1,155 @@ +# Grafana Advisor - Implementing New Checks + +This guide explains how to implement new checks in the Grafana Advisor system. The Advisor system allows you to create custom checks that can validate various aspects of your Grafana instance. + +## Check Structure + +A check in Grafana Advisor consists of two main components: + +1. A main check struct that implements the [`checks.Check`](https://github.com/grafana/grafana/blob/269226cb50b970ad9f692f1fdd220e9822e90db8/apps/advisor/pkg/app/checks/ifaces.go#L11-L25) interface +2. One or more step structs that implement the [`checks.Step`](https://github.com/grafana/grafana/blob/269226cb50b970ad9f692f1fdd220e9822e90db8/apps/advisor/pkg/app/checks/ifaces.go#L28-L39) interface + +## Implementing a New Check + +### 1. Create the Check Package + +Create a new package in `pkg/app/checks/` for your check. For example, if you're creating a check for validating configuration fields, you might create `pkg/app/checks/configchecks/`. Add a `check.go` file to the package, there we will implement the check interface. Let's start by implementing the `Check` interface but without any steps yet: + +```go +package configchecks + +var _ checks.Check = (*check)(nil) + +type check struct{} + +func New() checks.Check { + return &check{} +} + +func (c *check) ID() string { + return "config" +} + +func (c *check) Name() string { + return "config setting" +} + +func (c *check) Items(ctx context.Context) ([]any, error) { + return nil, nil +} + +func (c *check) Item(ctx context.Context, id string) (any, error) { + return nil, nil +} + +func (c *check) Init(ctx context.Context) error { + return nil +} + +func (c *check) Steps() []checks.Step { + return []checks.Step{} +} + +``` + +### 2. Define Dependencies and Register the Check + +In order to be able to implement a check, it will likely need some dependencies in the form of `wire` services. This is the internal dependency injection system used in Grafana and it allows you to access the services you need. + +For our example, we will need access to the grafana settings, which are exposed by wire as `*setting.Cfg` (in this case is a pointer to a struct, not an interface but the idea is the same). So let's add it to our check as a parameter for our `New` function: + +```go +type check struct { + cfg *setting.Cfg +} + +func New(cfg *setting.Cfg) checks.Check { + return &check{ + cfg: cfg, + } +} +``` + +Now, to register our check in the `checkregistry` package, we need to add it to the `ProvideService` function. First, we need to verify that the services we need are available in the `ProvideService` function, and if not, add them. Then, we need to add our check to the `Checks` slice. + +```go +func ProvideService(..., cfg *setting.Cfg, +) *Service { + return &Service{ + ... + cfg: cfg, + } +} + +func (s *Service) Checks() []checks.Check { + return []checks.Check{ + ... + configchecks.New(s.cfg), + } +} +``` + +### 3. Complete the Check Implementation + +Now that we have our check registered, we can implement the rest of the check logic. + +#### 3.1. Implement the `Items` and `Item` methods + +The `Items` method is used to return a list of items that the check will be run on (e.g. all data sources, all plugins, etc). The `Item` method is used to return a single item by its ID. + +These functions can return `any` type, we will convert them to the expected type in the step `Run` method. + +In our case, we will implement the `Items` method to return a list of config sections that we want to check. The `Item` method will return a single config section by its name. + +```go +func (c *check) Items(ctx context.Context) ([]any, error) { + return []any{"security.secret_key"}, nil +} + +func (c *check) Item(ctx context.Context, id string) (any, error) { + return id, nil +} +``` + +Check other checks for examples of how to implement these methods in more interesting ways. + +#### 3.2. Implement the `Init` method + +The `Init` method is used to initialize the check. It is called when the check is first created. It should be used to gather any information that is needed to run the check and for the steps to have some shared context. + +In our case, we don't need to do anything special so we can just return `nil`. + +```go +func (c *check) Init(ctx context.Context) error { + return nil +} +``` + +One more interesting example is the `plugincheck`, where we gather all the plugin information from GCOM and store it in the check struct. + +### 4. Implement Steps + +Like the `Check` interface, each `Step` needs to return some information (metadata) about the step, which will be used to populate the UI, and the logic to `Run` the step. + +In our example, we will implement a step that will check if the `security.secret_key` is set correctly. In case it's not correct, we recommend the user to follow the documentation. + +Check [`security_config_step.go`](./pkg/app/checks/configchecks/security_config_step.go) for the full implementation. + +## Best Practices + +1. **Error Handling**: In general, avoid returning errors for known issues, these will mark the check report as failed and the UI will render an error page. Only unexpected errors should be returned. + +2. **Type Safety**: Use type assertions to ensure you're working with the correct type of item. + +3. **Severity Levels**: Use appropriate severity levels: + + - `CheckReportFailureSeverityHigh`: For critical issues that need immediate attention + - `CheckReportFailureSeverityLow`: For non-critical issues that can be addressed later + +4. **Resolution Links**: Provide helpful links in the `CheckErrorLink` slice to help users resolve issues. + +5. **Logging**: Use the provided logger to log important information and errors. + +## Testing + +Create tests for your check and its steps to ensure they work as expected. Test both successful and failure scenarios. diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index ccf976b21bf..ef3c2710ba5 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -1,10 +1,10 @@ module github.com/grafana/grafana/apps/advisor -go 1.24.2 +go 1.24.3 require ( - github.com/grafana/grafana-app-sdk v0.35.1 - k8s.io/apimachinery v0.32.3 + github.com/grafana/grafana-app-sdk v0.38.2 + k8s.io/apimachinery v0.33.1 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff ) @@ -18,7 +18,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -31,24 +31,26 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb // indirect github.com/grafana/grafana-app-sdk/logging v0.35.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/invopop/yaml v0.3.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/openfga/openfga v1.8.13 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -60,24 +62,25 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/net v0.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.6.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.9.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect - google.golang.org/grpc v1.71.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/grpc v1.72.1 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.3 // indirect - k8s.io/apiextensions-apiserver v0.32.3 // indirect - k8s.io/client-go v0.32.3 // indirect + k8s.io/api v0.33.1 // indirect + k8s.io/apiextensions-apiserver v0.33.1 // indirect + k8s.io/client-go v0.33.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/apps/advisor/go.sum b/apps/advisor/go.sum index 97049070ac5..164b5efcca3 100644 --- a/apps/advisor/go.sum +++ b/apps/advisor/go.sum @@ -19,6 +19,8 @@ github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXE github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -52,6 +54,8 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb h1:oTl2j6/4miQUYmXANp2pBuYCWA5f8NVYFfCWpczpFso= +github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us= github.com/grafana/grafana-app-sdk v0.30.0 h1:Hqn2pETu2mQ4RpWkZYEQfu01P7xd1Z1Gj+HX/8aB0tw= github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1GTNtyNuExONtiNuk= github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= @@ -63,6 +67,7 @@ github.com/grafana/grafana-app-sdk/logging v0.35.0/go.mod h1:Y/bvbDhBiV/tkIle9RW github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -78,6 +83,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -99,6 +105,10 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/openfga/openfga v1.8.12 h1:xEirA6tFwaJfjBDtbHWCK0/Tw+B8XleRyhg9dcEpzHo= +github.com/openfga/openfga v1.8.12/go.mod h1:fIZyekdNB+tWQ6zIiglZonAc5ErZiDGMeHue/BzRYRM= +github.com/openfga/openfga v1.8.13 h1:ROURkotKhbmtyBX3188+cNElN8AOZmTl0CMkxUqwawo= +github.com/openfga/openfga v1.8.13/go.mod h1:h1VGcVW81eY1YyDtFx5+gxxAIEhIiOGR9SRGgs/X/k8= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -109,6 +119,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= @@ -116,6 +127,8 @@ github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbp github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= @@ -167,11 +180,14 @@ go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -184,6 +200,8 @@ golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= @@ -194,6 +212,7 @@ golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -201,16 +220,19 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -230,17 +252,22 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1: google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= +google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/apps/advisor/pkg/app/app.go b/apps/advisor/pkg/app/app.go index 5ce2128131a..b647e86a964 100644 --- a/apps/advisor/pkg/app/app.go +++ b/apps/advisor/pkg/app/app.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana-app-sdk/app" "github.com/grafana/grafana-app-sdk/k8s" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/resource" "github.com/grafana/grafana-app-sdk/simple" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" @@ -14,9 +15,7 @@ import ( "github.com/grafana/grafana/apps/advisor/pkg/app/checkscheduler" "github.com/grafana/grafana/apps/advisor/pkg/app/checktyperegisterer" "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/infra/log" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog/v2" ) func New(cfg app.Config) (app.App, error) { @@ -26,7 +25,7 @@ func New(cfg app.Config) (app.App, error) { return nil, fmt.Errorf("invalid config type") } checkRegistry := specificConfig.CheckRegistry - log := log.New("advisor.app") + log := logging.DefaultLogger.With("app", "advisor.app") // Prepare storage client clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{}) @@ -34,6 +33,10 @@ func New(cfg app.Config) (app.App, error) { if err != nil { return nil, err } + typesClient, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind()) + if err != nil { + return nil, err + } // Initialize checks checkMap := map[string]checks.Check{} @@ -46,7 +49,7 @@ func New(cfg app.Config) (app.App, error) { KubeConfig: cfg.KubeConfig, InformerConfig: simple.AppInformerConfig{ ErrorHandler: func(ctx context.Context, err error) { - klog.ErrorS(err, "Informer processing error") + log.WithContext(ctx).Error("Informer processing error", "error", err) }, }, ManagedKinds: []simple.AppManagedKind{ @@ -61,31 +64,33 @@ func New(cfg app.Config) (app.App, error) { } if req.Action == resource.AdmissionActionCreate { go func() { - log.Debug("Processing check", "namespace", req.Object.GetNamespace()) + logger := log.WithContext(ctx).With("check", check.ID()) + logger.Debug("Processing check", "namespace", req.Object.GetNamespace()) requester, err := identity.GetRequester(ctx) if err != nil { - log.Error("Error getting requester", "error", err) + logger.Error("Error getting requester", "error", err) return } ctx = identity.WithRequester(context.Background(), requester) - err = processCheck(ctx, client, req.Object, check) + err = processCheck(ctx, logger, client, typesClient, req.Object, check) if err != nil { - log.Error("Error processing check", "error", err) + logger.Error("Error processing check", "error", err) } }() } if req.Action == resource.AdmissionActionUpdate { go func() { - log.Debug("Updating check", "namespace", req.Object.GetNamespace(), "name", req.Object.GetName()) + logger := log.WithContext(ctx).With("check", check.ID()) + logger.Debug("Updating check", "namespace", req.Object.GetNamespace(), "name", req.Object.GetName()) requester, err := identity.GetRequester(ctx) if err != nil { - log.Error("Error getting requester", "error", err) + logger.Error("Error getting requester", "error", err) return } ctx = identity.WithRequester(context.Background(), requester) - err = processCheckRetry(ctx, client, req.Object, check) + err = processCheckRetry(ctx, logger, client, typesClient, req.Object, check) if err != nil { - log.Error("Error processing check retry", "error", err) + logger.Error("Error processing check retry", "error", err) } }() } @@ -111,14 +116,14 @@ func New(cfg app.Config) (app.App, error) { } // Save check types as resources - ctr, err := checktyperegisterer.New(cfg) + ctr, err := checktyperegisterer.New(cfg, log) if err != nil { return nil, err } a.AddRunnable(ctr) // Start scheduler - csch, err := checkscheduler.New(cfg) + csch, err := checkscheduler.New(cfg, log) if err != nil { return nil, err } diff --git a/apps/advisor/pkg/app/checkregistry/checkregistry.go b/apps/advisor/pkg/app/checkregistry/checkregistry.go index 5b07b827cdd..9fbf0ab66ef 100644 --- a/apps/advisor/pkg/app/checkregistry/checkregistry.go +++ b/apps/advisor/pkg/app/checkregistry/checkregistry.go @@ -2,16 +2,20 @@ package checkregistry import ( "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks/authchecks" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks/configchecks" "github.com/grafana/grafana/apps/advisor/pkg/app/checks/datasourcecheck" "github.com/grafana/grafana/apps/advisor/pkg/app/checks/plugincheck" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/repo" "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" - "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" + "github.com/grafana/grafana/pkg/services/ssosettings" + "github.com/grafana/grafana/pkg/setting" ) type CheckService interface { @@ -24,15 +28,22 @@ type Service struct { pluginContextProvider *plugincontext.Provider pluginClient plugins.Client pluginRepo repo.Service - pluginPreinstall plugininstaller.Preinstall + pluginErrorResolver plugins.ErrorResolver + updateChecker pluginchecker.PluginUpdateChecker + pluginPreinstall pluginchecker.Preinstall managedPlugins managedplugins.Manager provisionedPlugins provisionedplugins.Manager + ssoSettingsSvc ssosettings.Service + GrafanaVersion string + cfg *setting.Cfg } func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore pluginstore.Store, pluginContextProvider *plugincontext.Provider, pluginClient plugins.Client, - pluginRepo repo.Service, pluginPreinstall plugininstaller.Preinstall, managedPlugins managedplugins.Manager, - provisionedPlugins provisionedplugins.Manager, + updateChecker pluginchecker.PluginUpdateChecker, + pluginRepo repo.Service, pluginPreinstall pluginchecker.Preinstall, managedPlugins managedplugins.Manager, + provisionedPlugins provisionedplugins.Manager, ssoSettingsSvc ssosettings.Service, cfg *setting.Cfg, + pluginErrorResolver plugins.ErrorResolver, ) *Service { return &Service{ datasourceSvc: datasourceSvc, @@ -40,9 +51,14 @@ func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore plu pluginContextProvider: pluginContextProvider, pluginClient: pluginClient, pluginRepo: pluginRepo, + pluginErrorResolver: pluginErrorResolver, + updateChecker: updateChecker, pluginPreinstall: pluginPreinstall, managedPlugins: managedPlugins, provisionedPlugins: provisionedPlugins, + ssoSettingsSvc: ssoSettingsSvc, + GrafanaVersion: cfg.BuildVersion, + cfg: cfg, } } @@ -54,14 +70,17 @@ func (s *Service) Checks() []checks.Check { s.pluginContextProvider, s.pluginClient, s.pluginRepo, + s.GrafanaVersion, ), plugincheck.New( s.pluginStore, s.pluginRepo, - s.pluginPreinstall, - s.managedPlugins, - s.provisionedPlugins, + s.updateChecker, + s.pluginErrorResolver, + s.GrafanaVersion, ), + authchecks.New(s.ssoSettingsSvc), + configchecks.New(s.cfg), } } diff --git a/apps/advisor/pkg/app/checks/authchecks/check.go b/apps/advisor/pkg/app/checks/authchecks/check.go new file mode 100644 index 00000000000..74f6a07e7f6 --- /dev/null +++ b/apps/advisor/pkg/app/checks/authchecks/check.go @@ -0,0 +1,67 @@ +package authchecks + +import ( + "context" + "errors" + "fmt" + + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/services/ssosettings" +) + +const ( + CheckID = "ssosetting" +) + +var _ checks.Check = (*check)(nil) + +type check struct { + ssoSettingsService ssosettings.Service +} + +func New(ssoSettingsService ssosettings.Service) checks.Check { + return &check{ + ssoSettingsService: ssoSettingsService, + } +} + +func (c *check) ID() string { + return CheckID +} + +func (c *check) Name() string { + return "SSO setting" +} + +func (c *check) Init(ctx context.Context) error { + return nil +} + +func (c *check) Steps() []checks.Step { + return []checks.Step{ + &listFormatValidation{}, + } +} + +func (c *check) Items(ctx context.Context) ([]any, error) { + ssoSettings, err := c.ssoSettingsService.ListWithRedactedSecrets(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list SSO settings: %w", err) + } + res := make([]any, len(ssoSettings)) + for i, ds := range ssoSettings { + res[i] = ds + } + return res, nil +} + +func (c *check) Item(ctx context.Context, id string) (any, error) { + ssoSetting, err := c.ssoSettingsService.GetForProviderWithRedactedSecrets(ctx, id) + if err != nil { + if errors.Is(err, ssosettings.ErrNotFound) { + return nil, nil + } + return nil, err + } + return ssoSetting, nil +} diff --git a/apps/advisor/pkg/app/checks/authchecks/check_test.go b/apps/advisor/pkg/app/checks/authchecks/check_test.go new file mode 100644 index 00000000000..001c8f2beec --- /dev/null +++ b/apps/advisor/pkg/app/checks/authchecks/check_test.go @@ -0,0 +1,105 @@ +package authchecks + +import ( + "context" + "errors" + "testing" + + "github.com/grafana/grafana/pkg/services/ssosettings" + "github.com/grafana/grafana/pkg/services/ssosettings/models" + "github.com/grafana/grafana/pkg/services/ssosettings/ssosettingstests" // Correct import path for the mock + "github.com/stretchr/testify/require" +) + +func TestCheck_ID(t *testing.T) { + mockService := ssosettingstests.NewMockService(t) + c := New(mockService) + require.Equal(t, CheckID, c.ID()) +} + +func TestCheck_Items(t *testing.T) { + ctx := context.Background() + + t.Run("Success", func(t *testing.T) { + mockService := ssosettingstests.NewMockService(t) + expectedSettings := []*models.SSOSettings{ + {Provider: "google", Settings: map[string]any{"client_id": "id1"}}, + {Provider: "github", Settings: map[string]any{"client_id": "id2"}}, + } + mockService.On("ListWithRedactedSecrets", ctx).Return(expectedSettings, nil) + + c := New(mockService) + items, err := c.Items(ctx) + + require.NoError(t, err) + require.Len(t, items, len(expectedSettings)) + + actualSettings := make([]*models.SSOSettings, len(items)) + for i, item := range items { + setting, ok := item.(*models.SSOSettings) + require.True(t, ok, "Item should be of type *models.SSOSettings") + actualSettings[i] = setting + } + require.Equal(t, expectedSettings, actualSettings) + }) + + t.Run("Error from service", func(t *testing.T) { + mockService := ssosettingstests.NewMockService(t) + expectedErr := errors.New("database error") + mockService.On("ListWithRedactedSecrets", ctx).Return(nil, expectedErr) + + c := New(mockService) + items, err := c.Items(ctx) + + require.Error(t, err) + require.Nil(t, items) + require.ErrorContains(t, err, "failed to list SSO settings") + require.ErrorIs(t, err, expectedErr) + }) +} + +func TestCheck_Item(t *testing.T) { + ctx := context.Background() + providerID := "google" + + t.Run("Success", func(t *testing.T) { + mockService := ssosettingstests.NewMockService(t) + expectedSetting := &models.SSOSettings{ + Provider: providerID, + Settings: map[string]any{"client_id": "id1"}, + } + mockService.On("GetForProviderWithRedactedSecrets", ctx, providerID).Return(expectedSetting, nil) + + c := New(mockService) + item, err := c.Item(ctx, providerID) + + require.NoError(t, err) + require.NotNil(t, item) + actualSetting, ok := item.(*models.SSOSettings) + require.True(t, ok, "Item should be of type *models.SSOSettings") + require.Equal(t, expectedSetting, actualSetting) + }) + + t.Run("Error from service", func(t *testing.T) { + mockService := ssosettingstests.NewMockService(t) + expectedErr := errors.New("not found") + mockService.On("GetForProviderWithRedactedSecrets", ctx, providerID).Return(nil, expectedErr) + + c := New(mockService) + item, err := c.Item(ctx, providerID) + + require.Error(t, err) + require.Nil(t, item) + require.ErrorIs(t, err, expectedErr) + }) + + t.Run("should return nil when sso settings are not found", func(t *testing.T) { + mockService := ssosettingstests.NewMockService(t) + mockService.On("GetForProviderWithRedactedSecrets", ctx, providerID).Return(nil, ssosettings.ErrNotFound) + + c := New(mockService) + item, err := c.Item(ctx, providerID) + require.NoError(t, err) + require.Nil(t, item) + }) +} diff --git a/apps/advisor/pkg/app/checks/authchecks/list_format_validation.go b/apps/advisor/pkg/app/checks/authchecks/list_format_validation.go new file mode 100644 index 00000000000..c2627d735b1 --- /dev/null +++ b/apps/advisor/pkg/app/checks/authchecks/list_format_validation.go @@ -0,0 +1,107 @@ +package authchecks + +import ( + "context" + "fmt" + "strings" + + "github.com/grafana/grafana-app-sdk/logging" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/services/login" + "github.com/grafana/grafana/pkg/services/ssosettings/models" + "github.com/grafana/grafana/pkg/util" +) + +const ListFormatValidationStepID = "sso-list-format-validation" + +// listSettingKeys defines the SSO setting keys that expect a list format (space-separated, comma-separated or JSON array). +var listSettingKeys = []string{ + "allowed_domains", + "allowed_groups", + "allowed_organizations", + "role_values_none", + "role_values_grafana_admin", + "role_values_admin", + "role_values_editor", + "role_values_viewer", +} + +var _ checks.Step = (*listFormatValidation)(nil) + +// listFormatValidation checks if the specified list parameters in SSO settings are in a valid format. +type listFormatValidation struct{} + +func (s *listFormatValidation) ID() string { + return ListFormatValidationStepID +} + +func (s *listFormatValidation) Title() string { + return "SSO List Setting Format Validation" +} + +func (s *listFormatValidation) Description() string { + return "Checks if list configs in SSO settings are in a valid list format (space-separated, comma-separated or JSON array)." +} + +func (s *listFormatValidation) Resolution() string { + return "Configure the relevant SSO setting using a valid format, like space-separated (\"opt1 opt2\"), comma-separated values (\"opt1, opt2\") or JSON array format ([\"opt1\", \"opt2\"])." +} + +func (s *listFormatValidation) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, objToCheck any) ([]advisor.CheckReportFailure, error) { + setting, ok := objToCheck.(*models.SSOSettings) + if !ok { + return nil, fmt.Errorf("invalid item type %T", objToCheck) + } + + reportIssues := make([]advisor.CheckReportFailure, 0, 3) + + for _, settingKey := range listSettingKeys { + currentSettingValue, exists := setting.Settings[settingKey] + if !exists || currentSettingValue == nil { + // If the setting is not present or nil, its format is considered valid (or non-applicable). + continue + } + + currentSettingStr, ok := currentSettingValue.(string) + if !ok { + reportIssues = append(reportIssues, checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + fmt.Sprintf("%s - Invalid type for '%s': expected string, got %T", login.GetAuthProviderLabel(setting.Provider), settingKey, currentSettingValue), + setting.Provider, + s.generateLinks(setting.Provider), + )) + } + + if currentSettingStr == "" { + continue + } + + _, err := util.SplitStringWithError(currentSettingStr) + if err != nil { + reportIssues = append(reportIssues, checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + fmt.Sprintf("%s - Invalid format for '%s': %s", login.GetAuthProviderLabel(setting.Provider), settingKey, currentSettingStr), + setting.Provider, + s.generateLinks(setting.Provider), + )) + } + } + + return reportIssues, nil +} + +func (s *listFormatValidation) generateLinks(provider string) []advisor.CheckErrorLink { + return []advisor.CheckErrorLink{ + { + Url: fmt.Sprintf("https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/%s", strings.ReplaceAll(provider, "_", "-")), + Message: "Check the documentation", + }, + { + Url: fmt.Sprintf("/admin/authentication/%s", provider), + Message: "Configure provider", + }, + } +} diff --git a/apps/advisor/pkg/app/checks/authchecks/list_format_validation_test.go b/apps/advisor/pkg/app/checks/authchecks/list_format_validation_test.go new file mode 100644 index 00000000000..80b7ec38d29 --- /dev/null +++ b/apps/advisor/pkg/app/checks/authchecks/list_format_validation_test.go @@ -0,0 +1,265 @@ +package authchecks + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/grafana/grafana-app-sdk/logging" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/services/login" + "github.com/grafana/grafana/pkg/services/ssosettings/models" + "github.com/stretchr/testify/require" +) + +func TestListFormatValidation_Methods(t *testing.T) { + validator := &listFormatValidation{} + + require.Equal(t, ListFormatValidationStepID, validator.ID()) + require.Equal(t, "SSO List Setting Format Validation", validator.Title()) + require.Equal(t, "Checks if list configs in SSO settings are in a valid list format (space-separated, comma-separated or JSON array).", validator.Description()) + require.Equal(t, "Configure the relevant SSO setting using a valid format, like space-separated (\"opt1 opt2\"), comma-separated values (\"opt1, opt2\") or JSON array format ([\"opt1\", \"opt2\"]).", validator.Resolution()) +} + +func TestListFormatValidation_Run(t *testing.T) { + validator := &listFormatValidation{} + ctx := context.Background() + spec := &advisor.CheckSpec{} + provider := "generic_oauth" + providerLabel := login.GetAuthProviderLabel(provider) + + tests := []struct { + name string + objToCheck any + expectedError string + expectedFailures []advisor.CheckReportFailure + }{ + { + name: "invalid object type", + objToCheck: struct{}{}, + expectedError: "invalid item type struct {}", + }, + { + name: "no relevant settings exist", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{"other_setting": "value"}, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is nil", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": nil, + }, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is empty string", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": "", + }, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is empty JSON array", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": "[]", + }, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is valid (comma-separated)", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": "group1, group2", + }, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is valid (JSON array)", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_domains": `["domain1.com", "domain2.com"]`, + }, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is valid (space-separated)", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": "group1 group2", + }, + }, + expectedFailures: nil, + }, + { + name: "one setting exists and is not a string", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": 123, + }, + }, + expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid type for '%s': expected string, got %T", providerLabel, "allowed_groups", 123), + provider, + generateExpectedLinks(provider), + )}, + }, + { + name: "one setting exists and has invalid format (bad JSON)", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_groups": `["group1", "group2"`, + }, + }, + expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid format for '%s': %s", providerLabel, "allowed_groups", `["group1", "group2"`), + provider, + generateExpectedLinks(provider), + )}, + }, + { + name: "multiple settings exist, first one is invalid (type)", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_domains": 123, + "allowed_groups": "group1, group2", + }, + }, + expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid type for '%s': expected string, got %T", providerLabel, "allowed_domains", 123), + provider, + generateExpectedLinks(provider), + )}, + }, + { + name: "multiple settings exist, second one is invalid (format)", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_domains": "domain1.com", + "allowed_groups": `["group1",`, + }, + }, + expectedFailures: []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid format for '%s': %s", providerLabel, "allowed_groups", `["group1",`), + provider, + generateExpectedLinks(provider), + )}, + }, + { + name: "all settings exist and are valid", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_domains": "d1.com, d2.com", + "allowed_groups": `["g1", "g2"]`, + "allowed_organizations": "org1", + "role_values_none": "None", + "role_values_grafana_admin": "GrafanaAdmin", + "role_values_admin": "Admin", + "role_values_editor": "Editor", + "role_values_viewer": "Viewer", + }, + }, + expectedFailures: nil, + }, + { + name: "returns multiple errors", + objToCheck: &models.SSOSettings{ + Provider: provider, + Settings: map[string]any{ + "allowed_domains": `[\"group1\", \"group2\"]`, + "allowed_groups": `["group1", "group2"`, + "role_values_editor": `["group1-`, + }, + }, + expectedFailures: []advisor.CheckReportFailure{ + checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid format for '%s': %v", providerLabel, "allowed_domains", `[\"group1\", \"group2\"]`), + provider, + generateExpectedLinks(provider), + ), + checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid format for '%s': %s", providerLabel, "allowed_groups", `["group1", "group2"`), + provider, + generateExpectedLinks(provider), + ), + checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + ListFormatValidationStepID, + fmt.Sprintf("%s - Invalid format for '%s': %v", providerLabel, "role_values_editor", `["group1-`), + provider, + generateExpectedLinks(provider), + ), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + failures, err := validator.Run(ctx, logging.DefaultLogger, spec, tt.objToCheck) + + if tt.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedError) + require.Nil(t, failures) + return + } + + require.NoError(t, err) + if len(tt.expectedFailures) > 0 { + require.NotNil(t, failures) + require.Len(t, failures, len(tt.expectedFailures)) + require.ElementsMatch(t, tt.expectedFailures, failures) + } else { + require.Empty(t, failures, "Expected no failure reports, but got some: %+v", failures) + } + }) + } +} + +func generateExpectedLinks(provider string) []advisor.CheckErrorLink { + return []advisor.CheckErrorLink{ + { + Url: fmt.Sprintf("https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/%s", strings.ReplaceAll(provider, "_", "-")), + Message: "Check the documentation", + }, + { + Url: fmt.Sprintf("/admin/authentication/%s", provider), + Message: "Configure provider", + }, + } +} diff --git a/apps/advisor/pkg/app/checks/configchecks/check.go b/apps/advisor/pkg/app/checks/configchecks/check.go new file mode 100644 index 00000000000..3aad7eb7c0c --- /dev/null +++ b/apps/advisor/pkg/app/checks/configchecks/check.go @@ -0,0 +1,48 @@ +package configchecks + +import ( + "context" + + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/setting" +) + +var _ checks.Check = (*check)(nil) + +type check struct { + cfg *setting.Cfg +} + +func New(cfg *setting.Cfg) checks.Check { + return &check{ + cfg: cfg, + } +} + +func (c *check) ID() string { + return "config" +} + +func (c *check) Name() string { + return "config setting" +} + +func (c *check) Items(ctx context.Context) ([]any, error) { + return []any{"security.secret_key"}, nil +} + +func (c *check) Item(ctx context.Context, id string) (any, error) { + return id, nil +} + +func (c *check) Init(ctx context.Context) error { + return nil +} + +func (c *check) Steps() []checks.Step { + return []checks.Step{ + &securityConfigStep{ + securitySection: c.cfg.SectionWithEnvOverrides("security"), + }, + } +} diff --git a/apps/advisor/pkg/app/checks/configchecks/security_config_step.go b/apps/advisor/pkg/app/checks/configchecks/security_config_step.go new file mode 100644 index 00000000000..39d5599cbf3 --- /dev/null +++ b/apps/advisor/pkg/app/checks/configchecks/security_config_step.go @@ -0,0 +1,73 @@ +package configchecks + +import ( + "context" + "fmt" + "strings" + + "github.com/grafana/grafana-app-sdk/logging" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/setting" +) + +const ( + // nolint:gosec // Defined in defaults.ini originally + defaultSecretKey = "SW2YcwTIb9zpOOhoPsMm" +) + +type securityConfigStep struct { + securitySection *setting.DynamicSection +} + +func (s *securityConfigStep) Title() string { + return "Security config check" +} + +func (s *securityConfigStep) Description() string { + return "Check if the Grafana security config is set correctly." +} + +func (s *securityConfigStep) Resolution() string { + return "Follow the documentation for each element." +} + +func (s *securityConfigStep) ID() string { + return "security_config" +} + +func (s *securityConfigStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) { + itemPath, ok := it.(string) + if !ok { + return nil, fmt.Errorf("invalid item type %T", it) + } + items := strings.Split(itemPath, ".") + if len(items) != 2 { + // Not interested in this item + return nil, nil + } + section, key := items[0], items[1] + if section != "security" { + // Only interested in security section + return nil, nil + } + if key == "secret_key" { + secretKey := s.securitySection.Key("secret_key").Value() + if secretKey == defaultSecretKey { + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + "secret_key", + itemPath, + []advisor.CheckErrorLink{ + { + Message: "Avoid default value", + Url: "https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-database-encryption/", + }, + }, + )}, nil + } + } + + return nil, nil +} diff --git a/apps/advisor/pkg/app/checks/configchecks/security_config_step_test.go b/apps/advisor/pkg/app/checks/configchecks/security_config_step_test.go new file mode 100644 index 00000000000..bf1e275e993 --- /dev/null +++ b/apps/advisor/pkg/app/checks/configchecks/security_config_step_test.go @@ -0,0 +1,33 @@ +package configchecks + +import ( + "context" + "testing" + + "github.com/grafana/grafana-app-sdk/logging" + "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/require" +) + +func TestSecurityConfigStepSuccess(t *testing.T) { + cfg := setting.NewCfg() + step := &securityConfigStep{ + securitySection: cfg.SectionWithEnvOverrides("security"), + } + + errs, err := step.Run(context.Background(), logging.DefaultLogger, nil, "security.secret_key") + require.NoError(t, err) + require.Len(t, errs, 0) +} + +func TestSecurityConfigStepFailure(t *testing.T) { + cfg := setting.NewCfg() + cfg.SectionWithEnvOverrides("security").Key("secret_key").SetValue(defaultSecretKey) + step := &securityConfigStep{ + securitySection: cfg.SectionWithEnvOverrides("security"), + } + + errs, err := step.Run(context.Background(), logging.DefaultLogger, nil, "security.secret_key") + require.NoError(t, err) + require.Len(t, errs, 1) +} diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check.go b/apps/advisor/pkg/app/checks/datasourcecheck/check.go index 64d34c3bfad..e5bdc4c4e2e 100644 --- a/apps/advisor/pkg/app/checks/datasourcecheck/check.go +++ b/apps/advisor/pkg/app/checks/datasourcecheck/check.go @@ -4,12 +4,13 @@ import ( "context" "errors" "fmt" + sysruntime "runtime" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-plugin-sdk-go/backend" advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/apps/advisor/pkg/app/checks" "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/repo" "github.com/grafana/grafana/pkg/services/datasources" @@ -30,7 +31,7 @@ type check struct { PluginContextProvider pluginContextProvider PluginClient plugins.Client PluginRepo repo.Service - log log.Logger + GrafanaVersion string } func New( @@ -39,6 +40,7 @@ func New( pluginContextProvider pluginContextProvider, pluginClient plugins.Client, pluginRepo repo.Service, + grafanaVersion string, ) checks.Check { return &check{ DatasourceSvc: datasourceSvc, @@ -46,7 +48,7 @@ func New( PluginContextProvider: pluginContextProvider, PluginClient: pluginClient, PluginRepo: pluginRepo, - log: log.New("advisor.datasourcecheck"), + GrafanaVersion: grafanaVersion, } } @@ -67,28 +69,43 @@ func (c *check) Item(ctx context.Context, id string) (any, error) { if err != nil { return nil, err } - return c.DatasourceSvc.GetDataSource(ctx, &datasources.GetDataSourceQuery{ + ds, err := c.DatasourceSvc.GetDataSource(ctx, &datasources.GetDataSourceQuery{ UID: id, OrgID: requester.GetOrgID(), }) + if err != nil { + if errors.Is(err, datasources.ErrDataSourceNotFound) { + // The data source does not exist, skip the check + return nil, nil + } + return nil, err + } + return ds, nil } func (c *check) ID() string { return CheckID } +func (c *check) Name() string { + return "data source" +} + +func (c *check) Init(ctx context.Context) error { + return nil +} + func (c *check) Steps() []checks.Step { return []checks.Step{ &uidValidationStep{}, &healthCheckStep{ PluginContextProvider: c.PluginContextProvider, PluginClient: c.PluginClient, - log: c.log, }, &missingPluginStep{ - PluginStore: c.PluginStore, - PluginRepo: c.PluginRepo, - log: c.log, + PluginStore: c.PluginStore, + PluginRepo: c.PluginRepo, + GrafanaVersion: c.GrafanaVersion, }, } } @@ -112,7 +129,7 @@ func (s *uidValidationStep) Resolution() string { "target=_blank>documentation for more information or delete the data source and create a new one." } -func (s *uidValidationStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { +func (s *uidValidationStep) Run(ctx context.Context, log logging.Logger, obj *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) { ds, ok := i.(*datasources.DataSource) if !ok { return nil, fmt.Errorf("invalid item type %T", i) @@ -120,13 +137,13 @@ func (s *uidValidationStep) Run(ctx context.Context, obj *advisor.CheckSpec, i a // Data source UID validation err := util.ValidateUID(ds.UID) if err != nil { - return checks.NewCheckReportFailure( + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( advisor.CheckReportFailureSeverityLow, s.ID(), fmt.Sprintf("%s (%s)", ds.Name, ds.UID), ds.UID, []advisor.CheckErrorLink{}, - ), nil + )}, nil } return nil, nil } @@ -134,7 +151,6 @@ func (s *uidValidationStep) Run(ctx context.Context, obj *advisor.CheckSpec, i a type healthCheckStep struct { PluginContextProvider pluginContextProvider PluginClient plugins.Client - log log.Logger } func (s *healthCheckStep) Title() string { @@ -142,7 +158,7 @@ func (s *healthCheckStep) Title() string { } func (s *healthCheckStep) Description() string { - return "Checks if a data sources is healthy." + return "Checks if a data source is healthy." } func (s *healthCheckStep) Resolution() string { @@ -153,7 +169,7 @@ func (s *healthCheckStep) ID() string { return HealthCheckStepID } -func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { +func (s *healthCheckStep) Run(ctx context.Context, log logging.Logger, obj *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) { ds, ok := i.(*datasources.DataSource) if !ok { return nil, fmt.Errorf("invalid item type %T", i) @@ -171,7 +187,7 @@ func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any return nil, nil } // Unable to check health check - s.log.Error("Failed to get plugin context", "datasource_uid", ds.UID, "error", err) + log.Error("Failed to get plugin context", "datasource_uid", ds.UID, "error", err) return nil, nil } req := &backend.CheckHealthRequest{ @@ -181,15 +197,15 @@ func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any resp, err := s.PluginClient.CheckHealth(ctx, req) if err != nil || resp.Status != backend.HealthStatusOk { if err != nil { - s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "error", err) + log.Debug("Failed to check health", "datasource_uid", ds.UID, "error", err) if errors.Is(err, plugins.ErrMethodNotImplemented) || errors.Is(err, plugins.ErrPluginUnavailable) { // The plugin does not support backend health checks return nil, nil } } else { - s.log.Debug("Failed to check health", "datasource_uid", ds.UID, "status", resp.Status, "message", resp.Message) + log.Debug("Failed to check health", "datasource_uid", ds.UID, "status", resp.Status, "message", resp.Message) } - return checks.NewCheckReportFailure( + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( advisor.CheckReportFailureSeverityHigh, s.ID(), ds.Name, @@ -200,15 +216,15 @@ func (s *healthCheckStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID), }, }, - ), nil + )}, nil } return nil, nil } type missingPluginStep struct { - PluginStore pluginstore.Store - PluginRepo repo.Service - log log.Logger + PluginStore pluginstore.Store + PluginRepo repo.Service + GrafanaVersion string } func (s *missingPluginStep) Title() string { @@ -216,7 +232,7 @@ func (s *missingPluginStep) Title() string { } func (s *missingPluginStep) Description() string { - return "Checks if the plugin associated with the data source is installed." + return "Checks if the plugin associated with the data source is installed and available." } func (s *missingPluginStep) Resolution() string { @@ -227,7 +243,7 @@ func (s *missingPluginStep) ID() string { return MissingPluginStepID } -func (s *missingPluginStep) Run(ctx context.Context, obj *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { +func (s *missingPluginStep) Run(ctx context.Context, log logging.Logger, obj *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) { ds, ok := i.(*datasources.DataSource) if !ok { return nil, fmt.Errorf("invalid item type %T", i) @@ -241,22 +257,28 @@ func (s *missingPluginStep) Run(ctx context.Context, obj *advisor.CheckSpec, i a Url: fmt.Sprintf("/connections/datasources/edit/%s", ds.UID), }, } - _, err := s.PluginRepo.PluginInfo(ctx, ds.Type) - if err == nil { + plugins, err := s.PluginRepo.GetPluginsInfo(ctx, repo.GetPluginsInfoOptions{ + IncludeDeprecated: true, + Plugins: []string{ds.Type}, + }, repo.NewCompatOpts(s.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH)) + if err != nil { + return nil, err + } + if len(plugins) > 0 { // Plugin is available in the repo links = append(links, advisor.CheckErrorLink{ - Message: "Install plugin", + Message: "View plugin", Url: fmt.Sprintf("/plugins/%s", ds.Type), }) } // The plugin is not installed - return checks.NewCheckReportFailure( + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( advisor.CheckReportFailureSeverityHigh, s.ID(), ds.Name, ds.UID, links, - ), nil + )}, nil } return nil, nil } diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go b/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go index 7c538da83f7..d9cb532b9d5 100644 --- a/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go +++ b/apps/advisor/pkg/app/checks/datasourcecheck/check_test.go @@ -2,13 +2,12 @@ package datasourcecheck import ( "context" - "errors" "testing" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-plugin-sdk-go/backend" advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/repo" "github.com/grafana/grafana/pkg/services/datasources" @@ -26,14 +25,18 @@ func runChecks(check *check) ([]advisor.CheckReportFailure, error) { } failures := []advisor.CheckReportFailure{} + err = check.Init(ctx) + if err != nil { + return nil, err + } for _, step := range check.Steps() { for _, item := range items { - stepFailures, err := step.Run(ctx, &advisor.CheckSpec{}, item) + stepFailures, err := step.Run(ctx, logging.DefaultLogger, &advisor.CheckSpec{}, item) if err != nil { return nil, err } - if stepFailures != nil { - failures = append(failures, *stepFailures) + if len(stepFailures) > 0 { + failures = append(failures, stepFailures...) } } } @@ -51,7 +54,10 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}} - mockPluginRepo := &MockPluginRepo{exists: true} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{ + {ID: 1, Slug: "prometheus", Status: "active"}, + {ID: 2, Slug: "mysql", Status: "active"}, + }} mockPluginStore := &MockPluginStore{exists: true} check := &check{ @@ -60,7 +66,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -76,7 +81,9 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}} - mockPluginRepo := &MockPluginRepo{exists: true} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{ + {ID: 1, Slug: "prometheus", Status: "active"}, + }} mockPluginStore := &MockPluginStore{exists: true} check := &check{ @@ -85,7 +92,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -102,7 +108,9 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusError}} - mockPluginRepo := &MockPluginRepo{exists: true} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{ + {ID: 1, Slug: "prometheus", Status: "active"}, + }} mockPluginStore := &MockPluginStore{exists: true} check := &check{ @@ -111,7 +119,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -127,7 +134,9 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{err: plugins.ErrMethodNotImplemented} - mockPluginRepo := &MockPluginRepo{exists: true} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{ + {ID: 1, Slug: "prometheus", Status: "active"}, + }} mockPluginStore := &MockPluginStore{exists: true} check := &check{ @@ -136,7 +145,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -151,7 +159,9 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{err: plugins.ErrPluginNotRegistered} - mockPluginRepo := &MockPluginRepo{exists: true} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{ + {ID: 1, Slug: "prometheus", Status: "active"}, + }} mockPluginStore := &MockPluginStore{exists: true} check := &check{ @@ -160,7 +170,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -176,7 +185,9 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}} - mockPluginRepo := &MockPluginRepo{exists: true} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{ + {ID: 1, Slug: "prometheus", Status: "active"}, + }} mockPluginStore := &MockPluginStore{exists: false} check := &check{ @@ -185,7 +196,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -202,7 +212,7 @@ func TestCheck_Run(t *testing.T) { mockDatasourceSvc := &MockDatasourceSvc{dss: datasources} mockPluginContextProvider := &MockPluginContextProvider{pCtx: backend.PluginContext{}} mockPluginClient := &MockPluginClient{res: &backend.CheckHealthResult{Status: backend.HealthStatusOk}} - mockPluginRepo := &MockPluginRepo{exists: false} + mockPluginRepo := &MockPluginRepo{plugins: []repo.PluginInfo{}} mockPluginStore := &MockPluginStore{exists: false} check := &check{ @@ -211,7 +221,6 @@ func TestCheck_Run(t *testing.T) { PluginClient: mockPluginClient, PluginRepo: mockPluginRepo, PluginStore: mockPluginStore, - log: log.New("advisor.datasourcecheck"), } failures, err := runChecks(check) @@ -222,6 +231,19 @@ func TestCheck_Run(t *testing.T) { }) } +func TestCheck_Item(t *testing.T) { + t.Run("should return nil when datasource is not found", func(t *testing.T) { + mockDatasourceSvc := &MockDatasourceSvc{dss: []*datasources.DataSource{}} + check := &check{ + DatasourceSvc: mockDatasourceSvc, + } + ctx := identity.WithRequester(context.Background(), &user.SignedInUser{}) + item, err := check.Item(ctx, "invalid-uid") + assert.NoError(t, err) + assert.Nil(t, item) + }) +} + type MockDatasourceSvc struct { datasources.DataSourceService @@ -232,6 +254,13 @@ func (m *MockDatasourceSvc) GetAllDataSources(context.Context, *datasources.GetA return m.dss, nil } +func (m *MockDatasourceSvc) GetDataSource(context.Context, *datasources.GetDataSourceQuery) (*datasources.DataSource, error) { + if len(m.dss) == 0 { + return nil, datasources.ErrDataSourceNotFound + } + return m.dss[0], nil +} + type MockPluginContextProvider struct { pCtx backend.PluginContext } @@ -264,12 +293,9 @@ func (m *MockPluginStore) Plugin(context.Context, string) (pluginstore.Plugin, b type MockPluginRepo struct { repo.Service - exists bool + plugins []repo.PluginInfo } -func (m *MockPluginRepo) PluginInfo(context.Context, string) (*repo.PluginInfo, error) { - if !m.exists { - return nil, errors.New("plugin not found") - } - return &repo.PluginInfo{}, nil +func (m *MockPluginRepo) GetPluginsInfo(context.Context, repo.GetPluginsInfoOptions, repo.CompatOpts) ([]repo.PluginInfo, error) { + return m.plugins, nil } diff --git a/apps/advisor/pkg/app/checks/ifaces.go b/apps/advisor/pkg/app/checks/ifaces.go index 0dfd13e4f61..6573253b557 100644 --- a/apps/advisor/pkg/app/checks/ifaces.go +++ b/apps/advisor/pkg/app/checks/ifaces.go @@ -3,6 +3,7 @@ package checks import ( "context" + "github.com/grafana/grafana-app-sdk/logging" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" ) @@ -10,12 +11,17 @@ import ( type Check interface { // ID returns the unique identifier of the check ID() string + // Name returns the human-readable name of the check + Name() string // Item returns the item that will be checked Item(ctx context.Context, id string) (any, error) // Items returns the list of items that will be checked Items(ctx context.Context) ([]any, error) // Steps returns the list of steps that will be executed Steps() []Step + // Init initializes the check. It's called before running the steps and should be idempotent. + // The result should not be cached, it should be initialized from scratch. + Init(ctx context.Context) error } // Step is a single step in a check, including its metadata @@ -29,5 +35,5 @@ type Step interface { // Explains the action that needs to be taken to resolve the issue Resolution() string // Run executes the step for an item and returns a report - Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item any) (*advisorv0alpha1.CheckReportFailure, error) + Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error) } diff --git a/apps/advisor/pkg/app/checks/plugincheck/check.go b/apps/advisor/pkg/app/checks/plugincheck/check.go index b5d6edf09fe..3d261f81b67 100644 --- a/apps/advisor/pkg/app/checks/plugincheck/check.go +++ b/apps/advisor/pkg/app/checks/plugincheck/check.go @@ -2,244 +2,135 @@ package plugincheck import ( "context" - "fmt" sysruntime "runtime" - "slices" - "github.com/Masterminds/semver/v3" - advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/apps/advisor/pkg/app/checks" - "github.com/grafana/grafana/pkg/cmd/grafana-cli/services" - "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/repo" - "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" - "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" - "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" ) const ( - CheckID = "plugin" - DeprecationStepID = "deprecation" - UpdateStepID = "update" + CheckID = "plugin" ) func New( pluginStore pluginstore.Store, pluginRepo repo.Service, - pluginPreinstall plugininstaller.Preinstall, - managedPlugins managedplugins.Manager, - provisionedPlugins provisionedplugins.Manager, + updateChecker pluginchecker.PluginUpdateChecker, + pluginErrorResolver plugins.ErrorResolver, + grafanaVersion string, ) checks.Check { return &check{ - PluginStore: pluginStore, - PluginRepo: pluginRepo, - PluginPreinstall: pluginPreinstall, - ManagedPlugins: managedPlugins, - ProvisionedPlugins: provisionedPlugins, + PluginStore: pluginStore, + PluginRepo: pluginRepo, + GrafanaVersion: grafanaVersion, + updateChecker: updateChecker, + pluginErrorResolver: pluginErrorResolver, } } type check struct { - PluginStore pluginstore.Store - PluginRepo repo.Service - PluginPreinstall plugininstaller.Preinstall - ManagedPlugins managedplugins.Manager - ProvisionedPlugins provisionedplugins.Manager + PluginStore pluginstore.Store + PluginRepo repo.Service + updateChecker pluginchecker.PluginUpdateChecker + pluginErrorResolver plugins.ErrorResolver + GrafanaVersion string + pluginIndex map[string]repo.PluginInfo } func (c *check) ID() string { return CheckID } +func (c *check) Name() string { + return "plugin" +} + +type pluginItem struct { + Plugin *pluginstore.Plugin + Err *plugins.Error +} + func (c *check) Items(ctx context.Context) ([]any, error) { ps := c.PluginStore.Plugins(ctx) - res := make([]any, len(ps)) - for i, p := range ps { - res[i] = p + resMap := map[string]*pluginItem{} + for _, p := range ps { + resMap[p.ID] = &pluginItem{ + Plugin: &p, + Err: c.pluginErrorResolver.PluginError(ctx, p.ID), + } } + + // Plugins with errors are not added to the plugin store but + // we still want to show them in the check results so we add them to the map + pluginErrors := c.pluginErrorResolver.PluginErrors(ctx) + for _, e := range pluginErrors { + if _, exists := resMap[e.PluginID]; exists { + resMap[e.PluginID].Err = e + } else { + resMap[e.PluginID] = &pluginItem{ + Plugin: nil, + Err: e, + } + } + } + + res := make([]any, 0, len(resMap)) + for _, p := range resMap { + res = append(res, p) + } + return res, nil } func (c *check) Item(ctx context.Context, id string) (any, error) { p, exists := c.PluginStore.Plugin(ctx, id) if !exists { - return nil, fmt.Errorf("plugin %s not found", id) + return nil, nil } - return p, nil + return &pluginItem{ + Plugin: &p, + Err: c.pluginErrorResolver.PluginError(ctx, p.ID), + }, nil +} + +func (c *check) Init(ctx context.Context) error { + compatOpts := repo.NewCompatOpts(c.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH) + ps := c.PluginStore.Plugins(ctx) + pluginIDs := make([]string, len(ps)) + for i, p := range ps { + pluginIDs[i] = p.ID + } + plugins, err := c.PluginRepo.GetPluginsInfo(ctx, repo.GetPluginsInfoOptions{ + IncludeDeprecated: true, + Plugins: pluginIDs, + }, compatOpts) + if err != nil { + return err + } + c.pluginIndex = make(map[string]repo.PluginInfo) + for _, p := range plugins { + c.pluginIndex[p.Slug] = p + } + return nil } func (c *check) Steps() []checks.Step { return []checks.Step{ &deprecationStep{ - PluginRepo: c.PluginRepo, + GrafanaVersion: c.GrafanaVersion, + updateChecker: c.updateChecker, + pluginIndex: c.pluginIndex, }, &updateStep{ - PluginRepo: c.PluginRepo, - PluginPreinstall: c.PluginPreinstall, - ManagedPlugins: c.ManagedPlugins, - ProvisionedPlugins: c.ProvisionedPlugins, - log: log.New("advisor.check.plugin.update"), + GrafanaVersion: c.GrafanaVersion, + updateChecker: c.updateChecker, + pluginIndex: c.pluginIndex, + }, + &unsignedStep{ + pluginIndex: c.pluginIndex, }, } } - -type deprecationStep struct { - PluginRepo repo.Service -} - -func (s *deprecationStep) Title() string { - return "Deprecation check" -} - -func (s *deprecationStep) Description() string { - return "Check if any installed plugins are deprecated." -} - -func (s *deprecationStep) Resolution() string { - return "Check the documentation for recommended steps or delete the plugin." -} - -func (s *deprecationStep) ID() string { - return DeprecationStepID -} - -func (s *deprecationStep) Run(ctx context.Context, _ *advisor.CheckSpec, it any) (*advisor.CheckReportFailure, error) { - p, ok := it.(pluginstore.Plugin) - if !ok { - return nil, fmt.Errorf("invalid item type %T", it) - } - - // Skip if it's a core plugin - if p.IsCorePlugin() { - return nil, nil - } - - // Check if plugin is deprecated - i, err := s.PluginRepo.PluginInfo(ctx, p.ID) - if err != nil { - // Unable to check deprecation status - return nil, nil - } - if i.Status == "deprecated" { - return checks.NewCheckReportFailure( - advisor.CheckReportFailureSeverityHigh, - s.ID(), - p.Name, - p.ID, - []advisor.CheckErrorLink{ - { - Message: "Admin", - Url: fmt.Sprintf("/plugins/%s", p.ID), - }, - }, - ), nil - } - return nil, nil -} - -type updateStep struct { - PluginRepo repo.Service - PluginPreinstall plugininstaller.Preinstall - ManagedPlugins managedplugins.Manager - ProvisionedPlugins provisionedplugins.Manager - provisionedPlugins []string - log log.Logger -} - -func (s *updateStep) Title() string { - return "Update check" -} - -func (s *updateStep) Description() string { - return "Checks if an installed plugins has a newer version available." -} - -func (s *updateStep) Resolution() string { - return "Go to the plugin admin page and upgrade to the latest version." -} - -func (s *updateStep) ID() string { - return UpdateStepID -} - -func (s *updateStep) Run(ctx context.Context, _ *advisor.CheckSpec, i any) (*advisor.CheckReportFailure, error) { - p, ok := i.(pluginstore.Plugin) - if !ok { - return nil, fmt.Errorf("invalid item type %T", i) - } - - // Skip if it's a core plugin - if p.IsCorePlugin() { - s.log.Debug("Skipping core plugin", "plugin", p.ID) - return nil, nil - } - - // Skip if it's managed or pinned - if s.isManaged(ctx, p.ID) || s.PluginPreinstall.IsPinned(p.ID) { - s.log.Debug("Skipping managed or pinned plugin", "plugin", p.ID) - return nil, nil - } - - // Skip if it's provisioned - if s.isProvisioned(ctx, p.ID) { - s.log.Debug("Skipping provisioned plugin", "plugin", p.ID) - return nil, nil - } - - // Check if plugin has a newer version available - compatOpts := repo.NewCompatOpts(services.GrafanaVersion, sysruntime.GOOS, sysruntime.GOARCH) - info, err := s.PluginRepo.GetPluginArchiveInfo(ctx, p.ID, "", compatOpts) - if err != nil { - // Unable to check updates - return nil, nil - } - if hasUpdate(p, info) { - return checks.NewCheckReportFailure( - advisor.CheckReportFailureSeverityLow, - s.ID(), - p.Name, - p.ID, - []advisor.CheckErrorLink{ - { - Message: "Upgrade", - Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID), - }, - }, - ), nil - } - - return nil, nil -} - -func hasUpdate(current pluginstore.Plugin, latest *repo.PluginArchiveInfo) bool { - // If both versions are semver-valid, compare them - v1, err1 := semver.NewVersion(current.Info.Version) - v2, err2 := semver.NewVersion(latest.Version) - if err1 == nil && err2 == nil { - return v1.LessThan(v2) - } - // In other case, assume that a different latest version will always be newer - return current.Info.Version != latest.Version -} - -func (s *updateStep) isManaged(ctx context.Context, pluginID string) bool { - for _, managedPlugin := range s.ManagedPlugins.ManagedPlugins(ctx) { - if managedPlugin == pluginID { - return true - } - } - return false -} - -func (s *updateStep) isProvisioned(ctx context.Context, pluginID string) bool { - if s.provisionedPlugins == nil { - var err error - s.provisionedPlugins, err = s.ProvisionedPlugins.ProvisionedPlugins(ctx) - if err != nil { - return false - } - } - return slices.Contains(s.provisionedPlugins, pluginID) -} diff --git a/apps/advisor/pkg/app/checks/plugincheck/check_test.go b/apps/advisor/pkg/app/checks/plugincheck/check_test.go index 34fd0b8e544..60b49870af6 100644 --- a/apps/advisor/pkg/app/checks/plugincheck/check_test.go +++ b/apps/advisor/pkg/app/checks/plugincheck/check_test.go @@ -4,11 +4,12 @@ import ( "context" "testing" + "github.com/grafana/grafana-app-sdk/logging" advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/repo" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" - "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" "github.com/stretchr/testify/assert" @@ -18,11 +19,11 @@ func TestRun(t *testing.T) { tests := []struct { name string plugins []pluginstore.Plugin - pluginInfo map[string]*repo.PluginInfo - pluginArchives map[string]*repo.PluginArchiveInfo + pluginInfo []repo.PluginInfo pluginPreinstalled []string pluginManaged []string pluginProvisioned []string + pluginErrors []*plugins.Error expectedFailures []advisor.CheckReportFailure }{ { @@ -35,11 +36,8 @@ func TestRun(t *testing.T) { plugins: []pluginstore.Plugin{ {JSONData: plugins.JSONData{ID: "plugin1", Name: "Plugin 1", Info: plugins.Info{Version: "1.0.0"}}}, }, - pluginInfo: map[string]*repo.PluginInfo{ - "plugin1": {Status: "deprecated"}, - }, - pluginArchives: map[string]*repo.PluginArchiveInfo{ - "plugin1": {Version: "1.0.0"}, + pluginInfo: []repo.PluginInfo{ + {Status: "deprecated", Slug: "plugin1", Version: "1.0.0"}, }, expectedFailures: []advisor.CheckReportFailure{ { @@ -50,7 +48,7 @@ func TestRun(t *testing.T) { Links: []advisor.CheckErrorLink{ { Url: "/plugins/plugin1", - Message: "Admin", + Message: "View plugin", }, }, }, @@ -61,11 +59,8 @@ func TestRun(t *testing.T) { plugins: []pluginstore.Plugin{ {JSONData: plugins.JSONData{ID: "plugin2", Name: "Plugin 2", Info: plugins.Info{Version: "1.0.0"}}}, }, - pluginInfo: map[string]*repo.PluginInfo{ - "plugin2": {Status: "active"}, - }, - pluginArchives: map[string]*repo.PluginArchiveInfo{ - "plugin2": {Version: "1.1.0"}, + pluginInfo: []repo.PluginInfo{ + {Status: "active", Slug: "plugin2", Version: "1.1.0"}, }, expectedFailures: []advisor.CheckReportFailure{ { @@ -87,37 +82,18 @@ func TestRun(t *testing.T) { plugins: []pluginstore.Plugin{ {JSONData: plugins.JSONData{ID: "plugin2", Name: "Plugin 2", Info: plugins.Info{Version: "alpha"}}}, }, - pluginInfo: map[string]*repo.PluginInfo{ - "plugin2": {Status: "active"}, - }, - pluginArchives: map[string]*repo.PluginArchiveInfo{ - "plugin2": {Version: "beta"}, - }, - expectedFailures: []advisor.CheckReportFailure{ - { - Severity: advisor.CheckReportFailureSeverityLow, - StepID: "update", - Item: "Plugin 2", - ItemID: "plugin2", - Links: []advisor.CheckErrorLink{ - { - Url: "/plugins/plugin2?page=version-history", - Message: "Upgrade", - }, - }, - }, + pluginInfo: []repo.PluginInfo{ + {Status: "active", Slug: "plugin2", Version: "beta"}, }, + expectedFailures: []advisor.CheckReportFailure{}, // Cannot be compared because the version is not semver }, { name: "Plugin pinned", plugins: []pluginstore.Plugin{ {JSONData: plugins.JSONData{ID: "plugin3", Name: "Plugin 3", Info: plugins.Info{Version: "1.0.0"}}}, }, - pluginInfo: map[string]*repo.PluginInfo{ - "plugin3": {Status: "active"}, - }, - pluginArchives: map[string]*repo.PluginArchiveInfo{ - "plugin3": {Version: "1.1.0"}, + pluginInfo: []repo.PluginInfo{ + {Status: "deprecated", Slug: "plugin3"}, // This should be ignored }, pluginPreinstalled: []string{"plugin3"}, expectedFailures: []advisor.CheckReportFailure{}, @@ -127,11 +103,8 @@ func TestRun(t *testing.T) { plugins: []pluginstore.Plugin{ {JSONData: plugins.JSONData{ID: "plugin4", Name: "Plugin 4", Info: plugins.Info{Version: "1.0.0"}}}, }, - pluginInfo: map[string]*repo.PluginInfo{ - "plugin4": {Status: "active"}, - }, - pluginArchives: map[string]*repo.PluginArchiveInfo{ - "plugin4": {Version: "1.1.0"}, + pluginInfo: []repo.PluginInfo{ + {Status: "deprecated", Slug: "plugin4", Version: "1.1.0"}, // This should be ignored }, pluginManaged: []string{"plugin4"}, expectedFailures: []advisor.CheckReportFailure{}, @@ -141,48 +114,123 @@ func TestRun(t *testing.T) { plugins: []pluginstore.Plugin{ {JSONData: plugins.JSONData{ID: "plugin5", Name: "Plugin 5", Info: plugins.Info{Version: "1.0.0"}}}, }, - pluginInfo: map[string]*repo.PluginInfo{ - "plugin5": {Status: "active"}, - }, - pluginArchives: map[string]*repo.PluginArchiveInfo{ - "plugin5": {Version: "1.1.0"}, + pluginInfo: []repo.PluginInfo{ + {Status: "deprecated", Slug: "plugin5", Version: "1.1.0"}, // This should be ignored }, pluginProvisioned: []string{"plugin5"}, expectedFailures: []advisor.CheckReportFailure{}, }, + { + name: "Invalid signatures", + plugins: []pluginstore.Plugin{ + {JSONData: plugins.JSONData{ID: "plugin6", Name: "Plugin 6", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusInvalid}, + {JSONData: plugins.JSONData{ID: "plugin7", Name: "Plugin 7", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusModified}, + {JSONData: plugins.JSONData{ID: "plugin8", Name: "Plugin 8", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusUnsigned}, + }, + pluginInfo: []repo.PluginInfo{ + {Status: "active", Slug: "plugin6", Version: "1.0.0"}, + {Status: "active", Slug: "plugin7", Version: "1.0.0"}, + {Status: "active", Slug: "plugin8", Version: "1.0.0"}, + }, + pluginErrors: []*plugins.Error{ + {PluginID: "plugin9", ErrorCode: plugins.ErrorCodeSignatureInvalid}, + {PluginID: "plugin10", ErrorCode: plugins.ErrorCodeSignatureModified}, + {PluginID: "plugin11", ErrorCode: plugins.ErrorCodeSignatureMissing}, + {PluginID: "plugin12", ErrorCode: plugins.ErrorCodeFailedBackendStart}, // This should be ignored atm + }, + expectedFailures: []advisor.CheckReportFailure{ + { + Severity: advisor.CheckReportFailureSeverityLow, + StepID: UnsignedStepID, + Item: "Plugin 6", + ItemID: "plugin6", + Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin6", Message: "View plugin"}}, + }, + { + Severity: advisor.CheckReportFailureSeverityLow, + StepID: UnsignedStepID, + Item: "Plugin 7", + ItemID: "plugin7", + Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin7", Message: "View plugin"}}, + }, + { + Severity: advisor.CheckReportFailureSeverityLow, + StepID: UnsignedStepID, + Item: "Plugin 8", + ItemID: "plugin8", + Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin8", Message: "View plugin"}}, + }, + { + Severity: advisor.CheckReportFailureSeverityHigh, + StepID: UnsignedStepID, + Item: "plugin9", + ItemID: "plugin9", + Links: []advisor.CheckErrorLink{}, + }, + { + Severity: advisor.CheckReportFailureSeverityHigh, + StepID: UnsignedStepID, + Item: "plugin10", + ItemID: "plugin10", + Links: []advisor.CheckErrorLink{}, + }, + { + Severity: advisor.CheckReportFailureSeverityHigh, + StepID: UnsignedStepID, + Item: "plugin11", + ItemID: "plugin11", + Links: []advisor.CheckErrorLink{}, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pluginStore := &mockPluginStore{plugins: tt.plugins} pluginRepo := &mockPluginRepo{ - pluginInfo: tt.pluginInfo, - pluginArchiveInfo: tt.pluginArchives, + pluginInfo: tt.pluginInfo, } pluginPreinstall := &mockPluginPreinstall{pinned: tt.pluginPreinstalled} managedPlugins := &mockManagedPlugins{managed: tt.pluginManaged} provisionedPlugins := &mockProvisionedPlugins{provisioned: tt.pluginProvisioned} - check := New(pluginStore, pluginRepo, pluginPreinstall, managedPlugins, provisionedPlugins) + updateChecker := pluginchecker.ProvideService(managedPlugins, provisionedPlugins, pluginPreinstall) + pluginErrorResolver := &mockPluginErrorResolver{pluginErrors: tt.pluginErrors} + check := New(pluginStore, pluginRepo, updateChecker, pluginErrorResolver, "12.0.0") items, err := check.Items(context.Background()) assert.NoError(t, err) failures := []advisor.CheckReportFailure{} + err = check.Init(context.Background()) + assert.NoError(t, err) for _, step := range check.Steps() { for _, item := range items { - stepFailures, err := step.Run(context.Background(), &advisor.CheckSpec{}, item) + stepFailures, err := step.Run(context.Background(), logging.DefaultLogger, &advisor.CheckSpec{}, item) assert.NoError(t, err) - if stepFailures != nil { - failures = append(failures, *stepFailures) + if len(stepFailures) > 0 { + failures = append(failures, stepFailures...) } } } assert.NoError(t, err) - assert.Equal(t, len(tt.plugins), len(items)) - assert.Equal(t, tt.expectedFailures, failures) + assert.Equal(t, len(tt.plugins)+len(tt.pluginErrors), len(items)) + assert.ElementsMatch(t, tt.expectedFailures, failures) }) } } +func TestCheck_Item(t *testing.T) { + t.Run("should return nil when plugin is not found", func(t *testing.T) { + pluginStore := &mockPluginStore{plugins: []pluginstore.Plugin{}} + check := &check{ + PluginStore: pluginStore, + } + item, err := check.Item(context.Background(), "invalid-uid") + assert.NoError(t, err) + assert.Nil(t, item) + }) +} + type mockPluginStore struct { pluginstore.Store plugins []pluginstore.Plugin @@ -192,22 +240,24 @@ func (m *mockPluginStore) Plugins(ctx context.Context, t ...plugins.Type) []plug return m.plugins } +func (m *mockPluginStore) Plugin(ctx context.Context, id string) (pluginstore.Plugin, bool) { + if len(m.plugins) == 0 { + return pluginstore.Plugin{}, false + } + return m.plugins[0], true +} + type mockPluginRepo struct { repo.Service - pluginInfo map[string]*repo.PluginInfo - pluginArchiveInfo map[string]*repo.PluginArchiveInfo + pluginInfo []repo.PluginInfo } -func (m *mockPluginRepo) PluginInfo(ctx context.Context, id string) (*repo.PluginInfo, error) { - return m.pluginInfo[id], nil -} - -func (m *mockPluginRepo) GetPluginArchiveInfo(ctx context.Context, id, version string, opts repo.CompatOpts) (*repo.PluginArchiveInfo, error) { - return m.pluginArchiveInfo[id], nil +func (m *mockPluginRepo) GetPluginsInfo(ctx context.Context, options repo.GetPluginsInfoOptions, compatOpts repo.CompatOpts) ([]repo.PluginInfo, error) { + return m.pluginInfo, nil } type mockPluginPreinstall struct { - plugininstaller.Preinstall + pluginchecker.Preinstall pinned []string } @@ -237,3 +287,21 @@ type mockProvisionedPlugins struct { func (m *mockProvisionedPlugins) ProvisionedPlugins(ctx context.Context) ([]string, error) { return m.provisioned, nil } + +type mockPluginErrorResolver struct { + plugins.ErrorResolver + pluginErrors []*plugins.Error +} + +func (m *mockPluginErrorResolver) PluginErrors(ctx context.Context) []*plugins.Error { + return m.pluginErrors +} + +func (m *mockPluginErrorResolver) PluginError(ctx context.Context, id string) *plugins.Error { + for _, err := range m.pluginErrors { + if err.PluginID == id { + return err + } + } + return nil +} diff --git a/apps/advisor/pkg/app/checks/plugincheck/deprecation_step.go b/apps/advisor/pkg/app/checks/plugincheck/deprecation_step.go new file mode 100644 index 00000000000..b778e5d8979 --- /dev/null +++ b/apps/advisor/pkg/app/checks/plugincheck/deprecation_step.go @@ -0,0 +1,76 @@ +package plugincheck + +import ( + "context" + "fmt" + + "github.com/grafana/grafana-app-sdk/logging" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/plugins/repo" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" +) + +const ( + DeprecationStepID = "deprecation" +) + +type deprecationStep struct { + GrafanaVersion string + updateChecker pluginchecker.PluginUpdateChecker + pluginIndex map[string]repo.PluginInfo +} + +func (s *deprecationStep) Title() string { + return "Deprecation check" +} + +func (s *deprecationStep) Description() string { + return "Check if any installed plugins are deprecated." +} + +func (s *deprecationStep) Resolution() string { + return "Check the documentation for recommended steps or delete the plugin." +} + +func (s *deprecationStep) ID() string { + return DeprecationStepID +} + +func (s *deprecationStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) { + pi, ok := it.(*pluginItem) + if !ok { + return nil, fmt.Errorf("invalid item type %T", it) + } + p := pi.Plugin + if p == nil { + return nil, nil + } + + if !s.updateChecker.IsUpdatable(ctx, *p) { + return nil, nil + } + + // Check if plugin is deprecated + i, ok := s.pluginIndex[p.ID] + if !ok { + // Unable to check deprecation status + return nil, nil + } + if i.Status == "deprecated" { + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + p.Name, + p.ID, + []advisor.CheckErrorLink{ + { + Message: "View plugin", + Url: fmt.Sprintf("/plugins/%s", p.ID), + }, + }, + )}, nil + } + return nil, nil +} diff --git a/apps/advisor/pkg/app/checks/plugincheck/unsigned_step.go b/apps/advisor/pkg/app/checks/plugincheck/unsigned_step.go new file mode 100644 index 00000000000..a0d212d0f02 --- /dev/null +++ b/apps/advisor/pkg/app/checks/plugincheck/unsigned_step.go @@ -0,0 +1,94 @@ +package plugincheck + +import ( + "context" + "fmt" + "slices" + + "github.com/grafana/grafana-app-sdk/logging" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/repo" +) + +const ( + UnsignedStepID = "unsigned" +) + +type unsignedStep struct { + pluginIndex map[string]repo.PluginInfo +} + +func (s *unsignedStep) Title() string { + return "Plugin signature check" +} + +func (s *unsignedStep) Description() string { + return "Checks has a missing or invalid signature." +} + +func (s *unsignedStep) Resolution() string { + return "For security, we recommend only installing plugins from the catalog. " + + "Review the plugin's status and verify your allowlist if appropriate." +} + +func (s *unsignedStep) ID() string { + return UnsignedStepID +} + +func (s *unsignedStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) { + pi, ok := it.(*pluginItem) + if !ok { + return nil, fmt.Errorf("invalid item type %T", it) + } + + p := pi.Plugin + invalidSignatureTypes := []plugins.SignatureStatus{ + plugins.SignatureStatusUnsigned, + plugins.SignatureStatusModified, + plugins.SignatureStatusInvalid, + } + if p != nil && slices.Contains(invalidSignatureTypes, p.Signature) { + // This will only happen in dev mode or if the plugin is in the unsigned allow list + links := []advisor.CheckErrorLink{} + if _, ok := s.pluginIndex[p.ID]; ok { + links = append(links, advisor.CheckErrorLink{ + Message: "View plugin", + Url: fmt.Sprintf("/plugins/%s", p.ID), + }) + } + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityLow, + s.ID(), + p.Name, + p.ID, + links, + )}, nil + } + + pluginErr := pi.Err + invalidErrorCodeTypes := []plugins.ErrorCode{ + plugins.ErrorCodeSignatureMissing, + plugins.ErrorCodeSignatureInvalid, + plugins.ErrorCodeSignatureModified, + } + if pluginErr != nil && slices.Contains(invalidErrorCodeTypes, pluginErr.ErrorCode) { + links := []advisor.CheckErrorLink{} + if _, ok := s.pluginIndex[pluginErr.PluginID]; ok { + links = append(links, advisor.CheckErrorLink{ + Message: "View plugin", + Url: fmt.Sprintf("/plugins/%s", pluginErr.PluginID), + }) + } + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityHigh, + s.ID(), + pluginErr.PluginID, + pluginErr.PluginID, + links, + )}, nil + } + + return nil, nil +} diff --git a/apps/advisor/pkg/app/checks/plugincheck/update_step.go b/apps/advisor/pkg/app/checks/plugincheck/update_step.go new file mode 100644 index 00000000000..38eb8cf545b --- /dev/null +++ b/apps/advisor/pkg/app/checks/plugincheck/update_step.go @@ -0,0 +1,76 @@ +package plugincheck + +import ( + "context" + "fmt" + + "github.com/grafana/grafana-app-sdk/logging" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" + "github.com/grafana/grafana/apps/advisor/pkg/app/checks" + "github.com/grafana/grafana/pkg/plugins/repo" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" +) + +const ( + UpdateStepID = "update" +) + +type updateStep struct { + GrafanaVersion string + updateChecker pluginchecker.PluginUpdateChecker + pluginIndex map[string]repo.PluginInfo +} + +func (s *updateStep) Title() string { + return "Update check" +} + +func (s *updateStep) Description() string { + return "Checks if an installed plugins has a newer version available." +} + +func (s *updateStep) Resolution() string { + return "Go to the plugin admin page and upgrade to the latest version." +} + +func (s *updateStep) ID() string { + return UpdateStepID +} + +func (s *updateStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) { + pi, ok := it.(*pluginItem) + if !ok { + return nil, fmt.Errorf("invalid item type %T", it) + } + p := pi.Plugin + if p == nil { + return nil, nil + } + + if !s.updateChecker.IsUpdatable(ctx, *p) { + return nil, nil + } + + // Check if plugin has a newer version available + info, ok := s.pluginIndex[p.ID] + if !ok { + // Unable to check updates + return nil, nil + } + if s.updateChecker.CanUpdate(p.ID, p.Info.Version, info.Version, false) { + return []advisor.CheckReportFailure{checks.NewCheckReportFailure( + advisor.CheckReportFailureSeverityLow, + s.ID(), + p.Name, + p.ID, + []advisor.CheckErrorLink{ + { + Message: "Upgrade", + Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID), + }, + }, + )}, nil + } + + return nil, nil +} diff --git a/apps/advisor/pkg/app/checks/utils.go b/apps/advisor/pkg/app/checks/utils.go index 361f75b3d23..7ca9cc473b4 100644 --- a/apps/advisor/pkg/app/checks/utils.go +++ b/apps/advisor/pkg/app/checks/utils.go @@ -3,6 +3,7 @@ package checks import ( "context" "fmt" + "maps" "strconv" "github.com/grafana/authlib/types" @@ -15,6 +16,9 @@ const ( TypeLabel = "advisor.grafana.app/type" StatusAnnotation = "advisor.grafana.app/status" RetryAnnotation = "advisor.grafana.app/retry" + IgnoreStepsAnnotation = "advisor.grafana.app/ignore-steps" + IgnoreStepsAnnotationList = "advisor.grafana.app/ignore-steps-list" + NameAnnotation = "advisor.grafana.app/checktype-name" StatusAnnotationError = "error" StatusAnnotationProcessed = "processed" ) @@ -25,8 +29,8 @@ func NewCheckReportFailure( item string, itemID string, links []advisor.CheckErrorLink, -) *advisor.CheckReportFailure { - return &advisor.CheckReportFailure{ +) advisor.CheckReportFailure { + return advisor.CheckReportFailure{ Severity: severity, StepID: stepID, Item: item, @@ -54,12 +58,28 @@ func GetRetryAnnotation(obj resource.Object) string { return obj.GetAnnotations()[RetryAnnotation] } -func SetStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error { - annotations := obj.GetAnnotations() - if annotations == nil { - annotations = map[string]string{} +func AddAnnotations(ctx context.Context, obj resource.Object, annotations map[string]string) map[string]string { + existingAnnotations := obj.GetAnnotations() + if existingAnnotations == nil { + existingAnnotations = map[string]string{} } - annotations[StatusAnnotation] = status + maps.Copy(existingAnnotations, annotations) + return existingAnnotations +} + +func DeleteAnnotations(ctx context.Context, obj resource.Object, annotations []string) map[string]string { + existingAnnotations := obj.GetAnnotations() + if existingAnnotations == nil { + existingAnnotations = map[string]string{} + } + for _, annotation := range annotations { + delete(existingAnnotations, annotation) + } + return existingAnnotations +} + +func SetStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error { + annotations := AddAnnotations(ctx, obj, map[string]string{StatusAnnotation: status}) return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{ Operations: []resource.PatchOperation{{ Operation: resource.PatchOpAdd, diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go index 053a964259b..b8bf81299bf 100644 --- a/apps/advisor/pkg/app/checkscheduler/checkscheduler.go +++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go @@ -3,45 +3,44 @@ package checkscheduler import ( "context" "fmt" + "math/rand" "sort" "strconv" "time" "github.com/grafana/grafana-app-sdk/app" "github.com/grafana/grafana-app-sdk/k8s" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/resource" "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry" "github.com/grafana/grafana/apps/advisor/pkg/app/checks" - "github.com/grafana/grafana/pkg/infra/log" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/klog/v2" ) -const defaultEvaluationInterval = 24 * time.Hour +const defaultEvaluationInterval = 7 * 24 * time.Hour // 7 days const defaultMaxHistory = 10 // Runner is a "runnable" app used to be able to expose and API endpoint // with the existing checks types. This does not need to be a CRUD resource, but it is // the only way existing at the moment to expose the check types. type Runner struct { - checkRegistry checkregistry.CheckService client resource.Client + typesClient resource.Client evaluationInterval time.Duration maxHistory int namespace string - log log.Logger + log logging.Logger } // NewRunner creates a new Runner. -func New(cfg app.Config) (app.Runnable, error) { +func New(cfg app.Config, log logging.Logger) (app.Runnable, error) { // Read config specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig) if !ok { return nil, fmt.Errorf("invalid config type") } - checkRegistry := specificConfig.CheckRegistry evalInterval, err := getEvaluationInterval(specificConfig.PluginConfig) if err != nil { return nil, err @@ -61,21 +60,26 @@ func New(cfg app.Config) (app.Runnable, error) { if err != nil { return nil, err } + typesClient, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind()) + if err != nil { + return nil, err + } return &Runner{ - checkRegistry: checkRegistry, client: client, + typesClient: typesClient, evaluationInterval: evalInterval, maxHistory: maxHistory, namespace: namespace, - log: log.New("advisor.checkscheduler"), + log: log.With("runner", "advisor.checkscheduler"), }, nil } func (r *Runner) Run(ctx context.Context) error { - lastCreated, err := r.checkLastCreated(ctx) + logger := r.log.WithContext(ctx) + lastCreated, err := r.checkLastCreated(ctx, logger) if err != nil { - r.log.Error("Error getting last check creation time", "error", err) + logger.Error("Error getting last check creation time", "error", err) // Wait for interval to create the next scheduled check lastCreated = time.Now() } else { @@ -83,18 +87,14 @@ func (r *Runner) Run(ctx context.Context) error { if lastCreated.IsZero() { err = r.createChecks(ctx) if err != nil { - klog.Error("Error creating new check reports", "error", err) + logger.Error("Error creating new check reports", "error", err) } else { lastCreated = time.Now() } } } - nextSendInterval := time.Until(lastCreated.Add(r.evaluationInterval)) - if nextSendInterval < time.Minute { - nextSendInterval = 1 * time.Minute - } - + nextSendInterval := getNextSendInterval(lastCreated, r.evaluationInterval) ticker := time.NewTicker(nextSendInterval) defer ticker.Stop() @@ -103,12 +103,12 @@ func (r *Runner) Run(ctx context.Context) error { case <-ticker.C: err = r.createChecks(ctx) if err != nil { - klog.Error("Error creating new check reports", "error", err) + logger.Error("Error creating new check reports", "error", err) } - err = r.cleanupChecks(ctx) + err = r.cleanupChecks(ctx, logger) if err != nil { - klog.Error("Error cleaning up old check reports", "error", err) + logger.Error("Error cleaning up old check reports", "error", err) } if nextSendInterval != r.evaluationInterval { @@ -116,7 +116,6 @@ func (r *Runner) Run(ctx context.Context) error { } ticker.Reset(nextSendInterval) case <-ctx.Done(): - r.markUnprocessedChecksAsErrored(ctx) return ctx.Err() } } @@ -125,7 +124,8 @@ func (r *Runner) Run(ctx context.Context) error { // checkLastCreated returns the creation time of the last check created // regardless of its ID. This assumes that the checks are created in batches // so a batch will have a similar creation time. -func (r *Runner) checkLastCreated(ctx context.Context) (time.Time, error) { +// In case it finds an unprocessed check from a previous run, it will set it to error. +func (r *Runner) checkLastCreated(ctx context.Context, log logging.Logger) (time.Time, error) { list, err := r.client.List(ctx, r.namespace, resource.ListOptions{}) if err != nil { return time.Time{}, err @@ -136,19 +136,40 @@ func (r *Runner) checkLastCreated(ctx context.Context) (time.Time, error) { if itemCreated.After(lastCreated) { lastCreated = itemCreated } + + // If the check is unprocessed, set it to error + if checks.GetStatusAnnotation(item) == "" { + log.Error("Check is unprocessed", "check", item.GetStaticMetadata().Identifier()) + err := checks.SetStatusAnnotation(ctx, r.client, item, checks.StatusAnnotationError) + if err != nil { + log.Error("Error setting check status to error", "error", err) + } + } } return lastCreated, nil } // createChecks creates a new check for each check type in the registry. func (r *Runner) createChecks(ctx context.Context) error { - for _, check := range r.checkRegistry.Checks() { + // List existing CheckType objects + list, err := r.typesClient.List(ctx, r.namespace, resource.ListOptions{}) + if err != nil { + return fmt.Errorf("error listing check types: %w", err) + } + + // Create checks for each CheckType + for _, item := range list.GetItems() { + checkType, ok := item.(*advisorv0alpha1.CheckType) + if !ok { + continue + } + obj := &advisorv0alpha1.Check{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "check-", Namespace: r.namespace, Labels: map[string]string{ - checks.TypeLabel: check.ID(), + checks.TypeLabel: checkType.Spec.Name, }, }, Spec: advisorv0alpha1.CheckSpec{}, @@ -163,7 +184,7 @@ func (r *Runner) createChecks(ctx context.Context) error { } // cleanupChecks deletes the olders checks if the number of checks exceeds the limit. -func (r *Runner) cleanupChecks(ctx context.Context) error { +func (r *Runner) cleanupChecks(ctx context.Context, logger logging.Logger) error { list, err := r.client.List(ctx, r.namespace, resource.ListOptions{Limit: -1}) if err != nil { return err @@ -175,7 +196,7 @@ func (r *Runner) cleanupChecks(ctx context.Context) error { labels := check.GetLabels() checkType, ok := labels[checks.TypeLabel] if !ok { - klog.Error("Check type not found in labels", "check", check) + logger.Error("Check type not found in labels", "check", check) continue } checksByType[checkType] = append(checksByType[checkType], check) @@ -217,6 +238,17 @@ func getEvaluationInterval(pluginConfig map[string]string) (time.Duration, error return evaluationInterval, nil } +func getNextSendInterval(lastCreated time.Time, evaluationInterval time.Duration) time.Duration { + nextSendInterval := time.Until(lastCreated.Add(evaluationInterval)) + // Add random variation of one hour + randomVariation := time.Duration(rand.Int63n(time.Hour.Nanoseconds())) + nextSendInterval += randomVariation + if nextSendInterval < time.Minute { + nextSendInterval = 1 * time.Minute + } + return nextSendInterval +} + func getMaxHistory(pluginConfig map[string]string) (int, error) { maxHistory := defaultMaxHistory configMaxHistory, ok := pluginConfig["max_history"] @@ -229,21 +261,3 @@ func getMaxHistory(pluginConfig map[string]string) (int, error) { } return maxHistory, nil } - -func (r *Runner) markUnprocessedChecksAsErrored(ctx context.Context) { - list, err := r.client.List(ctx, r.namespace, resource.ListOptions{}) - if err != nil { - r.log.Error("Error getting checks", "error", err) - return - } - - for _, check := range list.GetItems() { - if checks.GetStatusAnnotation(check) == "" { - r.log.Error("Check is unprocessed", "check", check.GetStaticMetadata().Identifier()) - err := checks.SetStatusAnnotation(ctx, r.client, check, checks.StatusAnnotationError) - if err != nil { - r.log.Error("Error setting check status to error", "error", err) - } - } - } -} diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go index 16ff33102d6..b699b50e7b7 100644 --- a/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go +++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler_test.go @@ -8,17 +8,16 @@ import ( "testing" "time" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/resource" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/apps/advisor/pkg/app/checks" - "github.com/grafana/grafana/pkg/infra/log" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestRunner_Run(t *testing.T) { t.Run("does not crash when error on list", func(t *testing.T) { - mockCheckService := &MockCheckService{} mockClient := &MockClient{ listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { return nil, errors.New("list error") @@ -28,10 +27,16 @@ func TestRunner_Run(t *testing.T) { }, } + mockTypesClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return &advisorv0alpha1.CheckTypeList{Items: []advisorv0alpha1.CheckType{}}, nil + }, + } + runner := &Runner{ - checkRegistry: mockCheckService, client: mockClient, - log: log.NewNopLogger(), + typesClient: mockTypesClient, + log: logging.DefaultLogger, evaluationInterval: 1 * time.Hour, } @@ -51,32 +56,74 @@ func TestRunner_checkLastCreated_ErrorOnList(t *testing.T) { runner := &Runner{ client: mockClient, - log: log.NewNopLogger(), + log: logging.DefaultLogger, } - lastCreated, err := runner.checkLastCreated(context.Background()) + lastCreated, err := runner.checkLastCreated(context.Background(), logging.DefaultLogger) assert.Error(t, err) assert.True(t, lastCreated.IsZero()) } -func TestRunner_createChecks_ErrorOnCreate(t *testing.T) { - mockCheckService := &MockCheckService{ - checks: []checks.Check{ - &mockCheck{ - id: "check-1", - }, +func TestRunner_checkLastCreated_UnprocessedCheck(t *testing.T) { + patchOperation := resource.PatchOperation{} + identifier := resource.Identifier{} + + mockClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + return &advisorv0alpha1.CheckList{ + Items: []advisorv0alpha1.Check{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "check-1", + }, + }, + }, + }, nil + }, + patchFunc: func(ctx context.Context, id resource.Identifier, patch resource.PatchRequest, options resource.PatchOptions, into resource.Object) error { + patchOperation = patch.Operations[0] + identifier = id + return nil }, } + + runner := &Runner{ + client: mockClient, + log: logging.DefaultLogger, + } + + lastCreated, err := runner.checkLastCreated(context.Background(), logging.DefaultLogger) + assert.NoError(t, err) + assert.True(t, lastCreated.IsZero()) + assert.Equal(t, "check-1", identifier.Name) + assert.Equal(t, "/metadata/annotations", patchOperation.Path) + expectedAnnotations := map[string]string{ + checks.StatusAnnotation: "error", + } + assert.Equal(t, expectedAnnotations, patchOperation.Value) +} + +func TestRunner_createChecks_ErrorOnCreate(t *testing.T) { mockClient := &MockClient{ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { return nil, errors.New("create error") }, } + mockTypesClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + checkType := &advisorv0alpha1.CheckType{} + checkType.Spec.Name = "check-1" + return &advisorv0alpha1.CheckTypeList{ + Items: []advisorv0alpha1.CheckType{*checkType}, + }, nil + }, + } + runner := &Runner{ - checkRegistry: mockCheckService, - client: mockClient, - log: log.NewNopLogger(), + client: mockClient, + typesClient: mockTypesClient, + log: logging.DefaultLogger, } err := runner.createChecks(context.Background()) @@ -84,23 +131,26 @@ func TestRunner_createChecks_ErrorOnCreate(t *testing.T) { } func TestRunner_createChecks_Success(t *testing.T) { - mockCheckService := &MockCheckService{ - checks: []checks.Check{ - &mockCheck{ - id: "check-1", - }, - }, - } mockClient := &MockClient{ createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) { return &advisorv0alpha1.Check{}, nil }, } + mockTypesClient := &MockClient{ + listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { + checkType := &advisorv0alpha1.CheckType{} + checkType.Spec.Name = "check-1" + return &advisorv0alpha1.CheckTypeList{ + Items: []advisorv0alpha1.CheckType{*checkType}, + }, nil + }, + } + runner := &Runner{ - checkRegistry: mockCheckService, - client: mockClient, - log: log.NewNopLogger(), + client: mockClient, + typesClient: mockTypesClient, + log: logging.DefaultLogger, } err := runner.createChecks(context.Background()) @@ -116,10 +166,10 @@ func TestRunner_cleanupChecks_ErrorOnList(t *testing.T) { runner := &Runner{ client: mockClient, - log: log.NewNopLogger(), + log: logging.DefaultLogger, } - err := runner.cleanupChecks(context.Background()) + err := runner.cleanupChecks(context.Background(), logging.DefaultLogger) assert.Error(t, err) } @@ -137,10 +187,10 @@ func TestRunner_cleanupChecks_WithinMax(t *testing.T) { runner := &Runner{ client: mockClient, - log: log.NewNopLogger(), + log: logging.DefaultLogger, } - err := runner.cleanupChecks(context.Background()) + err := runner.cleanupChecks(context.Background(), logging.DefaultLogger) assert.NoError(t, err) } @@ -167,9 +217,9 @@ func TestRunner_cleanupChecks_ErrorOnDelete(t *testing.T) { runner := &Runner{ client: mockClient, maxHistory: defaultMaxHistory, - log: log.NewNopLogger(), + log: logging.DefaultLogger, } - err := runner.cleanupChecks(context.Background()) + err := runner.cleanupChecks(context.Background(), logging.DefaultLogger) assert.ErrorContains(t, err, "delete error") } @@ -203,9 +253,9 @@ func TestRunner_cleanupChecks_Success(t *testing.T) { runner := &Runner{ client: mockClient, maxHistory: defaultMaxHistory, - log: log.NewNopLogger(), + log: logging.DefaultLogger, } - err := runner.cleanupChecks(context.Background()) + err := runner.cleanupChecks(context.Background(), logging.DefaultLogger) assert.NoError(t, err) assert.Equal(t, []string{"check-0"}, itemsDeleted) } @@ -214,7 +264,7 @@ func Test_getEvaluationInterval(t *testing.T) { t.Run("default", func(t *testing.T) { interval, err := getEvaluationInterval(map[string]string{}) assert.NoError(t, err) - assert.Equal(t, 24*time.Hour, interval) + assert.Equal(t, 7*24*time.Hour, interval) }) t.Run("invalid", func(t *testing.T) { @@ -250,47 +300,15 @@ func Test_getMaxHistory(t *testing.T) { }) } -func Test_markUnprocessedChecksAsErrored(t *testing.T) { - checkList := &advisorv0alpha1.CheckList{ - Items: []advisorv0alpha1.Check{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "check-1", - }, - }, - }, - } - patchOperation := resource.PatchOperation{} - identifier := resource.Identifier{} - mockClient := &MockClient{ - listFunc: func(ctx context.Context, namespace string, options resource.ListOptions) (resource.ListObject, error) { - return checkList, nil - }, - patchFunc: func(ctx context.Context, id resource.Identifier, patch resource.PatchRequest, options resource.PatchOptions, into resource.Object) error { - patchOperation = patch.Operations[0] - identifier = id - return nil - }, - } - runner := &Runner{ - client: mockClient, - log: log.NewNopLogger(), - } - runner.markUnprocessedChecksAsErrored(context.Background()) - assert.Equal(t, "check-1", identifier.Name) - assert.Equal(t, "/metadata/annotations", patchOperation.Path) - expectedAnnotations := map[string]string{ - checks.StatusAnnotation: "error", - } - assert.Equal(t, expectedAnnotations, patchOperation.Value) -} - -type MockCheckService struct { - checks []checks.Check -} - -func (m *MockCheckService) Checks() []checks.Check { - return m.checks +func Test_getNextSendInterval(t *testing.T) { + lastCreated := time.Now().Add(-7 * 24 * time.Hour) + evaluationInterval := 7 * 24 * time.Hour + nextSendInterval := getNextSendInterval(lastCreated, evaluationInterval) + // The next send interval should be in < 1 hour + assert.True(t, nextSendInterval < time.Hour) + // Calculate the next send interval again and it should be different + nextSendInterval2 := getNextSendInterval(lastCreated, evaluationInterval) + assert.NotEqual(t, nextSendInterval, nextSendInterval2) } type MockClient struct { @@ -316,18 +334,3 @@ func (m *MockClient) Delete(ctx context.Context, identifier resource.Identifier, func (m *MockClient) PatchInto(ctx context.Context, identifier resource.Identifier, patch resource.PatchRequest, options resource.PatchOptions, into resource.Object) error { return m.patchFunc(ctx, identifier, patch, options, into) } - -type mockCheck struct { - checks.Check - - id string - steps []checks.Step -} - -func (m *mockCheck) ID() string { - return m.id -} - -func (m *mockCheck) Steps() []checks.Step { - return m.steps -} diff --git a/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go index f8c7e222368..aaf9cc80d86 100644 --- a/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go +++ b/apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go @@ -3,15 +3,16 @@ package checktyperegisterer import ( "context" "fmt" + "maps" "time" "github.com/grafana/grafana-app-sdk/app" "github.com/grafana/grafana-app-sdk/k8s" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/resource" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry" "github.com/grafana/grafana/apps/advisor/pkg/app/checks" - "github.com/grafana/grafana/pkg/infra/log" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -23,13 +24,13 @@ type Runner struct { checkRegistry checkregistry.CheckService client resource.Client namespace string - log log.Logger + log logging.Logger retryAttempts int retryDelay time.Duration } // NewRunner creates a new Runner. -func New(cfg app.Config) (app.Runnable, error) { +func New(cfg app.Config, log logging.Logger) (app.Runnable, error) { // Read config specificConfig, ok := cfg.SpecificConfig.(checkregistry.AdvisorAppConfig) if !ok { @@ -52,33 +53,46 @@ func New(cfg app.Config) (app.Runnable, error) { checkRegistry: checkRegistry, client: client, namespace: namespace, - log: log.New("advisor.checktyperegisterer"), - retryAttempts: 3, - retryDelay: time.Second * 5, + log: log.With("runner", "advisor.checktyperegisterer"), + retryAttempts: 5, + retryDelay: time.Second * 10, }, nil } -func (r *Runner) createOrUpdate(ctx context.Context, obj resource.Object) error { +func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj resource.Object) error { id := obj.GetStaticMetadata().Identifier() _, err := r.client.Create(ctx, id, obj, resource.CreateOptions{}) if err != nil { if errors.IsAlreadyExists(err) { // Already exists, update - r.log.Debug("Check type already exists, updating", "identifier", id) - _, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{}) + log.Debug("Check type already exists, updating", "identifier", id) + // Retrieve current annotations to avoid overriding them + current, err := r.client.Get(ctx, obj.GetStaticMetadata().Identifier()) if err != nil { + return err + } + currentAnnotations := current.GetAnnotations() + if currentAnnotations == nil { + currentAnnotations = make(map[string]string) + } + annotations := obj.GetAnnotations() + maps.Copy(currentAnnotations, annotations) + obj.SetAnnotations(currentAnnotations) // This will update the annotations in the object + _, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{}) + if err != nil && !errors.IsAlreadyExists(err) { // Ignore the error, it's probably due to a race condition - r.log.Error("Error updating check type", "error", err) + log.Error("Error updating check type", "error", err) } return nil } return err } - r.log.Debug("Check type registered successfully", "identifier", id) + log.Debug("Check type registered successfully", "identifier", id) return nil } func (r *Runner) Run(ctx context.Context) error { + logger := r.log.WithContext(ctx) for _, t := range r.checkRegistry.Checks() { steps := t.Steps() stepTypes := make([]advisorv0alpha1.CheckTypeStep, len(steps)) @@ -95,8 +109,10 @@ func (r *Runner) Run(ctx context.Context) error { Name: t.ID(), Namespace: r.namespace, Annotations: map[string]string{ + checks.NameAnnotation: t.Name(), // Flag to indicate feature availability - checks.RetryAnnotation: "1", + checks.RetryAnnotation: "1", + checks.IgnoreStepsAnnotation: "1", }, }, Spec: advisorv0alpha1.CheckTypeSpec{ @@ -105,17 +121,19 @@ func (r *Runner) Run(ctx context.Context) error { }, } for i := 0; i < r.retryAttempts; i++ { - err := r.createOrUpdate(ctx, obj) + err := r.createOrUpdate(context.WithoutCancel(ctx), logger, obj) if err != nil { - r.log.Error("Error creating check type, retrying", "error", err, "attempt", i+1) + logger.Error("Error creating check type, retrying", "error", err, "attempt", i+1) if i == r.retryAttempts-1 { - r.log.Error("Unable to register check type") + logger.Error("Unable to register check type") } else { - time.Sleep(r.retryDelay) + // Calculate exponential backoff delay: baseDelay * 2^attempt + delay := r.retryDelay * time.Duration(1< 0 { + reportFailures = append(reportFailures, stepErr...) } }(step, item) } @@ -183,3 +227,18 @@ func runStepsInParallel(ctx context.Context, spec *advisorv0alpha1.CheckSpec, st wg.Wait() return reportFailures, internalErr } + +func filterSteps(checkType resource.Object, steps []checks.Step) ([]checks.Step, error) { + ignoreStepsList := checkType.GetAnnotations()[checks.IgnoreStepsAnnotationList] + if ignoreStepsList != "" { + filteredSteps := []checks.Step{} + ignoreStepsList := strings.Split(ignoreStepsList, ",") + for _, step := range steps { + if !slices.Contains(ignoreStepsList, step.ID()) { + filteredSteps = append(filteredSteps, step) + } + } + return filteredSteps, nil + } + return steps, nil +} diff --git a/apps/advisor/pkg/app/utils_test.go b/apps/advisor/pkg/app/utils_test.go index 5d8e4441a18..f813b9dfea6 100644 --- a/apps/advisor/pkg/app/utils_test.go +++ b/apps/advisor/pkg/app/utils_test.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/resource" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/apps/advisor/pkg/app/checks" @@ -57,14 +58,15 @@ func TestProcessCheck(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{ items: []any{"item"}, } - err = processCheck(ctx, client, obj, check) + err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.NoError(t, err) - assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation]) } func TestProcessMultipleCheckItems(t *testing.T) { @@ -76,6 +78,7 @@ func TestProcessMultipleCheckItems(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() items := make([]any, 100) for i := range items { @@ -89,9 +92,9 @@ func TestProcessMultipleCheckItems(t *testing.T) { items: items, } - err = processCheck(ctx, client, obj, check) + err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.NoError(t, err) - assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation]) r := client.lastValue.(advisorv0alpha1.CheckV0alpha1StatusReport) assert.Equal(t, r.Count, int64(100)) assert.Len(t, r.Failures, 50) @@ -99,12 +102,13 @@ func TestProcessMultipleCheckItems(t *testing.T) { func TestProcessCheck_AlreadyProcessed(t *testing.T) { obj := &advisorv0alpha1.Check{} - obj.SetAnnotations(map[string]string{checks.StatusAnnotation: "processed"}) + obj.SetAnnotations(map[string]string{checks.StatusAnnotation: checks.StatusAnnotationProcessed}) client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{} - err := processCheck(ctx, client, obj, check) + err := processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.NoError(t, err) } @@ -117,6 +121,7 @@ func TestProcessCheck_RunError(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{ @@ -124,9 +129,35 @@ func TestProcessCheck_RunError(t *testing.T) { err: errors.New("run error"), } - err = processCheck(ctx, client, obj, check) + err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.Error(t, err) - assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation]) +} + +func TestProcessCheck_IgnoreSteps(t *testing.T) { + checkType := &advisorv0alpha1.CheckType{} + checkType.SetAnnotations(map[string]string{checks.IgnoreStepsAnnotationList: "mock"}) + typesClient := &mockTypesClient{ + res: checkType, + } + ctx := context.TODO() + check := &mockCheck{ + items: []any{"item"}, + err: errors.New("run error, should not be triggered"), + } + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{}) + meta, err := utils.MetaAccessor(obj) + if err != nil { + t.Fatal(err) + } + meta.SetCreatedBy("user:1") + client := &mockClient{} + + err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check) + assert.NoError(t, err) + assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, "mock", obj.GetAnnotations()[checks.IgnoreStepsAnnotationList]) } func TestProcessCheck_RunRecoversFromPanic(t *testing.T) { @@ -138,6 +169,7 @@ func TestProcessCheck_RunRecoversFromPanic(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{ @@ -145,10 +177,10 @@ func TestProcessCheck_RunRecoversFromPanic(t *testing.T) { runPanics: true, } - err = processCheck(ctx, client, obj, check) + err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.Error(t, err) assert.Contains(t, err.Error(), "panic recovered in step") - assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation]) } func TestProcessCheckRetry_NoRetry(t *testing.T) { @@ -160,11 +192,12 @@ func TestProcessCheckRetry_NoRetry(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{} - err = processCheckRetry(ctx, client, obj, check) + err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.NoError(t, err) } @@ -172,7 +205,7 @@ func TestProcessCheckRetry_RetryError(t *testing.T) { obj := &advisorv0alpha1.Check{} obj.SetAnnotations(map[string]string{ checks.RetryAnnotation: "item", - checks.StatusAnnotation: "processed", + checks.StatusAnnotation: checks.StatusAnnotationProcessed, }) meta, err := utils.MetaAccessor(obj) if err != nil { @@ -180,6 +213,7 @@ func TestProcessCheckRetry_RetryError(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{ @@ -187,16 +221,16 @@ func TestProcessCheckRetry_RetryError(t *testing.T) { err: errors.New("retry error"), } - err = processCheckRetry(ctx, client, obj, check) + err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.Error(t, err) - assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation]) } -func TestProcessCheckRetry_Success(t *testing.T) { +func TestProcessCheckRetry_SkipMissingItem(t *testing.T) { obj := &advisorv0alpha1.Check{} obj.SetAnnotations(map[string]string{ checks.RetryAnnotation: "item", - checks.StatusAnnotation: "processed", + checks.StatusAnnotation: checks.StatusAnnotationProcessed, }) obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{ { @@ -210,15 +244,48 @@ func TestProcessCheckRetry_Success(t *testing.T) { } meta.SetCreatedBy("user:1") client := &mockClient{} + typesClient := &mockTypesClient{} + ctx := context.TODO() + + check := &mockCheck{ + items: []any{nil}, + } + + err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check) + assert.NoError(t, err) + assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation]) + assert.Empty(t, obj.CheckStatus.Report.Failures) +} + +func TestProcessCheckRetry_Success(t *testing.T) { + obj := &advisorv0alpha1.Check{} + obj.SetAnnotations(map[string]string{ + checks.RetryAnnotation: "item", + checks.StatusAnnotation: checks.StatusAnnotationProcessed, + }) + obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{ + { + ItemID: "item", + StepID: "step", + }, + } + meta, err := utils.MetaAccessor(obj) + if err != nil { + t.Fatal(err) + } + meta.SetCreatedBy("user:1") + client := &mockClient{} + typesClient := &mockTypesClient{} ctx := context.TODO() check := &mockCheck{ items: []any{"item"}, } - err = processCheckRetry(ctx, client, obj, check) + err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check) assert.NoError(t, err) - assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation]) + assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation]) assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation]) assert.Empty(t, obj.CheckStatus.Report.Failures) } @@ -233,6 +300,18 @@ func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req return nil } +type mockTypesClient struct { + resource.Client + res resource.Object +} + +func (m *mockTypesClient) Get(ctx context.Context, id resource.Identifier) (resource.Object, error) { + if m.res == nil { + return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil + } + return m.res, nil +} + type mockCheck struct { err error items []any @@ -243,6 +322,10 @@ func (m *mockCheck) ID() string { return "mock" } +func (m *mockCheck) Name() string { + return "Mock" +} + func (m *mockCheck) Items(ctx context.Context) ([]any, error) { return m.items, nil } @@ -251,6 +334,10 @@ func (m *mockCheck) Item(ctx context.Context, id string) (any, error) { return m.items[0], nil } +func (m *mockCheck) Init(ctx context.Context) error { + return nil +} + func (m *mockCheck) Steps() []checks.Step { return []checks.Step{ &mockStep{err: m.err, panics: m.runPanics}, @@ -262,7 +349,7 @@ type mockStep struct { panics bool } -func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, items any) (*advisorv0alpha1.CheckReportFailure, error) { +func (m *mockStep) Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, items any) ([]advisorv0alpha1.CheckReportFailure, error) { if m.panics { panic("panic") } @@ -270,7 +357,7 @@ func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, item return nil, m.err } if _, ok := items.(error); ok { - return &advisorv0alpha1.CheckReportFailure{}, nil + return []advisorv0alpha1.CheckReportFailure{{}}, nil } return nil, nil } diff --git a/apps/alerting/notifications/Makefile b/apps/alerting/notifications/Makefile index df1d9cd7797..d6addf6bd95 100644 --- a/apps/alerting/notifications/Makefile +++ b/apps/alerting/notifications/Makefile @@ -1,8 +1,27 @@ +APP_SDK_VERSION := v0.38.2 +APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION) +APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk + +.PHONY: install-app-sdk +install-app-sdk: $(APP_SDK_BIN) ## Install the Grafana App SDK + +$(APP_SDK_BIN): + @echo "Installing Grafana App SDK version $(APP_SDK_VERSION)" + @mkdir -p $(APP_SDK_DIR) + # The only way to install specific versions of binaries using `go install` + # is by setting GOBIN to the directory you want to install the binary to. + GOBIN=$(APP_SDK_DIR) go install github.com/grafana/grafana-app-sdk/cmd/grafana-app-sdk@$(APP_SDK_VERSION) + @touch $@ + +.PHONY: update-app-sdk +update-app-sdk: ## Update the Grafana App SDK dependency in go.mod + go get github.com/grafana/grafana-app-sdk@$(APP_SDK_VERSION) + go mod tidy + .PHONY: generate -generate: - ## --defencoding=none is needed to avoid infinite loop while generating recursive models (see routingtree.cue) - @grafana-app-sdk generate \ - --gogenpath=./pkg/apis \ - --source=./kinds/ \ - --defencoding=none \ - --noschemasinmanifest +generate: do-generate ## Run Grafana App SDK code generation + +.PHONY: do-generate +do-generate: install-app-sdk update-app-sdk + ## --defencoding=none and noschemasinmanifest are needed to avoid infinite loop while generating recursive models (see routingtree.cue) + @$(APP_SDK_BIN) generate --grouping=group --gogenpath=./pkg/apis --defencoding=none --postprocess --noschemasinmanifest diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index 98ff5fa11d9..aac76670d3b 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -1,50 +1,49 @@ module github.com/grafana/grafana/apps/alerting/notifications -go 1.24.2 - -replace github.com/grafana/grafana => ../../.. +go 1.24.3 require ( - github.com/grafana/grafana v11.4.0-00010101000000-000000000000+incompatible - github.com/grafana/grafana-app-sdk v0.35.1 - k8s.io/apimachinery v0.32.3 - k8s.io/apiserver v0.32.3 + github.com/grafana/grafana-app-sdk v0.38.2 + github.com/grafana/grafana-app-sdk/logging v0.38.2 + k8s.io/apimachinery v0.33.1 + k8s.io/apiserver v0.33.1 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/bwmarrin/snowflake v0.3.0 // indirect + github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jmespath-community/go-jmespath v1.1.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -57,49 +56,54 @@ require ( github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/etcd/api/v3 v3.5.16 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect - go.etcd.io/etcd/client/v3 v3.5.16 // indirect + go.etcd.io/bbolt v1.4.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.21 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect + go.etcd.io/etcd/client/v3 v3.5.21 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect + go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/grpc v1.71.1 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.72.1 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.3 // indirect - k8s.io/client-go v0.32.3 // indirect - k8s.io/component-base v0.32.3 // indirect + k8s.io/api v0.33.1 // indirect + k8s.io/apiextensions-apiserver v0.33.1 // indirect + k8s.io/client-go v0.33.1 // indirect + k8s.io/component-base v0.33.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 6a20491f6c7..4d64d624cff 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -1,21 +1,24 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= -github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -24,12 +27,20 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= -github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -41,10 +52,11 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -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-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -53,30 +65,29 @@ github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK7NGmlX3RI= -github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI= -github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM= -github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2 h1:kvG92f3XbJlQPUcZfXlTNLziI4e8LYeA9Jv2ixmM5Ic= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2/go.mod h1:ll14OJrUGYgXApz3YX6zmxYjRMZHL+pgQjoKBuRzaRs= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/grafana/grafana-app-sdk v0.38.2 h1:raQ1mqCmNJ2AyCSV3m6ehFC0TGVZcqRcM6iOIDT6ZIE= +github.com/grafana/grafana-app-sdk v0.38.2/go.mod h1:LhZ4i2IXGnW5FF/jAczX7SnGFNai7x2qwB8YHNZWiZI= +github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc= +github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= @@ -85,12 +96,13 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jmespath-community/go-jmespath v1.1.1 h1:bFikPhsi/FdmlZhVgSCd2jj1e7G/rw+zyQfyg5UF+L4= -github.com/jmespath-community/go-jmespath v1.1.1/go.mod h1:4gOyFJsR/Gk+05RgTKYrifT7tBPWD8Lubtb5jRrfy9I= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -99,8 +111,12 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -125,43 +141,50 @@ github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= +github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= -github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -174,20 +197,20 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= -go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0= -go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28= -go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q= -go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E= -go.etcd.io/etcd/client/v2 v2.305.16 h1:kQrn9o5czVNaukf2A2At43cE9ZtWauOtf9vRZuiKXow= -go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE= -go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE= -go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50= -go.etcd.io/etcd/pkg/v3 v3.5.16 h1:cnavs5WSPWeK4TYwPYfmcr3Joz9BH+TZ6qoUtz6/+mc= -go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY= -go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk= -go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI= -go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE= -go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s= +go.etcd.io/etcd/api/v3 v3.5.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8= +go.etcd.io/etcd/api/v3 v3.5.21/go.mod h1:c3aH5wcvXv/9dqIw2Y810LDXJfhSYdHQ0vxmP3CCHVY= +go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoBqUTc= +go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs= +go.etcd.io/etcd/client/v2 v2.305.21 h1:eLiFfexc2mE+pTLz9WwnoEsX5JTTpLCYVivKkmVXIRA= +go.etcd.io/etcd/client/v2 v2.305.21/go.mod h1:OKkn4hlYNf43hpjEM3Ke3aRdUkhSl8xjKjSf8eCq2J8= +go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY= +go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU= +go.etcd.io/etcd/pkg/v3 v3.5.21 h1:jUItxeKyrDuVuWhdh0HtjUANwyuzcb7/FAeUfABmQsk= +go.etcd.io/etcd/pkg/v3 v3.5.21/go.mod h1:wpZx8Egv1g4y+N7JAsqi2zoUiBIUWznLjqJbylDjWgU= +go.etcd.io/etcd/raft/v3 v3.5.21 h1:dOmE0mT55dIUsX77TKBLq+RgyumsQuYeiRQnW/ylugk= +go.etcd.io/etcd/raft/v3 v3.5.21/go.mod h1:fmcuY5R2SNkklU4+fKVBQi2biVp5vafMrWUEj4TJ4Cs= +go.etcd.io/etcd/server/v3 v3.5.21 h1:9w0/k12majtgarGmlMVuhwXRI2ob3/d1Ik3X5TKo0yU= +go.etcd.io/etcd/server/v3 v3.5.21/go.mod h1:G1mOzdwuzKT1VRL7SqRchli/qcFrtLBTAQ4lV20sXXo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= @@ -200,6 +223,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0f go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= @@ -208,33 +233,41 @@ go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5J go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= @@ -243,45 +276,65 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= @@ -290,27 +343,35 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8= -k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= -k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k= -k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apiextensions-apiserver v0.33.1 h1:N7ccbSlRN6I2QBcXevB73PixX2dQNIW0ZRuguEE91zI= +k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.1 h1:yLgLUPDVC6tHbNcw5uE9mo1T6ELhJj7B0geifra3Qdo= +k8s.io/apiserver v0.33.1/go.mod h1:VMbE4ArWYLO01omz+k8hFjAdYfc3GVAYPrhP2tTKccs= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= +k8s.io/component-base v0.33.1 h1:EoJ0xA+wr77T+G8p6T3l4efT2oNwbqBVKR71E0tBIaI= +k8s.io/component-base v0.33.1/go.mod h1:guT/w/6piyPfTgq7gfvgetyXMIh10zuXA6cRRm3rDuY= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= diff --git a/apps/alerting/notifications/kinds/v0alpha1/routingtree_spec.cue b/apps/alerting/notifications/kinds/v0alpha1/routingtree_spec.cue index 2c30c9b94a5..26f460627b7 100644 --- a/apps/alerting/notifications/kinds/v0alpha1/routingtree_spec.cue +++ b/apps/alerting/notifications/kinds/v0alpha1/routingtree_spec.cue @@ -26,6 +26,7 @@ RouteTreeSpec: { group_by?: [...string] mute_time_intervals?: [...string] + active_time_intervals?: [...string] routes?: [...#Route] group_wait?: string group_interval?: string diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/constants.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/constants.go index 6122c92cd43..bf2bced1a30 100644 --- a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/constants.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/constants.go @@ -3,16 +3,16 @@ package v0alpha1 import "k8s.io/apimachinery/pkg/runtime/schema" const ( - // Group is the API group used by all kinds in this package - Group = "notifications.alerting.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" + // APIGroup is the API group used by all kinds in this package + APIGroup = "notifications.alerting.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v0alpha1" ) var ( // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, + Group: APIGroup, + Version: APIVersion, } ) diff --git a/apps/alerting/common/constants.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/ext.go similarity index 89% rename from apps/alerting/common/constants.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/ext.go index 2cc7c413276..772e823528d 100644 --- a/apps/alerting/common/constants.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/ext.go @@ -1,4 +1,4 @@ -package common +package v0alpha1 const InternalPrefix = "grafana.com/" const ProvenanceStatusAnnotationKey = InternalPrefix + "provenance" diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/fakes/gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/fakes/timeinterval_gen.go similarity index 82% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/fakes/gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/fakes/timeinterval_gen.go index 86b2fa427fe..3cf2ffc8e7f 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/fakes/gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/fakes/timeinterval_gen.go @@ -7,12 +7,13 @@ import ( "strings" "time" - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1" - "github.com/grafana/grafana/pkg/util" + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1" ) +var UTCLocation = "UTC" + // +k8s:openapi-gen=false -type IntervalMutator func(spec *v0alpha1.Interval) +type IntervalMutator func(spec *v0alpha1.TimeIntervalInterval) // +k8s:openapi-gen=false type IntervalGenerator struct { @@ -35,10 +36,10 @@ func (t IntervalGenerator) generateDaysOfMonth() string { return fmt.Sprintf("%d:%d", from, to) } -func (t IntervalGenerator) generateTimeRange() v0alpha1.TimeRange { +func (t IntervalGenerator) generateTimeRange() v0alpha1.TimeIntervalTimeRange { from := rand.Int63n(1440 / 2) // [0, 719] to := from + rand.Int63n(1440/2) + 1 // from < ([0,719] + [1,720]) < 1440 - return v0alpha1.TimeRange{ + return v0alpha1.TimeIntervalTimeRange{ StartTime: time.Unix(from*60, 0).UTC().Format("15:04"), EndTime: time.Unix(to*60, 0).UTC().Format("15:04"), } @@ -62,23 +63,23 @@ func (t IntervalGenerator) generateLocation() *string { if rand.Int()%3 == 0 { return nil } - return util.Pointer("UTC") + return &UTCLocation } func (t IntervalGenerator) generateMonth() string { return fmt.Sprintf("%d", rand.Intn(12)+1) } -func (t IntervalGenerator) GenerateMany(count int) []v0alpha1.Interval { - result := make([]v0alpha1.Interval, 0, count) +func (t IntervalGenerator) GenerateMany(count int) []v0alpha1.TimeIntervalInterval { + result := make([]v0alpha1.TimeIntervalInterval, 0, count) for i := 0; i < count; i++ { result = append(result, t.Generate()) } return result } -func (t IntervalGenerator) Generate() v0alpha1.Interval { - i := v0alpha1.Interval{ +func (t IntervalGenerator) Generate() v0alpha1.TimeIntervalInterval { + i := v0alpha1.TimeIntervalInterval{ DaysOfMonth: generateMany(rand.Intn(6), true, t.generateDaysOfMonth), Location: t.generateLocation(), Months: generateMany(rand.Intn(3), true, t.generateMonth), diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_codec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_codec_gen.go similarity index 57% rename from apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_codec_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_codec_gen.go index 7b5050a3af4..a821e7f2a11 100644 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_codec_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_codec_gen.go @@ -11,18 +11,18 @@ import ( "github.com/grafana/grafana-app-sdk/resource" ) -// JSONCodec is an implementation of resource.Codec for kubernetes JSON encoding -type JSONCodec struct{} +// ReceiverJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type ReceiverJSONCodec struct{} // Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` -func (*JSONCodec) Read(reader io.Reader, into resource.Object) error { +func (*ReceiverJSONCodec) Read(reader io.Reader, into resource.Object) error { return json.NewDecoder(reader).Decode(into) } // Write writes JSON-encoded bytes into `writer` marshaled from `from` -func (*JSONCodec) Write(writer io.Writer, from resource.Object) error { +func (*ReceiverJSONCodec) Write(writer io.Writer, from resource.Object) error { return json.NewEncoder(writer).Encode(from) } // Interface compliance checks -var _ resource.Codec = &JSONCodec{} +var _ resource.Codec = &ReceiverJSONCodec{} diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/ext.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_ext.go similarity index 76% rename from apps/alerting/notifications/pkg/apis/receiver/v0alpha1/ext.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_ext.go index da17a9f55df..73815d2250b 100644 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/ext.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_ext.go @@ -5,17 +5,15 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apiserver/pkg/registry/generic" - - "github.com/grafana/grafana/apps/alerting/common" ) func (o *Receiver) GetProvenanceStatus() string { if o == nil || o.Annotations == nil { return "" } - s, ok := o.Annotations[common.ProvenanceStatusAnnotationKey] + s, ok := o.Annotations[ProvenanceStatusAnnotationKey] if !ok || s == "" { - return common.ProvenanceStatusNone + return ProvenanceStatusNone } return s } @@ -25,9 +23,9 @@ func (o *Receiver) SetProvenanceStatus(status string) { o.Annotations = make(map[string]string, 1) } if status == "" { - status = common.ProvenanceStatusNone + status = ProvenanceStatusNone } - o.Annotations[common.ProvenanceStatusAnnotationKey] = status + o.Annotations[ProvenanceStatusAnnotationKey] = status } func (o *Receiver) SetAccessControl(action string) { @@ -40,7 +38,7 @@ func (o *Receiver) SetAccessControl(action string) { // AccessControlAnnotation returns the key for the access control annotation for the given action. // Ex. grafana.com/access/canDelete. func AccessControlAnnotation(action string) string { - return fmt.Sprintf("%s%s/%s", common.InternalPrefix, "access", action) + return fmt.Sprintf("%s%s/%s", InternalPrefix, "access", action) } func (o *Receiver) SetInUse(routesCnt int, rules []string) { @@ -54,14 +52,14 @@ func (o *Receiver) SetInUse(routesCnt int, rules []string) { // InUseAnnotation returns the key for the in-use annotation for the given resource. // Ex. grafana.com/inUse/routes, grafana.com/inUse/rules. func InUseAnnotation(resource string) string { - return fmt.Sprintf("%s%s/%s", common.InternalPrefix, "inUse", resource) + return fmt.Sprintf("%s%s/%s", InternalPrefix, "inUse", resource) } -func SelectableFields(obj *Receiver) fields.Set { +func ReceiverSelectableFields(obj *Receiver) fields.Set { if obj == nil { return nil } - selectable := Schema().SelectableFields() + selectable := ReceiverSchema().SelectableFields() set := make(fields.Set, len(selectable)) for _, field := range selectable { f, err := field.FieldValueFunc(obj) diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_metadata_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_metadata_gen.go similarity index 80% rename from apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_metadata_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_metadata_gen.go index 14a00e0563f..9de6fc7e64b 100644 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_metadata_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_metadata_gen.go @@ -9,7 +9,7 @@ import ( // metadata contains embedded CommonMetadata and can be extended with custom string fields // TODO: use CommonMetadata instead of redefining here; currently needs to be defined here // without external reference as using the CommonMetadata reference breaks thema codegen. -type Metadata struct { +type ReceiverMetadata struct { UpdateTimestamp time.Time `json:"updateTimestamp"` CreatedBy string `json:"createdBy"` Uid string `json:"uid"` @@ -22,7 +22,10 @@ type Metadata struct { Labels map[string]string `json:"labels"` } -// NewMetadata creates a new Metadata object. -func NewMetadata() *Metadata { - return &Metadata{} +// NewReceiverMetadata creates a new ReceiverMetadata object. +func NewReceiverMetadata() *ReceiverMetadata { + return &ReceiverMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_object_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_object_gen.go similarity index 92% rename from apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_object_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_object_gen.go index 32d2e03a63e..3ff0363cc88 100644 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_object_gen.go @@ -20,9 +20,9 @@ type Receiver struct { metav1.ObjectMeta `json:"metadata" yaml:"metadata"` // Spec is the spec of the Receiver - Spec Spec `json:"spec" yaml:"spec"` + Spec ReceiverSpec `json:"spec" yaml:"spec"` - Status Status `json:"status" yaml:"status"` + Status ReceiverStatus `json:"status" yaml:"status"` } func (o *Receiver) GetSpec() any { @@ -30,7 +30,7 @@ func (o *Receiver) GetSpec() any { } func (o *Receiver) SetSpec(spec any) error { - cast, ok := spec.(Spec) + cast, ok := spec.(ReceiverSpec) if !ok { return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) } @@ -56,9 +56,9 @@ func (o *Receiver) GetSubresource(name string) (any, bool) { func (o *Receiver) SetSubresource(name string, value any) error { switch name { case "status": - cast, ok := value.(Status) + cast, ok := value.(ReceiverStatus) if !ok { - return fmt.Errorf("cannot set status type %#v, not of type Status", value) + return fmt.Errorf("cannot set status type %#v, not of type ReceiverStatus", value) } o.Status = cast return nil @@ -295,25 +295,25 @@ var _ resource.ListObject = &ReceiverList{} // Copy methods for all subresource types // DeepCopy creates a full deep copy of Spec -func (s *Spec) DeepCopy() *Spec { - cpy := &Spec{} +func (s *ReceiverSpec) DeepCopy() *ReceiverSpec { + cpy := &ReceiverSpec{} s.DeepCopyInto(cpy) return cpy } // DeepCopyInto deep copies Spec into another Spec object -func (s *Spec) DeepCopyInto(dst *Spec) { +func (s *ReceiverSpec) DeepCopyInto(dst *ReceiverSpec) { resource.CopyObjectInto(dst, s) } -// DeepCopy creates a full deep copy of Status -func (s *Status) DeepCopy() *Status { - cpy := &Status{} +// DeepCopy creates a full deep copy of ReceiverStatus +func (s *ReceiverStatus) DeepCopy() *ReceiverStatus { + cpy := &ReceiverStatus{} s.DeepCopyInto(cpy) return cpy } -// DeepCopyInto deep copies Status into another Status object -func (s *Status) DeepCopyInto(dst *Status) { +// DeepCopyInto deep copies ReceiverStatus into another ReceiverStatus object +func (s *ReceiverStatus) DeepCopyInto(dst *ReceiverStatus) { resource.CopyObjectInto(dst, s) } diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_schema_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_schema_gen.go similarity index 86% rename from apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_schema_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_schema_gen.go index d159d0d9cb9..acda5c7d1c9 100644 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_schema_gen.go @@ -13,7 +13,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( schemaReceiver = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &Receiver{}, &ReceiverList{}, resource.WithKind("Receiver"), - resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{ + resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{ FieldSelector: "spec.title", FieldValueFunc: func(o resource.Object) (string, error) { cast, ok := o.(*Receiver) @@ -27,18 +27,18 @@ var ( kindReceiver = resource.Kind{ Schema: schemaReceiver, Codecs: map[resource.KindEncoding]resource.Codec{ - resource.KindEncodingJSON: &JSONCodec{}, + resource.KindEncodingJSON: &ReceiverJSONCodec{}, }, } ) // Kind returns a resource.Kind for this Schema with a JSON codec -func Kind() resource.Kind { +func ReceiverKind() resource.Kind { return kindReceiver } // Schema returns a resource.SimpleSchema representation of Receiver -func Schema() *resource.SimpleSchema { +func ReceiverSchema() *resource.SimpleSchema { return schemaReceiver } diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_spec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_spec_gen.go new file mode 100644 index 00000000000..d43a3c51883 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_spec_gen.go @@ -0,0 +1,32 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type ReceiverIntegration struct { + Uid *string `json:"uid,omitempty"` + Type string `json:"type"` + DisableResolveMessage *bool `json:"disableResolveMessage,omitempty"` + Settings map[string]interface{} `json:"settings"` + SecureFields map[string]bool `json:"secureFields,omitempty"` +} + +// NewReceiverIntegration creates a new ReceiverIntegration object. +func NewReceiverIntegration() *ReceiverIntegration { + return &ReceiverIntegration{ + Settings: map[string]interface{}{}, + } +} + +// +k8s:openapi-gen=true +type ReceiverSpec struct { + Title string `json:"title"` + Integrations []ReceiverIntegration `json:"integrations"` +} + +// NewReceiverSpec creates a new ReceiverSpec object. +func NewReceiverSpec() *ReceiverSpec { + return &ReceiverSpec{ + Integrations: []ReceiverIntegration{}, + } +} diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_status_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_status_gen.go similarity index 53% rename from apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_status_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_status_gen.go index 16a8d994f82..b1b12ddfb33 100644 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_status_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/receiver_status_gen.go @@ -3,42 +3,42 @@ package v0alpha1 // +k8s:openapi-gen=true -type StatusOperatorState struct { +type ReceiverstatusOperatorState struct { // lastEvaluation is the ResourceVersion last evaluated LastEvaluation string `json:"lastEvaluation"` // state describes the state of the lastEvaluation. // It is limited to three possible states for machine evaluation. - State StatusOperatorStateState `json:"state"` + State ReceiverStatusOperatorStateState `json:"state"` // descriptiveState is an optional more descriptive state field which has no requirements on format DescriptiveState *string `json:"descriptiveState,omitempty"` // details contains any extra information that is operator-specific Details map[string]interface{} `json:"details,omitempty"` } -// NewStatusOperatorState creates a new StatusOperatorState object. -func NewStatusOperatorState() *StatusOperatorState { - return &StatusOperatorState{} +// NewReceiverstatusOperatorState creates a new ReceiverstatusOperatorState object. +func NewReceiverstatusOperatorState() *ReceiverstatusOperatorState { + return &ReceiverstatusOperatorState{} } // +k8s:openapi-gen=true -type Status struct { +type ReceiverStatus struct { // operatorStates is a map of operator ID to operator state evaluations. // Any operator which consumes this kind SHOULD add its state evaluation information to this field. - OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"` + OperatorStates map[string]ReceiverstatusOperatorState `json:"operatorStates,omitempty"` // additionalFields is reserved for future use AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` } -// NewStatus creates a new Status object. -func NewStatus() *Status { - return &Status{} +// NewReceiverStatus creates a new ReceiverStatus object. +func NewReceiverStatus() *ReceiverStatus { + return &ReceiverStatus{} } // +k8s:openapi-gen=true -type StatusOperatorStateState string +type ReceiverStatusOperatorStateState string const ( - StatusOperatorStateStateSuccess StatusOperatorStateState = "success" - StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress" - StatusOperatorStateStateFailed StatusOperatorStateState = "failed" + ReceiverStatusOperatorStateStateSuccess ReceiverStatusOperatorStateState = "success" + ReceiverStatusOperatorStateStateInProgress ReceiverStatusOperatorStateState = "in_progress" + ReceiverStatusOperatorStateStateFailed ReceiverStatusOperatorStateState = "failed" ) diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_codec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_codec_gen.go new file mode 100644 index 00000000000..4cd2ab11de1 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// RoutingTreeJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type RoutingTreeJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*RoutingTreeJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*RoutingTreeJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &RoutingTreeJSONCodec{} diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/ext.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_ext.go similarity index 58% rename from apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/ext.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_ext.go index b913c8c5d56..7d89fedaa4a 100644 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/ext.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_ext.go @@ -1,18 +1,14 @@ package v0alpha1 -import ( - "github.com/grafana/grafana/apps/alerting/common" -) - const UserDefinedRoutingTreeName = "user-defined" func (o *RoutingTree) GetProvenanceStatus() string { if o == nil || o.Annotations == nil { return "" } - s, ok := o.Annotations[common.ProvenanceStatusAnnotationKey] + s, ok := o.Annotations[ProvenanceStatusAnnotationKey] if !ok || s == "" { - return common.ProvenanceStatusNone + return ProvenanceStatusNone } return s } @@ -22,7 +18,7 @@ func (o *RoutingTree) SetProvenanceStatus(status string) { o.Annotations = make(map[string]string, 1) } if status == "" { - status = common.ProvenanceStatusNone + status = ProvenanceStatusNone } - o.Annotations[common.ProvenanceStatusAnnotationKey] = status + o.Annotations[ProvenanceStatusAnnotationKey] = status } diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_metadata_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_metadata_gen.go similarity index 79% rename from apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_metadata_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_metadata_gen.go index 14a00e0563f..6391ce78f9f 100644 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_metadata_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_metadata_gen.go @@ -9,7 +9,7 @@ import ( // metadata contains embedded CommonMetadata and can be extended with custom string fields // TODO: use CommonMetadata instead of redefining here; currently needs to be defined here // without external reference as using the CommonMetadata reference breaks thema codegen. -type Metadata struct { +type RoutingTreeMetadata struct { UpdateTimestamp time.Time `json:"updateTimestamp"` CreatedBy string `json:"createdBy"` Uid string `json:"uid"` @@ -22,7 +22,10 @@ type Metadata struct { Labels map[string]string `json:"labels"` } -// NewMetadata creates a new Metadata object. -func NewMetadata() *Metadata { - return &Metadata{} +// NewRoutingTreeMetadata creates a new RoutingTreeMetadata object. +func NewRoutingTreeMetadata() *RoutingTreeMetadata { + return &RoutingTreeMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_object_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_object_gen.go similarity index 91% rename from apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_object_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_object_gen.go index 9a23f6de557..f51fc9732a9 100644 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_object_gen.go @@ -20,9 +20,9 @@ type RoutingTree struct { metav1.ObjectMeta `json:"metadata" yaml:"metadata"` // Spec is the spec of the RoutingTree - Spec Spec `json:"spec" yaml:"spec"` + Spec RoutingTreeSpec `json:"spec" yaml:"spec"` - Status Status `json:"status" yaml:"status"` + Status RoutingTreeStatus `json:"status" yaml:"status"` } func (o *RoutingTree) GetSpec() any { @@ -30,7 +30,7 @@ func (o *RoutingTree) GetSpec() any { } func (o *RoutingTree) SetSpec(spec any) error { - cast, ok := spec.(Spec) + cast, ok := spec.(RoutingTreeSpec) if !ok { return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) } @@ -56,9 +56,9 @@ func (o *RoutingTree) GetSubresource(name string) (any, bool) { func (o *RoutingTree) SetSubresource(name string, value any) error { switch name { case "status": - cast, ok := value.(Status) + cast, ok := value.(RoutingTreeStatus) if !ok { - return fmt.Errorf("cannot set status type %#v, not of type Status", value) + return fmt.Errorf("cannot set status type %#v, not of type RoutingTreeStatus", value) } o.Status = cast return nil @@ -295,25 +295,25 @@ var _ resource.ListObject = &RoutingTreeList{} // Copy methods for all subresource types // DeepCopy creates a full deep copy of Spec -func (s *Spec) DeepCopy() *Spec { - cpy := &Spec{} +func (s *RoutingTreeSpec) DeepCopy() *RoutingTreeSpec { + cpy := &RoutingTreeSpec{} s.DeepCopyInto(cpy) return cpy } // DeepCopyInto deep copies Spec into another Spec object -func (s *Spec) DeepCopyInto(dst *Spec) { +func (s *RoutingTreeSpec) DeepCopyInto(dst *RoutingTreeSpec) { resource.CopyObjectInto(dst, s) } -// DeepCopy creates a full deep copy of Status -func (s *Status) DeepCopy() *Status { - cpy := &Status{} +// DeepCopy creates a full deep copy of RoutingTreeStatus +func (s *RoutingTreeStatus) DeepCopy() *RoutingTreeStatus { + cpy := &RoutingTreeStatus{} s.DeepCopyInto(cpy) return cpy } -// DeepCopyInto deep copies Status into another Status object -func (s *Status) DeepCopyInto(dst *Status) { +// DeepCopyInto deep copies RoutingTreeStatus into another RoutingTreeStatus object +func (s *RoutingTreeStatus) DeepCopyInto(dst *RoutingTreeStatus) { resource.CopyObjectInto(dst, s) } diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_schema_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_schema_gen.go similarity index 85% rename from apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_schema_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_schema_gen.go index 5a7ed4b24c0..6838c9cdebd 100644 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_schema_gen.go @@ -15,18 +15,18 @@ var ( kindRoutingTree = resource.Kind{ Schema: schemaRoutingTree, Codecs: map[resource.KindEncoding]resource.Codec{ - resource.KindEncodingJSON: &JSONCodec{}, + resource.KindEncodingJSON: &RoutingTreeJSONCodec{}, }, } ) // Kind returns a resource.Kind for this Schema with a JSON codec -func Kind() resource.Kind { +func RoutingTreeKind() resource.Kind { return kindRoutingTree } // Schema returns a resource.SimpleSchema representation of RoutingTree -func Schema() *resource.SimpleSchema { +func RoutingTreeSchema() *resource.SimpleSchema { return schemaRoutingTree } diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_spec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_spec_gen.go new file mode 100644 index 00000000000..bd00f12ab68 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_spec_gen.go @@ -0,0 +1,72 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RoutingTreeRouteDefaults struct { + Receiver string `json:"receiver"` + GroupBy []string `json:"group_by,omitempty"` + GroupWait *string `json:"group_wait,omitempty"` + GroupInterval *string `json:"group_interval,omitempty"` + RepeatInterval *string `json:"repeat_interval,omitempty"` +} + +// NewRoutingTreeRouteDefaults creates a new RoutingTreeRouteDefaults object. +func NewRoutingTreeRouteDefaults() *RoutingTreeRouteDefaults { + return &RoutingTreeRouteDefaults{} +} + +// +k8s:openapi-gen=true +type RoutingTreeRoute struct { + Receiver *string `json:"receiver,omitempty"` + Matchers []RoutingTreeMatcher `json:"matchers,omitempty"` + Continue bool `json:"continue"` + GroupBy []string `json:"group_by,omitempty"` + MuteTimeIntervals []string `json:"mute_time_intervals,omitempty"` + ActiveTimeIntervals []string `json:"active_time_intervals,omitempty"` + Routes []RoutingTreeRoute `json:"routes,omitempty"` + GroupWait *string `json:"group_wait,omitempty"` + GroupInterval *string `json:"group_interval,omitempty"` + RepeatInterval *string `json:"repeat_interval,omitempty"` +} + +// NewRoutingTreeRoute creates a new RoutingTreeRoute object. +func NewRoutingTreeRoute() *RoutingTreeRoute { + return &RoutingTreeRoute{} +} + +// +k8s:openapi-gen=true +type RoutingTreeMatcher struct { + Type RoutingTreeMatcherType `json:"type"` + Label string `json:"label"` + Value string `json:"value"` +} + +// NewRoutingTreeMatcher creates a new RoutingTreeMatcher object. +func NewRoutingTreeMatcher() *RoutingTreeMatcher { + return &RoutingTreeMatcher{} +} + +// +k8s:openapi-gen=true +type RoutingTreeSpec struct { + Defaults RoutingTreeRouteDefaults `json:"defaults"` + Routes []RoutingTreeRoute `json:"routes"` +} + +// NewRoutingTreeSpec creates a new RoutingTreeSpec object. +func NewRoutingTreeSpec() *RoutingTreeSpec { + return &RoutingTreeSpec{ + Defaults: *NewRoutingTreeRouteDefaults(), + Routes: []RoutingTreeRoute{}, + } +} + +// +k8s:openapi-gen=true +type RoutingTreeMatcherType string + +const ( + RoutingTreeMatcherTypeEqual RoutingTreeMatcherType = "=" + RoutingTreeMatcherTypeNotEqual RoutingTreeMatcherType = "!=" + RoutingTreeMatcherTypeEqualRegex RoutingTreeMatcherType = "=~" + RoutingTreeMatcherTypeNotEqualRegex RoutingTreeMatcherType = "!~" +) diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_status_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_status_gen.go new file mode 100644 index 00000000000..4ee320a974c --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/routingtree_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RoutingTreestatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State RoutingTreeStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewRoutingTreestatusOperatorState creates a new RoutingTreestatusOperatorState object. +func NewRoutingTreestatusOperatorState() *RoutingTreestatusOperatorState { + return &RoutingTreestatusOperatorState{} +} + +// +k8s:openapi-gen=true +type RoutingTreeStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]RoutingTreestatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewRoutingTreeStatus creates a new RoutingTreeStatus object. +func NewRoutingTreeStatus() *RoutingTreeStatus { + return &RoutingTreeStatus{} +} + +// +k8s:openapi-gen=true +type RoutingTreeStatusOperatorStateState string + +const ( + RoutingTreeStatusOperatorStateStateSuccess RoutingTreeStatusOperatorStateState = "success" + RoutingTreeStatusOperatorStateStateInProgress RoutingTreeStatusOperatorStateState = "in_progress" + RoutingTreeStatusOperatorStateStateFailed RoutingTreeStatusOperatorStateState = "failed" +) diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_codec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_codec_gen.go new file mode 100644 index 00000000000..738a273b8c5 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// TemplateGroupJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type TemplateGroupJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*TemplateGroupJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*TemplateGroupJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &TemplateGroupJSONCodec{} diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/ext.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_ext.go similarity index 68% rename from apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/ext.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_ext.go index a9934d3a110..d72eb72914b 100644 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/ext.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_ext.go @@ -3,8 +3,6 @@ package v0alpha1 import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apiserver/pkg/registry/generic" - - "github.com/grafana/grafana/apps/alerting/common" ) const DefaultTemplateTitle = "Built-in Templates" @@ -13,9 +11,9 @@ func (o *TemplateGroup) GetProvenanceStatus() string { if o == nil || o.Annotations == nil { return "" } - s, ok := o.Annotations[common.ProvenanceStatusAnnotationKey] + s, ok := o.Annotations[ProvenanceStatusAnnotationKey] if !ok || s == "" { - return common.ProvenanceStatusNone + return ProvenanceStatusNone } return s } @@ -25,16 +23,16 @@ func (o *TemplateGroup) SetProvenanceStatus(status string) { o.Annotations = make(map[string]string, 1) } if status == "" { - status = common.ProvenanceStatusNone + status = ProvenanceStatusNone } - o.Annotations[common.ProvenanceStatusAnnotationKey] = status + o.Annotations[ProvenanceStatusAnnotationKey] = status } -func SelectableFields(obj *TemplateGroup) fields.Set { +func TemplateGroupSelectableFields(obj *TemplateGroup) fields.Set { if obj == nil { return nil } - selectable := Schema().SelectableFields() + selectable := TemplateGroupSchema().SelectableFields() set := make(fields.Set, len(selectable)) for _, field := range selectable { f, err := field.FieldValueFunc(obj) diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_metadata_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_metadata_gen.go similarity index 78% rename from apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_metadata_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_metadata_gen.go index 14a00e0563f..639ce76dcb8 100644 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_metadata_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_metadata_gen.go @@ -9,7 +9,7 @@ import ( // metadata contains embedded CommonMetadata and can be extended with custom string fields // TODO: use CommonMetadata instead of redefining here; currently needs to be defined here // without external reference as using the CommonMetadata reference breaks thema codegen. -type Metadata struct { +type TemplateGroupMetadata struct { UpdateTimestamp time.Time `json:"updateTimestamp"` CreatedBy string `json:"createdBy"` Uid string `json:"uid"` @@ -22,7 +22,10 @@ type Metadata struct { Labels map[string]string `json:"labels"` } -// NewMetadata creates a new Metadata object. -func NewMetadata() *Metadata { - return &Metadata{} +// NewTemplateGroupMetadata creates a new TemplateGroupMetadata object. +func NewTemplateGroupMetadata() *TemplateGroupMetadata { + return &TemplateGroupMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_object_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_object_gen.go similarity index 91% rename from apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_object_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_object_gen.go index f1de0477759..459b07343c5 100644 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_object_gen.go @@ -20,9 +20,9 @@ type TemplateGroup struct { metav1.ObjectMeta `json:"metadata" yaml:"metadata"` // Spec is the spec of the TemplateGroup - Spec Spec `json:"spec" yaml:"spec"` + Spec TemplateGroupSpec `json:"spec" yaml:"spec"` - Status Status `json:"status" yaml:"status"` + Status TemplateGroupStatus `json:"status" yaml:"status"` } func (o *TemplateGroup) GetSpec() any { @@ -30,7 +30,7 @@ func (o *TemplateGroup) GetSpec() any { } func (o *TemplateGroup) SetSpec(spec any) error { - cast, ok := spec.(Spec) + cast, ok := spec.(TemplateGroupSpec) if !ok { return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) } @@ -56,9 +56,9 @@ func (o *TemplateGroup) GetSubresource(name string) (any, bool) { func (o *TemplateGroup) SetSubresource(name string, value any) error { switch name { case "status": - cast, ok := value.(Status) + cast, ok := value.(TemplateGroupStatus) if !ok { - return fmt.Errorf("cannot set status type %#v, not of type Status", value) + return fmt.Errorf("cannot set status type %#v, not of type TemplateGroupStatus", value) } o.Status = cast return nil @@ -295,25 +295,25 @@ var _ resource.ListObject = &TemplateGroupList{} // Copy methods for all subresource types // DeepCopy creates a full deep copy of Spec -func (s *Spec) DeepCopy() *Spec { - cpy := &Spec{} +func (s *TemplateGroupSpec) DeepCopy() *TemplateGroupSpec { + cpy := &TemplateGroupSpec{} s.DeepCopyInto(cpy) return cpy } // DeepCopyInto deep copies Spec into another Spec object -func (s *Spec) DeepCopyInto(dst *Spec) { +func (s *TemplateGroupSpec) DeepCopyInto(dst *TemplateGroupSpec) { resource.CopyObjectInto(dst, s) } -// DeepCopy creates a full deep copy of Status -func (s *Status) DeepCopy() *Status { - cpy := &Status{} +// DeepCopy creates a full deep copy of TemplateGroupStatus +func (s *TemplateGroupStatus) DeepCopy() *TemplateGroupStatus { + cpy := &TemplateGroupStatus{} s.DeepCopyInto(cpy) return cpy } -// DeepCopyInto deep copies Status into another Status object -func (s *Status) DeepCopyInto(dst *Status) { +// DeepCopyInto deep copies TemplateGroupStatus into another TemplateGroupStatus object +func (s *TemplateGroupStatus) DeepCopyInto(dst *TemplateGroupStatus) { resource.CopyObjectInto(dst, s) } diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_schema_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_schema_gen.go similarity index 86% rename from apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_schema_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_schema_gen.go index 073e8eb9058..339d0bd6d04 100644 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_schema_gen.go @@ -13,7 +13,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( schemaTemplateGroup = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &TemplateGroup{}, &TemplateGroupList{}, resource.WithKind("TemplateGroup"), - resource.WithPlural("templategroups"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{ + resource.WithPlural("templategroups"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{ FieldSelector: "spec.title", FieldValueFunc: func(o resource.Object) (string, error) { cast, ok := o.(*TemplateGroup) @@ -27,18 +27,18 @@ var ( kindTemplateGroup = resource.Kind{ Schema: schemaTemplateGroup, Codecs: map[resource.KindEncoding]resource.Codec{ - resource.KindEncodingJSON: &JSONCodec{}, + resource.KindEncodingJSON: &TemplateGroupJSONCodec{}, }, } ) // Kind returns a resource.Kind for this Schema with a JSON codec -func Kind() resource.Kind { +func TemplateGroupKind() resource.Kind { return kindTemplateGroup } // Schema returns a resource.SimpleSchema representation of TemplateGroup -func Schema() *resource.SimpleSchema { +func TemplateGroupSchema() *resource.SimpleSchema { return schemaTemplateGroup } diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_spec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_spec_gen.go new file mode 100644 index 00000000000..ecba2959665 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_spec_gen.go @@ -0,0 +1,14 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type TemplateGroupSpec struct { + Title string `json:"title"` + Content string `json:"content"` +} + +// NewTemplateGroupSpec creates a new TemplateGroupSpec object. +func NewTemplateGroupSpec() *TemplateGroupSpec { + return &TemplateGroupSpec{} +} diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_status_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_status_gen.go new file mode 100644 index 00000000000..8312358cda5 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/templategroup_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type TemplateGroupstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State TemplateGroupStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewTemplateGroupstatusOperatorState creates a new TemplateGroupstatusOperatorState object. +func NewTemplateGroupstatusOperatorState() *TemplateGroupstatusOperatorState { + return &TemplateGroupstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type TemplateGroupStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]TemplateGroupstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewTemplateGroupStatus creates a new TemplateGroupStatus object. +func NewTemplateGroupStatus() *TemplateGroupStatus { + return &TemplateGroupStatus{} +} + +// +k8s:openapi-gen=true +type TemplateGroupStatusOperatorStateState string + +const ( + TemplateGroupStatusOperatorStateStateSuccess TemplateGroupStatusOperatorStateState = "success" + TemplateGroupStatusOperatorStateStateInProgress TemplateGroupStatusOperatorStateState = "in_progress" + TemplateGroupStatusOperatorStateStateFailed TemplateGroupStatusOperatorStateState = "failed" +) diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_codec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_codec_gen.go new file mode 100644 index 00000000000..d5598f0e10d --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// TimeIntervalJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type TimeIntervalJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*TimeIntervalJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*TimeIntervalJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &TimeIntervalJSONCodec{} diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/ext.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_ext.go similarity index 67% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/ext.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_ext.go index 481818c7471..df54dd4f2c9 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/ext.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_ext.go @@ -3,17 +3,15 @@ package v0alpha1 import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apiserver/pkg/registry/generic" - - "github.com/grafana/grafana/apps/alerting/common" ) func (o *TimeInterval) GetProvenanceStatus() string { if o == nil || o.Annotations == nil { return "" } - s, ok := o.Annotations[common.ProvenanceStatusAnnotationKey] + s, ok := o.Annotations[ProvenanceStatusAnnotationKey] if !ok || s == "" { - return common.ProvenanceStatusNone + return ProvenanceStatusNone } return s } @@ -23,16 +21,16 @@ func (o *TimeInterval) SetProvenanceStatus(status string) { o.Annotations = make(map[string]string, 1) } if status == "" { - status = common.ProvenanceStatusNone + status = ProvenanceStatusNone } - o.Annotations[common.ProvenanceStatusAnnotationKey] = status + o.Annotations[ProvenanceStatusAnnotationKey] = status } -func SelectableFields(obj *TimeInterval) fields.Set { +func TimeIntervalSelectableFields(obj *TimeInterval) fields.Set { if obj == nil { return nil } - selectable := Schema().SelectableFields() + selectable := TimeIntervalSchema().SelectableFields() set := make(fields.Set, len(selectable)) for _, field := range selectable { f, err := field.FieldValueFunc(obj) diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_metadata_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_metadata_gen.go similarity index 79% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_metadata_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_metadata_gen.go index 14a00e0563f..81ebbdf8cc9 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_metadata_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_metadata_gen.go @@ -9,7 +9,7 @@ import ( // metadata contains embedded CommonMetadata and can be extended with custom string fields // TODO: use CommonMetadata instead of redefining here; currently needs to be defined here // without external reference as using the CommonMetadata reference breaks thema codegen. -type Metadata struct { +type TimeIntervalMetadata struct { UpdateTimestamp time.Time `json:"updateTimestamp"` CreatedBy string `json:"createdBy"` Uid string `json:"uid"` @@ -22,7 +22,10 @@ type Metadata struct { Labels map[string]string `json:"labels"` } -// NewMetadata creates a new Metadata object. -func NewMetadata() *Metadata { - return &Metadata{} +// NewTimeIntervalMetadata creates a new TimeIntervalMetadata object. +func NewTimeIntervalMetadata() *TimeIntervalMetadata { + return &TimeIntervalMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_object_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_object_gen.go similarity index 91% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_object_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_object_gen.go index 9952baeab8a..b4646bb8422 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_object_gen.go @@ -20,9 +20,9 @@ type TimeInterval struct { metav1.ObjectMeta `json:"metadata" yaml:"metadata"` // Spec is the spec of the TimeInterval - Spec Spec `json:"spec" yaml:"spec"` + Spec TimeIntervalSpec `json:"spec" yaml:"spec"` - Status Status `json:"status" yaml:"status"` + Status TimeIntervalStatus `json:"status" yaml:"status"` } func (o *TimeInterval) GetSpec() any { @@ -30,7 +30,7 @@ func (o *TimeInterval) GetSpec() any { } func (o *TimeInterval) SetSpec(spec any) error { - cast, ok := spec.(Spec) + cast, ok := spec.(TimeIntervalSpec) if !ok { return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) } @@ -56,9 +56,9 @@ func (o *TimeInterval) GetSubresource(name string) (any, bool) { func (o *TimeInterval) SetSubresource(name string, value any) error { switch name { case "status": - cast, ok := value.(Status) + cast, ok := value.(TimeIntervalStatus) if !ok { - return fmt.Errorf("cannot set status type %#v, not of type Status", value) + return fmt.Errorf("cannot set status type %#v, not of type TimeIntervalStatus", value) } o.Status = cast return nil @@ -295,25 +295,25 @@ var _ resource.ListObject = &TimeIntervalList{} // Copy methods for all subresource types // DeepCopy creates a full deep copy of Spec -func (s *Spec) DeepCopy() *Spec { - cpy := &Spec{} +func (s *TimeIntervalSpec) DeepCopy() *TimeIntervalSpec { + cpy := &TimeIntervalSpec{} s.DeepCopyInto(cpy) return cpy } // DeepCopyInto deep copies Spec into another Spec object -func (s *Spec) DeepCopyInto(dst *Spec) { +func (s *TimeIntervalSpec) DeepCopyInto(dst *TimeIntervalSpec) { resource.CopyObjectInto(dst, s) } -// DeepCopy creates a full deep copy of Status -func (s *Status) DeepCopy() *Status { - cpy := &Status{} +// DeepCopy creates a full deep copy of TimeIntervalStatus +func (s *TimeIntervalStatus) DeepCopy() *TimeIntervalStatus { + cpy := &TimeIntervalStatus{} s.DeepCopyInto(cpy) return cpy } -// DeepCopyInto deep copies Status into another Status object -func (s *Status) DeepCopyInto(dst *Status) { +// DeepCopyInto deep copies TimeIntervalStatus into another TimeIntervalStatus object +func (s *TimeIntervalStatus) DeepCopyInto(dst *TimeIntervalStatus) { resource.CopyObjectInto(dst, s) } diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_schema_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_schema_gen.go similarity index 86% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_schema_gen.go rename to apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_schema_gen.go index af8ff6454a5..a6c809e0d3b 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_schema_gen.go @@ -13,7 +13,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( schemaTimeInterval = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &TimeInterval{}, &TimeIntervalList{}, resource.WithKind("TimeInterval"), - resource.WithPlural("timeintervals"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{ + resource.WithPlural("timeintervals"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{ FieldSelector: "spec.name", FieldValueFunc: func(o resource.Object) (string, error) { cast, ok := o.(*TimeInterval) @@ -27,18 +27,18 @@ var ( kindTimeInterval = resource.Kind{ Schema: schemaTimeInterval, Codecs: map[resource.KindEncoding]resource.Codec{ - resource.KindEncodingJSON: &JSONCodec{}, + resource.KindEncodingJSON: &TimeIntervalJSONCodec{}, }, } ) // Kind returns a resource.Kind for this Schema with a JSON codec -func Kind() resource.Kind { +func TimeIntervalKind() resource.Kind { return kindTimeInterval } // Schema returns a resource.SimpleSchema representation of TimeInterval -func Schema() *resource.SimpleSchema { +func TimeIntervalSchema() *resource.SimpleSchema { return schemaTimeInterval } diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_spec_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_spec_gen.go new file mode 100644 index 00000000000..4f943026beb --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_spec_gen.go @@ -0,0 +1,42 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type TimeIntervalInterval struct { + Times []TimeIntervalTimeRange `json:"times,omitempty"` + Weekdays []string `json:"weekdays,omitempty"` + DaysOfMonth []string `json:"days_of_month,omitempty"` + Months []string `json:"months,omitempty"` + Years []string `json:"years,omitempty"` + Location *string `json:"location,omitempty"` +} + +// NewTimeIntervalInterval creates a new TimeIntervalInterval object. +func NewTimeIntervalInterval() *TimeIntervalInterval { + return &TimeIntervalInterval{} +} + +// +k8s:openapi-gen=true +type TimeIntervalTimeRange struct { + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` +} + +// NewTimeIntervalTimeRange creates a new TimeIntervalTimeRange object. +func NewTimeIntervalTimeRange() *TimeIntervalTimeRange { + return &TimeIntervalTimeRange{} +} + +// +k8s:openapi-gen=true +type TimeIntervalSpec struct { + Name string `json:"name"` + TimeIntervals []TimeIntervalInterval `json:"time_intervals"` +} + +// NewTimeIntervalSpec creates a new TimeIntervalSpec object. +func NewTimeIntervalSpec() *TimeIntervalSpec { + return &TimeIntervalSpec{ + TimeIntervals: []TimeIntervalInterval{}, + } +} diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_status_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_status_gen.go new file mode 100644 index 00000000000..a6128624acd --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/timeinterval_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type TimeIntervalstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State TimeIntervalStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewTimeIntervalstatusOperatorState creates a new TimeIntervalstatusOperatorState object. +func NewTimeIntervalstatusOperatorState() *TimeIntervalstatusOperatorState { + return &TimeIntervalstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type TimeIntervalStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]TimeIntervalstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewTimeIntervalStatus creates a new TimeIntervalStatus object. +func NewTimeIntervalStatus() *TimeIntervalStatus { + return &TimeIntervalStatus{} +} + +// +k8s:openapi-gen=true +type TimeIntervalStatusOperatorStateState string + +const ( + TimeIntervalStatusOperatorStateStateSuccess TimeIntervalStatusOperatorStateState = "success" + TimeIntervalStatusOperatorStateStateInProgress TimeIntervalStatusOperatorStateState = "in_progress" + TimeIntervalStatusOperatorStateStateFailed TimeIntervalStatusOperatorStateState = "failed" +) diff --git a/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/zz_openapi_gen.go b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/zz_openapi_gen.go new file mode 100644 index 00000000000..e79028361b2 --- /dev/null +++ b/apps/alerting/notifications/pkg/apis/alerting/v0alpha1/zz_openapi_gen.go @@ -0,0 +1,1314 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by grafana-app-sdk. DO NOT EDIT. + +package v0alpha1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.Receiver": schema_pkg_apis_alerting_v0alpha1_Receiver(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverIntegration": schema_pkg_apis_alerting_v0alpha1_ReceiverIntegration(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverList": schema_pkg_apis_alerting_v0alpha1_ReceiverList(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverSpec": schema_pkg_apis_alerting_v0alpha1_ReceiverSpec(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverStatus": schema_pkg_apis_alerting_v0alpha1_ReceiverStatus(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverstatusOperatorState": schema_pkg_apis_alerting_v0alpha1_ReceiverstatusOperatorState(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTree": schema_pkg_apis_alerting_v0alpha1_RoutingTree(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeList": schema_pkg_apis_alerting_v0alpha1_RoutingTreeList(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeMatcher": schema_pkg_apis_alerting_v0alpha1_RoutingTreeMatcher(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRoute": schema_pkg_apis_alerting_v0alpha1_RoutingTreeRoute(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRouteDefaults": schema_pkg_apis_alerting_v0alpha1_RoutingTreeRouteDefaults(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeSpec": schema_pkg_apis_alerting_v0alpha1_RoutingTreeSpec(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeStatus": schema_pkg_apis_alerting_v0alpha1_RoutingTreeStatus(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreestatusOperatorState": schema_pkg_apis_alerting_v0alpha1_RoutingTreestatusOperatorState(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroup": schema_pkg_apis_alerting_v0alpha1_TemplateGroup(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupList": schema_pkg_apis_alerting_v0alpha1_TemplateGroupList(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupSpec": schema_pkg_apis_alerting_v0alpha1_TemplateGroupSpec(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupStatus": schema_pkg_apis_alerting_v0alpha1_TemplateGroupStatus(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupstatusOperatorState": schema_pkg_apis_alerting_v0alpha1_TemplateGroupstatusOperatorState(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeInterval": schema_pkg_apis_alerting_v0alpha1_TimeInterval(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalInterval": schema_pkg_apis_alerting_v0alpha1_TimeIntervalInterval(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalList": schema_pkg_apis_alerting_v0alpha1_TimeIntervalList(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalSpec": schema_pkg_apis_alerting_v0alpha1_TimeIntervalSpec(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalStatus": schema_pkg_apis_alerting_v0alpha1_TimeIntervalStatus(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalTimeRange": schema_pkg_apis_alerting_v0alpha1_TimeIntervalTimeRange(ref), + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalstatusOperatorState": schema_pkg_apis_alerting_v0alpha1_TimeIntervalstatusOperatorState(ref), + } +} + +func schema_pkg_apis_alerting_v0alpha1_Receiver(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the Receiver", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverSpec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_ReceiverIntegration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "disableResolveMessage": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "settings": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "secureFields": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"type", "settings"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_ReceiverList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.Receiver"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.Receiver", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_ReceiverSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "integrations": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverIntegration"), + }, + }, + }, + }, + }, + }, + Required: []string{"title", "integrations"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverIntegration"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_ReceiverStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.ReceiverstatusOperatorState"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_ReceiverstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTree(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the RoutingTree", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeSpec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTree"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTree", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreeMatcher(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "label", "value"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreeRoute(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "receiver": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "matchers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeMatcher"), + }, + }, + }, + }, + }, + "continue": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "group_by": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "mute_time_intervals": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "active_time_intervals": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "routes": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRoute"), + }, + }, + }, + }, + }, + "group_wait": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "group_interval": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "repeat_interval": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"continue"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeMatcher", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRoute"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreeRouteDefaults(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "receiver": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "group_by": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "group_wait": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "group_interval": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "repeat_interval": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"receiver"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "defaults": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRouteDefaults"), + }, + }, + "routes": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRoute"), + }, + }, + }, + }, + }, + }, + Required: []string{"defaults", "routes"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRoute", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreeRouteDefaults"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreestatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.RoutingTreestatusOperatorState"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_RoutingTreestatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TemplateGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the TemplateGroup", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupSpec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TemplateGroupList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroup"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroup", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TemplateGroupSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "content": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"title", "content"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TemplateGroupStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TemplateGroupstatusOperatorState"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TemplateGroupstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeInterval(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the TimeInterval", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalSpec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeIntervalInterval(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "times": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalTimeRange"), + }, + }, + }, + }, + }, + "weekdays": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "days_of_month": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "months": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "years": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "location": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalTimeRange"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeIntervalList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeInterval"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeInterval", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeIntervalSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "time_intervals": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalInterval"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "time_intervals"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalInterval"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeIntervalStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1.TimeIntervalstatusOperatorState"}, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeIntervalTimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "start_time": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "end_time": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"start_time", "end_time"}, + }, + }, + } +} + +func schema_pkg_apis_alerting_v0alpha1_TimeIntervalstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} diff --git a/apps/alerting/notifications/pkg/apis/alerting_manifest.go b/apps/alerting/notifications/pkg/apis/alerting_manifest.go index 9a97a47f78a..9b1e424d463 100644 --- a/apps/alerting/notifications/pkg/apis/alerting_manifest.go +++ b/apps/alerting/notifications/pkg/apis/alerting_manifest.go @@ -6,9 +6,12 @@ package apis import ( - "encoding/json" + "fmt" "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + + v0alpha1 "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1" ) var appManifestData = app.ManifestData{ @@ -70,12 +73,6 @@ var appManifestData = app.ManifestData{ }, } -func jsonToMap(j string) map[string]any { - m := make(map[string]any) - json.Unmarshal([]byte(j), &j) - return m -} - func LocalManifest() app.Manifest { return app.NewEmbeddedManifest(appManifestData) } @@ -83,3 +80,17 @@ func LocalManifest() app.Manifest { func RemoteManifest() app.Manifest { return app.NewAPIServerManifest("alerting") } + +var kindVersionToGoType = map[string]resource.Kind{ + "Receiver/v0alpha1": v0alpha1.ReceiverKind(), + "RoutingTree/v0alpha1": v0alpha1.RoutingTreeKind(), + "TemplateGroup/v0alpha1": v0alpha1.TemplateGroupKind(), + "TimeInterval/v0alpha1": v0alpha1.TimeIntervalKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/constants.go b/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/constants.go deleted file mode 100644 index 6122c92cd43..00000000000 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package v0alpha1 - -import "k8s.io/apimachinery/pkg/runtime/schema" - -const ( - // Group is the API group used by all kinds in this package - Group = "notifications.alerting.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" -) - -var ( - // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package - GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, - } -) diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_spec_gen.go b/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_spec_gen.go deleted file mode 100644 index 25a9b9270c3..00000000000 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_spec_gen.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. - -package v0alpha1 - -// +k8s:openapi-gen=true -type Integration struct { - Uid *string `json:"uid,omitempty"` - Type string `json:"type"` - DisableResolveMessage *bool `json:"disableResolveMessage,omitempty"` - Settings map[string]interface{} `json:"settings"` - SecureFields map[string]bool `json:"secureFields,omitempty"` -} - -// NewIntegration creates a new Integration object. -func NewIntegration() *Integration { - return &Integration{} -} - -// +k8s:openapi-gen=true -type Spec struct { - Title string `json:"title"` - Integrations []Integration `json:"integrations"` -} - -// NewSpec creates a new Spec object. -func NewSpec() *Spec { - return &Spec{} -} diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/zz_generated.openapi.go b/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/zz_generated.openapi.go deleted file mode 100644 index 77fe1c178e0..00000000000 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/zz_generated.openapi.go +++ /dev/null @@ -1,311 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// SPDX-License-Identifier: AGPL-3.0-only - -// Code generated by openapi-gen. DO NOT EDIT. - -package v0alpha1 - -import ( - common "k8s.io/kube-openapi/pkg/common" - spec "k8s.io/kube-openapi/pkg/validation/spec" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Integration": schema_pkg_apis_receiver_v0alpha1_Integration(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Receiver": schema_pkg_apis_receiver_v0alpha1_Receiver(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.ReceiverList": schema_pkg_apis_receiver_v0alpha1_ReceiverList(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Spec": schema_pkg_apis_receiver_v0alpha1_Spec(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Status": schema_pkg_apis_receiver_v0alpha1_Status(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.StatusOperatorState": schema_pkg_apis_receiver_v0alpha1_StatusOperatorState(ref), - } -} - -func schema_pkg_apis_receiver_v0alpha1_Integration(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "uid": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "disableResolveMessage": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "settings": { - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - "secureFields": { - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: false, - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"type", "settings"}, - }, - }, - } -} - -func schema_pkg_apis_receiver_v0alpha1_Receiver(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec is the spec of the Receiver", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Spec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Status"), - }, - }, - }, - Required: []string{"metadata", "spec", "status"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Spec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Status", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_receiver_v0alpha1_ReceiverList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Receiver"), - }, - }, - }, - }, - }, - }, - Required: []string{"metadata", "items"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Receiver", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_receiver_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "title": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "integrations": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Integration"), - }, - }, - }, - }, - }, - }, - Required: []string{"title", "integrations"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.Integration"}, - } -} - -func schema_pkg_apis_receiver_v0alpha1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "operatorStates": { - SchemaProps: spec.SchemaProps{ - Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.StatusOperatorState"), - }, - }, - }, - }, - }, - "additionalFields": { - SchemaProps: spec.SchemaProps{ - Description: "additionalFields is reserved for future use", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1.StatusOperatorState"}, - } -} - -func schema_pkg_apis_receiver_v0alpha1_StatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "lastEvaluation": { - SchemaProps: spec.SchemaProps{ - Description: "lastEvaluation is the ResourceVersion last evaluated", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "descriptiveState": { - SchemaProps: spec.SchemaProps{ - Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "details contains any extra information that is operator-specific", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"lastEvaluation", "state"}, - }, - }, - } -} diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/zz_generated.openapi_violation_exceptions.list b/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/zz_generated.openapi_violation_exceptions.list deleted file mode 100644 index 31a8a58e245..00000000000 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ /dev/null @@ -1 +0,0 @@ -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1,Spec,Integrations diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/constants.go b/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/constants.go deleted file mode 100644 index 6122c92cd43..00000000000 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package v0alpha1 - -import "k8s.io/apimachinery/pkg/runtime/schema" - -const ( - // Group is the API group used by all kinds in this package - Group = "notifications.alerting.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" -) - -var ( - // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package - GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, - } -) diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_spec_gen.go b/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_spec_gen.go deleted file mode 100644 index d2a7727bc7e..00000000000 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_spec_gen.go +++ /dev/null @@ -1,70 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. - -package v0alpha1 - -// +k8s:openapi-gen=true -type RouteDefaults struct { - Receiver string `json:"receiver"` - GroupBy []string `json:"group_by,omitempty"` - GroupWait *string `json:"group_wait,omitempty"` - GroupInterval *string `json:"group_interval,omitempty"` - RepeatInterval *string `json:"repeat_interval,omitempty"` -} - -// NewRouteDefaults creates a new RouteDefaults object. -func NewRouteDefaults() *RouteDefaults { - return &RouteDefaults{} -} - -// +k8s:openapi-gen=true -type Route struct { - Receiver *string `json:"receiver,omitempty"` - Matchers []Matcher `json:"matchers,omitempty"` - Continue bool `json:"continue"` - GroupBy []string `json:"group_by,omitempty"` - MuteTimeIntervals []string `json:"mute_time_intervals,omitempty"` - Routes []Route `json:"routes,omitempty"` - GroupWait *string `json:"group_wait,omitempty"` - GroupInterval *string `json:"group_interval,omitempty"` - RepeatInterval *string `json:"repeat_interval,omitempty"` -} - -// NewRoute creates a new Route object. -func NewRoute() *Route { - return &Route{} -} - -// +k8s:openapi-gen=true -type Matcher struct { - Type MatcherType `json:"type"` - Label string `json:"label"` - Value string `json:"value"` -} - -// NewMatcher creates a new Matcher object. -func NewMatcher() *Matcher { - return &Matcher{} -} - -// +k8s:openapi-gen=true -type Spec struct { - Defaults RouteDefaults `json:"defaults"` - Routes []Route `json:"routes"` -} - -// NewSpec creates a new Spec object. -func NewSpec() *Spec { - return &Spec{ - Defaults: *NewRouteDefaults(), - } -} - -// +k8s:openapi-gen=true -type MatcherType string - -const ( - MatcherTypeEqual MatcherType = "=" - MatcherTypeNotEqual MatcherType = "!=" - MatcherTypeEqualRegex MatcherType = "=~" - MatcherTypeNotEqualRegex MatcherType = "!~" -) diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/zz_generated.openapi.go b/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/zz_generated.openapi.go deleted file mode 100644 index 02fa2fe59f3..00000000000 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/zz_generated.openapi.go +++ /dev/null @@ -1,437 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// SPDX-License-Identifier: AGPL-3.0-only - -// Code generated by openapi-gen. DO NOT EDIT. - -package v0alpha1 - -import ( - common "k8s.io/kube-openapi/pkg/common" - spec "k8s.io/kube-openapi/pkg/validation/spec" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Matcher": schema_pkg_apis_routingtree_v0alpha1_Matcher(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Route": schema_pkg_apis_routingtree_v0alpha1_Route(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RouteDefaults": schema_pkg_apis_routingtree_v0alpha1_RouteDefaults(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RoutingTree": schema_pkg_apis_routingtree_v0alpha1_RoutingTree(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RoutingTreeList": schema_pkg_apis_routingtree_v0alpha1_RoutingTreeList(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Spec": schema_pkg_apis_routingtree_v0alpha1_Spec(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Status": schema_pkg_apis_routingtree_v0alpha1_Status(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.StatusOperatorState": schema_pkg_apis_routingtree_v0alpha1_StatusOperatorState(ref), - } -} - -func schema_pkg_apis_routingtree_v0alpha1_Matcher(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "label": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "label", "value"}, - }, - }, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_Route(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "receiver": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "matchers": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Matcher"), - }, - }, - }, - }, - }, - "continue": { - SchemaProps: spec.SchemaProps{ - Default: false, - Type: []string{"boolean"}, - Format: "", - }, - }, - "group_by": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "mute_time_intervals": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "routes": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Route"), - }, - }, - }, - }, - }, - "group_wait": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "group_interval": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "repeat_interval": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"continue"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Matcher", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Route"}, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_RouteDefaults(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "receiver": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "group_by": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "group_wait": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "group_interval": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "repeat_interval": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"receiver"}, - }, - }, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_RoutingTree(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec is the spec of the RoutingTree", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Spec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Status"), - }, - }, - }, - Required: []string{"metadata", "spec", "status"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Spec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Status", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_RoutingTreeList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RoutingTree"), - }, - }, - }, - }, - }, - }, - Required: []string{"metadata", "items"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RoutingTree", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "defaults": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RouteDefaults"), - }, - }, - "routes": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Route"), - }, - }, - }, - }, - }, - }, - Required: []string{"defaults", "routes"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.Route", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.RouteDefaults"}, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "operatorStates": { - SchemaProps: spec.SchemaProps{ - Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.StatusOperatorState"), - }, - }, - }, - }, - }, - "additionalFields": { - SchemaProps: spec.SchemaProps{ - Description: "additionalFields is reserved for future use", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1.StatusOperatorState"}, - } -} - -func schema_pkg_apis_routingtree_v0alpha1_StatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "lastEvaluation": { - SchemaProps: spec.SchemaProps{ - Description: "lastEvaluation is the ResourceVersion last evaluated", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "descriptiveState": { - SchemaProps: spec.SchemaProps{ - Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "details contains any extra information that is operator-specific", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"lastEvaluation", "state"}, - }, - }, - } -} diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/zz_generated.openapi_violation_exceptions.list b/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/zz_generated.openapi_violation_exceptions.list deleted file mode 100644 index 9dcfaaadf27..00000000000 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ /dev/null @@ -1,15 +0,0 @@ -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,GroupBy -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,Matchers -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,MuteTimeIntervals -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,Routes -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,RouteDefaults,GroupBy -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Spec,Routes -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,GroupBy -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,GroupInterval -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,GroupWait -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,MuteTimeIntervals -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,Route,RepeatInterval -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,RouteDefaults,GroupBy -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,RouteDefaults,GroupInterval -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,RouteDefaults,GroupWait -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1,RouteDefaults,RepeatInterval diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/constants.go b/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/constants.go deleted file mode 100644 index 6122c92cd43..00000000000 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package v0alpha1 - -import "k8s.io/apimachinery/pkg/runtime/schema" - -const ( - // Group is the API group used by all kinds in this package - Group = "notifications.alerting.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" -) - -var ( - // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package - GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, - } -) diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_spec_gen.go b/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_spec_gen.go deleted file mode 100644 index bd3ff4fa52b..00000000000 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_spec_gen.go +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. - -package v0alpha1 - -// +k8s:openapi-gen=true -type Spec struct { - Title string `json:"title"` - Content string `json:"content"` -} - -// NewSpec creates a new Spec object. -func NewSpec() *Spec { - return &Spec{} -} diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/zz_generated.openapi.go b/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/zz_generated.openapi.go deleted file mode 100644 index 1fdaf1369a7..00000000000 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/zz_generated.openapi.go +++ /dev/null @@ -1,241 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// SPDX-License-Identifier: AGPL-3.0-only - -// Code generated by openapi-gen. DO NOT EDIT. - -package v0alpha1 - -import ( - common "k8s.io/kube-openapi/pkg/common" - spec "k8s.io/kube-openapi/pkg/validation/spec" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.Spec": schema_pkg_apis_templategroup_v0alpha1_Spec(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.Status": schema_pkg_apis_templategroup_v0alpha1_Status(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.StatusOperatorState": schema_pkg_apis_templategroup_v0alpha1_StatusOperatorState(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.TemplateGroup": schema_pkg_apis_templategroup_v0alpha1_TemplateGroup(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.TemplateGroupList": schema_pkg_apis_templategroup_v0alpha1_TemplateGroupList(ref), - } -} - -func schema_pkg_apis_templategroup_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "title": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "content": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"title", "content"}, - }, - }, - } -} - -func schema_pkg_apis_templategroup_v0alpha1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "operatorStates": { - SchemaProps: spec.SchemaProps{ - Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.StatusOperatorState"), - }, - }, - }, - }, - }, - "additionalFields": { - SchemaProps: spec.SchemaProps{ - Description: "additionalFields is reserved for future use", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.StatusOperatorState"}, - } -} - -func schema_pkg_apis_templategroup_v0alpha1_StatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "lastEvaluation": { - SchemaProps: spec.SchemaProps{ - Description: "lastEvaluation is the ResourceVersion last evaluated", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "descriptiveState": { - SchemaProps: spec.SchemaProps{ - Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "details contains any extra information that is operator-specific", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"lastEvaluation", "state"}, - }, - }, - } -} - -func schema_pkg_apis_templategroup_v0alpha1_TemplateGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec is the spec of the TemplateGroup", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.Spec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.Status"), - }, - }, - }, - Required: []string{"metadata", "spec", "status"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.Spec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.Status", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_templategroup_v0alpha1_TemplateGroupList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.TemplateGroup"), - }, - }, - }, - }, - }, - }, - Required: []string{"metadata", "items"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1.TemplateGroup", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/constants.go b/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/constants.go deleted file mode 100644 index 6122c92cd43..00000000000 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/constants.go +++ /dev/null @@ -1,18 +0,0 @@ -package v0alpha1 - -import "k8s.io/apimachinery/pkg/runtime/schema" - -const ( - // Group is the API group used by all kinds in this package - Group = "notifications.alerting.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" -) - -var ( - // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package - GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, - } -) diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_spec_gen.go b/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_spec_gen.go deleted file mode 100644 index b3f3ce72148..00000000000 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_spec_gen.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. - -package v0alpha1 - -// +k8s:openapi-gen=true -type Interval struct { - Times []TimeRange `json:"times,omitempty"` - Weekdays []string `json:"weekdays,omitempty"` - DaysOfMonth []string `json:"days_of_month,omitempty"` - Months []string `json:"months,omitempty"` - Years []string `json:"years,omitempty"` - Location *string `json:"location,omitempty"` -} - -// NewInterval creates a new Interval object. -func NewInterval() *Interval { - return &Interval{} -} - -// +k8s:openapi-gen=true -type TimeRange struct { - StartTime string `json:"start_time"` - EndTime string `json:"end_time"` -} - -// NewTimeRange creates a new TimeRange object. -func NewTimeRange() *TimeRange { - return &TimeRange{} -} - -// +k8s:openapi-gen=true -type Spec struct { - Name string `json:"name"` - TimeIntervals []Interval `json:"time_intervals"` -} - -// NewSpec creates a new Spec object. -func NewSpec() *Spec { - return &Spec{} -} diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/zz_generated.openapi.go b/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/zz_generated.openapi.go deleted file mode 100644 index 192dd1b7dda..00000000000 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/zz_generated.openapi.go +++ /dev/null @@ -1,367 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// SPDX-License-Identifier: AGPL-3.0-only - -// Code generated by openapi-gen. DO NOT EDIT. - -package v0alpha1 - -import ( - common "k8s.io/kube-openapi/pkg/common" - spec "k8s.io/kube-openapi/pkg/validation/spec" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Interval": schema_pkg_apis_timeinterval_v0alpha1_Interval(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Spec": schema_pkg_apis_timeinterval_v0alpha1_Spec(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Status": schema_pkg_apis_timeinterval_v0alpha1_Status(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.StatusOperatorState": schema_pkg_apis_timeinterval_v0alpha1_StatusOperatorState(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeInterval": schema_pkg_apis_timeinterval_v0alpha1_TimeInterval(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeIntervalList": schema_pkg_apis_timeinterval_v0alpha1_TimeIntervalList(ref), - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeRange": schema_pkg_apis_timeinterval_v0alpha1_TimeRange(ref), - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_Interval(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "times": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeRange"), - }, - }, - }, - }, - }, - "weekdays": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "days_of_month": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "months": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "years": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "location": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeRange"}, - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "time_intervals": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Interval"), - }, - }, - }, - }, - }, - }, - Required: []string{"name", "time_intervals"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Interval"}, - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "operatorStates": { - SchemaProps: spec.SchemaProps{ - Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.StatusOperatorState"), - }, - }, - }, - }, - }, - "additionalFields": { - SchemaProps: spec.SchemaProps{ - Description: "additionalFields is reserved for future use", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.StatusOperatorState"}, - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_StatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "lastEvaluation": { - SchemaProps: spec.SchemaProps{ - Description: "lastEvaluation is the ResourceVersion last evaluated", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "descriptiveState": { - SchemaProps: spec.SchemaProps{ - Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "details contains any extra information that is operator-specific", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"lastEvaluation", "state"}, - }, - }, - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_TimeInterval(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec is the spec of the TimeInterval", - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Spec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Status"), - }, - }, - }, - Required: []string{"metadata", "spec", "status"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Spec", "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.Status", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_TimeIntervalList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeInterval"), - }, - }, - }, - }, - }, - }, - Required: []string{"metadata", "items"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1.TimeInterval", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_timeinterval_v0alpha1_TimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "start_time": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "end_time": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"start_time", "end_time"}, - }, - }, - } -} diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/zz_generated.openapi_violation_exceptions.list b/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/zz_generated.openapi_violation_exceptions.list deleted file mode 100644 index b8ae58a24e9..00000000000 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ /dev/null @@ -1,10 +0,0 @@ -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Interval,DaysOfMonth -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Interval,Months -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Interval,Times -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Interval,Weekdays -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Interval,Years -API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Spec,TimeIntervals -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Interval,DaysOfMonth -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,Spec,TimeIntervals -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,TimeRange,EndTime -API rule violation: names_match,github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1,TimeRange,StartTime diff --git a/apps/alerting/notifications/pkg/apis/type.go b/apps/alerting/notifications/pkg/apis/type.go index 464697370d9..3c15f5284b5 100644 --- a/apps/alerting/notifications/pkg/apis/type.go +++ b/apps/alerting/notifications/pkg/apis/type.go @@ -1,5 +1,20 @@ package apis -import "k8s.io/apimachinery/pkg/runtime/schema" +import ( + sdkResource "github.com/grafana/grafana-app-sdk/resource" + "k8s.io/apimachinery/pkg/runtime/schema" -var GroupVersion = schema.GroupVersion{Group: "notifications.alerting.grafana.app", Version: "v0alpha1"} + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alerting/v0alpha1" +) + +func GetKinds() map[schema.GroupVersion][]sdkResource.Kind { + result := map[schema.GroupVersion][]sdkResource.Kind{ + v0alpha1.GroupVersion: { + v0alpha1.ReceiverKind(), + v0alpha1.RoutingTreeKind(), + v0alpha1.TemplateGroupKind(), + v0alpha1.TimeIntervalKind(), + }, + } + return result +} diff --git a/apps/alerting/notifications/pkg/app/app.go b/apps/alerting/notifications/pkg/app/app.go new file mode 100644 index 00000000000..b57ed0cd143 --- /dev/null +++ b/apps/alerting/notifications/pkg/app/app.go @@ -0,0 +1,43 @@ +package app + +import ( + "context" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/logging" + "github.com/grafana/grafana-app-sdk/simple" + + "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis" +) + +func New(cfg app.Config) (app.App, error) { + managedKinds := make([]simple.AppManagedKind, 0) + for _, kinds := range apis.GetKinds() { + for _, kind := range kinds { + managedKinds = append(managedKinds, simple.AppManagedKind{Kind: kind}) + } + } + + c := simple.AppConfig{ + Name: "alerting.notification", + KubeConfig: cfg.KubeConfig, + InformerConfig: simple.AppInformerConfig{ + ErrorHandler: func(ctx context.Context, err error) { + logging.DefaultLogger.With("error", err).Error("Informer processing error") + }, + }, + ManagedKinds: managedKinds, + } + + a, err := simple.NewApp(c) + if err != nil { + return nil, err + } + + err = a.ValidateManifest(cfg.ManifestData) + if err != nil { + return nil, err + } + + return a, nil +} diff --git a/apps/dashboard/Makefile b/apps/dashboard/Makefile index 7ea4489a97c..9b7fc2955fe 100644 --- a/apps/dashboard/Makefile +++ b/apps/dashboard/Makefile @@ -1,4 +1,4 @@ -APP_SDK_VERSION := v0.35.1 +APP_SDK_VERSION := v0.38.2 APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION) APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index 099f2af37ff..dd46e08e462 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -1,14 +1,14 @@ module github.com/grafana/grafana/apps/dashboard -go 1.24.2 +go 1.24.3 require ( cuelang.org/go v0.11.1 - github.com/grafana/grafana-app-sdk v0.35.1 + github.com/grafana/grafana-app-sdk v0.38.2 github.com/grafana/grafana-plugin-sdk-go v0.277.0 - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2 + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e github.com/stretchr/testify v1.10.0 - k8s.io/apimachinery v0.32.3 + k8s.io/apimachinery v0.33.1 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff ) @@ -26,28 +26,29 @@ require ( github.com/elazarl/goproxy v1.7.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect + github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect @@ -76,10 +77,10 @@ require ( github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect @@ -101,26 +102,27 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + go.opentelemetry.io/proto/otlp v1.6.0 // indirect + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/tools v0.33.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/grpc v1.71.1 // indirect + gonum.org/v1/gonum v0.16.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.72.1 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/client-go v0.32.3 // indirect + k8s.io/client-go v0.33.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index d0e3fb06447..14cee45cf02 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -46,12 +46,12 @@ github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= -github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -66,16 +66,16 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= @@ -85,8 +85,6 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -94,22 +92,22 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK7NGmlX3RI= -github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI= -github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM= -github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= +github.com/grafana/grafana-app-sdk v0.38.2 h1:raQ1mqCmNJ2AyCSV3m6ehFC0TGVZcqRcM6iOIDT6ZIE= +github.com/grafana/grafana-app-sdk v0.38.2/go.mod h1:LhZ4i2IXGnW5FF/jAczX7SnGFNai7x2qwB8YHNZWiZI= +github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc= +github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss= github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2 h1:kvG92f3XbJlQPUcZfXlTNLziI4e8LYeA9Jv2ixmM5Ic= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2/go.mod h1:ll14OJrUGYgXApz3YX6zmxYjRMZHL+pgQjoKBuRzaRs= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdixfIJjVzuUJdnv+5xsPutog= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -202,14 +200,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -292,15 +290,15 @@ go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstF go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= @@ -310,15 +308,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -330,14 +328,14 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -345,22 +343,22 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= -gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -377,12 +375,12 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= diff --git a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue index f2da39df16c..3928838bd33 100644 --- a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue +++ b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue @@ -393,7 +393,7 @@ AnnotationQuerySpec: { name: string builtIn?: bool | *false filter?: AnnotationPanelFilter - options?: [string]: _ //Catch-all field for datasource-specific properties + legacyOptions?: [string]: _ //Catch-all field for datasource-specific properties } AnnotationQueryKind: { @@ -518,21 +518,8 @@ GridLayoutItemKind: { spec: GridLayoutItemSpec } -GridLayoutRowKind: { - kind: "GridLayoutRow" - spec: GridLayoutRowSpec -} - -GridLayoutRowSpec: { - y: int - collapsed: bool - title: string - elements: [...GridLayoutItemKind] // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. - repeat?: RowRepeatOptions -} - GridLayoutSpec: { - items: [...GridLayoutItemKind | GridLayoutRowKind] + items: [...GridLayoutItemKind] } GridLayoutKind: { @@ -747,6 +734,7 @@ QueryVariableSpec: { includeAll: bool | *false allValue?: string placeholder?: string + allowCustomValue: bool | *true } // Query variable kind @@ -813,6 +801,7 @@ DatasourceVariableSpec: { hide: VariableHide skipUrlSync: bool | *false description?: string + allowCustomValue: bool | *true } // Datasource variable kind @@ -859,6 +848,7 @@ CustomVariableSpec: { hide: VariableHide skipUrlSync: bool | *false description?: string + allowCustomValue: bool | *true } // Custom variable kind @@ -871,6 +861,7 @@ CustomVariableKind: { GroupByVariableSpec: { name: string | *"" datasource?: DataSourceRef + defaultValue?: VariableOption current: VariableOption | *{ text: "" value: "" @@ -900,6 +891,7 @@ AdhocVariableSpec: { hide: VariableHide skipUrlSync: bool | *false description?: string + allowCustomValue: bool | *true } // Define the MetricFindValue type diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/constants.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/constants.go index 90cb8b29db7..e0f4696af93 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/constants.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/constants.go @@ -3,16 +3,16 @@ package v0alpha1 import "k8s.io/apimachinery/pkg/runtime/schema" const ( - // Group is the API group used by all kinds in this package - Group = "dashboard.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" + // APIGroup is the API group used by all kinds in this package + APIGroup = "dashboard.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v0alpha1" ) var ( // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, + Group: APIGroup, + Version: APIVersion, } ) diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go index 6e22219dbf4..444662b8072 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go @@ -24,5 +24,8 @@ type DashboardMetadata struct { // NewDashboardMetadata creates a new DashboardMetadata object. func NewDashboardMetadata() *DashboardMetadata { - return &DashboardMetadata{} + return &DashboardMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go index a267e0c8df8..50847df87c3 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go @@ -294,6 +294,8 @@ var _ resource.ListObject = &DashboardList{} // Copy methods for all subresource types + + // DeepCopy creates a full deep copy of DashboardStatus func (s *DashboardStatus) DeepCopy() *DashboardStatus { cpy := &DashboardStatus{} diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/types.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/types.go index ff4e38841d3..c12ae17cbf8 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/types.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/types.go @@ -11,10 +11,9 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardVersionList struct { metav1.TypeMeta `json:",inline"` - // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []DashboardVersionInfo `json:"items,omitempty"` + Items []DashboardVersionInfo `json:"items"` } // +k8s:deepcopy-gen=true @@ -72,7 +71,7 @@ type LibraryPanelList struct { // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []LibraryPanel `json:"items,omitempty"` + Items []LibraryPanel `json:"items"` } // +k8s:deepcopy-gen=true diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go index a9d0f5b73c7..1b818580c45 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go @@ -603,6 +603,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref common.Referenc }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ @@ -801,6 +802,7 @@ func schema_pkg_apis_dashboard_v0alpha1_LibraryPanelList(ref common.ReferenceCal }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list index 68505c7d05d..5003217ce4e 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -7,3 +7,4 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/ API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1,SortBy,Descending API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1,SortableField,Field API rule violation: names_match,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,Unstructured,Object +API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1,DashboardList,ListMeta diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/constants.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/constants.go index d47d860d95f..f14b80cdecc 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/constants.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/constants.go @@ -3,16 +3,16 @@ package v1beta1 import "k8s.io/apimachinery/pkg/runtime/schema" const ( - // Group is the API group used by all kinds in this package - Group = "dashboard.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v1beta1" + // APIGroup is the API group used by all kinds in this package + APIGroup = "dashboard.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v1beta1" ) var ( // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, + Group: APIGroup, + Version: APIVersion, } ) diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_metadata_gen.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_metadata_gen.go index dfad3b5cfe7..21f21f753b0 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_metadata_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_metadata_gen.go @@ -24,5 +24,8 @@ type DashboardMetadata struct { // NewDashboardMetadata creates a new DashboardMetadata object. func NewDashboardMetadata() *DashboardMetadata { - return &DashboardMetadata{} + return &DashboardMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go index be021b5f003..1423c7b0603 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go @@ -294,6 +294,8 @@ var _ resource.ListObject = &DashboardList{} // Copy methods for all subresource types + + // DeepCopy creates a full deep copy of DashboardStatus func (s *DashboardStatus) DeepCopy() *DashboardStatus { cpy := &DashboardStatus{} diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/types.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/types.go index a3f93c3a29c..ee4125d8f6a 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/types.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/types.go @@ -11,10 +11,9 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardVersionList struct { metav1.TypeMeta `json:",inline"` - // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []DashboardVersionInfo `json:"items,omitempty"` + Items []DashboardVersionInfo `json:"items"` } // +k8s:deepcopy-gen=true @@ -69,10 +68,9 @@ type LibraryPanel struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanelList struct { metav1.TypeMeta `json:",inline"` - // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []LibraryPanel `json:"items,omitempty"` + Items []LibraryPanel `json:"items"` } // +k8s:deepcopy-gen=true diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go index 3b2175bd003..88a534a0746 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go @@ -517,6 +517,7 @@ func schema_pkg_apis_dashboard_v1beta1_DashboardVersionList(ref common.Reference }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ @@ -667,6 +668,7 @@ func schema_pkg_apis_dashboard_v1beta1_LibraryPanelList(ref common.ReferenceCall }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi_violation_exceptions.list b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi_violation_exceptions.list index a4bab110b14..4bcb47dd752 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi_violation_exceptions.list +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi_violation_exceptions.list @@ -1,3 +1,4 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1,DashboardMetadata,Finalizers API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1,LibraryPanelStatus,Warnings API rule violation: names_match,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,Unstructured,Object +API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1,DashboardList,ListMeta diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/constants.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/constants.go index 84b030c8994..a05b38f3aac 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/constants.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/constants.go @@ -3,16 +3,16 @@ package v2alpha1 import "k8s.io/apimachinery/pkg/runtime/schema" const ( - // Group is the API group used by all kinds in this package - Group = "dashboard.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v2alpha1" + // APIGroup is the API group used by all kinds in this package + APIGroup = "dashboard.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v2alpha1" ) var ( // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, + Group: APIGroup, + Version: APIVersion, } ) diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go index f4e92178780..235e26d307d 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go @@ -24,5 +24,8 @@ type DashboardMetadata struct { // NewDashboardMetadata creates a new DashboardMetadata object. func NewDashboardMetadata() *DashboardMetadata { - return &DashboardMetadata{} + return &DashboardMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue index 85641129095..a08f1f6eff6 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue @@ -397,7 +397,7 @@ AnnotationQuerySpec: { name: string builtIn?: bool | *false filter?: AnnotationPanelFilter - options?: [string]: _ //Catch-all field for datasource-specific properties + legacyOptions?: [string]: _ //Catch-all field for datasource-specific properties } AnnotationQueryKind: { @@ -522,21 +522,8 @@ GridLayoutItemKind: { spec: GridLayoutItemSpec } -GridLayoutRowKind: { - kind: "GridLayoutRow" - spec: GridLayoutRowSpec -} - -GridLayoutRowSpec: { - y: int - collapsed: bool - title: string - elements: [...GridLayoutItemKind] // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. - repeat?: RowRepeatOptions -} - GridLayoutSpec: { - items: [...GridLayoutItemKind | GridLayoutRowKind] + items: [...GridLayoutItemKind] } GridLayoutKind: { @@ -751,6 +738,7 @@ QueryVariableSpec: { includeAll: bool | *false allValue?: string placeholder?: string + allowCustomValue: bool | *true } // Query variable kind @@ -817,6 +805,7 @@ DatasourceVariableSpec: { hide: VariableHide skipUrlSync: bool | *false description?: string + allowCustomValue: bool | *true } // Datasource variable kind @@ -863,6 +852,7 @@ CustomVariableSpec: { hide: VariableHide skipUrlSync: bool | *false description?: string + allowCustomValue: bool | *true } // Custom variable kind @@ -875,6 +865,7 @@ CustomVariableKind: { GroupByVariableSpec: { name: string | *"" datasource?: DataSourceRef + defaultValue?: VariableOption current: VariableOption | *{ text: "" value: "" @@ -904,6 +895,7 @@ AdhocVariableSpec: { hide: VariableHide skipUrlSync: bool | *false description?: string + allowCustomValue: bool | *true } // Define the MetricFindValue type diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go index 877c2acc44e..9fda0a85c6f 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go @@ -5,7 +5,6 @@ package v2alpha1 import ( json "encoding/json" errors "errors" - fmt "fmt" ) // +k8s:openapi-gen=true @@ -33,7 +32,7 @@ type DashboardAnnotationQuerySpec struct { BuiltIn *bool `json:"builtIn,omitempty"` Filter *DashboardAnnotationPanelFilter `json:"filter,omitempty"` // Catch-all field for datasource-specific properties - Options map[string]interface{} `json:"options,omitempty"` + LegacyOptions map[string]interface{} `json:"legacyOptions,omitempty"` } // NewDashboardAnnotationQuerySpec creates a new DashboardAnnotationQuerySpec object. @@ -65,7 +64,9 @@ type DashboardDataQueryKind struct { // NewDashboardDataQueryKind creates a new DashboardDataQueryKind object. func NewDashboardDataQueryKind() *DashboardDataQueryKind { - return &DashboardDataQueryKind{} + return &DashboardDataQueryKind{ + Spec: map[string]interface{}{}, + } } // +k8s:openapi-gen=true @@ -80,6 +81,7 @@ type DashboardAnnotationPanelFilter struct { func NewDashboardAnnotationPanelFilter() *DashboardAnnotationPanelFilter { return &DashboardAnnotationPanelFilter{ Exclude: (func(input bool) *bool { return &input })(false), + Ids: []uint32{}, } } @@ -133,6 +135,7 @@ type DashboardPanelSpec struct { // NewDashboardPanelSpec creates a new DashboardPanelSpec object. func NewDashboardPanelSpec() *DashboardPanelSpec { return &DashboardPanelSpec{ + Links: []DashboardDataLink{}, Data: *NewDashboardQueryGroupKind(), VizConfig: *NewDashboardVizConfigKind(), } @@ -174,7 +177,9 @@ type DashboardQueryGroupSpec struct { // NewDashboardQueryGroupSpec creates a new DashboardQueryGroupSpec object. func NewDashboardQueryGroupSpec() *DashboardQueryGroupSpec { return &DashboardQueryGroupSpec{ - QueryOptions: *NewDashboardQueryOptionsSpec(), + Queries: []DashboardPanelQueryKind{}, + Transformations: []DashboardTransformationKind{}, + QueryOptions: *NewDashboardQueryOptionsSpec(), } } @@ -313,6 +318,7 @@ type DashboardVizConfigSpec struct { // NewDashboardVizConfigSpec creates a new DashboardVizConfigSpec object. func NewDashboardVizConfigSpec() *DashboardVizConfigSpec { return &DashboardVizConfigSpec{ + Options: map[string]interface{}{}, FieldConfig: *NewDashboardFieldConfigSource(), } } @@ -331,7 +337,8 @@ type DashboardFieldConfigSource struct { // NewDashboardFieldConfigSource creates a new DashboardFieldConfigSource object. func NewDashboardFieldConfigSource() *DashboardFieldConfigSource { return &DashboardFieldConfigSource{ - Defaults: *NewDashboardFieldConfig(), + Defaults: *NewDashboardFieldConfig(), + Overrides: []DashboardV2alpha1FieldConfigSourceOverrides{}, } } @@ -417,7 +424,8 @@ type DashboardValueMap struct { // NewDashboardValueMap creates a new DashboardValueMap object. func NewDashboardValueMap() *DashboardValueMap { return &DashboardValueMap{ - Type: DashboardMappingTypeValue, + Type: DashboardMappingTypeValue, + Options: map[string]DashboardValueMappingResult{}, } } @@ -526,7 +534,9 @@ type DashboardThresholdsConfig struct { // NewDashboardThresholdsConfig creates a new DashboardThresholdsConfig object. func NewDashboardThresholdsConfig() *DashboardThresholdsConfig { - return &DashboardThresholdsConfig{} + return &DashboardThresholdsConfig{ + Steps: []DashboardThreshold{}, + } } // +k8s:openapi-gen=true @@ -688,12 +698,14 @@ func NewDashboardGridLayoutKind() *DashboardGridLayoutKind { // +k8s:openapi-gen=true type DashboardGridLayoutSpec struct { - Items []DashboardGridLayoutItemKindOrGridLayoutRowKind `json:"items"` + Items []DashboardGridLayoutItemKind `json:"items"` } // NewDashboardGridLayoutSpec creates a new DashboardGridLayoutSpec object. func NewDashboardGridLayoutSpec() *DashboardGridLayoutSpec { - return &DashboardGridLayoutSpec{} + return &DashboardGridLayoutSpec{ + Items: []DashboardGridLayoutItemKind{}, + } } // +k8s:openapi-gen=true @@ -758,46 +770,6 @@ func NewDashboardRepeatOptions() *DashboardRepeatOptions { // +k8s:openapi-gen=true const DashboardRepeatMode = "variable" -// +k8s:openapi-gen=true -type DashboardGridLayoutRowKind struct { - Kind string `json:"kind"` - Spec DashboardGridLayoutRowSpec `json:"spec"` -} - -// NewDashboardGridLayoutRowKind creates a new DashboardGridLayoutRowKind object. -func NewDashboardGridLayoutRowKind() *DashboardGridLayoutRowKind { - return &DashboardGridLayoutRowKind{ - Kind: "GridLayoutRow", - Spec: *NewDashboardGridLayoutRowSpec(), - } -} - -// +k8s:openapi-gen=true -type DashboardGridLayoutRowSpec struct { - Y int64 `json:"y"` - Collapsed bool `json:"collapsed"` - Title string `json:"title"` - // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. - Elements []DashboardGridLayoutItemKind `json:"elements"` - Repeat *DashboardRowRepeatOptions `json:"repeat,omitempty"` -} - -// NewDashboardGridLayoutRowSpec creates a new DashboardGridLayoutRowSpec object. -func NewDashboardGridLayoutRowSpec() *DashboardGridLayoutRowSpec { - return &DashboardGridLayoutRowSpec{} -} - -// +k8s:openapi-gen=true -type DashboardRowRepeatOptions struct { - Mode string `json:"mode"` - Value string `json:"value"` -} - -// NewDashboardRowRepeatOptions creates a new DashboardRowRepeatOptions object. -func NewDashboardRowRepeatOptions() *DashboardRowRepeatOptions { - return &DashboardRowRepeatOptions{} -} - // +k8s:openapi-gen=true type DashboardRowsLayoutKind struct { Kind string `json:"kind"` @@ -819,7 +791,9 @@ type DashboardRowsLayoutSpec struct { // NewDashboardRowsLayoutSpec creates a new DashboardRowsLayoutSpec object. func NewDashboardRowsLayoutSpec() *DashboardRowsLayoutSpec { - return &DashboardRowsLayoutSpec{} + return &DashboardRowsLayoutSpec{ + Rows: []DashboardRowsLayoutRowKind{}, + } } // +k8s:openapi-gen=true @@ -877,7 +851,9 @@ type DashboardConditionalRenderingGroupSpec struct { // NewDashboardConditionalRenderingGroupSpec creates a new DashboardConditionalRenderingGroupSpec object. func NewDashboardConditionalRenderingGroupSpec() *DashboardConditionalRenderingGroupSpec { - return &DashboardConditionalRenderingGroupSpec{} + return &DashboardConditionalRenderingGroupSpec{ + Items: []DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind{}, + } } // +k8s:openapi-gen=true @@ -954,6 +930,17 @@ func NewDashboardConditionalRenderingTimeRangeSizeSpec() *DashboardConditionalRe return &DashboardConditionalRenderingTimeRangeSizeSpec{} } +// +k8s:openapi-gen=true +type DashboardRowRepeatOptions struct { + Mode string `json:"mode"` + Value string `json:"value"` +} + +// NewDashboardRowRepeatOptions creates a new DashboardRowRepeatOptions object. +func NewDashboardRowRepeatOptions() *DashboardRowRepeatOptions { + return &DashboardRowRepeatOptions{} +} + // +k8s:openapi-gen=true type DashboardAutoGridLayoutKind struct { Kind string `json:"kind"` @@ -984,6 +971,7 @@ func NewDashboardAutoGridLayoutSpec() *DashboardAutoGridLayoutSpec { return &DashboardAutoGridLayoutSpec{ MaxColumnCount: (func(input float64) *float64 { return &input })(3), FillScreen: (func(input bool) *bool { return &input })(false), + Items: []DashboardAutoGridLayoutItemKind{}, } } @@ -1047,7 +1035,9 @@ type DashboardTabsLayoutSpec struct { // NewDashboardTabsLayoutSpec creates a new DashboardTabsLayoutSpec object. func NewDashboardTabsLayoutSpec() *DashboardTabsLayoutSpec { - return &DashboardTabsLayoutSpec{} + return &DashboardTabsLayoutSpec{ + Tabs: []DashboardTabsLayoutTabKind{}, + } } // +k8s:openapi-gen=true @@ -1119,6 +1109,7 @@ type DashboardDashboardLink struct { // NewDashboardDashboardLink creates a new DashboardDashboardLink object. func NewDashboardDashboardLink() *DashboardDashboardLink { return &DashboardDashboardLink{ + Tags: []string{}, AsDropdown: false, TargetBlank: false, IncludeVars: false, @@ -1223,23 +1214,24 @@ func NewDashboardQueryVariableKind() *DashboardQueryVariableKind { // Query variable specification // +k8s:openapi-gen=true type DashboardQueryVariableSpec struct { - Name string `json:"name"` - Current DashboardVariableOption `json:"current"` - Label *string `json:"label,omitempty"` - Hide DashboardVariableHide `json:"hide"` - Refresh DashboardVariableRefresh `json:"refresh"` - SkipUrlSync bool `json:"skipUrlSync"` - Description *string `json:"description,omitempty"` - Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` - Query DashboardDataQueryKind `json:"query"` - Regex string `json:"regex"` - Sort DashboardVariableSort `json:"sort"` - Definition *string `json:"definition,omitempty"` - Options []DashboardVariableOption `json:"options"` - Multi bool `json:"multi"` - IncludeAll bool `json:"includeAll"` - AllValue *string `json:"allValue,omitempty"` - Placeholder *string `json:"placeholder,omitempty"` + Name string `json:"name"` + Current DashboardVariableOption `json:"current"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + Refresh DashboardVariableRefresh `json:"refresh"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + Query DashboardDataQueryKind `json:"query"` + Regex string `json:"regex"` + Sort DashboardVariableSort `json:"sort"` + Definition *string `json:"definition,omitempty"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + IncludeAll bool `json:"includeAll"` + AllValue *string `json:"allValue,omitempty"` + Placeholder *string `json:"placeholder,omitempty"` + AllowCustomValue bool `json:"allowCustomValue"` } // NewDashboardQueryVariableSpec creates a new DashboardQueryVariableSpec object. @@ -1254,13 +1246,15 @@ func NewDashboardQueryVariableSpec() *DashboardQueryVariableSpec { String: (func(input string) *string { return &input })(""), }, }, - Hide: DashboardVariableHideDontHide, - Refresh: DashboardVariableRefreshNever, - SkipUrlSync: false, - Query: *NewDashboardDataQueryKind(), - Regex: "", - Multi: false, - IncludeAll: false, + Hide: DashboardVariableHideDontHide, + Refresh: DashboardVariableRefreshNever, + SkipUrlSync: false, + Query: *NewDashboardDataQueryKind(), + Regex: "", + Options: []DashboardVariableOption{}, + Multi: false, + IncludeAll: false, + AllowCustomValue: true, } } @@ -1442,19 +1436,20 @@ func NewDashboardDatasourceVariableKind() *DashboardDatasourceVariableKind { // Datasource variable specification // +k8s:openapi-gen=true type DashboardDatasourceVariableSpec struct { - Name string `json:"name"` - PluginId string `json:"pluginId"` - Refresh DashboardVariableRefresh `json:"refresh"` - Regex string `json:"regex"` - Current DashboardVariableOption `json:"current"` - Options []DashboardVariableOption `json:"options"` - Multi bool `json:"multi"` - IncludeAll bool `json:"includeAll"` - AllValue *string `json:"allValue,omitempty"` - Label *string `json:"label,omitempty"` - Hide DashboardVariableHide `json:"hide"` - SkipUrlSync bool `json:"skipUrlSync"` - Description *string `json:"description,omitempty"` + Name string `json:"name"` + PluginId string `json:"pluginId"` + Refresh DashboardVariableRefresh `json:"refresh"` + Regex string `json:"regex"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + IncludeAll bool `json:"includeAll"` + AllValue *string `json:"allValue,omitempty"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` + AllowCustomValue bool `json:"allowCustomValue"` } // NewDashboardDatasourceVariableSpec creates a new DashboardDatasourceVariableSpec object. @@ -1472,10 +1467,12 @@ func NewDashboardDatasourceVariableSpec() *DashboardDatasourceVariableSpec { String: (func(input string) *string { return &input })(""), }, }, - Multi: false, - IncludeAll: false, - Hide: DashboardVariableHideDontHide, - SkipUrlSync: false, + Options: []DashboardVariableOption{}, + Multi: false, + IncludeAll: false, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + AllowCustomValue: true, } } @@ -1524,6 +1521,7 @@ func NewDashboardIntervalVariableSpec() *DashboardIntervalVariableSpec { String: (func(input string) *string { return &input })(""), }, }, + Options: []DashboardVariableOption{}, Auto: false, AutoMin: "", AutoCount: 0, @@ -1551,29 +1549,32 @@ func NewDashboardCustomVariableKind() *DashboardCustomVariableKind { // Custom variable specification // +k8s:openapi-gen=true type DashboardCustomVariableSpec struct { - Name string `json:"name"` - Query string `json:"query"` - Current DashboardVariableOption `json:"current"` - Options []DashboardVariableOption `json:"options"` - Multi bool `json:"multi"` - IncludeAll bool `json:"includeAll"` - AllValue *string `json:"allValue,omitempty"` - Label *string `json:"label,omitempty"` - Hide DashboardVariableHide `json:"hide"` - SkipUrlSync bool `json:"skipUrlSync"` - Description *string `json:"description,omitempty"` + Name string `json:"name"` + Query string `json:"query"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + IncludeAll bool `json:"includeAll"` + AllValue *string `json:"allValue,omitempty"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` + AllowCustomValue bool `json:"allowCustomValue"` } // NewDashboardCustomVariableSpec creates a new DashboardCustomVariableSpec object. func NewDashboardCustomVariableSpec() *DashboardCustomVariableSpec { return &DashboardCustomVariableSpec{ - Name: "", - Query: "", - Current: *NewDashboardVariableOption(), - Multi: false, - IncludeAll: false, - Hide: DashboardVariableHideDontHide, - SkipUrlSync: false, + Name: "", + Query: "", + Current: *NewDashboardVariableOption(), + Options: []DashboardVariableOption{}, + Multi: false, + IncludeAll: false, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + AllowCustomValue: true, } } @@ -1595,15 +1596,16 @@ func NewDashboardGroupByVariableKind() *DashboardGroupByVariableKind { // GroupBy variable specification // +k8s:openapi-gen=true type DashboardGroupByVariableSpec struct { - Name string `json:"name"` - Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` - Current DashboardVariableOption `json:"current"` - Options []DashboardVariableOption `json:"options"` - Multi bool `json:"multi"` - Label *string `json:"label,omitempty"` - Hide DashboardVariableHide `json:"hide"` - SkipUrlSync bool `json:"skipUrlSync"` - Description *string `json:"description,omitempty"` + Name string `json:"name"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + DefaultValue *DashboardVariableOption `json:"defaultValue,omitempty"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` } // NewDashboardGroupByVariableSpec creates a new DashboardGroupByVariableSpec object. @@ -1618,6 +1620,7 @@ func NewDashboardGroupByVariableSpec() *DashboardGroupByVariableSpec { String: (func(input string) *string { return &input })(""), }, }, + Options: []DashboardVariableOption{}, Multi: false, Hide: DashboardVariableHideDontHide, SkipUrlSync: false, @@ -1642,23 +1645,28 @@ func NewDashboardAdhocVariableKind() *DashboardAdhocVariableKind { // Adhoc variable specification // +k8s:openapi-gen=true type DashboardAdhocVariableSpec struct { - Name string `json:"name"` - Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` - BaseFilters []DashboardAdHocFilterWithLabels `json:"baseFilters"` - Filters []DashboardAdHocFilterWithLabels `json:"filters"` - DefaultKeys []DashboardMetricFindValue `json:"defaultKeys"` - Label *string `json:"label,omitempty"` - Hide DashboardVariableHide `json:"hide"` - SkipUrlSync bool `json:"skipUrlSync"` - Description *string `json:"description,omitempty"` + Name string `json:"name"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + BaseFilters []DashboardAdHocFilterWithLabels `json:"baseFilters"` + Filters []DashboardAdHocFilterWithLabels `json:"filters"` + DefaultKeys []DashboardMetricFindValue `json:"defaultKeys"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` + AllowCustomValue bool `json:"allowCustomValue"` } // NewDashboardAdhocVariableSpec creates a new DashboardAdhocVariableSpec object. func NewDashboardAdhocVariableSpec() *DashboardAdhocVariableSpec { return &DashboardAdhocVariableSpec{ - Name: "", - Hide: DashboardVariableHideDontHide, - SkipUrlSync: false, + Name: "", + BaseFilters: []DashboardAdHocFilterWithLabels{}, + Filters: []DashboardAdHocFilterWithLabels{}, + DefaultKeys: []DashboardMetricFindValue{}, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + AllowCustomValue: true, } } @@ -1743,11 +1751,16 @@ type DashboardSpec struct { // NewDashboardSpec creates a new DashboardSpec object. func NewDashboardSpec() *DashboardSpec { return &DashboardSpec{ + Annotations: []DashboardAnnotationQueryKind{}, CursorSync: DashboardDashboardCursorSyncOff, Editable: (func(input bool) *bool { return &input })(true), + Elements: map[string]DashboardElement{}, Layout: *NewDashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind(), + Links: []DashboardDashboardLink{}, Preload: false, + Tags: []string{}, TimeSettings: *NewDashboardTimeSettingsSpec(), + Variables: []DashboardVariableKind{}, } } @@ -1760,7 +1773,8 @@ type DashboardV2alpha1FieldConfigSourceOverrides struct { // NewDashboardV2alpha1FieldConfigSourceOverrides creates a new DashboardV2alpha1FieldConfigSourceOverrides object. func NewDashboardV2alpha1FieldConfigSourceOverrides() *DashboardV2alpha1FieldConfigSourceOverrides { return &DashboardV2alpha1FieldConfigSourceOverrides{ - Matcher: *NewDashboardMatcherConfig(), + Matcher: *NewDashboardMatcherConfig(), + Properties: []DashboardDynamicConfigValue{}, } } @@ -1891,6 +1905,7 @@ func (resource DashboardPanelKindOrLibraryPanelKind) MarshalJSON() ([]byte, erro if resource.LibraryPanelKind != nil { return json.Marshal(resource.LibraryPanelKind) } + return []byte("null"), nil } @@ -1908,7 +1923,7 @@ func (resource *DashboardPanelKindOrLibraryPanelKind) UnmarshalJSON(raw []byte) discriminator, found := parsedAsMap["kind"] if !found { - return errors.New("discriminator field 'kind' not found in payload") + return nil } switch discriminator { @@ -1930,7 +1945,7 @@ func (resource *DashboardPanelKindOrLibraryPanelKind) UnmarshalJSON(raw []byte) return nil } - return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) + return nil } // +k8s:openapi-gen=true @@ -1960,6 +1975,7 @@ func (resource DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap) MarshalJS if resource.SpecialValueMap != nil { return json.Marshal(resource.SpecialValueMap) } + return []byte("null"), nil } @@ -1977,7 +1993,7 @@ func (resource *DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap) Unmarsha discriminator, found := parsedAsMap["type"] if !found { - return errors.New("discriminator field 'type' not found in payload") + return nil } switch discriminator { @@ -2015,68 +2031,7 @@ func (resource *DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap) Unmarsha return nil } - return fmt.Errorf("could not unmarshal resource with `type = %v`", discriminator) -} - -// +k8s:openapi-gen=true -type DashboardGridLayoutItemKindOrGridLayoutRowKind struct { - GridLayoutItemKind *DashboardGridLayoutItemKind `json:"GridLayoutItemKind,omitempty"` - GridLayoutRowKind *DashboardGridLayoutRowKind `json:"GridLayoutRowKind,omitempty"` -} - -// NewDashboardGridLayoutItemKindOrGridLayoutRowKind creates a new DashboardGridLayoutItemKindOrGridLayoutRowKind object. -func NewDashboardGridLayoutItemKindOrGridLayoutRowKind() *DashboardGridLayoutItemKindOrGridLayoutRowKind { - return &DashboardGridLayoutItemKindOrGridLayoutRowKind{} -} - -// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutItemKindOrGridLayoutRowKind` as JSON. -func (resource DashboardGridLayoutItemKindOrGridLayoutRowKind) MarshalJSON() ([]byte, error) { - if resource.GridLayoutItemKind != nil { - return json.Marshal(resource.GridLayoutItemKind) - } - if resource.GridLayoutRowKind != nil { - return json.Marshal(resource.GridLayoutRowKind) - } - return []byte("null"), nil -} - -// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutItemKindOrGridLayoutRowKind` from JSON. -func (resource *DashboardGridLayoutItemKindOrGridLayoutRowKind) UnmarshalJSON(raw []byte) error { - if raw == nil { - return nil - } - - // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. - parsedAsMap := make(map[string]interface{}) - if err := json.Unmarshal(raw, &parsedAsMap); err != nil { - return err - } - - discriminator, found := parsedAsMap["kind"] - if !found { - return errors.New("discriminator field 'kind' not found in payload") - } - - switch discriminator { - case "GridLayoutItem": - var dashboardGridLayoutItemKind DashboardGridLayoutItemKind - if err := json.Unmarshal(raw, &dashboardGridLayoutItemKind); err != nil { - return err - } - - resource.GridLayoutItemKind = &dashboardGridLayoutItemKind - return nil - case "GridLayoutRow": - var dashboardGridLayoutRowKind DashboardGridLayoutRowKind - if err := json.Unmarshal(raw, &dashboardGridLayoutRowKind); err != nil { - return err - } - - resource.GridLayoutRowKind = &dashboardGridLayoutRowKind - return nil - } - - return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) + return nil } // +k8s:openapi-gen=true @@ -2106,6 +2061,7 @@ func (resource DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRows if resource.RowsLayoutKind != nil { return json.Marshal(resource.RowsLayoutKind) } + return []byte("null"), nil } @@ -2123,7 +2079,7 @@ func (resource *DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRow discriminator, found := parsedAsMap["kind"] if !found { - return errors.New("discriminator field 'kind' not found in payload") + return nil } switch discriminator { @@ -2161,7 +2117,7 @@ func (resource *DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRow return nil } - return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) + return nil } // +k8s:openapi-gen=true @@ -2187,6 +2143,7 @@ func (resource DashboardConditionalRenderingVariableKindOrConditionalRenderingDa if resource.ConditionalRenderingTimeRangeSizeKind != nil { return json.Marshal(resource.ConditionalRenderingTimeRangeSizeKind) } + return []byte("null"), nil } @@ -2204,7 +2161,7 @@ func (resource *DashboardConditionalRenderingVariableKindOrConditionalRenderingD discriminator, found := parsedAsMap["kind"] if !found { - return errors.New("discriminator field 'kind' not found in payload") + return nil } switch discriminator { @@ -2234,7 +2191,7 @@ func (resource *DashboardConditionalRenderingVariableKindOrConditionalRenderingD return nil } - return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) + return nil } // +k8s:openapi-gen=true @@ -2264,6 +2221,7 @@ func (resource DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabs if resource.TabsLayoutKind != nil { return json.Marshal(resource.TabsLayoutKind) } + return []byte("null"), nil } @@ -2281,7 +2239,7 @@ func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTab discriminator, found := parsedAsMap["kind"] if !found { - return errors.New("discriminator field 'kind' not found in payload") + return nil } switch discriminator { @@ -2319,7 +2277,7 @@ func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTab return nil } - return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) + return nil } // +k8s:openapi-gen=true @@ -2365,6 +2323,7 @@ func (resource DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKin if resource.AdhocVariableKind != nil { return json.Marshal(resource.AdhocVariableKind) } + return []byte("null"), nil } @@ -2382,7 +2341,7 @@ func (resource *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKi discriminator, found := parsedAsMap["kind"] if !found { - return errors.New("discriminator field 'kind' not found in payload") + return nil } switch discriminator { @@ -2452,7 +2411,7 @@ func (resource *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKi return nil } - return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) + return nil } // +k8s:openapi-gen=true diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/types.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/types.go index 7cc9d29d149..d3599063b7a 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/types.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/types.go @@ -11,10 +11,9 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardVersionList struct { metav1.TypeMeta `json:",inline"` - // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []DashboardVersionInfo `json:"items,omitempty"` + Items []DashboardVersionInfo `json:"items"` } // +k8s:deepcopy-gen=true @@ -69,10 +68,9 @@ type LibraryPanel struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanelList struct { metav1.TypeMeta `json:",inline"` - // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []LibraryPanel `json:"items,omitempty"` + Items []LibraryPanel `json:"items"` } // +k8s:deepcopy-gen=true diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go index 8df2424f977..85b5a6f65cb 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go @@ -56,13 +56,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfigSource(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKindOrGridLayoutRowKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKindOrGridLayoutRowKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableKind(ref), "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref), @@ -513,8 +510,15 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableSpec(ref common.Re Format: "", }, }, + "allowCustomValue": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, }, - Required: []string{"name", "baseFilters", "filters", "defaultKeys", "hide", "skipUrlSync"}, + Required: []string{"name", "baseFilters", "filters", "defaultKeys", "hide", "skipUrlSync", "allowCustomValue"}, }, }, Dependencies: []string{ @@ -640,7 +644,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQuerySpec(ref common. Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAnnotationPanelFilter"), }, }, - "options": { + "legacyOptions": { SchemaProps: spec.SchemaProps{ Description: "Catch-all field for datasource-specific properties", Type: []string{"object"}, @@ -1344,8 +1348,15 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardCustomVariableSpec(ref common.R Format: "", }, }, + "allowCustomValue": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, }, - Required: []string{"name", "query", "current", "options", "multi", "includeAll", "hide", "skipUrlSync"}, + Required: []string{"name", "query", "current", "options", "multi", "includeAll", "hide", "skipUrlSync", "allowCustomValue"}, }, }, Dependencies: []string{ @@ -1727,8 +1738,15 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableSpec(ref comm Format: "", }, }, + "allowCustomValue": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, }, - Required: []string{"name", "pluginId", "refresh", "regex", "current", "options", "multi", "includeAll", "hide", "skipUrlSync"}, + Required: []string{"name", "pluginId", "refresh", "regex", "current", "options", "multi", "includeAll", "hide", "skipUrlSync", "allowCustomValue"}, }, }, Dependencies: []string{ @@ -2036,30 +2054,6 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKind(ref common.R } } -func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKindOrGridLayoutRowKind(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "GridLayoutItemKind": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind"), - }, - }, - "GridLayoutRowKind": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowKind"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowKind"}, - } -} - func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2211,89 +2205,6 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrRowsLayoutKindO } } -func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowKind(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowSpec"), - }, - }, - }, - Required: []string{"kind", "spec"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowSpec"}, - } -} - -func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "y": { - SchemaProps: spec.SchemaProps{ - Default: 0, - Type: []string{"integer"}, - Format: "int64", - }, - }, - "collapsed": { - SchemaProps: spec.SchemaProps{ - Default: false, - Type: []string{"boolean"}, - Format: "", - }, - }, - "title": { - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "elements": { - SchemaProps: spec.SchemaProps{ - Description: "Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind"), - }, - }, - }, - }, - }, - "repeat": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions"), - }, - }, - }, - Required: []string{"y", "collapsed", "title", "elements"}, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions"}, - } -} - func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2306,7 +2217,8 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref common.Refer Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKindOrGridLayoutRowKind"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind"), }, }, }, @@ -2317,7 +2229,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref common.Refer }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKindOrGridLayoutRowKind"}, + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind"}, } } @@ -2369,6 +2281,11 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref common. Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"), }, }, + "defaultValue": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, "current": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, @@ -3403,8 +3320,15 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.Re Format: "", }, }, + "allowCustomValue": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, }, - Required: []string{"name", "current", "hide", "refresh", "skipUrlSync", "query", "regex", "sort", "options", "multi", "includeAll"}, + Required: []string{"name", "current", "hide", "refresh", "skipUrlSync", "query", "regex", "sort", "options", "multi", "includeAll", "allowCustomValue"}, }, }, Dependencies: []string{ @@ -4764,6 +4688,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardVersionList(ref common.Referenc }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ @@ -4987,6 +4912,7 @@ func schema_pkg_apis_dashboard_v2alpha1_LibraryPanelList(ref common.ReferenceCal }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list index 889a4fcdfed..d0755a7cd1f 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list @@ -12,7 +12,6 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Links API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Mappings API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfigSource,Overrides -API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutRowSpec,Elements API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutSpec,Items API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGroupByVariableSpec,Options API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardIntervalVariableSpec,Options @@ -36,8 +35,6 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/ API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind,ConditionalRenderingDataKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind,ConditionalRenderingTimeRangeSizeKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind,ConditionalRenderingVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutItemKindOrGridLayoutRowKind,GridLayoutItemKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutItemKindOrGridLayoutRowKind,GridLayoutRowKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,AutoGridLayoutKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,GridLayoutKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,RowsLayoutKind @@ -66,3 +63,4 @@ API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/ap API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,RegexMap API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,SpecialValueMap API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,ValueMap +API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardList,ListMeta diff --git a/apps/dashboard/pkg/apis/dashboard_manifest.go b/apps/dashboard/pkg/apis/dashboard_manifest.go index cc5451a78e8..e9fa97d8017 100644 --- a/apps/dashboard/pkg/apis/dashboard_manifest.go +++ b/apps/dashboard/pkg/apis/dashboard_manifest.go @@ -6,9 +6,12 @@ package apis import ( - "encoding/json" - + "fmt" "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + v0alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" + v1beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" + v2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" ) var appManifestData = app.ManifestData{ @@ -36,12 +39,6 @@ var appManifestData = app.ManifestData{ }, } -func jsonToMap(j string) map[string]any { - m := make(map[string]any) - json.Unmarshal([]byte(j), &j) - return m -} - func LocalManifest() app.Manifest { return app.NewEmbeddedManifest(appManifestData) } @@ -49,3 +46,16 @@ func LocalManifest() app.Manifest { func RemoteManifest() app.Manifest { return app.NewAPIServerManifest("dashboard") } + +var kindVersionToGoType = map[string]resource.Kind{ + "Dashboard/v0alpha1": v0alpha1.DashboardKind(), + "Dashboard/v1beta1": v1beta1.DashboardKind(), + "Dashboard/v2alpha1": v2alpha1.DashboardKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} diff --git a/apps/folder/Makefile b/apps/folder/Makefile index 95a9469d052..16180b36af0 100644 --- a/apps/folder/Makefile +++ b/apps/folder/Makefile @@ -1,4 +1,4 @@ -APP_SDK_VERSION := v0.35.1 +APP_SDK_VERSION := v0.38.2 APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION) APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk diff --git a/apps/folder/go.mod b/apps/folder/go.mod index 3ce40a1004c..84685631d82 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -1,11 +1,11 @@ module github.com/grafana/grafana/apps/folder -go 1.24.2 +go 1.24.3 require ( - github.com/grafana/grafana-app-sdk v0.35.1 - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2 - k8s.io/apimachinery v0.32.3 + github.com/grafana/grafana-app-sdk v0.38.2 + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e + k8s.io/apimachinery v0.33.1 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff ) @@ -15,18 +15,17 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect + github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -36,25 +35,25 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/client-go v0.32.3 // indirect + k8s.io/client-go v0.33.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/apps/folder/go.sum b/apps/folder/go.sum index 6af3bcad380..192c9f9f797 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -10,8 +10,8 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= -github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= @@ -20,28 +20,24 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK7NGmlX3RI= -github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI= -github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM= -github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2 h1:kvG92f3XbJlQPUcZfXlTNLziI4e8LYeA9Jv2ixmM5Ic= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250422074709-7c8433fbb2c2/go.mod h1:ll14OJrUGYgXApz3YX6zmxYjRMZHL+pgQjoKBuRzaRs= +github.com/grafana/grafana-app-sdk v0.38.2 h1:raQ1mqCmNJ2AyCSV3m6ehFC0TGVZcqRcM6iOIDT6ZIE= +github.com/grafana/grafana-app-sdk v0.38.2/go.mod h1:LhZ4i2IXGnW5FF/jAczX7SnGFNai7x2qwB8YHNZWiZI= +github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc= +github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -78,14 +74,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= @@ -115,8 +111,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -125,14 +121,14 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -154,12 +150,12 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= diff --git a/apps/folder/pkg/apis/folder/v1beta1/constants.go b/apps/folder/pkg/apis/folder/v1beta1/constants.go index 9ff1a8be368..38d45074658 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/constants.go +++ b/apps/folder/pkg/apis/folder/v1beta1/constants.go @@ -3,16 +3,16 @@ package v1beta1 import "k8s.io/apimachinery/pkg/runtime/schema" const ( - // Group is the API group used by all kinds in this package - Group = "folder.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v1beta1" + // APIGroup is the API group used by all kinds in this package + APIGroup = "folder.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v1beta1" ) var ( // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, + Group: APIGroup, + Version: APIVersion, } ) diff --git a/apps/folder/pkg/apis/folder/v1beta1/folder_metadata_gen.go b/apps/folder/pkg/apis/folder/v1beta1/folder_metadata_gen.go index 9f29f863e67..f9c9f5caf98 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/folder_metadata_gen.go +++ b/apps/folder/pkg/apis/folder/v1beta1/folder_metadata_gen.go @@ -24,5 +24,8 @@ type FolderMetadata struct { // NewFolderMetadata creates a new FolderMetadata object. func NewFolderMetadata() *FolderMetadata { - return &FolderMetadata{} + return &FolderMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/folder/pkg/apis/folder/v1beta1/register.go b/apps/folder/pkg/apis/folder/v1beta1/register.go index 2ce4c2888ba..c660221124c 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/register.go +++ b/apps/folder/pkg/apis/folder/v1beta1/register.go @@ -11,8 +11,8 @@ import ( ) const ( - GROUP = Group - VERSION = Version + GROUP = APIGroup + VERSION = APIVersion RESOURCE = "folders" APIVERSION = GROUP + "/" + VERSION RESOURCEGROUP = RESOURCE + "." + GROUP diff --git a/apps/folder/pkg/apis/folder/v1beta1/types.go b/apps/folder/pkg/apis/folder/v1beta1/types.go index 6783583b499..8c77a144305 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/types.go +++ b/apps/folder/pkg/apis/folder/v1beta1/types.go @@ -16,7 +16,7 @@ type FolderInfoList struct { // +listType=map // +listMapKey=uid - Items []FolderInfo `json:"items,omitempty"` + Items []FolderInfo `json:"items"` } // FolderInfo briefly describes a folder -- unlike a folder resource, diff --git a/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go b/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go index 92d505388f3..02c4e7b301d 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go +++ b/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go @@ -274,6 +274,7 @@ func schema_pkg_apis_folder_v1beta1_FolderInfoList(ref common.ReferenceCallback) }, }, }, + Required: []string{"items"}, }, }, Dependencies: []string{ diff --git a/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi_violation_exceptions.list b/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi_violation_exceptions.list index 74df3d545ad..41cadea22c0 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi_violation_exceptions.list +++ b/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi_violation_exceptions.list @@ -1,3 +1,4 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1,DescendantCounts,Counts API rule violation: list_type_missing,github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1,FolderInfoList,Items API rule violation: list_type_missing,github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1,FolderMetadata,Finalizers +API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1,FolderList,ListMeta diff --git a/apps/folder/pkg/apis/folder_manifest.go b/apps/folder/pkg/apis/folder_manifest.go index 4d45b12e405..8491ddc6083 100644 --- a/apps/folder/pkg/apis/folder_manifest.go +++ b/apps/folder/pkg/apis/folder_manifest.go @@ -6,9 +6,12 @@ package apis import ( - "encoding/json" + "fmt" "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + + v1beta1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" ) var appManifestData = app.ManifestData{ @@ -28,12 +31,6 @@ var appManifestData = app.ManifestData{ }, } -func jsonToMap(j string) map[string]any { - m := make(map[string]any) - json.Unmarshal([]byte(j), &j) - return m -} - func LocalManifest() app.Manifest { return app.NewEmbeddedManifest(appManifestData) } @@ -41,3 +38,14 @@ func LocalManifest() app.Manifest { func RemoteManifest() app.Manifest { return app.NewAPIServerManifest("folder") } + +var kindVersionToGoType = map[string]resource.Kind{ + "Folder/v1beta1": v1beta1.FolderKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} diff --git a/apps/iam/Makefile b/apps/iam/Makefile new file mode 100644 index 00000000000..56de939d428 --- /dev/null +++ b/apps/iam/Makefile @@ -0,0 +1,29 @@ +APP_SDK_VERSION := v0.38.2 +APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION) +APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk + +.PHONY: install-app-sdk +install-app-sdk: $(APP_SDK_BIN) ## Install the Grafana App SDK + +$(APP_SDK_BIN): + @echo "Installing Grafana App SDK version $(APP_SDK_VERSION)" + @mkdir -p $(APP_SDK_DIR) + # The only way to install specific versions of binaries using `go install` + # is by setting GOBIN to the directory you want to install the binary to. + GOBIN=$(APP_SDK_DIR) go install github.com/grafana/grafana-app-sdk/cmd/grafana-app-sdk@$(APP_SDK_VERSION) + @touch $@ + +.PHONY: update-app-sdk +update-app-sdk: ## Update the Grafana App SDK dependency in go.mod + go get github.com/grafana/grafana-app-sdk@$(APP_SDK_VERSION) + go mod tidy + +.PHONY: generate +generate: install-app-sdk update-app-sdk ## Run Grafana App SDK code generation + @$(APP_SDK_BIN) generate \ + --source=./kinds/ \ + --gogenpath=./pkg/apis \ + --grouping=group \ + --defencoding=none \ + --noschemasinmanifest \ + --postprocess \ No newline at end of file diff --git a/apps/iam/go.mod b/apps/iam/go.mod new file mode 100644 index 00000000000..4501e755d56 --- /dev/null +++ b/apps/iam/go.mod @@ -0,0 +1,60 @@ +module github.com/grafana/grafana/apps/iam + +go 1.24.3 + +require ( + github.com/grafana/grafana-app-sdk v0.38.2 + k8s.io/apimachinery v0.33.1 + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.63.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/time v0.11.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.33.1 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/apps/iam/go.sum b/apps/iam/go.sum new file mode 100644 index 00000000000..21060a8fa64 --- /dev/null +++ b/apps/iam/go.sum @@ -0,0 +1,171 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/grafana-app-sdk v0.38.2 h1:raQ1mqCmNJ2AyCSV3m6ehFC0TGVZcqRcM6iOIDT6ZIE= +github.com/grafana/grafana-app-sdk v0.38.2/go.mod h1:LhZ4i2IXGnW5FF/jAczX7SnGFNai7x2qwB8YHNZWiZI= +github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc= +github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= +github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/apps/iam/kinds/cue.mod/module.cue b/apps/iam/kinds/cue.mod/module.cue new file mode 100644 index 00000000000..d1ddb24fcea --- /dev/null +++ b/apps/iam/kinds/cue.mod/module.cue @@ -0,0 +1,4 @@ +module: "github.com/grafana/grafana/apps/iam/kinds" +language: { + version: "v0.9.0" +} diff --git a/apps/iam/kinds/manifest.cue b/apps/iam/kinds/manifest.cue new file mode 100644 index 00000000000..f2f4b519d7b --- /dev/null +++ b/apps/iam/kinds/manifest.cue @@ -0,0 +1,7 @@ +package kinds + +manifest: { + appName: "iam" + groupOverride: "iam.grafana.app" + kinds: [ globalrole, globalrolebinding, corerole, role, rolebinding, resourcepermission ] +} diff --git a/apps/iam/kinds/resourcepermission.cue b/apps/iam/kinds/resourcepermission.cue new file mode 100644 index 00000000000..46e27b160fe --- /dev/null +++ b/apps/iam/kinds/resourcepermission.cue @@ -0,0 +1,24 @@ +package kinds + +import ( + "github.com/grafana/grafana/apps/iam/kinds/v0alpha1" +) + +resourcepermission: { + kind: "ResourcePermission" + pluralName: "ResourcePermissions" + current: "v0alpha1" + + codegen: { + ts: { enabled: false } + go: { enabled: true } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.ResourcePermission + } + } + } +} diff --git a/apps/iam/kinds/role.cue b/apps/iam/kinds/role.cue new file mode 100644 index 00000000000..1422a57d246 --- /dev/null +++ b/apps/iam/kinds/role.cue @@ -0,0 +1,62 @@ +package kinds + +import ( + "github.com/grafana/grafana/apps/iam/kinds/v0alpha1" +) + +role: { + kind: "Role" + pluralName: "Roles" + current: "v0alpha1" + + codegen: { + ts: { enabled: false } + go: { enabled: true } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.RoleSpec + } + } + } +} + +corerole: { + kind: "CoreRole" + pluralName: "CoreRoles" + current: "v0alpha1" + + codegen: { + ts: { enabled: false } + go: { enabled: true } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.RoleSpec + } + } + } +} + +globalrole: { + kind: "GlobalRole" + pluralName: "GlobalRoles" + current: "v0alpha1" + + codegen: { + ts: { enabled: false } + go: { enabled: true } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.RoleSpec + } + } + } +} \ No newline at end of file diff --git a/apps/iam/kinds/rolebinding.cue b/apps/iam/kinds/rolebinding.cue new file mode 100644 index 00000000000..caee8c22717 --- /dev/null +++ b/apps/iam/kinds/rolebinding.cue @@ -0,0 +1,43 @@ +package kinds + +import ( + "github.com/grafana/grafana/apps/iam/kinds/v0alpha1" +) + +rolebinding: { + kind: "RoleBinding" + pluralName: "RoleBindings" + current: "v0alpha1" + + codegen: { + ts: { enabled: false } + go: { enabled: true } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.RoleBindingSpec + } + } + } +} + +globalrolebinding: { + kind: "GlobalRoleBinding" + pluralName: "GlobalRoleBindings" + current: "v0alpha1" + + codegen: { + ts: { enabled: false } + go: { enabled: true } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.GlobalRoleBindingSpec + } + } + } +} diff --git a/apps/iam/kinds/v0alpha1/resourcepermission.cue b/apps/iam/kinds/v0alpha1/resourcepermission.cue new file mode 100644 index 00000000000..4cb02c3c400 --- /dev/null +++ b/apps/iam/kinds/v0alpha1/resourcepermission.cue @@ -0,0 +1,25 @@ +package v0alpha1 + +ResourcePermission: { + #Resource: { + // api group of the resource (e.g: "folder.grafana.app") + apiGroup: string + // kind of the resource (e.g: "folders") + resource: string + // uid of the resource (e.g: "fold1") + name: string + } + #Permission: { + // kind of the identity getting the permission + kind: "User" | "ServiceAccount" | "Team" | "BasicRole" + // uid of the identity getting the permission + name: string + // list of actions granted to the user (e.g. "admin" or "get", "update") + verbs: [...string] + } + + resource: #Resource + permissions: [...#Permission] +} + + diff --git a/apps/iam/kinds/v0alpha1/rolebindingspec.cue b/apps/iam/kinds/v0alpha1/rolebindingspec.cue new file mode 100644 index 00000000000..0b4a6396f26 --- /dev/null +++ b/apps/iam/kinds/v0alpha1/rolebindingspec.cue @@ -0,0 +1,37 @@ +package v0alpha1 + +RoleBindingSpec: { + #Subject: { + // kind of the identity getting the permission + kind: "User" | "ServiceAccount" | "Team" | "BasicRole" + // uid of the identity + name: string + } + #RoleRef: { + // kind of role + kind: "Role" | "CoreRole" | "GlobalRole" + // uid of the role + name: string + } + + subjects: [...#Subject] + roleRef: #RoleRef +} + +GlobalRoleBindingSpec: { + #Subject: { + // kind of the identity getting the permission + kind: "User" | "ServiceAccount" | "Team" | "BasicRole" + // uid of the identity + name: string + } + #RoleRef: { + // kind of role + kind: "CoreRole" | "GlobalRole" + // uid of the role + name: string + } + + subjects: [...#Subject] + roleRef: #RoleRef +} diff --git a/apps/iam/kinds/v0alpha1/rolespec.cue b/apps/iam/kinds/v0alpha1/rolespec.cue new file mode 100644 index 00000000000..46d6ce1f2c4 --- /dev/null +++ b/apps/iam/kinds/v0alpha1/rolespec.cue @@ -0,0 +1,23 @@ +package v0alpha1 + +RoleSpec: { + #Permission: { + // RBAC action (e.g: "dashbaords:read") + action: string + // RBAC scope (e.g: "dashboards:uid:dash1") + scope: string + } + + // Display name of the role + title: string + + version: int + group: string + permissions: [...#Permission] + + // TODO: + // delegatable?: bool + // created? + // updated? +} + diff --git a/apps/iam/pkg/apis/iam/v0alpha1/constants.go b/apps/iam/pkg/apis/iam/v0alpha1/constants.go new file mode 100644 index 00000000000..f145b04b736 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/constants.go @@ -0,0 +1,18 @@ +package v0alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // APIGroup is the API group used by all kinds in this package + APIGroup = "iam.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v0alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: APIGroup, + Version: APIVersion, + } +) diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_codec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_codec_gen.go similarity index 57% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_codec_gen.go rename to apps/iam/pkg/apis/iam/v0alpha1/corerole_codec_gen.go index 7b5050a3af4..9b7e0b0cfef 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_codec_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_codec_gen.go @@ -11,18 +11,18 @@ import ( "github.com/grafana/grafana-app-sdk/resource" ) -// JSONCodec is an implementation of resource.Codec for kubernetes JSON encoding -type JSONCodec struct{} +// CoreRoleJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type CoreRoleJSONCodec struct{} // Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` -func (*JSONCodec) Read(reader io.Reader, into resource.Object) error { +func (*CoreRoleJSONCodec) Read(reader io.Reader, into resource.Object) error { return json.NewDecoder(reader).Decode(into) } // Write writes JSON-encoded bytes into `writer` marshaled from `from` -func (*JSONCodec) Write(writer io.Writer, from resource.Object) error { +func (*CoreRoleJSONCodec) Write(writer io.Writer, from resource.Object) error { return json.NewEncoder(writer).Encode(from) } // Interface compliance checks -var _ resource.Codec = &JSONCodec{} +var _ resource.Codec = &CoreRoleJSONCodec{} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/corerole_metadata_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_metadata_gen.go new file mode 100644 index 00000000000..8f174cec316 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type CoreRoleMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewCoreRoleMetadata creates a new CoreRoleMetadata object. +func NewCoreRoleMetadata() *CoreRoleMetadata { + return &CoreRoleMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/corerole_object_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_object_gen.go new file mode 100644 index 00000000000..4aba32da755 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type CoreRole struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the CoreRole + Spec CoreRoleSpec `json:"spec" yaml:"spec"` + + Status CoreRoleStatus `json:"status" yaml:"status"` +} + +func (o *CoreRole) GetSpec() any { + return o.Spec +} + +func (o *CoreRole) SetSpec(spec any) error { + cast, ok := spec.(CoreRoleSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *CoreRole) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *CoreRole) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *CoreRole) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(CoreRoleStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type CoreRoleStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *CoreRole) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *CoreRole) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *CoreRole) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *CoreRole) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *CoreRole) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *CoreRole) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *CoreRole) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *CoreRole) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *CoreRole) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *CoreRole) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *CoreRole) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *CoreRole) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *CoreRole) DeepCopy() *CoreRole { + cpy := &CoreRole{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *CoreRole) DeepCopyInto(dst *CoreRole) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &CoreRole{} + +// +k8s:openapi-gen=true +type CoreRoleList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []CoreRole `json:"items" yaml:"items"` +} + +func (o *CoreRoleList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *CoreRoleList) Copy() resource.ListObject { + cpy := &CoreRoleList{ + TypeMeta: o.TypeMeta, + Items: make([]CoreRole, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*CoreRole); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *CoreRoleList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *CoreRoleList) SetItems(items []resource.Object) { + o.Items = make([]CoreRole, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*CoreRole) + } +} + +func (o *CoreRoleList) DeepCopy() *CoreRoleList { + cpy := &CoreRoleList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *CoreRoleList) DeepCopyInto(dst *CoreRoleList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &CoreRoleList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *CoreRoleSpec) DeepCopy() *CoreRoleSpec { + cpy := &CoreRoleSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *CoreRoleSpec) DeepCopyInto(dst *CoreRoleSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of CoreRoleStatus +func (s *CoreRoleStatus) DeepCopy() *CoreRoleStatus { + cpy := &CoreRoleStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies CoreRoleStatus into another CoreRoleStatus object +func (s *CoreRoleStatus) DeepCopyInto(dst *CoreRoleStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/corerole_schema_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_schema_gen.go new file mode 100644 index 00000000000..637f78355f8 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaCoreRole = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", &CoreRole{}, &CoreRoleList{}, resource.WithKind("CoreRole"), + resource.WithPlural("coreroles"), resource.WithScope(resource.NamespacedScope)) + kindCoreRole = resource.Kind{ + Schema: schemaCoreRole, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &CoreRoleJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func CoreRoleKind() resource.Kind { + return kindCoreRole +} + +// Schema returns a resource.SimpleSchema representation of CoreRole +func CoreRoleSchema() *resource.SimpleSchema { + return schemaCoreRole +} + +// Interface compliance checks +var _ resource.Schema = kindCoreRole diff --git a/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go new file mode 100644 index 00000000000..eb2c1d52e40 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_spec_gen.go @@ -0,0 +1,36 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type CoreRolespecPermission struct { + // RBAC action (e.g: "dashbaords:read") + Action string `json:"action"` + // RBAC scope (e.g: "dashboards:uid:dash1") + Scope string `json:"scope"` +} + +// NewCoreRolespecPermission creates a new CoreRolespecPermission object. +func NewCoreRolespecPermission() *CoreRolespecPermission { + return &CoreRolespecPermission{} +} + +// +k8s:openapi-gen=true +type CoreRoleSpec struct { + // Display name of the role + Title string `json:"title"` + Version int64 `json:"version"` + Group string `json:"group"` + // TODO: + // delegatable?: bool + // created? + // updated? + Permissions []CoreRolespecPermission `json:"permissions"` +} + +// NewCoreRoleSpec creates a new CoreRoleSpec object. +func NewCoreRoleSpec() *CoreRoleSpec { + return &CoreRoleSpec{ + Permissions: []CoreRolespecPermission{}, + } +} diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_status_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/corerole_status_gen.go similarity index 53% rename from apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_status_gen.go rename to apps/iam/pkg/apis/iam/v0alpha1/corerole_status_gen.go index 16a8d994f82..628374526cd 100644 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_status_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/corerole_status_gen.go @@ -3,42 +3,42 @@ package v0alpha1 // +k8s:openapi-gen=true -type StatusOperatorState struct { +type CoreRolestatusOperatorState struct { // lastEvaluation is the ResourceVersion last evaluated LastEvaluation string `json:"lastEvaluation"` // state describes the state of the lastEvaluation. // It is limited to three possible states for machine evaluation. - State StatusOperatorStateState `json:"state"` + State CoreRoleStatusOperatorStateState `json:"state"` // descriptiveState is an optional more descriptive state field which has no requirements on format DescriptiveState *string `json:"descriptiveState,omitempty"` // details contains any extra information that is operator-specific Details map[string]interface{} `json:"details,omitempty"` } -// NewStatusOperatorState creates a new StatusOperatorState object. -func NewStatusOperatorState() *StatusOperatorState { - return &StatusOperatorState{} +// NewCoreRolestatusOperatorState creates a new CoreRolestatusOperatorState object. +func NewCoreRolestatusOperatorState() *CoreRolestatusOperatorState { + return &CoreRolestatusOperatorState{} } // +k8s:openapi-gen=true -type Status struct { +type CoreRoleStatus struct { // operatorStates is a map of operator ID to operator state evaluations. // Any operator which consumes this kind SHOULD add its state evaluation information to this field. - OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"` + OperatorStates map[string]CoreRolestatusOperatorState `json:"operatorStates,omitempty"` // additionalFields is reserved for future use AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` } -// NewStatus creates a new Status object. -func NewStatus() *Status { - return &Status{} +// NewCoreRoleStatus creates a new CoreRoleStatus object. +func NewCoreRoleStatus() *CoreRoleStatus { + return &CoreRoleStatus{} } // +k8s:openapi-gen=true -type StatusOperatorStateState string +type CoreRoleStatusOperatorStateState string const ( - StatusOperatorStateStateSuccess StatusOperatorStateState = "success" - StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress" - StatusOperatorStateStateFailed StatusOperatorStateState = "failed" + CoreRoleStatusOperatorStateStateSuccess CoreRoleStatusOperatorStateState = "success" + CoreRoleStatusOperatorStateStateInProgress CoreRoleStatusOperatorStateState = "in_progress" + CoreRoleStatusOperatorStateStateFailed CoreRoleStatusOperatorStateState = "failed" ) diff --git a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_codec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_codec_gen.go similarity index 56% rename from apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_codec_gen.go rename to apps/iam/pkg/apis/iam/v0alpha1/globalrole_codec_gen.go index 7b5050a3af4..583403a5f03 100644 --- a/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1/routingtree_codec_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_codec_gen.go @@ -11,18 +11,18 @@ import ( "github.com/grafana/grafana-app-sdk/resource" ) -// JSONCodec is an implementation of resource.Codec for kubernetes JSON encoding -type JSONCodec struct{} +// GlobalRoleJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type GlobalRoleJSONCodec struct{} // Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` -func (*JSONCodec) Read(reader io.Reader, into resource.Object) error { +func (*GlobalRoleJSONCodec) Read(reader io.Reader, into resource.Object) error { return json.NewDecoder(reader).Decode(into) } // Write writes JSON-encoded bytes into `writer` marshaled from `from` -func (*JSONCodec) Write(writer io.Writer, from resource.Object) error { +func (*GlobalRoleJSONCodec) Write(writer io.Writer, from resource.Object) error { return json.NewEncoder(writer).Encode(from) } // Interface compliance checks -var _ resource.Codec = &JSONCodec{} +var _ resource.Codec = &GlobalRoleJSONCodec{} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrole_metadata_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_metadata_gen.go new file mode 100644 index 00000000000..293082a4aaa --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type GlobalRoleMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewGlobalRoleMetadata creates a new GlobalRoleMetadata object. +func NewGlobalRoleMetadata() *GlobalRoleMetadata { + return &GlobalRoleMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrole_object_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_object_gen.go new file mode 100644 index 00000000000..2081adb8507 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type GlobalRole struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the GlobalRole + Spec GlobalRoleSpec `json:"spec" yaml:"spec"` + + Status GlobalRoleStatus `json:"status" yaml:"status"` +} + +func (o *GlobalRole) GetSpec() any { + return o.Spec +} + +func (o *GlobalRole) SetSpec(spec any) error { + cast, ok := spec.(GlobalRoleSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *GlobalRole) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *GlobalRole) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *GlobalRole) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(GlobalRoleStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type GlobalRoleStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *GlobalRole) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *GlobalRole) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *GlobalRole) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *GlobalRole) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *GlobalRole) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *GlobalRole) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *GlobalRole) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *GlobalRole) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *GlobalRole) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *GlobalRole) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *GlobalRole) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *GlobalRole) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *GlobalRole) DeepCopy() *GlobalRole { + cpy := &GlobalRole{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *GlobalRole) DeepCopyInto(dst *GlobalRole) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &GlobalRole{} + +// +k8s:openapi-gen=true +type GlobalRoleList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []GlobalRole `json:"items" yaml:"items"` +} + +func (o *GlobalRoleList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *GlobalRoleList) Copy() resource.ListObject { + cpy := &GlobalRoleList{ + TypeMeta: o.TypeMeta, + Items: make([]GlobalRole, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*GlobalRole); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *GlobalRoleList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *GlobalRoleList) SetItems(items []resource.Object) { + o.Items = make([]GlobalRole, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*GlobalRole) + } +} + +func (o *GlobalRoleList) DeepCopy() *GlobalRoleList { + cpy := &GlobalRoleList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *GlobalRoleList) DeepCopyInto(dst *GlobalRoleList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &GlobalRoleList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *GlobalRoleSpec) DeepCopy() *GlobalRoleSpec { + cpy := &GlobalRoleSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *GlobalRoleSpec) DeepCopyInto(dst *GlobalRoleSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of GlobalRoleStatus +func (s *GlobalRoleStatus) DeepCopy() *GlobalRoleStatus { + cpy := &GlobalRoleStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies GlobalRoleStatus into another GlobalRoleStatus object +func (s *GlobalRoleStatus) DeepCopyInto(dst *GlobalRoleStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrole_schema_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_schema_gen.go new file mode 100644 index 00000000000..ce3322f97c0 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaGlobalRole = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", &GlobalRole{}, &GlobalRoleList{}, resource.WithKind("GlobalRole"), + resource.WithPlural("globalroles"), resource.WithScope(resource.NamespacedScope)) + kindGlobalRole = resource.Kind{ + Schema: schemaGlobalRole, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &GlobalRoleJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func GlobalRoleKind() resource.Kind { + return kindGlobalRole +} + +// Schema returns a resource.SimpleSchema representation of GlobalRole +func GlobalRoleSchema() *resource.SimpleSchema { + return schemaGlobalRole +} + +// Interface compliance checks +var _ resource.Schema = kindGlobalRole diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go new file mode 100644 index 00000000000..785d2d5724b --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_spec_gen.go @@ -0,0 +1,36 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type GlobalRolespecPermission struct { + // RBAC action (e.g: "dashbaords:read") + Action string `json:"action"` + // RBAC scope (e.g: "dashboards:uid:dash1") + Scope string `json:"scope"` +} + +// NewGlobalRolespecPermission creates a new GlobalRolespecPermission object. +func NewGlobalRolespecPermission() *GlobalRolespecPermission { + return &GlobalRolespecPermission{} +} + +// +k8s:openapi-gen=true +type GlobalRoleSpec struct { + // Display name of the role + Title string `json:"title"` + Version int64 `json:"version"` + Group string `json:"group"` + // TODO: + // delegatable?: bool + // created? + // updated? + Permissions []GlobalRolespecPermission `json:"permissions"` +} + +// NewGlobalRoleSpec creates a new GlobalRoleSpec object. +func NewGlobalRoleSpec() *GlobalRoleSpec { + return &GlobalRoleSpec{ + Permissions: []GlobalRolespecPermission{}, + } +} diff --git a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_status_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_status_gen.go similarity index 52% rename from apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_status_gen.go rename to apps/iam/pkg/apis/iam/v0alpha1/globalrole_status_gen.go index 16a8d994f82..b6052559754 100644 --- a/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/timeinterval_status_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrole_status_gen.go @@ -3,42 +3,42 @@ package v0alpha1 // +k8s:openapi-gen=true -type StatusOperatorState struct { +type GlobalRolestatusOperatorState struct { // lastEvaluation is the ResourceVersion last evaluated LastEvaluation string `json:"lastEvaluation"` // state describes the state of the lastEvaluation. // It is limited to three possible states for machine evaluation. - State StatusOperatorStateState `json:"state"` + State GlobalRoleStatusOperatorStateState `json:"state"` // descriptiveState is an optional more descriptive state field which has no requirements on format DescriptiveState *string `json:"descriptiveState,omitempty"` // details contains any extra information that is operator-specific Details map[string]interface{} `json:"details,omitempty"` } -// NewStatusOperatorState creates a new StatusOperatorState object. -func NewStatusOperatorState() *StatusOperatorState { - return &StatusOperatorState{} +// NewGlobalRolestatusOperatorState creates a new GlobalRolestatusOperatorState object. +func NewGlobalRolestatusOperatorState() *GlobalRolestatusOperatorState { + return &GlobalRolestatusOperatorState{} } // +k8s:openapi-gen=true -type Status struct { +type GlobalRoleStatus struct { // operatorStates is a map of operator ID to operator state evaluations. // Any operator which consumes this kind SHOULD add its state evaluation information to this field. - OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"` + OperatorStates map[string]GlobalRolestatusOperatorState `json:"operatorStates,omitempty"` // additionalFields is reserved for future use AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` } -// NewStatus creates a new Status object. -func NewStatus() *Status { - return &Status{} +// NewGlobalRoleStatus creates a new GlobalRoleStatus object. +func NewGlobalRoleStatus() *GlobalRoleStatus { + return &GlobalRoleStatus{} } // +k8s:openapi-gen=true -type StatusOperatorStateState string +type GlobalRoleStatusOperatorStateState string const ( - StatusOperatorStateStateSuccess StatusOperatorStateState = "success" - StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress" - StatusOperatorStateStateFailed StatusOperatorStateState = "failed" + GlobalRoleStatusOperatorStateStateSuccess GlobalRoleStatusOperatorStateState = "success" + GlobalRoleStatusOperatorStateStateInProgress GlobalRoleStatusOperatorStateState = "in_progress" + GlobalRoleStatusOperatorStateStateFailed GlobalRoleStatusOperatorStateState = "failed" ) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_codec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_codec_gen.go new file mode 100644 index 00000000000..9db4adc1876 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// GlobalRoleBindingJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type GlobalRoleBindingJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*GlobalRoleBindingJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*GlobalRoleBindingJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &GlobalRoleBindingJSONCodec{} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_metadata_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_metadata_gen.go new file mode 100644 index 00000000000..b4024454959 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type GlobalRoleBindingMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewGlobalRoleBindingMetadata creates a new GlobalRoleBindingMetadata object. +func NewGlobalRoleBindingMetadata() *GlobalRoleBindingMetadata { + return &GlobalRoleBindingMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_object_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_object_gen.go new file mode 100644 index 00000000000..657f1830ff1 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type GlobalRoleBinding struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the GlobalRoleBinding + Spec GlobalRoleBindingSpec `json:"spec" yaml:"spec"` + + Status GlobalRoleBindingStatus `json:"status" yaml:"status"` +} + +func (o *GlobalRoleBinding) GetSpec() any { + return o.Spec +} + +func (o *GlobalRoleBinding) SetSpec(spec any) error { + cast, ok := spec.(GlobalRoleBindingSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *GlobalRoleBinding) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *GlobalRoleBinding) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *GlobalRoleBinding) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(GlobalRoleBindingStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type GlobalRoleBindingStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *GlobalRoleBinding) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *GlobalRoleBinding) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *GlobalRoleBinding) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *GlobalRoleBinding) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *GlobalRoleBinding) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *GlobalRoleBinding) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *GlobalRoleBinding) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *GlobalRoleBinding) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *GlobalRoleBinding) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *GlobalRoleBinding) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *GlobalRoleBinding) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *GlobalRoleBinding) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *GlobalRoleBinding) DeepCopy() *GlobalRoleBinding { + cpy := &GlobalRoleBinding{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *GlobalRoleBinding) DeepCopyInto(dst *GlobalRoleBinding) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &GlobalRoleBinding{} + +// +k8s:openapi-gen=true +type GlobalRoleBindingList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []GlobalRoleBinding `json:"items" yaml:"items"` +} + +func (o *GlobalRoleBindingList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *GlobalRoleBindingList) Copy() resource.ListObject { + cpy := &GlobalRoleBindingList{ + TypeMeta: o.TypeMeta, + Items: make([]GlobalRoleBinding, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*GlobalRoleBinding); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *GlobalRoleBindingList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *GlobalRoleBindingList) SetItems(items []resource.Object) { + o.Items = make([]GlobalRoleBinding, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*GlobalRoleBinding) + } +} + +func (o *GlobalRoleBindingList) DeepCopy() *GlobalRoleBindingList { + cpy := &GlobalRoleBindingList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *GlobalRoleBindingList) DeepCopyInto(dst *GlobalRoleBindingList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &GlobalRoleBindingList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *GlobalRoleBindingSpec) DeepCopy() *GlobalRoleBindingSpec { + cpy := &GlobalRoleBindingSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *GlobalRoleBindingSpec) DeepCopyInto(dst *GlobalRoleBindingSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of GlobalRoleBindingStatus +func (s *GlobalRoleBindingStatus) DeepCopy() *GlobalRoleBindingStatus { + cpy := &GlobalRoleBindingStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies GlobalRoleBindingStatus into another GlobalRoleBindingStatus object +func (s *GlobalRoleBindingStatus) DeepCopyInto(dst *GlobalRoleBindingStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_schema_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_schema_gen.go new file mode 100644 index 00000000000..9b4e65aa5e6 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaGlobalRoleBinding = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", &GlobalRoleBinding{}, &GlobalRoleBindingList{}, resource.WithKind("GlobalRoleBinding"), + resource.WithPlural("globalrolebindings"), resource.WithScope(resource.NamespacedScope)) + kindGlobalRoleBinding = resource.Kind{ + Schema: schemaGlobalRoleBinding, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &GlobalRoleBindingJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func GlobalRoleBindingKind() resource.Kind { + return kindGlobalRoleBinding +} + +// Schema returns a resource.SimpleSchema representation of GlobalRoleBinding +func GlobalRoleBindingSchema() *resource.SimpleSchema { + return schemaGlobalRoleBinding +} + +// Interface compliance checks +var _ resource.Schema = kindGlobalRoleBinding diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_spec_gen.go new file mode 100644 index 00000000000..a60921ecab5 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_spec_gen.go @@ -0,0 +1,61 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type GlobalRoleBindingspecSubject struct { + // kind of the identity getting the permission + Kind GlobalRoleBindingSpecSubjectKind `json:"kind"` + // uid of the identity + Name string `json:"name"` +} + +// NewGlobalRoleBindingspecSubject creates a new GlobalRoleBindingspecSubject object. +func NewGlobalRoleBindingspecSubject() *GlobalRoleBindingspecSubject { + return &GlobalRoleBindingspecSubject{} +} + +// +k8s:openapi-gen=true +type GlobalRoleBindingspecRoleRef struct { + // kind of role + Kind GlobalRoleBindingSpecRoleRefKind `json:"kind"` + // uid of the role + Name string `json:"name"` +} + +// NewGlobalRoleBindingspecRoleRef creates a new GlobalRoleBindingspecRoleRef object. +func NewGlobalRoleBindingspecRoleRef() *GlobalRoleBindingspecRoleRef { + return &GlobalRoleBindingspecRoleRef{} +} + +// +k8s:openapi-gen=true +type GlobalRoleBindingSpec struct { + Subjects []GlobalRoleBindingspecSubject `json:"subjects"` + RoleRef GlobalRoleBindingspecRoleRef `json:"roleRef"` +} + +// NewGlobalRoleBindingSpec creates a new GlobalRoleBindingSpec object. +func NewGlobalRoleBindingSpec() *GlobalRoleBindingSpec { + return &GlobalRoleBindingSpec{ + Subjects: []GlobalRoleBindingspecSubject{}, + RoleRef: *NewGlobalRoleBindingspecRoleRef(), + } +} + +// +k8s:openapi-gen=true +type GlobalRoleBindingSpecSubjectKind string + +const ( + GlobalRoleBindingSpecSubjectKindUser GlobalRoleBindingSpecSubjectKind = "User" + GlobalRoleBindingSpecSubjectKindServiceAccount GlobalRoleBindingSpecSubjectKind = "ServiceAccount" + GlobalRoleBindingSpecSubjectKindTeam GlobalRoleBindingSpecSubjectKind = "Team" + GlobalRoleBindingSpecSubjectKindBasicRole GlobalRoleBindingSpecSubjectKind = "BasicRole" +) + +// +k8s:openapi-gen=true +type GlobalRoleBindingSpecRoleRefKind string + +const ( + GlobalRoleBindingSpecRoleRefKindCoreRole GlobalRoleBindingSpecRoleRefKind = "CoreRole" + GlobalRoleBindingSpecRoleRefKindGlobalRole GlobalRoleBindingSpecRoleRefKind = "GlobalRole" +) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_status_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_status_gen.go new file mode 100644 index 00000000000..0e67d6a98d1 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/globalrolebinding_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type GlobalRoleBindingstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State GlobalRoleBindingStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewGlobalRoleBindingstatusOperatorState creates a new GlobalRoleBindingstatusOperatorState object. +func NewGlobalRoleBindingstatusOperatorState() *GlobalRoleBindingstatusOperatorState { + return &GlobalRoleBindingstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type GlobalRoleBindingStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]GlobalRoleBindingstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewGlobalRoleBindingStatus creates a new GlobalRoleBindingStatus object. +func NewGlobalRoleBindingStatus() *GlobalRoleBindingStatus { + return &GlobalRoleBindingStatus{} +} + +// +k8s:openapi-gen=true +type GlobalRoleBindingStatusOperatorStateState string + +const ( + GlobalRoleBindingStatusOperatorStateStateSuccess GlobalRoleBindingStatusOperatorStateState = "success" + GlobalRoleBindingStatusOperatorStateStateInProgress GlobalRoleBindingStatusOperatorStateState = "in_progress" + GlobalRoleBindingStatusOperatorStateStateFailed GlobalRoleBindingStatusOperatorStateState = "failed" +) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_codec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_codec_gen.go new file mode 100644 index 00000000000..6c7fd9a20c6 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// ResourcePermissionJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type ResourcePermissionJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*ResourcePermissionJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*ResourcePermissionJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &ResourcePermissionJSONCodec{} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_metadata_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_metadata_gen.go new file mode 100644 index 00000000000..93089079830 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type ResourcePermissionMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewResourcePermissionMetadata creates a new ResourcePermissionMetadata object. +func NewResourcePermissionMetadata() *ResourcePermissionMetadata { + return &ResourcePermissionMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_object_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_object_gen.go new file mode 100644 index 00000000000..4fcebde9e01 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type ResourcePermission struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the ResourcePermission + Spec ResourcePermissionSpec `json:"spec" yaml:"spec"` + + Status ResourcePermissionStatus `json:"status" yaml:"status"` +} + +func (o *ResourcePermission) GetSpec() any { + return o.Spec +} + +func (o *ResourcePermission) SetSpec(spec any) error { + cast, ok := spec.(ResourcePermissionSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *ResourcePermission) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *ResourcePermission) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *ResourcePermission) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(ResourcePermissionStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type ResourcePermissionStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *ResourcePermission) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *ResourcePermission) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *ResourcePermission) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *ResourcePermission) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *ResourcePermission) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *ResourcePermission) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *ResourcePermission) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *ResourcePermission) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *ResourcePermission) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *ResourcePermission) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *ResourcePermission) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *ResourcePermission) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *ResourcePermission) DeepCopy() *ResourcePermission { + cpy := &ResourcePermission{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *ResourcePermission) DeepCopyInto(dst *ResourcePermission) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &ResourcePermission{} + +// +k8s:openapi-gen=true +type ResourcePermissionList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []ResourcePermission `json:"items" yaml:"items"` +} + +func (o *ResourcePermissionList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *ResourcePermissionList) Copy() resource.ListObject { + cpy := &ResourcePermissionList{ + TypeMeta: o.TypeMeta, + Items: make([]ResourcePermission, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*ResourcePermission); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *ResourcePermissionList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *ResourcePermissionList) SetItems(items []resource.Object) { + o.Items = make([]ResourcePermission, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*ResourcePermission) + } +} + +func (o *ResourcePermissionList) DeepCopy() *ResourcePermissionList { + cpy := &ResourcePermissionList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *ResourcePermissionList) DeepCopyInto(dst *ResourcePermissionList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &ResourcePermissionList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *ResourcePermissionSpec) DeepCopy() *ResourcePermissionSpec { + cpy := &ResourcePermissionSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *ResourcePermissionSpec) DeepCopyInto(dst *ResourcePermissionSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of ResourcePermissionStatus +func (s *ResourcePermissionStatus) DeepCopy() *ResourcePermissionStatus { + cpy := &ResourcePermissionStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies ResourcePermissionStatus into another ResourcePermissionStatus object +func (s *ResourcePermissionStatus) DeepCopyInto(dst *ResourcePermissionStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_schema_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_schema_gen.go new file mode 100644 index 00000000000..257255a58fe --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaResourcePermission = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", &ResourcePermission{}, &ResourcePermissionList{}, resource.WithKind("ResourcePermission"), + resource.WithPlural("resourcepermissions"), resource.WithScope(resource.NamespacedScope)) + kindResourcePermission = resource.Kind{ + Schema: schemaResourcePermission, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &ResourcePermissionJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func ResourcePermissionKind() resource.Kind { + return kindResourcePermission +} + +// Schema returns a resource.SimpleSchema representation of ResourcePermission +func ResourcePermissionSchema() *resource.SimpleSchema { + return schemaResourcePermission +} + +// Interface compliance checks +var _ resource.Schema = kindResourcePermission diff --git a/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_spec_gen.go new file mode 100644 index 00000000000..57a17ac1bf9 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_spec_gen.go @@ -0,0 +1,59 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type ResourcePermissionspecResource struct { + // api group of the resource (e.g: "folder.grafana.app") + ApiGroup string `json:"apiGroup"` + // kind of the resource (e.g: "folders") + Resource string `json:"resource"` + // uid of the resource (e.g: "fold1") + Name string `json:"name"` +} + +// NewResourcePermissionspecResource creates a new ResourcePermissionspecResource object. +func NewResourcePermissionspecResource() *ResourcePermissionspecResource { + return &ResourcePermissionspecResource{} +} + +// +k8s:openapi-gen=true +type ResourcePermissionspecPermission struct { + // kind of the identity getting the permission + Kind ResourcePermissionSpecPermissionKind `json:"kind"` + // uid of the identity getting the permission + Name string `json:"name"` + // list of actions granted to the user (e.g. "admin" or "get", "update") + Verbs []string `json:"verbs"` +} + +// NewResourcePermissionspecPermission creates a new ResourcePermissionspecPermission object. +func NewResourcePermissionspecPermission() *ResourcePermissionspecPermission { + return &ResourcePermissionspecPermission{ + Verbs: []string{}, + } +} + +// +k8s:openapi-gen=true +type ResourcePermissionSpec struct { + Resource ResourcePermissionspecResource `json:"resource"` + Permissions []ResourcePermissionspecPermission `json:"permissions"` +} + +// NewResourcePermissionSpec creates a new ResourcePermissionSpec object. +func NewResourcePermissionSpec() *ResourcePermissionSpec { + return &ResourcePermissionSpec{ + Resource: *NewResourcePermissionspecResource(), + Permissions: []ResourcePermissionspecPermission{}, + } +} + +// +k8s:openapi-gen=true +type ResourcePermissionSpecPermissionKind string + +const ( + ResourcePermissionSpecPermissionKindUser ResourcePermissionSpecPermissionKind = "User" + ResourcePermissionSpecPermissionKindServiceAccount ResourcePermissionSpecPermissionKind = "ServiceAccount" + ResourcePermissionSpecPermissionKindTeam ResourcePermissionSpecPermissionKind = "Team" + ResourcePermissionSpecPermissionKindBasicRole ResourcePermissionSpecPermissionKind = "BasicRole" +) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_status_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_status_gen.go new file mode 100644 index 00000000000..187bbd0f384 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/resourcepermission_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type ResourcePermissionstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State ResourcePermissionStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewResourcePermissionstatusOperatorState creates a new ResourcePermissionstatusOperatorState object. +func NewResourcePermissionstatusOperatorState() *ResourcePermissionstatusOperatorState { + return &ResourcePermissionstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type ResourcePermissionStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]ResourcePermissionstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewResourcePermissionStatus creates a new ResourcePermissionStatus object. +func NewResourcePermissionStatus() *ResourcePermissionStatus { + return &ResourcePermissionStatus{} +} + +// +k8s:openapi-gen=true +type ResourcePermissionStatusOperatorStateState string + +const ( + ResourcePermissionStatusOperatorStateStateSuccess ResourcePermissionStatusOperatorStateState = "success" + ResourcePermissionStatusOperatorStateStateInProgress ResourcePermissionStatusOperatorStateState = "in_progress" + ResourcePermissionStatusOperatorStateStateFailed ResourcePermissionStatusOperatorStateState = "failed" +) diff --git a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_codec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_codec_gen.go similarity index 58% rename from apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_codec_gen.go rename to apps/iam/pkg/apis/iam/v0alpha1/role_codec_gen.go index 7b5050a3af4..b6b7b3884af 100644 --- a/apps/alerting/notifications/pkg/apis/templategroup/v0alpha1/templategroup_codec_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_codec_gen.go @@ -11,18 +11,18 @@ import ( "github.com/grafana/grafana-app-sdk/resource" ) -// JSONCodec is an implementation of resource.Codec for kubernetes JSON encoding -type JSONCodec struct{} +// RoleJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type RoleJSONCodec struct{} // Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` -func (*JSONCodec) Read(reader io.Reader, into resource.Object) error { +func (*RoleJSONCodec) Read(reader io.Reader, into resource.Object) error { return json.NewDecoder(reader).Decode(into) } // Write writes JSON-encoded bytes into `writer` marshaled from `from` -func (*JSONCodec) Write(writer io.Writer, from resource.Object) error { +func (*RoleJSONCodec) Write(writer io.Writer, from resource.Object) error { return json.NewEncoder(writer).Encode(from) } // Interface compliance checks -var _ resource.Codec = &JSONCodec{} +var _ resource.Codec = &RoleJSONCodec{} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/role_metadata_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_metadata_gen.go new file mode 100644 index 00000000000..f200cad25b5 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type RoleMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewRoleMetadata creates a new RoleMetadata object. +func NewRoleMetadata() *RoleMetadata { + return &RoleMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/role_object_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_object_gen.go new file mode 100644 index 00000000000..9256a618c9f --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type Role struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the Role + Spec RoleSpec `json:"spec" yaml:"spec"` + + Status RoleStatus `json:"status" yaml:"status"` +} + +func (o *Role) GetSpec() any { + return o.Spec +} + +func (o *Role) SetSpec(spec any) error { + cast, ok := spec.(RoleSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *Role) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *Role) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *Role) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(RoleStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type RoleStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *Role) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *Role) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *Role) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *Role) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *Role) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *Role) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *Role) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *Role) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *Role) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *Role) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *Role) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *Role) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *Role) DeepCopy() *Role { + cpy := &Role{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *Role) DeepCopyInto(dst *Role) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &Role{} + +// +k8s:openapi-gen=true +type RoleList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Role `json:"items" yaml:"items"` +} + +func (o *RoleList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *RoleList) Copy() resource.ListObject { + cpy := &RoleList{ + TypeMeta: o.TypeMeta, + Items: make([]Role, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*Role); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *RoleList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *RoleList) SetItems(items []resource.Object) { + o.Items = make([]Role, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*Role) + } +} + +func (o *RoleList) DeepCopy() *RoleList { + cpy := &RoleList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *RoleList) DeepCopyInto(dst *RoleList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &RoleList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *RoleSpec) DeepCopy() *RoleSpec { + cpy := &RoleSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *RoleSpec) DeepCopyInto(dst *RoleSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of RoleStatus +func (s *RoleStatus) DeepCopy() *RoleStatus { + cpy := &RoleStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies RoleStatus into another RoleStatus object +func (s *RoleStatus) DeepCopyInto(dst *RoleStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/role_schema_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_schema_gen.go new file mode 100644 index 00000000000..74cc8730026 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaRole = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", &Role{}, &RoleList{}, resource.WithKind("Role"), + resource.WithPlural("roles"), resource.WithScope(resource.NamespacedScope)) + kindRole = resource.Kind{ + Schema: schemaRole, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &RoleJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func RoleKind() resource.Kind { + return kindRole +} + +// Schema returns a resource.SimpleSchema representation of Role +func RoleSchema() *resource.SimpleSchema { + return schemaRole +} + +// Interface compliance checks +var _ resource.Schema = kindRole diff --git a/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go new file mode 100644 index 00000000000..f1a165dba4e --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_spec_gen.go @@ -0,0 +1,36 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RolespecPermission struct { + // RBAC action (e.g: "dashbaords:read") + Action string `json:"action"` + // RBAC scope (e.g: "dashboards:uid:dash1") + Scope string `json:"scope"` +} + +// NewRolespecPermission creates a new RolespecPermission object. +func NewRolespecPermission() *RolespecPermission { + return &RolespecPermission{} +} + +// +k8s:openapi-gen=true +type RoleSpec struct { + // Display name of the role + Title string `json:"title"` + Version int64 `json:"version"` + Group string `json:"group"` + // TODO: + // delegatable?: bool + // created? + // updated? + Permissions []RolespecPermission `json:"permissions"` +} + +// NewRoleSpec creates a new RoleSpec object. +func NewRoleSpec() *RoleSpec { + return &RoleSpec{ + Permissions: []RolespecPermission{}, + } +} diff --git a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_status_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/role_status_gen.go similarity index 56% rename from apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_status_gen.go rename to apps/iam/pkg/apis/iam/v0alpha1/role_status_gen.go index 16a8d994f82..3d2171f76f0 100644 --- a/apps/alerting/notifications/pkg/apis/receiver/v0alpha1/receiver_status_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/role_status_gen.go @@ -3,42 +3,42 @@ package v0alpha1 // +k8s:openapi-gen=true -type StatusOperatorState struct { +type RolestatusOperatorState struct { // lastEvaluation is the ResourceVersion last evaluated LastEvaluation string `json:"lastEvaluation"` // state describes the state of the lastEvaluation. // It is limited to three possible states for machine evaluation. - State StatusOperatorStateState `json:"state"` + State RoleStatusOperatorStateState `json:"state"` // descriptiveState is an optional more descriptive state field which has no requirements on format DescriptiveState *string `json:"descriptiveState,omitempty"` // details contains any extra information that is operator-specific Details map[string]interface{} `json:"details,omitempty"` } -// NewStatusOperatorState creates a new StatusOperatorState object. -func NewStatusOperatorState() *StatusOperatorState { - return &StatusOperatorState{} +// NewRolestatusOperatorState creates a new RolestatusOperatorState object. +func NewRolestatusOperatorState() *RolestatusOperatorState { + return &RolestatusOperatorState{} } // +k8s:openapi-gen=true -type Status struct { +type RoleStatus struct { // operatorStates is a map of operator ID to operator state evaluations. // Any operator which consumes this kind SHOULD add its state evaluation information to this field. - OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"` + OperatorStates map[string]RolestatusOperatorState `json:"operatorStates,omitempty"` // additionalFields is reserved for future use AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` } -// NewStatus creates a new Status object. -func NewStatus() *Status { - return &Status{} +// NewRoleStatus creates a new RoleStatus object. +func NewRoleStatus() *RoleStatus { + return &RoleStatus{} } // +k8s:openapi-gen=true -type StatusOperatorStateState string +type RoleStatusOperatorStateState string const ( - StatusOperatorStateStateSuccess StatusOperatorStateState = "success" - StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress" - StatusOperatorStateStateFailed StatusOperatorStateState = "failed" + RoleStatusOperatorStateStateSuccess RoleStatusOperatorStateState = "success" + RoleStatusOperatorStateStateInProgress RoleStatusOperatorStateState = "in_progress" + RoleStatusOperatorStateStateFailed RoleStatusOperatorStateState = "failed" ) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_codec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_codec_gen.go new file mode 100644 index 00000000000..2bc3811c6b6 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// RoleBindingJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type RoleBindingJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*RoleBindingJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*RoleBindingJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &RoleBindingJSONCodec{} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_metadata_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_metadata_gen.go new file mode 100644 index 00000000000..9af43bab574 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type RoleBindingMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewRoleBindingMetadata creates a new RoleBindingMetadata object. +func NewRoleBindingMetadata() *RoleBindingMetadata { + return &RoleBindingMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_object_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_object_gen.go new file mode 100644 index 00000000000..bec75890d89 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type RoleBinding struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the RoleBinding + Spec RoleBindingSpec `json:"spec" yaml:"spec"` + + Status RoleBindingStatus `json:"status" yaml:"status"` +} + +func (o *RoleBinding) GetSpec() any { + return o.Spec +} + +func (o *RoleBinding) SetSpec(spec any) error { + cast, ok := spec.(RoleBindingSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *RoleBinding) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *RoleBinding) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *RoleBinding) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(RoleBindingStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type RoleBindingStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *RoleBinding) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *RoleBinding) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *RoleBinding) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *RoleBinding) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *RoleBinding) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *RoleBinding) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *RoleBinding) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *RoleBinding) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *RoleBinding) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *RoleBinding) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *RoleBinding) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *RoleBinding) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *RoleBinding) DeepCopy() *RoleBinding { + cpy := &RoleBinding{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *RoleBinding) DeepCopyInto(dst *RoleBinding) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &RoleBinding{} + +// +k8s:openapi-gen=true +type RoleBindingList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []RoleBinding `json:"items" yaml:"items"` +} + +func (o *RoleBindingList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *RoleBindingList) Copy() resource.ListObject { + cpy := &RoleBindingList{ + TypeMeta: o.TypeMeta, + Items: make([]RoleBinding, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*RoleBinding); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *RoleBindingList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *RoleBindingList) SetItems(items []resource.Object) { + o.Items = make([]RoleBinding, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*RoleBinding) + } +} + +func (o *RoleBindingList) DeepCopy() *RoleBindingList { + cpy := &RoleBindingList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *RoleBindingList) DeepCopyInto(dst *RoleBindingList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &RoleBindingList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *RoleBindingSpec) DeepCopy() *RoleBindingSpec { + cpy := &RoleBindingSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *RoleBindingSpec) DeepCopyInto(dst *RoleBindingSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of RoleBindingStatus +func (s *RoleBindingStatus) DeepCopy() *RoleBindingStatus { + cpy := &RoleBindingStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies RoleBindingStatus into another RoleBindingStatus object +func (s *RoleBindingStatus) DeepCopyInto(dst *RoleBindingStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_schema_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_schema_gen.go new file mode 100644 index 00000000000..abb2b3eddf9 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaRoleBinding = resource.NewSimpleSchema("iam.grafana.app", "v0alpha1", &RoleBinding{}, &RoleBindingList{}, resource.WithKind("RoleBinding"), + resource.WithPlural("rolebindings"), resource.WithScope(resource.NamespacedScope)) + kindRoleBinding = resource.Kind{ + Schema: schemaRoleBinding, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &RoleBindingJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func RoleBindingKind() resource.Kind { + return kindRoleBinding +} + +// Schema returns a resource.SimpleSchema representation of RoleBinding +func RoleBindingSchema() *resource.SimpleSchema { + return schemaRoleBinding +} + +// Interface compliance checks +var _ resource.Schema = kindRoleBinding diff --git a/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_spec_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_spec_gen.go new file mode 100644 index 00000000000..cd214ca6cdd --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_spec_gen.go @@ -0,0 +1,62 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RoleBindingspecSubject struct { + // kind of the identity getting the permission + Kind RoleBindingSpecSubjectKind `json:"kind"` + // uid of the identity + Name string `json:"name"` +} + +// NewRoleBindingspecSubject creates a new RoleBindingspecSubject object. +func NewRoleBindingspecSubject() *RoleBindingspecSubject { + return &RoleBindingspecSubject{} +} + +// +k8s:openapi-gen=true +type RoleBindingspecRoleRef struct { + // kind of role + Kind RoleBindingSpecRoleRefKind `json:"kind"` + // uid of the role + Name string `json:"name"` +} + +// NewRoleBindingspecRoleRef creates a new RoleBindingspecRoleRef object. +func NewRoleBindingspecRoleRef() *RoleBindingspecRoleRef { + return &RoleBindingspecRoleRef{} +} + +// +k8s:openapi-gen=true +type RoleBindingSpec struct { + Subjects []RoleBindingspecSubject `json:"subjects"` + RoleRef RoleBindingspecRoleRef `json:"roleRef"` +} + +// NewRoleBindingSpec creates a new RoleBindingSpec object. +func NewRoleBindingSpec() *RoleBindingSpec { + return &RoleBindingSpec{ + Subjects: []RoleBindingspecSubject{}, + RoleRef: *NewRoleBindingspecRoleRef(), + } +} + +// +k8s:openapi-gen=true +type RoleBindingSpecSubjectKind string + +const ( + RoleBindingSpecSubjectKindUser RoleBindingSpecSubjectKind = "User" + RoleBindingSpecSubjectKindServiceAccount RoleBindingSpecSubjectKind = "ServiceAccount" + RoleBindingSpecSubjectKindTeam RoleBindingSpecSubjectKind = "Team" + RoleBindingSpecSubjectKindBasicRole RoleBindingSpecSubjectKind = "BasicRole" +) + +// +k8s:openapi-gen=true +type RoleBindingSpecRoleRefKind string + +const ( + RoleBindingSpecRoleRefKindRole RoleBindingSpecRoleRefKind = "Role" + RoleBindingSpecRoleRefKindCoreRole RoleBindingSpecRoleRefKind = "CoreRole" + RoleBindingSpecRoleRefKindGlobalRole RoleBindingSpecRoleRefKind = "GlobalRole" +) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_status_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_status_gen.go new file mode 100644 index 00000000000..7e85c3d8aef --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/rolebinding_status_gen.go @@ -0,0 +1,44 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RoleBindingstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State RoleBindingStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewRoleBindingstatusOperatorState creates a new RoleBindingstatusOperatorState object. +func NewRoleBindingstatusOperatorState() *RoleBindingstatusOperatorState { + return &RoleBindingstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type RoleBindingStatus struct { + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]RoleBindingstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewRoleBindingStatus creates a new RoleBindingStatus object. +func NewRoleBindingStatus() *RoleBindingStatus { + return &RoleBindingStatus{} +} + +// +k8s:openapi-gen=true +type RoleBindingStatusOperatorStateState string + +const ( + RoleBindingStatusOperatorStateStateSuccess RoleBindingStatusOperatorStateState = "success" + RoleBindingStatusOperatorStateStateInProgress RoleBindingStatusOperatorStateState = "in_progress" + RoleBindingStatusOperatorStateStateFailed RoleBindingStatusOperatorStateState = "failed" +) diff --git a/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go new file mode 100644 index 00000000000..df62f68072a --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go @@ -0,0 +1,1740 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by grafana-app-sdk. DO NOT EDIT. + +package v0alpha1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRole": schema_pkg_apis_iam_v0alpha1_CoreRole(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleList": schema_pkg_apis_iam_v0alpha1_CoreRoleList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleSpec": schema_pkg_apis_iam_v0alpha1_CoreRoleSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleStatus": schema_pkg_apis_iam_v0alpha1_CoreRoleStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolespecPermission": schema_pkg_apis_iam_v0alpha1_CoreRolespecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_CoreRolestatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole": schema_pkg_apis_iam_v0alpha1_GlobalRole(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBinding": schema_pkg_apis_iam_v0alpha1_GlobalRoleBinding(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingList": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingSpec": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingStatus": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecRoleRef": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecRoleRef(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecSubject": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecSubject(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleList": schema_pkg_apis_iam_v0alpha1_GlobalRoleList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleSpec": schema_pkg_apis_iam_v0alpha1_GlobalRoleSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleStatus": schema_pkg_apis_iam_v0alpha1_GlobalRoleStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolespecPermission": schema_pkg_apis_iam_v0alpha1_GlobalRolespecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_GlobalRolestatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermission": schema_pkg_apis_iam_v0alpha1_ResourcePermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionList": schema_pkg_apis_iam_v0alpha1_ResourcePermissionList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionSpec": schema_pkg_apis_iam_v0alpha1_ResourcePermissionSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionStatus": schema_pkg_apis_iam_v0alpha1_ResourcePermissionStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecPermission": schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecResource": schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecResource(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionstatusOperatorState": schema_pkg_apis_iam_v0alpha1_ResourcePermissionstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Role": schema_pkg_apis_iam_v0alpha1_Role(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBinding": schema_pkg_apis_iam_v0alpha1_RoleBinding(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingList": schema_pkg_apis_iam_v0alpha1_RoleBindingList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingSpec": schema_pkg_apis_iam_v0alpha1_RoleBindingSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingStatus": schema_pkg_apis_iam_v0alpha1_RoleBindingStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecRoleRef": schema_pkg_apis_iam_v0alpha1_RoleBindingspecRoleRef(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecSubject": schema_pkg_apis_iam_v0alpha1_RoleBindingspecSubject(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_RoleBindingstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleList": schema_pkg_apis_iam_v0alpha1_RoleList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleSpec": schema_pkg_apis_iam_v0alpha1_RoleSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleStatus": schema_pkg_apis_iam_v0alpha1_RoleStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolespecPermission": schema_pkg_apis_iam_v0alpha1_RolespecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_RolestatusOperatorState(ref), + } +} + +func schema_pkg_apis_iam_v0alpha1_CoreRole(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the CoreRole", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleSpec", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_CoreRoleList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRole"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRole", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_CoreRoleSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Display name of the role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "permissions": { + SchemaProps: spec.SchemaProps{ + Description: "delegatable?: bool created? updated?", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolespecPermission"), + }, + }, + }, + }, + }, + }, + Required: []string{"title", "version", "group", "permissions"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolespecPermission"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_CoreRoleStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolestatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolestatusOperatorState"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_CoreRolespecPermission(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "action": { + SchemaProps: spec.SchemaProps{ + Description: "RBAC action (e.g: \"dashbaords:read\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "scope": { + SchemaProps: spec.SchemaProps{ + Description: "RBAC scope (e.g: \"dashboards:uid:dash1\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"action", "scope"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_CoreRolestatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRole(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the GlobalRole", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleSpec", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the GlobalRoleBinding", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingSpec", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBinding"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "subjects": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecSubject"), + }, + }, + }, + }, + }, + "roleRef": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecRoleRef"), + }, + }, + }, + Required: []string{"subjects", "roleRef"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecRoleRef", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecSubject"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingstatusOperatorState"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecRoleRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind of role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "uid of the role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecSubject(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind of the identity getting the permission", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "uid of the identity", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Display name of the role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "permissions": { + SchemaProps: spec.SchemaProps{ + Description: "delegatable?: bool created? updated?", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolespecPermission"), + }, + }, + }, + }, + }, + }, + Required: []string{"title", "version", "group", "permissions"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolespecPermission"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRoleStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolestatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolestatusOperatorState"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRolespecPermission(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "action": { + SchemaProps: spec.SchemaProps{ + Description: "RBAC action (e.g: \"dashbaords:read\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "scope": { + SchemaProps: spec.SchemaProps{ + Description: "RBAC scope (e.g: \"dashboards:uid:dash1\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"action", "scope"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_GlobalRolestatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermission(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the ResourcePermission", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionSpec", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermissionList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermission"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermission", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermissionSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resource": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecResource"), + }, + }, + "permissions": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecPermission"), + }, + }, + }, + }, + }, + }, + Required: []string{"resource", "permissions"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecPermission", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecResource"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermissionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionstatusOperatorState"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecPermission(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind of the identity getting the permission", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "uid of the identity getting the permission", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "verbs": { + SchemaProps: spec.SchemaProps{ + Description: "list of actions granted to the user (e.g. \"admin\" or \"get\", \"update\")", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"kind", "name", "verbs"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiGroup": { + SchemaProps: spec.SchemaProps{ + Description: "api group of the resource (e.g: \"folder.grafana.app\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "kind of the resource (e.g: \"folders\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "uid of the resource (e.g: \"fold1\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"apiGroup", "resource", "name"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_ResourcePermissionstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_Role(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the Role", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleSpec", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the RoleBinding", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingSpec", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBinding"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "subjects": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecSubject"), + }, + }, + }, + }, + }, + "roleRef": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecRoleRef"), + }, + }, + }, + Required: []string{"subjects", "roleRef"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecRoleRef", "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecSubject"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBindingStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingstatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingstatusOperatorState"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBindingspecRoleRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind of role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "uid of the role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBindingspecSubject(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind of the identity getting the permission", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "uid of the identity", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleBindingstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Role"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Role", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Display name of the role", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "permissions": { + SchemaProps: spec.SchemaProps{ + Description: "delegatable?: bool created? updated?", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolespecPermission"), + }, + }, + }, + }, + }, + }, + Required: []string{"title", "version", "group", "permissions"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolespecPermission"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RoleStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolestatusOperatorState"), + }, + }, + }, + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolestatusOperatorState"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_RolespecPermission(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "action": { + SchemaProps: spec.SchemaProps{ + Description: "RBAC action (e.g: \"dashbaords:read\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "scope": { + SchemaProps: spec.SchemaProps{ + Description: "RBAC scope (e.g: \"dashboards:uid:dash1\")", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"action", "scope"}, + }, + }, + } +} + +func schema_pkg_apis_iam_v0alpha1_RolestatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} diff --git a/apps/iam/pkg/apis/iam_manifest.go b/apps/iam/pkg/apis/iam_manifest.go new file mode 100644 index 00000000000..b80f5e5eee3 --- /dev/null +++ b/apps/iam/pkg/apis/iam_manifest.go @@ -0,0 +1,111 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package apis + +import ( + "fmt" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + + v0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1" +) + +var appManifestData = app.ManifestData{ + AppName: "iam", + Group: "iam.grafana.app", + Kinds: []app.ManifestKind{ + { + Kind: "GlobalRole", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + }, + }, + + { + Kind: "GlobalRoleBinding", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + }, + }, + + { + Kind: "CoreRole", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + }, + }, + + { + Kind: "Role", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + }, + }, + + { + Kind: "RoleBinding", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + }, + }, + + { + Kind: "ResourcePermission", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + }, + }, + }, +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("iam") +} + +var kindVersionToGoType = map[string]resource.Kind{ + "GlobalRole/v0alpha1": v0alpha1.GlobalRoleKind(), + "GlobalRoleBinding/v0alpha1": v0alpha1.GlobalRoleBindingKind(), + "CoreRole/v0alpha1": v0alpha1.CoreRoleKind(), + "Role/v0alpha1": v0alpha1.RoleKind(), + "RoleBinding/v0alpha1": v0alpha1.RoleBindingKind(), + "ResourcePermission/v0alpha1": v0alpha1.ResourcePermissionKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index 639657d1083..a82f1b8b0ba 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -1,10 +1,10 @@ module github.com/grafana/grafana/apps/investigations -go 1.24.2 +go 1.24.3 require ( - github.com/grafana/grafana-app-sdk v0.35.1 - k8s.io/apimachinery v0.32.3 + github.com/grafana/grafana-app-sdk v0.38.2 + k8s.io/apimachinery v0.33.1 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff ) @@ -18,7 +18,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -30,7 +30,8 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect + github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect + github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -46,11 +47,12 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/onsi/gomega v1.36.2 // indirect + github.com/openfga/openfga v1.8.13 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/pflag v1.0.6 // indirect @@ -63,25 +65,26 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/net v0.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.6.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.32.0 // indirect + golang.org/x/tools v0.33.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/grpc v1.71.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/grpc v1.72.1 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.3 // indirect - k8s.io/apiextensions-apiserver v0.32.3 // indirect - k8s.io/client-go v0.32.3 // indirect + k8s.io/api v0.33.1 // indirect + k8s.io/apiextensions-apiserver v0.33.1 // indirect + k8s.io/client-go v0.33.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index 3afdcda6ee6..08f6c1c3849 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -18,6 +18,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -49,6 +51,8 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb h1:oTl2j6/4miQUYmXANp2pBuYCWA5f8NVYFfCWpczpFso= +github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us= github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK7NGmlX3RI= github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI= github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM= @@ -93,6 +97,10 @@ github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/openfga/openfga v1.8.12 h1:xEirA6tFwaJfjBDtbHWCK0/Tw+B8XleRyhg9dcEpzHo= +github.com/openfga/openfga v1.8.12/go.mod h1:fIZyekdNB+tWQ6zIiglZonAc5ErZiDGMeHue/BzRYRM= +github.com/openfga/openfga v1.8.13 h1:ROURkotKhbmtyBX3188+cNElN8AOZmTl0CMkxUqwawo= +github.com/openfga/openfga v1.8.13/go.mod h1:h1VGcVW81eY1YyDtFx5+gxxAIEhIiOGR9SRGgs/X/k8= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -102,12 +110,15 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -144,11 +155,14 @@ go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -157,6 +171,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -164,17 +180,21 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -183,6 +203,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -191,10 +212,14 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= +google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/apps/playlist/Makefile b/apps/playlist/Makefile index 7a5f1c99365..56de939d428 100644 --- a/apps/playlist/Makefile +++ b/apps/playlist/Makefile @@ -1,3 +1,29 @@ +APP_SDK_VERSION := v0.38.2 +APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION) +APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk + +.PHONY: install-app-sdk +install-app-sdk: $(APP_SDK_BIN) ## Install the Grafana App SDK + +$(APP_SDK_BIN): + @echo "Installing Grafana App SDK version $(APP_SDK_VERSION)" + @mkdir -p $(APP_SDK_DIR) + # The only way to install specific versions of binaries using `go install` + # is by setting GOBIN to the directory you want to install the binary to. + GOBIN=$(APP_SDK_DIR) go install github.com/grafana/grafana-app-sdk/cmd/grafana-app-sdk@$(APP_SDK_VERSION) + @touch $@ + +.PHONY: update-app-sdk +update-app-sdk: ## Update the Grafana App SDK dependency in go.mod + go get github.com/grafana/grafana-app-sdk@$(APP_SDK_VERSION) + go mod tidy + .PHONY: generate -generate: - @grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none +generate: install-app-sdk update-app-sdk ## Run Grafana App SDK code generation + @$(APP_SDK_BIN) generate \ + --source=./kinds/ \ + --gogenpath=./pkg/apis \ + --grouping=group \ + --defencoding=none \ + --noschemasinmanifest \ + --postprocess \ No newline at end of file diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index f63b1190197..fd759ee3fdd 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -1,11 +1,11 @@ module github.com/grafana/grafana/apps/playlist -go 1.24.2 +go 1.24.3 require ( - github.com/grafana/grafana-app-sdk v0.35.1 - k8s.io/apimachinery v0.32.3 - k8s.io/client-go v0.32.3 + github.com/grafana/grafana-app-sdk v0.38.2 + k8s.io/apimachinery v0.33.1 + k8s.io/client-go v0.33.1 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff ) @@ -17,27 +17,26 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/getkin/kin-openapi v0.131.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect + github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -48,10 +47,10 @@ require ( github.com/onsi/ginkgo/v2 v2.22.2 // indirect github.com/onsi/gomega v1.36.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/pflag v1.0.6 // indirect @@ -64,24 +63,23 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/net v0.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.6.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.32.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/grpc v1.71.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.72.1 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.3 // indirect - k8s.io/apiextensions-apiserver v0.32.3 // indirect + k8s.io/api v0.33.1 // indirect + k8s.io/apiextensions-apiserver v0.33.1 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 3afdcda6ee6..73a44c3f607 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -12,12 +12,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= -github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -31,8 +31,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -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-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -43,16 +43,14 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK7NGmlX3RI= -github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI= -github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM= -github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= +github.com/grafana/grafana-app-sdk v0.38.2 h1:raQ1mqCmNJ2AyCSV3m6ehFC0TGVZcqRcM6iOIDT6ZIE= +github.com/grafana/grafana-app-sdk v0.38.2/go.mod h1:LhZ4i2IXGnW5FF/jAczX7SnGFNai7x2qwB8YHNZWiZI= +github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc= +github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -100,14 +98,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -115,6 +113,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -142,8 +142,8 @@ go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5J go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -155,46 +155,46 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= -google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -206,14 +206,14 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= -k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apiextensions-apiserver v0.33.1 h1:N7ccbSlRN6I2QBcXevB73PixX2dQNIW0ZRuguEE91zI= +k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= diff --git a/apps/playlist/pkg/apis/playlist/v0alpha1/constants.go b/apps/playlist/pkg/apis/playlist/v0alpha1/constants.go index 3948d102966..ea5ccae1d63 100644 --- a/apps/playlist/pkg/apis/playlist/v0alpha1/constants.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/constants.go @@ -3,16 +3,16 @@ package v0alpha1 import "k8s.io/apimachinery/pkg/runtime/schema" const ( - // Group is the API group used by all kinds in this package - Group = "playlist.grafana.app" - // Version is the API version used by all kinds in this package - Version = "v0alpha1" + // APIGroup is the API group used by all kinds in this package + APIGroup = "playlist.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v0alpha1" ) var ( // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package GroupVersion = schema.GroupVersion{ - Group: Group, - Version: Version, + Group: APIGroup, + Version: APIVersion, } ) diff --git a/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_metadata_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_metadata_gen.go index 1858b2081cc..b9577db0559 100644 --- a/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_metadata_gen.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_metadata_gen.go @@ -24,5 +24,8 @@ type PlaylistMetadata struct { // NewPlaylistMetadata creates a new PlaylistMetadata object. func NewPlaylistMetadata() *PlaylistMetadata { - return &PlaylistMetadata{} + return &PlaylistMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } } diff --git a/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go index 5e6f1ff8224..d4c603b3531 100644 --- a/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_object_gen.go @@ -18,8 +18,11 @@ import ( type Playlist struct { metav1.TypeMeta `json:",inline" yaml:",inline"` metav1.ObjectMeta `json:"metadata" yaml:"metadata"` - Spec PlaylistSpec `json:"spec" yaml:"spec"` - PlaylistStatus PlaylistStatus `json:"status" yaml:"status"` + + // Spec is the spec of the Playlist + Spec PlaylistSpec `json:"spec" yaml:"spec"` + + Status PlaylistStatus `json:"status" yaml:"status"` } func (o *Playlist) GetSpec() any { @@ -37,14 +40,14 @@ func (o *Playlist) SetSpec(spec any) error { func (o *Playlist) GetSubresources() map[string]any { return map[string]any{ - "status": o.PlaylistStatus, + "status": o.Status, } } func (o *Playlist) GetSubresource(name string) (any, bool) { switch name { case "status": - return o.PlaylistStatus, true + return o.Status, true default: return nil, false } @@ -57,7 +60,7 @@ func (o *Playlist) SetSubresource(name string, value any) error { if !ok { return fmt.Errorf("cannot set status type %#v, not of type PlaylistStatus", value) } - o.PlaylistStatus = cast + o.Status = cast return nil default: return fmt.Errorf("subresource '%s' does not exist", name) @@ -219,6 +222,20 @@ func (o *Playlist) DeepCopyObject() runtime.Object { return o.Copy() } +func (o *Playlist) DeepCopy() *Playlist { + cpy := &Playlist{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *Playlist) DeepCopyInto(dst *Playlist) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + // Interface compliance compile-time check var _ resource.Object = &Playlist{} @@ -262,5 +279,41 @@ func (o *PlaylistList) SetItems(items []resource.Object) { } } +func (o *PlaylistList) DeepCopy() *PlaylistList { + cpy := &PlaylistList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *PlaylistList) DeepCopyInto(dst *PlaylistList) { + resource.CopyObjectInto(dst, o) +} + // Interface compliance compile-time check var _ resource.ListObject = &PlaylistList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *PlaylistSpec) DeepCopy() *PlaylistSpec { + cpy := &PlaylistSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *PlaylistSpec) DeepCopyInto(dst *PlaylistSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of PlaylistStatus +func (s *PlaylistStatus) DeepCopy() *PlaylistStatus { + cpy := &PlaylistStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies PlaylistStatus into another PlaylistStatus object +func (s *PlaylistStatus) DeepCopyInto(dst *PlaylistStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_spec_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_spec_gen.go index 280d7da6aee..8bcdaf08b83 100644 --- a/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_spec_gen.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/playlist_spec_gen.go @@ -30,7 +30,9 @@ type PlaylistSpec struct { // NewPlaylistSpec creates a new PlaylistSpec object. func NewPlaylistSpec() *PlaylistSpec { - return &PlaylistSpec{} + return &PlaylistSpec{ + Items: []PlaylistItem{}, + } } // +k8s:openapi-gen=true diff --git a/apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go b/apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go index 48df277c032..8dba9b173b6 100644 --- a/apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go +++ b/apps/playlist/pkg/apis/playlist/v0alpha1/zz_openapi_gen.go @@ -49,8 +49,9 @@ func schema_pkg_apis_playlist_v0alpha1_Playlist(ref common.ReferenceCallback) co }, "spec": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistSpec"), + Description: "Spec is the spec of the Playlist", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1.PlaylistSpec"), }, }, "status": { diff --git a/apps/playlist/pkg/apis/playlist_manifest.go b/apps/playlist/pkg/apis/playlist_manifest.go index 369f30a62fd..7973fdf4e6d 100644 --- a/apps/playlist/pkg/apis/playlist_manifest.go +++ b/apps/playlist/pkg/apis/playlist_manifest.go @@ -6,15 +6,12 @@ package apis import ( - "encoding/json" + "fmt" "github.com/grafana/grafana-app-sdk/app" -) + "github.com/grafana/grafana-app-sdk/resource" -var ( - rawSchemaPlaylistv0alpha1 = []byte(`{"spec":{"properties":{"interval":{"type":"string"},"items":{"items":{"properties":{"type":{"description":"type of the item.","enum":["dashboard_by_tag","dashboard_by_uid","dashboard_by_id"],"type":"string"},"value":{"description":"Value depends on type and describes the playlist item.\n - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This\n is not portable as the numerical identifier is non-deterministic between different instances.\n Will be replaced by dashboard_by_uid in the future. (deprecated)\n - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All\n dashboards behind the tag will be added to the playlist.\n - dashboard_by_uid: The value is the dashboard UID","type":"string"}},"required":["type","value"],"type":"object"},"type":"array"},"title":{"type":"string"}},"required":["title","interval","items"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`) - versionSchemaPlaylistv0alpha1 app.VersionSchema - _ = json.Unmarshal(rawSchemaPlaylistv0alpha1, &versionSchemaPlaylistv0alpha1) + v0alpha1 "github.com/grafana/grafana/apps/playlist/pkg/apis/playlist/v0alpha1" ) var appManifestData = app.ManifestData{ @@ -42,19 +39,12 @@ var appManifestData = app.ManifestData{ }, }, }, - Schema: &versionSchemaPlaylistv0alpha1, }, }, }, }, } -func jsonToMap(j string) map[string]any { - m := make(map[string]any) - json.Unmarshal([]byte(j), &j) - return m -} - func LocalManifest() app.Manifest { return app.NewEmbeddedManifest(appManifestData) } @@ -62,3 +52,14 @@ func LocalManifest() app.Manifest { func RemoteManifest() app.Manifest { return app.NewAPIServerManifest("playlist") } + +var kindVersionToGoType = map[string]resource.Kind{ + "Playlist/v0alpha1": v0alpha1.PlaylistKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} diff --git a/conf/defaults.ini b/conf/defaults.ini index e5b1f010c8f..ca4561fc4e0 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -812,6 +812,7 @@ client_id = some_client_id client_secret = managed_identity_client_id = federated_credential_audience = +workload_identity_token_file = /var/run/secrets/azure/tokens/azure-identity-token scopes = openid email profile auth_url = https://login.microsoftonline.com//oauth2/v2.0/authorize token_url = https://login.microsoftonline.com//oauth2/v2.0/token @@ -1287,39 +1288,51 @@ alertmanager_max_silences_count = # Maximum silence size in bytes. Default: 0 (no limit). alertmanager_max_silence_size_bytes = -# Set to true when using redis in cluster mode. -ha_redis_cluster_mode_enabled = false - -# The redis server address(es) that should be connected to. -# Can either be a single address, or if using redis in cluster mode, -# the cluster configuration address or a comma-separated list of addresses. +# Redis server address or addresses. It can be a single Redis address if using Redis standalone, +# or a list of comma-separated addresses if using Redis Cluster/Sentinel. ha_redis_address = -# The username that should be used to authenticate with the redis server. +# Set to true when using Redis in Cluster mode. Mutually exclusive with ha_redis_sentinel_mode_enabled. +ha_redis_cluster_mode_enabled = false + +# Set to true when using Redis in Sentinel mode. Mutually exclusive with ha_redis_cluster_mode_enabled. +ha_redis_sentinel_mode_enabled = false + +# Redis Sentinel master name. Only applicable when ha_redis_sentinel_mode_enabled is set to true. +ha_redis_sentinel_master_name = + +# The username that should be used to authenticate with Redis. ha_redis_username = -# The password that should be used to authenticate with the redis server. +# The password that should be used to authenticate with Redis. ha_redis_password = -# The redis database, by default it's 0. +# The username that should be used to authenticate with Redis Sentinel. +# Only applicable when ha_redis_sentinel_mode_enabled is set to true. +ha_redis_sentinel_username = + +# The password that should be used to authenticate with Redis Sentinel. +# Only applicable when ha_redis_sentinel_mode_enabled is set to true. +ha_redis_sentinel_password = + +# The Redis database. The default value is 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. +# A prefix that is used for every key or channel that is created on the Redis server as part of HA for alerting. +# Useful if you plan to share Redis with multiple Grafana instances. 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. +# The name of the cluster peer to use as an identifier. If none is provided, a random one is generated. ha_redis_peer_name = -# The maximum number of simultaneous redis connections. +# The maximum number of simultaneous Redis connections. ha_redis_max_conns = 5 -# Enable TLS on the client used to communicate with the redis server. This should be set to true +# Enable TLS on the client used to communicate with the Redis server. This should be set to true # if using any of the other ha_redis_tls_* fields. ha_redis_tls_enabled = false -# Path to the PEM-encoded TLS client certificate file used to authenticate with the redis server. +# Path to the PEM-encoded TLS client certificate file used to authenticate with the Redis server. # Required if using Mutual TLS. ha_redis_tls_cert_path = @@ -1330,10 +1343,10 @@ ha_redis_tls_key_path = # Path to the PEM-encoded CA certificates file. If not set, the host's root CA certificates are used. ha_redis_tls_ca_path = -# Overrides the expected name of the redis server certificate. +# Overrides the expected name of the Redis server certificate. ha_redis_tls_server_name = -# Skips validating the redis server certificate. +# Skips validating the Redis server certificate. ha_redis_tls_insecure_skip_verify = # Overrides the default TLS cipher suite list. @@ -1538,36 +1551,20 @@ max_annotations_to_keep = rule_query_offset = 1m [recording_rules] -# Enable recording rules. You must provide write credentials below. -enabled = false - -# Target URL (including write path) for recording rules. -url = - -# Optional username for basic authentication on recording rule write requests. Can be left blank to disable basic auth -basic_auth_username = - -# Optional assword for basic authentication on recording rule write requests. Can be left blank. -basic_auth_password = +# Enable recording rules. +enabled = true # Request timeout for recording rule writes. timeout = 10s # Default data source UID to write to if not specified in the rule definition. -# Only has effect if the grafanaManagedRecordingRulesDatasources feature toggle is enabled. default_datasource_uid = # Optional custom headers to include in recording rule write requests. [recording_rules.custom_headers] # exampleHeader = exampleValue -# NOTE: this configuration options are not used yet. [remote.alertmanager] - -# Enable the use of the configured remote Alertmanager and disable the internal one. -# The default value is `false`. -enabled = false - # URL of the remote Alertmanager that will replace the internal one. # This URL should be the root path, Grafana will automatically append an "/alertmanager" suffix for certain HTTP calls. # Required if `enabled` is set to `true`. @@ -1583,6 +1580,9 @@ password = sync_interval = 5m +# Timeout for the HTTP client. Default is 30 seconds. +timeout = 30s + #################################### Annotations ######################### [annotations] # Configures the batch size for the annotation clean-up job. This setting is used for dashboard, API, and alert annotations. @@ -1840,12 +1840,16 @@ hide_angular_deprecation = # Comma separated list of plugin ids for which environment variables should be forwarded. Used only when feature flag pluginsSkipHostEnvVars is enabled. forward_host_env_vars = # Comma separated list of plugin ids to install as part of the startup process. -# By default, the following plugins will be preinstalled: "grafana-lokiexplore-app" +# These will be installed, by default, asynchronously (in the background) while starting Grafana. preinstall = -# Controls whether preinstall plugins asynchronously (in the background) or synchronously (blocking). Useful when preinstalled plugins are used with provisioning. -preinstall_async = true +# Comma separated list of plugin ids to install before the startup process +# These will be installed before starting Grafana. Useful when used with provisioning. +preinstall_sync = # Disables preinstall feature. It has the same effect as setting preinstall to an empty list. preinstall_disabled = false +# Update strategy for plugins. +# Available options: "latest", "minor" +update_strategy = minor #################################### Grafana Live ########################################## [live] @@ -2128,9 +2132,6 @@ alert_rules_state = "paused" # Should UI tests fail when console log/warn/erroring? # Does not affect the result when running on CI - only for allowing devs to choose this behaviour locally fail_tests_on_console = true -# Whether or not to enable the MSW mock API, which intercepts requests and returns mock data -# Should only be used for local development or demo purposes -mock_api = false # Whether to enable betterer eslint rules for local development # Useful if you want to always see betterer rules that we're trying to fix so they're more prevalent betterer_eslint_rules = false diff --git a/conf/sample.ini b/conf/sample.ini index 39afdec6fea..5d50ad20207 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -787,6 +787,7 @@ ;client_secret = some_client_secret ;managed_identity_client_id = ;federated_credential_audience = +;workload_identity_token_file = ;scopes = openid email profile ;auth_url = https://login.microsoftonline.com//oauth2/v2.0/authorize ;token_url = https://login.microsoftonline.com//oauth2/v2.0/token @@ -1260,68 +1261,79 @@ # 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 - # Maximum number of active and pending silences that a tenant can have at once. Default: 0 (no limit). ;alertmanager_max_silences_count = # Maximum silence size in bytes. Default: 0 (no limit). ;alertmanager_max_silence_size_bytes = -# Set to true when using redis in cluster mode. -;ha_redis_cluster_mode_enabled = false - -# The redis server address(es) that should be connected to. -# Can either be a single address, or if using redis in cluster mode, -# the cluster configuration address or a comma-separated list of addresses. +# Redis server address or addresses. It can be a single Redis address if using Redis standalone, +# or a list of comma-separated addresses if using Redis Cluster/Sentinel. ;ha_redis_address = -# The username that should be used to authenticate with the redis server. +# Set to true when using Redis in Cluster mode. Mutually exclusive with ha_redis_sentinel_mode_enabled. +;ha_redis_cluster_mode_enabled = false + +# Set to true when using Redis in Sentinel mode. Mutually exclusive with ha_redis_cluster_mode_enabled. +;ha_redis_sentinel_mode_enabled = false + +# Redis Sentinel master name. Only applicable when ha_redis_sentinel_mode_enabled is set to true. +;ha_redis_sentinel_master_name = + +# The username that should be used to authenticate with Redis. ;ha_redis_username = -# The password that should be used to authenticate with the redis server. +# The password that should be used to authenticate with Redis. ;ha_redis_password = -# The redis database, by default it's 0. +# The username that should be used to authenticate with Redis Sentinel. +# Only applicable when ha_redis_sentinel_mode_enabled is set to true. +;ha_redis_sentinel_username = + +# The password that should be used to authenticate with Redis Sentinel. +# Only applicable when ha_redis_sentinel_mode_enabled is set to true. +;ha_redis_sentinel_password = + +# The Redis database. The default value is 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. +# A prefix that is used for every key or channel that is created on the Redis server as part of HA for alerting. +# Useful if you plan to share Redis with multiple Grafana instances. ;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. +# The name of the cluster peer to use as an identifier. If none is provided, a random one is generated. ;ha_redis_peer_name = -# The maximum number of simultaneous redis connections. -# ha_redis_max_conns = 5 +# The maximum number of simultaneous Redis connections. +;ha_redis_max_conns = 5 -# Enable TLS on the client used to communicate with the redis server. This should be set to true +# Enable TLS on the client used to communicate with the Redis server. This should be set to true # if using any of the other ha_redis_tls_* fields. -# ha_redis_tls_enabled = false +;ha_redis_tls_enabled = false -# Path to the PEM-encoded TLS client certificate file used to authenticate with the redis server. +# Path to the PEM-encoded TLS client certificate file used to authenticate with the Redis server. # Required if using Mutual TLS. -# ha_redis_tls_cert_path = +;ha_redis_tls_cert_path = # Path to the PEM-encoded TLS private key file. Also requires the client certificate to be configured. # Required if using Mutual TLS. -# ha_redis_tls_key_path = +;ha_redis_tls_key_path = # Path to the PEM-encoded CA certificates file. If not set, the host's root CA certificates are used. -# ha_redis_tls_ca_path = +;ha_redis_tls_ca_path = -# Overrides the expected name of the redis server certificate. -# ha_redis_tls_server_name = +# Overrides the expected name of the Redis server certificate. +;ha_redis_tls_server_name = -# Skips validating the redis server certificate. -# ha_redis_tls_insecure_skip_verify = +# Skips validating the Redis server certificate. +;ha_redis_tls_insecure_skip_verify = # Overrides the default TLS cipher suite list. -# ha_redis_tls_cipher_suites = +;ha_redis_tls_cipher_suites = # Overrides the default minimum TLS version. # Allowed values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13 -# ha_redis_tls_min_version = +;ha_redis_tls_min_version = # 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" @@ -1517,23 +1529,13 @@ rule_query_offset = 1m #################################### Recording Rules ##################### [recording_rules] -# Enable recording rules. You must provide write credentials below. -enabled = false - -# Target URL (including write path) for recording rules. -url = - -# Optional username for basic authentication on recording rule write requests. Can be left blank to disable basic auth -basic_auth_username = - -# Optional assword for basic authentication on recording rule write requests. Can be left blank. -basic_auth_password = +# Enable recording rules. +enabled = true # Request timeout for recording rule writes. timeout = 30s # Default data source UID to write to if not specified in the rule definition. -# Only has effect if the grafanaManagedRecordingRulesDatasources feature toggle is enabled. default_datasource_uid = # Optional custom headers to include in recording rule write requests. @@ -1782,6 +1784,14 @@ default_datasource_uid = ; public_key_retrieval_on_startup = false # Enter a comma-separated list of plugin identifiers to avoid loading (including core plugins). These plugins will be hidden in the catalog. ; disable_plugins = +# Comma separated list of plugin ids to install as part of the startup process. +# These will be installed, by default, asynchronously (in the background) while starting Grafana. +; preinstall = +# Comma separated list of plugin ids to install before the startup process +# These will be installed before starting Grafana. Useful when used with provisioning. +; preinstall_sync = +# Disables preinstall feature. It has the same effect as setting preinstall to an empty list. +; preinstall_disabled = false #################################### Grafana Live ########################################## [live] diff --git a/contribute/architecture/k8s-inspired-backend-arch.md b/contribute/architecture/k8s-inspired-backend-arch.md new file mode 100644 index 00000000000..c17f18384b4 --- /dev/null +++ b/contribute/architecture/k8s-inspired-backend-arch.md @@ -0,0 +1,301 @@ +# Grafana Kubernetes-inspired backend architecture + +Grafana is adopting a Kubernetes-inspired, resource-oriented model for all Grafana core resources (dashboards, folders, datasources, alert rules, ..) where users can interact with these resources by defining their desired state via well-structured objects sent to Grafana's new Resource APIs (`/apis/...`). Grafana then acts upon these declarations to align the system's current state with the requested state. + +On top of adopting the declarative, eventually-consistent system design, we are also adopting Kubernetes API's strict approach to URL structures, versioning and schemas. This approach addresses limitations of Grafana's traditional Legacy APIs (`/api/...`), including: + +- **Inconsistent designs:** Variations in structure, parameters, and response formats across different endpoints. +- **No versioning:** Difficulty managing breaking changes and API evolution without clear versioning. +- **Absence of schemas:** Lack of machine-readable schemas hindered programmatic interaction, validation, and tooling. +- **Limited extensibility model:** No standardized way for Grafana plugins to extend core APIs or use Grafana database for storing custom configs + +The end goal is for the Resource APIs to become the only interface for managing core Grafana resources. We will eventually deprecate and remove all Legacy API endpoints. + +## 1. Key Differences from Legacy API Endpoints + +- **URL structure & versioning:** + + - **Resource APIs:** Follow Kubernetes conventions (`/apis///namespaces///`). Includes explicit API versions (e.g., `v0alpha1`, `v1`, `v2beta1`) in the path, allowing for controlled evolution and multiple versions of a resource API to co-exist. + - **Legacy APIs:** Variable path structures (e.g., `/api/dashboards/uid/:uid`, `/api/ruler/grafana/api/v1/rules/:uid/:uid`). Less explicit versioning. + +- **Resource schemas:** + + - **Resource APIs:** Each resource has a well-defined schema (`spec`) and is wrapped with an envelope with common metadata, all APIs come with an always-in-sync OpenAPI spec. + - **Legacy APIs:** Structures vary + +- **Namespacing / org context:** + + - **Resource APIs:** Uses explicit namespaces in the path (`/namespaces//...`) mapping to Grafana Organization IDs in OSS/Enterprise and Grafana Cloud Stack IDs in Grafana Cloud for scoping. + - **Legacy APIs:** Org context determined implicitly (session, API key), not usually part of the URL structure. + +- **Consistency of convenience features:** + + - **Resource APIs:** Convenience features such as resource history, restore and observability-as-code tooling come out of the box; a single implementation of convenience features works across all resources because of the standardization + - **Legacy APIs:** Different APIs support different convenience features; implementations are feature-specific + +- **Error Handling:** + - **Resource APIs:** Uses Kubernetes error response conventions (HTTP status codes 400, 401/403, 404, 409; structured error body). + - **Legacy APIs:** Variable error formats; Grafana-specific. + +## 2. Core Concepts + +- **Resource APIs:** New Grafana API endpoints that mimic the Kubernetes API structure (e.g., `/apis/dashboard.grafana.app/v1alpha1/dashboards`). These are served by Grafana itself and are distinct from any Kubernetes API running on the same system. + +- **API implementation approaches:** + - **Registry Approach (`pkg/registry/apis/...`):** Original method using Go code (`.../register.go` files). While newer resources are primarily defined using CUE in `apps/`, this path is still used for things such as legacy fallbacks for App-defined resources when data for that resource exists outside of unified storage. + - **Apps Approach (`apps/...`):** The newer, more modular implementation where APIs are defined using CUE in self-contained modules. Each app defines resources in `kinds/*.cue` files and resources are registered via the App SDK. This is the direction Grafana is moving toward for all resources. +- **Unified Storage (`pkg/storage/unified/...`):** Internal abstraction handling resource persistence. It handles the conversion between the Resource API and the underlying storage backend. +- **`K8sHandler` (`pkg/services/apiserver/client/client.go`):** Internal client facade used by Grafana services. Provides a Kubernetes-like client interface (`Get`, `Create`, etc.) for the Resource API. Acts as an adapter during migration, enabling legacy services to use the Resource API. Does not interact with external systems - it interact only with Grafana's own APIs. +- **Configured SQL database:** Grafana's primary persistence layer (PostgreSQL, MySQL, SQLite). The persistence target for actions initiated within Grafana's UI/Legacy API. It's the default backend for Unified Storage +- **App SDK (`github.com/grafana/grafana-app-sdk`):** SDK for the Apps Approach. Provides tools for defining resources (CUE) and generating code. + +### 2.1 Unified Storage + +The Unified Storage (`pkg/storage/unified/...`) is Grafana's internal persistence layer backing the Resource API. It decouples API handlers from the storage backend. + +**Design Rationale:** + +- **Addressing legacy limitations:** Traditionally, Grafana stored entire resource definitions (like dashboard JSON) as opaque blobs in resource-specific tables (e.g. `dashboard`). This made it difficult to query specific fields, implement consistent versioning/watching, or natively support Kubernetes concepts. It also made it impossible for plugins to store custom configs alongside core resources in Grafana database. +- **Supporting existing Databases (vs. etcd/Kine):** Instead of `etcd`, Unified Storage uses Grafana's existing SQL database support (PostgreSQL, MySQL, SQLite) for operational simplicity - we do not want to introduce `etcd` as a mandatory dependency - and backwards compatibility - we need to ensure Grafana continues to work in all the varied environments its being used. + +**Key Characteristics:** + +- **Implementing Kubernetes [`storage.Interface`](https://github.com/kubernetes/apiserver/blob/master/pkg/storage/interfaces.go#L168):** Provides an implementation of `k8s.io/apiserver/pkg/storage.Interface` via `apistore.Storage` (`pkg/storage/unified/apistore/store.go`). This adapter translates Kubernetes storage operations (`Create`, `Get`, `List`, `Watch`) to calls against Grafana's `resource.ResourceClient`. +- **Resource versioning:** Each resource stored in the Unified Storage has a `resourceVersion` that changes whenever the resource is modified. It's stored as a monotonically increasing counter and is used for optimistic concurrency control in updates; if the stored version differs from the provided version, the update is rejected with a 409 Conflict error. +- **Native `Watch` support:** `resourceVersion` enables efficient `Watch` capabilities, even on standard SQL databases. Every resource modification writes an entry to a `resource_history` table, tagged with the new `resourceVersion`. In HA mode, an internal poller reads this table based on `resourceVersion` to detect changes. In non-HA mode, writes trigger direct notifications via Go channels. The API server then streams these detected events (from polling or direct notification) to connected Watch clients. + +### 2.2 Defining APIs through `/apps/*` + +The `apps` directory structure is Grafana's intended future for defining APIs and implementing the Kubernetes-style resource model. Compared to the older Registry Approach, it offers: + +- **Schema-first:** CUE definitions provide strong typing, validation, and code generation. +- **Modularity:** Each app is self-contained with its own versioning and dependencies. +- **Controllers/Reconcilers:** The App SDK supports controllers (reconcilers) that watch resources and align system state with the resource `spec`. +- **Admission Webhooks:** Apps can define validation or mutation logic via admission webhooks in their manifests, applied before resources are saved. + +**Example: Playlist App (`apps/playlist/`)** + +- `kinds/playlist.cue`: CUE schema for the `Playlist` resource kind and its `spec`. +- `kinds/manifest.cue`: App and resource kind metadata. +- `pkg/apis/playlist/v0alpha1/`: Generated Go types from the CUE schema. +- `pkg/app/reconciler_playlist.go`: Reconciler acting on `Playlist` resources. +- `pkg/registry/apps/playlist/register.go`: Registers the `Playlist` kind and API endpoints using the App SDK. + +The Apps Approach creates the same Resource API endpoints (`/apis/...`) as the Registry Approach, but with a cleaner implementation and improved developer experience. It's the standard pattern for all future Grafana resources. + +### 2.3 Kubernetes API Compatibility + +Grafana's Resource APIs adopts many Kubernetes API conventions but is **not strictly conformant** to the official Kubernetes API specification. We diverge from the Kubernetes architecture and Kubernetes API specification when needed for Grafana's own unique requirements or to avoid operational complexity. Each architectural difference can surface as behavioral or wire-level incompatibility with the vanilla Kubernetes API. + +In other words, even when the URLs / resources look Kubernetes-like, some API Server guarantees might not hold. We are adopting Kubernetes API patterns, general structure, and declarative style. A strict, byte-for-byte conformance to the API spec is currently not a goal. + +The list of major architectural changes include: + +- **Persistence:** Uses Grafana's configured SQL database (PostgreSQL, MySQL, SQLite) via Unified Storage, not `etcd`. +- **Auth:** Uses Grafana's standard auth (API keys, sessions, RBAC/Permissions), not Kubernetes ServiceAccounts or RBAC. +- **Resource Definition:** Uses golang (Registry Approach) or CUE schemas (Apps Approach), not Kubernetes CRDs applied to an external `kube-apiserver`. +- **Controllers/Reconcilers:** Run **internally within the Grafana server process**, not as external operators talking to `kube-apiserver`. +- **Dealing with large resources:** Some of Grafana resources can be much larger than what Kubernetes/etcd allows which impacts design of some operations (e.g. `LIST`) +- **Dataplane routes:** Not all Grafana Legacy APIs can be expressed as simple CRUD operations (e.g. datasource queries or proxy routes). We are going to define custom, resource- or group-level routes to handle these scenarios. + +## 3. Diagrams + +### Request Flow + +```mermaid +graph TD +%% Main entities + User([User]) + UI[Grafana UI] + +%% API Layer + subgraph API["API Layer"] + LegacyAPI["Legacy API
/api/...
(Routes defined in pkg/api/api.go)"] + ResourceAPI["Resource API Endpoints
/apis/...
(Registered via Registry/Apps)"] + end + +%% User connections + User --> UI + User -->|"Direct API Call"| LegacyAPI + +%% UI connections + UI -->|"Uses"| LegacyAPI + UI -->|"Uses"| ResourceAPI + +%% Additional User connection to ResourceAPI - moved here to avoid crossing + User -->|"Direct API Call"| ResourceAPI + +%% Rest of diagram connections + LegacyAPI --> Services + ResourceAPI --> APIHandlers + +%% Service Layer + subgraph Service["Service & Internal Logic"] + Services["Service Implementations
pkg/services/.../service.go
pkg/api/*.go"] + LegacyStore["Legacy Store Implementation
(e.g., sqlstore)"] + K8sHandlerFacade["K8sHandler Facade
client.K8sHandler
(Loopback Client for Resource API)"] + APIHandlers["Resource Handlers
(Implementations for /apis/...)"] + end + + Services -->|"Legacy Path"| LegacyStore + Services -.->|"When Feature
Flag Enabled"| K8sHandlerFacade + K8sHandlerFacade -.-> ResourceAPI + +%% Storage Layer + subgraph Storage["Storage Layer"] + UnifiedStorage["Unified Storage
pkg/storage/unified/..."] + DB[("Configured SQL Database
(PostgreSQL, MySQL, SQLite)")] + FileStorage[("Optional
File Storage")] + RemoteStorage[("Optional
Remote gRPC Storage")] + end + + LegacyStore --> DB + APIHandlers --> UnifiedStorage + APIHandlers -.->|"During Migration"|LegacyStore + UnifiedStorage --> DB + UnifiedStorage -.-> FileStorage + UnifiedStorage -.-> RemoteStorage + +%% Define Styles + classDef primaryPath fill:#f5f5f5,stroke:#333,stroke-width:1px; + classDef secondaryPath fill:#f9f9f9,stroke:#999,stroke-width:1px,stroke-dasharray: 5 5; + classDef storage fill:#e6f7ff,stroke:#0066cc,stroke-width:1px; + + class LegacyAPI,Services,LegacyStore,DB primaryPath; + class ResourceAPI,APIHandlers,UnifiedStorage,K8sHandlerFacade secondaryPath; + class FileStorage,RemoteStorage,DB storage; +``` + +### Registry vs Apps Approach + +```mermaid +graph TB + subgraph "Registry API Implementation
(pkg/registry/apis/... & pkg/apis/...)" + + DA2-->RegisterAPI + class DA2 disable; + linkStyle 0 display:none; + + RegisterAPI["API Registration
pkg/registry/apis/apis.go
(DI Entrypoint)
"] --> |Instantiates| RegistryAPIBuilder["Registry API Builder
pkg/registry/apis/\*/register.go"] + RegistryAPIBuilder --> |Defines| APIGroups["API Group/Version
e.g., dashboard.grafana.app/v1alpha1"] + APIGroups --> |Contains| Resources["Resource Types
Dashboard, Folder, etc."] + RegistryAPIBuilder --> |Implements| HandlerFunc["rest.Storage implementation
Get, List, Create.., Delete"] + RegistryAPIBuilder --> |Calls during InstallSchema| APISchemaRegister["API Schema Registration
pkg/apis/\*/\*/register.go"] + APISchemaRegister --> |Uses| SchemeBuilder["Scheme Builder
runtime.SchemeBuilder"] + APISchemaRegister --> |Defines| TypeDefs["Go Type Definitions
struct Dashboard{}"] + end + + subgraph "Apps-based API Implementation
(apps/... & pkg/registry/apps/...)" + + DA3-->DA1 + class DA3 disable; + DA1-->CUEDefinition + class DA1 disable; + classDef disable display:none; + linkStyle 8 display:none; + linkStyle 9 display:none; + + CUEDefinition["CUE Schema Definition
apps/\*/kinds/\*.cue"] --> |Generates| GeneratedCode["Generated Go/TS Code
apps/\*/pkg/apis/\*/types.go"] + CUEDefinition --> |Defines| Manifest["App Manifest
apps/\*/kinds/manifest.cue"] + AppSDK["Grafana App SDK
grafana-app-sdk"] --o |Provides| AppSDKTypes["SDK Interfaces/Types
app.Provider, etc."] + Manifest --> |Configures & Uses| AppRegisterGo["App Registration
pkg/registry/apps/\*/register.go"] + AppSDKTypes --> AppRegisterGo + AppRegisterGo --> |Creates| AppProvider["App Provider Instance
(Implements app.Provider)"] + AppRegistryService["App Registry Service
pkg/registry/apps/apps.go"] + end + + AppRegistryService --> |Uses| APIGroupRunner + AppProvider --> |Collected by| AppRegistryService + APIGroupRunner --> |Creates & Registers
AppBuilder with| APIRegistrar + + APIGroupRunner["APIGroupRunner
pkg/services/apiserver/builder/runner"] + APIRegistrar["API Registrar
pkg/services/apiserver/builder"] + Endpoints["API Endpoints
/apis/\*/v\*/namespaces/\*/resources"] + RESTOptionsGetter["RESTOptionsGetter
Provides storage config"] + UnifiedStorage["Unified Storage
pkg/storage/unified/..."] + + RegistryAPIBuilder --> |Registers builder with| APIRegistrar + + APIRegistrar --> |Builds Server Config leading to| Endpoints + APIRegistrar --> |Uses| RESTOptionsGetter + + + RESTOptionsGetter --> |Connects to| UnifiedStorage + + classDef registry fill:#f5f5f5,stroke:#333,stroke-width:1px; + classDef appsStyle fill:#e6f7ff,stroke:#0066cc,stroke-width:1px; + classDef shared fill:#f0f0f0,stroke:#666,stroke-width:1px; + + class RegisterAPI,RegistryAPIBuilder,APIGroups,Resources,HandlerFunc,APISchemaRegister,SchemeBuilder,TypeDefs registry; + class CUEDefinition,GeneratedCode,Manifest,AppProvider,AppSDK,AppSDKTypes,AppRegistryService,AppRegisterGo appsStyle; + class APIRegistrar,Endpoints,UnifiedStorage,BackendDB,RESTOptionsGetter,APIGroupRunner shared; +``` + +## 4. Migration Path: From Legacy APIs to Resource APIs + +**Coexistence During Migration:** + +Both API styles will coexist for a while. How they interact depends on the specific resource and the stage of migration. The migration generally follows these steps per resource type: + +1. **Define schema:** Define the resource using CUE (Apps Approach). Introduce an Alpha version (`v1alpha1`). +2. **Implement handlers:** Create Resource API handlers (CRUD) interacting with legacy storage. The goal for the initial alpha version is to expose the current functionality of the legacy API through the new Resource API structure, using CUE for schema definition, rather than to undertake a full resource/data model redesign. +3. **Add feature flag:** Create a flag (e.g., `kubernetesClient`), initially off. +4. **Implement routing/translation:** Modify Legacy API handlers to check the flag and delegate calls to the Resource API or storage layer when active. +5. **Testing & promotion (Beta):** Enable the flag by default after testing. Promote API to Beta (`v1beta1`). Beta APIs aim for stability but may have breaking changes following a deprecation policy. +6. **Migrate data to Unified Storage:** Under another feature flag, gradually change the source of truth from legacy to unified storage. +7. **Stabilization (GA):** Promote to GA (`v1`) when stable. GA APIs provide strong stability guarantees. +8. **Deprecate Legacy API:** Officially deprecate the corresponding Legacy API endpoint after the Resource API reaches GA. Communicate deprecation timelines (e.g., deprecated in X, removed in Y). +9. **Remove Legacy API:** Remove the Legacy API endpoint after the deprecation period. + +This process, using feature flags and versioning (Alpha/Beta/GA), ensures backward compatibility while progressively moving towards the target API structure. + +The frontend code is also updated gradually. As Resource API endpoints stabilize, frontend code managing those resources switches from Legacy to Resource APIs, benefiting from improved consistency and validation. + +## 5. So does it all mean I can point `kubectl` directly at the Grafana server and use it to create dashboards? + +**Yes, with caveats.** `kubectl` is supported under a dev-mode-only, experimental `grafanaAPIServerEnsureKubectlAccess` feature flag and requires `server.protocol` set to `https`. Enabling `https` causes Grafana to configure its web server with TLS certificates, either loading specified certificate/key files or generating self-signed ones if none are provided. + +```bash +#!/bin/bash +# Assumes Grafana running from repo root - adjust if running packaged Grafana (e.g., /usr/local/etc/grafana/grafana-apiserver/grafana.kubeconfig). +export KUBECONFIG=../../data/grafana-apiserver/grafana.kubeconfig + +cat < **Caution:** Do not use `go get` to download Grafana. Recent versions of Go have added behavior which isn't compatible with the way the Grafana repository is structured. +### Set up yarn + +In the repository enable and install yarn via corepack + +``` +corepack enable +corepack install +``` + ### Configure precommit hooks We use pre-commit hooks (via [lefthook](https://github.com/evilmartians/lefthook)) to lint, fix, and format code as you commit your changes. Previously, the Grafana repository automatically installed these hook when you ran `yarn install`, but they are now opt-in for all contributors. @@ -422,6 +424,10 @@ Set NODE_OPTIONS="--max-old-space-size=8192" If you encounter an `AggregateError` when building new tests, this is probably due to a call to our client [backend service](https://github.com/grafana/grafana/blob/main/public/app/core/services/backend_srv.ts) not being mocked. Our backend service anticipates multiple responses being returned and was built to return errors as an array. A test encountering errors from the service will group those errors as an `AggregateError` without breaking down the individual errors within. `backend_srv.processRequestError` is called once per error and is a great place to return information on what the individual errors might contain. +### Getting `error reading debug_info: decoding dwarf section line_str at offset` trying to run VSCode debugger + +If you are trying to run the server from VS code and get this error, run `go install github.com/go-delve/delve/cmd/dlv@master` in the terminal. + ## Next steps - Read our [style guides](/contribute/style-guides). diff --git a/contribute/internationalization.md b/contribute/internationalization.md index 8729f8175f5..d33e354a5a7 100644 --- a/contribute/internationalization.md +++ b/contribute/internationalization.md @@ -2,7 +2,7 @@ Grafana uses the [i18next](https://www.i18next.com/) framework for managing translating phrases in the Grafana frontend. -## TL;DR +## tl;dr **Note:** We don't currently accept contributions for translations. Please don't submit pull requests translating `grafana.json` files - they will be rejected. We do accept contributions to mark up phrases for translation. @@ -17,10 +17,10 @@ Grafana uses the [i18next](https://www.i18next.com/) framework for managing tran ### JSX -1. For JSX children, use the `` component from `app/core/internationalization` with the `i18nKey`, ensuring it conforms to the following guidelines, with the default English translation. For example: +1. For JSX children, use the `` component from `@grafana/i18n` with the `i18nKey`, ensuring it conforms to the following guidelines, with the default English translation. For example: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; const SearchTitle = ({ term }) => Results for {{ term }}; ``` @@ -32,7 +32,7 @@ There may be cases where you need to interpolate variables inside other componen If the nested component is displaying the variable only (e.g. to add emphasis or color), the best solution is to create a new wrapping component: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; import { Text } from '@grafana/ui'; const SearchTerm = ({ term }) => {term}; @@ -47,7 +47,7 @@ const SearchTitle = ({ term }) => ( However there are also cases where the nested component might be displaying additional text which also needs to be translated. In this case, you can use the `values` prop to explicitly pass variables to the translation, and reference them as templated strings in the markup. For example: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; import { Text } from '@grafana/ui'; const SearchTitle = ({ term }) => ( @@ -75,8 +75,9 @@ const ErrorMessage = ({ id, message }) => There Sometimes you may need to translate a string cannot be represented in JSX, such as `placeholder` props. Use the `t` macro for this. ```jsx -import { t } from "app/core/internationalization" +import { useTranslate } from "@grafana/i18n" +const { t } = useTranslate(); const placeholder = t('form.username-placeholder','Username'); return @@ -146,7 +147,7 @@ Refer to the [i18next](https://www.i18next.com/) and [react-i18next](https://rea For fixed phrases: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; Hello user!; ``` @@ -154,7 +155,7 @@ import { Trans } from 'app/core/internationalization'; To interpolate a variable, include it as an object child. It's a weird syntax, but `Trans` will do its magic to make it work: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; Hello {{ name: user.name }}!; @@ -165,7 +166,7 @@ const userName = user.name; Variables must be strings (or, must support calling `.toString()`, which we almost never want). For example: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; // This will not work const userName = user.name; @@ -183,7 +184,7 @@ const userName = user.name; Both HTML tags and React components can be included in a phase. The `Trans` function handles interpolating for its children. ```js -import { Trans } from "app/core/internationalization" +import { Trans } from "@grafana/i18n" Click to learn more. @@ -202,7 +203,7 @@ import { Trans } from "app/core/internationalization" Plurals require special handling to make sure they can be translated according to the rules of each locale (which may be more complex than you think). Use either the `` component or the `t` function, with the `count` prop to provide a singular form. For example: ```js -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; You got {{ count: messages.length }} messages @@ -210,8 +211,9 @@ import { Trans } from 'app/core/internationalization'; ``` ```js -import { t } from 'app/core/internationalization'; +import { useTranslate } from '@grafana/i18n'; +const { t } = useTranslate(); const translatedString = t('inbox.heading', 'You got {{count}} messages', { count: messages.length }); ``` diff --git a/contribute/merge-pull-request.md b/contribute/merge-pull-request.md index 0e60768892f..6636daded45 100644 --- a/contribute/merge-pull-request.md +++ b/contribute/merge-pull-request.md @@ -118,9 +118,9 @@ In case the pull request introduces a breaking change you should document this. Backporting is the process of copying the pull request into the version branch of one or multiple previous releases. -Backporting is a rare exception, should only be done for _critical bug fixes_, and involves the intervention of a Grafana Labs employee. +Backporting should be a rare exception, reserved only for critical bug fixes, and must be initiated by a Grafana Labs employee. We generally avoid automatic backports, as these changes carry some risk: they typically receive less manual testing than changes included in regular point releases. -If your pull request fixes a critical bug and needs to be backported, add it to the "Critical Bug Release" form so the team can approve the backport and know that a release needs to be made. Specify the correct `backport vx.x` labels for the releases to which the fix needs to be backported. Once approved, the backporting process continues. +If a pull request addresses a critical bug and backporting is warranted, a Grafana Labs team member can apply the appropriate `backport vx.x` labels for the relevant release branches. The team will review and approve the backport before proceeding. When the pull request is merged, seperate backport PRs will automatically be creataed. #### Required labels diff --git a/crowdin.yml b/crowdin.yml index 0d1bab25f7c..59366d9a3c0 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,5 +1,24 @@ -files: - - type: i18next_json # The following are pulled from env variables project_id_env: CROWDIN_PROJECT_ID api_token_env: CROWDIN_PERSONAL_TOKEN + +preserve_hierarchy: true +files: [ + { + "source": "public/locales/en-US/grafana.json", + "translation": "public/locales/%locale%/%original_file_name%", + "type": "i18next_json", + }, + { + "source": "public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json", + "translation": "public/app/plugins/datasource/azuremonitor/locales/%locale%/%original_file_name%", + "type": "i18next_json", + "dest": "plugins/azuremonitor/en-US/%original_file_name%" + }, + { + "source": "public/app/plugins/datasource/mssql/locales/en-US/mssql.json", + "translation": "public/app/plugins/datasource/mssql/locales/%locale%/%original_file_name%", + "type": "i18next_json", + "dest": "plugins/mssql/en-US/%original_file_name%" + }, +] diff --git a/devenv/docker/blocks/collectd/Dockerfile b/devenv/docker/blocks/collectd/Dockerfile index 00762cd0149..13768c86c04 100644 --- a/devenv/docker/blocks/collectd/Dockerfile +++ b/devenv/docker/blocks/collectd/Dockerfile @@ -1,16 +1,19 @@ -FROM ubuntu:xenial +FROM ubuntu:jammy ENV DEBIAN_FRONTEND=noninteractive RUN apt-get -y update -RUN apt-get -y install collectd curl python-pip +RUN apt-get -y install collectd curl python3-pip # add a fake mtab for host disk stats ADD etc_mtab /etc/mtab ADD collectd.conf.tpl /etc/collectd/collectd.conf.tpl -RUN pip install --no-cache-dir envtpl +# Install Python packages +ADD requirements.txt /tmp/requirements.txt +RUN pip3 install --require-hashes --no-cache-dir -r /tmp/requirements.txt + ADD start_container /usr/bin/start_container RUN chmod +x /usr/bin/start_container CMD start_container diff --git a/devenv/docker/blocks/collectd/requirements.txt b/devenv/docker/blocks/collectd/requirements.txt new file mode 100644 index 00000000000..7a77b9a89aa --- /dev/null +++ b/devenv/docker/blocks/collectd/requirements.txt @@ -0,0 +1,6 @@ +envtpl==0.7.0 \ + --hash=sha256:cfc5bec6c554fa2b1578980f4c2931dd0db128f14f5a7f4c91c3280167e30cf9 +Jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d +MarkupSafe==2.1.5 \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ No newline at end of file diff --git a/devenv/docker/blocks/graphite09/Dockerfile b/devenv/docker/blocks/graphite09/Dockerfile deleted file mode 100644 index 9b78dfa7f8f..00000000000 --- a/devenv/docker/blocks/graphite09/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -from ubuntu:14.04 - -run apt-get -y update - -run apt-get -y install libcairo2-dev libffi-dev pkg-config python-dev python-pip fontconfig apache2 libapache2-mod-wsgi git-core collectd memcached gcc g++ make supervisor nginx-light gunicorn - -run cd /usr/local/src && git clone https://github.com/graphite-project/graphite-web.git -run cd /usr/local/src && git clone https://github.com/graphite-project/carbon.git -run cd /usr/local/src && git clone https://github.com/graphite-project/whisper.git - -run pip install twisted==13.2.0 && pip install sphinx==1.5.2 && pip install cairocffi==0.9.0 -run cd /usr/local/src/whisper && git checkout master && python setup.py install -run cd /usr/local/src/carbon && git checkout 0.9.x && pip install -r requirements.txt && python setup.py install -run cd /usr/local/src/graphite-web && git checkout 0.9.x && pip install -r requirements.txt && python check-dependencies.py && python setup.py install - -# Add graphite config -add ./files/initial_data.json /opt/graphite/webapp/graphite/initial_data.json -add ./files/local_settings.py /opt/graphite/webapp/graphite/local_settings.py -add ./files/carbon.conf /opt/graphite/conf/carbon.conf -add ./files/storage-schemas.conf /opt/graphite/conf/storage-schemas.conf -add ./files/storage-aggregation.conf /opt/graphite/conf/storage-aggregation.conf -add ./files/events_views.py /opt/graphite/webapp/graphite/events/views.py - -run mkdir -p /opt/graphite/storage/whisper -run touch /opt/graphite/storage/graphite.db /opt/graphite/storage/index -run chown -R www-data /opt/graphite/storage -run chmod 0775 /opt/graphite/storage /opt/graphite/storage/whisper -run chmod 0664 /opt/graphite/storage/graphite.db -run cd /opt/graphite/webapp/graphite && python manage.py syncdb --noinput - -add ./files/my_htpasswd /etc/nginx/.htpasswd - -# Add system service config -add ./files/nginx.conf /etc/nginx/nginx.conf -add ./files/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - -# Nginx -# -# graphite -expose 80 - -# Carbon line receiver port -expose 2003 - -# Carbon cache query port -expose 7002 - -VOLUME ["/opt/graphite/storage/whisper"] -VOLUME ["/var/lib/log/supervisor"] - -cmd ["/usr/bin/supervisord"] - -# vim:ts=8:noet: diff --git a/devenv/docker/blocks/graphite09/docker-compose.yaml b/devenv/docker/blocks/graphite09/docker-compose.yaml deleted file mode 100644 index abeb8183168..00000000000 --- a/devenv/docker/blocks/graphite09/docker-compose.yaml +++ /dev/null @@ -1,16 +0,0 @@ - graphite09: - build: docker/blocks/graphite09 - ports: - - "8380:80" - - "2303:2003" - - "2304:2004" - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro - - fake-graphite09-data: - image: grafana/fake-data-gen - environment: - FD_DATASOURCE: graphite - FD_PORT: 2303 - FD_GRAPHITE_VERSION: 0.9 diff --git a/devenv/docker/blocks/graphite09/files/carbon.conf b/devenv/docker/blocks/graphite09/files/carbon.conf deleted file mode 100644 index fc03aba6398..00000000000 --- a/devenv/docker/blocks/graphite09/files/carbon.conf +++ /dev/null @@ -1,76 +0,0 @@ -[cache] -LOCAL_DATA_DIR = /opt/graphite/storage/whisper/ - -# Specify the user to drop privileges to -# If this is blank carbon runs as the user that invokes it -# This user must have write access to the local data directory -USER = - -# Limit the size of the cache to avoid swapping or becoming CPU bound. -# Sorts and serving cache queries gets more expensive as the cache grows. -# Use the value "inf" (infinity) for an unlimited cache size. -MAX_CACHE_SIZE = inf - -# Limits the number of whisper update_many() calls per second, which effectively -# means the number of write requests sent to the disk. This is intended to -# prevent over-utilizing the disk and thus starving the rest of the system. -# When the rate of required updates exceeds this, then carbon's caching will -# take effect and increase the overall throughput accordingly. -MAX_UPDATES_PER_SECOND = 1000 - -# Softly limits the number of whisper files that get created each minute. -# Setting this value low (like at 50) is a good way to ensure your graphite -# system will not be adversely impacted when a bunch of new metrics are -# sent to it. The trade off is that it will take much longer for those metrics' -# database files to all get created and thus longer until the data becomes usable. -# Setting this value high (like "inf" for infinity) will cause graphite to create -# the files quickly but at the risk of slowing I/O down considerably for a while. -MAX_CREATES_PER_MINUTE = inf - -LINE_RECEIVER_INTERFACE = 0.0.0.0 -LINE_RECEIVER_PORT = 2003 - -PICKLE_RECEIVER_INTERFACE = 0.0.0.0 -PICKLE_RECEIVER_PORT = 2004 - -CACHE_QUERY_INTERFACE = 0.0.0.0 -CACHE_QUERY_PORT = 7002 - -LOG_UPDATES = False - -# Enable AMQP if you want to receive metrics using an amqp broker -# ENABLE_AMQP = False - -# Verbose means a line will be logged for every metric received -# useful for testing -# AMQP_VERBOSE = False - -# AMQP_HOST = localhost -# AMQP_PORT = 5672 -# AMQP_VHOST = / -# AMQP_USER = guest -# AMQP_PASSWORD = guest -# AMQP_EXCHANGE = graphite - -# Patterns for all of the metrics this machine will store. Read more at -# http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol#Bindings -# -# Example: store all sales, linux servers, and utilization metrics -# BIND_PATTERNS = sales.#, servers.linux.#, #.utilization -# -# Example: store everything -# BIND_PATTERNS = # - -# NOTE: you cannot run both a cache and a relay on the same server -# with the default configuration, you have to specify a distinict -# interfaces and ports for the listeners. - -[relay] -LINE_RECEIVER_INTERFACE = 0.0.0.0 -LINE_RECEIVER_PORT = 2003 - -PICKLE_RECEIVER_INTERFACE = 0.0.0.0 -PICKLE_RECEIVER_PORT = 2004 - -CACHE_SERVERS = server1, server2, server3 -MAX_QUEUE_SIZE = 10000 diff --git a/devenv/docker/blocks/graphite09/files/events_views.py b/devenv/docker/blocks/graphite09/files/events_views.py deleted file mode 100644 index 6c12987af3c..00000000000 --- a/devenv/docker/blocks/graphite09/files/events_views.py +++ /dev/null @@ -1,102 +0,0 @@ -import datetime -import time - -from django.utils.timezone import get_current_timezone -from django.core.urlresolvers import get_script_prefix -from django.http import HttpResponse -from django.shortcuts import render_to_response, get_object_or_404 -from pytz import timezone - -from graphite.util import json -from graphite.events import models -from graphite.render.attime import parseATTime - - -def to_timestamp(dt): - return time.mktime(dt.timetuple()) - - -class EventEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, datetime.datetime): - return to_timestamp(obj) - return json.JSONEncoder.default(self, obj) - - -def view_events(request): - if request.method == "GET": - context = { 'events' : fetch(request), - 'slash' : get_script_prefix() - } - return render_to_response("events.html", context) - else: - return post_event(request) - -def detail(request, event_id): - e = get_object_or_404(models.Event, pk=event_id) - context = { 'event' : e, - 'slash' : get_script_prefix() - } - return render_to_response("event.html", context) - - -def post_event(request): - if request.method == 'POST': - event = json.loads(request.body) - assert isinstance(event, dict) - - values = {} - values["what"] = event["what"] - values["tags"] = event.get("tags", None) - values["when"] = datetime.datetime.fromtimestamp( - event.get("when", time.time())) - if "data" in event: - values["data"] = event["data"] - - e = models.Event(**values) - e.save() - - return HttpResponse(status=200) - else: - return HttpResponse(status=405) - -def get_data(request): - if 'jsonp' in request.REQUEST: - response = HttpResponse( - "%s(%s)" % (request.REQUEST.get('jsonp'), - json.dumps(fetch(request), cls=EventEncoder)), - mimetype='text/javascript') - else: - response = HttpResponse( - json.dumps(fetch(request), cls=EventEncoder), - mimetype="application/json") - return response - -def fetch(request): - #XXX we need to move to USE_TZ=True to get rid of naive-time conversions - def make_naive(dt): - if 'tz' in request.GET: - tz = timezone(request.GET['tz']) - else: - tz = get_current_timezone() - local_dt = dt.astimezone(tz) - if hasattr(local_dt, 'normalize'): - local_dt = local_dt.normalize() - return local_dt.replace(tzinfo=None) - - if request.GET.get("from", None) is not None: - time_from = make_naive(parseATTime(request.GET["from"])) - else: - time_from = datetime.datetime.fromtimestamp(0) - - if request.GET.get("until", None) is not None: - time_until = make_naive(parseATTime(request.GET["until"])) - else: - time_until = datetime.datetime.now() - - tags = request.GET.get("tags", None) - if tags is not None: - tags = request.GET.get("tags").split(" ") - - return [x.as_dict() for x in - models.Event.find_events(time_from, time_until, tags=tags)] diff --git a/devenv/docker/blocks/graphite09/files/initial_data.json b/devenv/docker/blocks/graphite09/files/initial_data.json deleted file mode 100644 index b3ac9b1ebb0..00000000000 --- a/devenv/docker/blocks/graphite09/files/initial_data.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "pk": 1, - "model": "auth.user", - "fields": { - "username": "admin", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": true, - "is_staff": true, - "last_login": "2011-09-20 17:02:14", - "groups": [], - "user_permissions": [], - "password": "sha1$1b11b$edeb0a67a9622f1f2cfeabf9188a711f5ac7d236", - "email": "root@example.com", - "date_joined": "2011-09-20 17:02:14" - } - } -] diff --git a/devenv/docker/blocks/graphite09/files/local_settings.py b/devenv/docker/blocks/graphite09/files/local_settings.py deleted file mode 100644 index 177d674e9dd..00000000000 --- a/devenv/docker/blocks/graphite09/files/local_settings.py +++ /dev/null @@ -1,42 +0,0 @@ -# Edit this file to override the default graphite settings, do not edit settings.py - -# Turn on debugging and restart apache if you ever see an "Internal Server Error" page -#DEBUG = True - -# Set your local timezone (django will try to figure this out automatically) -TIME_ZONE = 'UTC' - -# Setting MEMCACHE_HOSTS to be empty will turn off use of memcached entirely -#MEMCACHE_HOSTS = ['127.0.0.1:11211'] - -# Sometimes you need to do a lot of rendering work but cannot share your storage mount -#REMOTE_RENDERING = True -#RENDERING_HOSTS = ['fastserver01','fastserver02'] -#LOG_RENDERING_PERFORMANCE = True -#LOG_CACHE_PERFORMANCE = True - -# If you've got more than one backend server they should all be listed here -#CLUSTER_SERVERS = [] - -# Override this if you need to provide documentation specific to your graphite deployment -#DOCUMENTATION_URL = "http://wiki.mycompany.com/graphite" - -# Enable email-related features -#SMTP_SERVER = "mail.mycompany.com" - -# LDAP / ActiveDirectory authentication setup -#USE_LDAP_AUTH = True -#LDAP_SERVER = "ldap.mycompany.com" -#LDAP_PORT = 389 -#LDAP_SEARCH_BASE = "OU=users,DC=mycompany,DC=com" -#LDAP_BASE_USER = "CN=some_readonly_account,DC=mycompany,DC=com" -#LDAP_BASE_PASS = "readonly_account_password" -#LDAP_USER_QUERY = "(username=%s)" #For Active Directory use "(sAMAccountName=%s)" - -# If sqlite won't cut it, configure your real database here (don't forget to run manage.py syncdb!) -#DATABASE_ENGINE = 'mysql' # or 'postgres' -#DATABASE_NAME = 'graphite' -#DATABASE_USER = 'graphite' -#DATABASE_PASSWORD = 'graphite-is-awesome' -#DATABASE_HOST = 'mysql.mycompany.com' -#DATABASE_PORT = '3306' diff --git a/devenv/docker/blocks/graphite09/files/my_htpasswd b/devenv/docker/blocks/graphite09/files/my_htpasswd deleted file mode 100644 index 52a72d01b4c..00000000000 --- a/devenv/docker/blocks/graphite09/files/my_htpasswd +++ /dev/null @@ -1 +0,0 @@ -grafana:$apr1$4R/20xhC$8t37jPP5dbcLr48btdkU// diff --git a/devenv/docker/blocks/graphite09/files/nginx.conf b/devenv/docker/blocks/graphite09/files/nginx.conf deleted file mode 100644 index 5b5c562ce9d..00000000000 --- a/devenv/docker/blocks/graphite09/files/nginx.conf +++ /dev/null @@ -1,70 +0,0 @@ -daemon off; -user www-data; -worker_processes 1; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - types_hash_max_size 2048; - server_tokens off; - - server_names_hash_bucket_size 32; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - - gzip on; - gzip_disable "msie6"; - - server { - listen 80 default_server; - server_name _; - - open_log_file_cache max=1000 inactive=20s min_uses=2 valid=1m; - - location / { - proxy_pass http://127.0.0.1:8000; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Server $host; - proxy_set_header X-Forwarded-Host $host; - proxy_set_header Host $host; - - client_max_body_size 10m; - client_body_buffer_size 128k; - - proxy_connect_timeout 90; - proxy_send_timeout 90; - proxy_read_timeout 90; - - proxy_buffer_size 4k; - proxy_buffers 4 32k; - proxy_busy_buffers_size 64k; - proxy_temp_file_write_size 64k; - } - - add_header Access-Control-Allow-Origin "*"; - add_header Access-Control-Allow-Methods "GET, OPTIONS"; - add_header Access-Control-Allow-Headers "origin, authorization, accept"; - - location /content { - alias /opt/graphite/webapp/content; - - } - - location /media { - alias /usr/share/pyshared/django/contrib/admin/media; - } - } -} diff --git a/devenv/docker/blocks/graphite09/files/statsd_config.js b/devenv/docker/blocks/graphite09/files/statsd_config.js deleted file mode 100644 index a03003dcd79..00000000000 --- a/devenv/docker/blocks/graphite09/files/statsd_config.js +++ /dev/null @@ -1,8 +0,0 @@ -{ - graphitePort: 2003, - graphiteHost: "127.0.0.1", - port: 8125, - mgmt_port: 8126, - backends: ['./backends/graphite'], - debug: true -} diff --git a/devenv/docker/blocks/graphite09/files/storage-aggregation.conf b/devenv/docker/blocks/graphite09/files/storage-aggregation.conf deleted file mode 100644 index 5c4bc19fde1..00000000000 --- a/devenv/docker/blocks/graphite09/files/storage-aggregation.conf +++ /dev/null @@ -1,19 +0,0 @@ -[min] -pattern = \.min$ -xFilesFactor = 0.1 -aggregationMethod = min - -[max] -pattern = \.max$ -xFilesFactor = 0.1 -aggregationMethod = max - -[sum] -pattern = \.count$ -xFilesFactor = 0 -aggregationMethod = sum - -[default_average] -pattern = .* -xFilesFactor = 0.5 -aggregationMethod = average diff --git a/devenv/docker/blocks/graphite09/files/storage-schemas.conf b/devenv/docker/blocks/graphite09/files/storage-schemas.conf deleted file mode 100644 index 7e28d3a6649..00000000000 --- a/devenv/docker/blocks/graphite09/files/storage-schemas.conf +++ /dev/null @@ -1,16 +0,0 @@ -[carbon] -pattern = ^carbon\..* -retentions = 1m:31d,10m:1y,1h:5y - -[highres] -pattern = ^highres.* -retentions = 1s:1d,1m:7d - -[statsd] -pattern = ^statsd.* -retentions = 1m:7d,10m:1y - -[default] -pattern = .* -retentions = 10s:1d,1m:7d,10m:1y - diff --git a/devenv/docker/blocks/graphite09/files/supervisord.conf b/devenv/docker/blocks/graphite09/files/supervisord.conf deleted file mode 100644 index c9812bb16dc..00000000000 --- a/devenv/docker/blocks/graphite09/files/supervisord.conf +++ /dev/null @@ -1,26 +0,0 @@ -[supervisord] -nodaemon = true -environment = GRAPHITE_STORAGE_DIR='/opt/graphite/storage',GRAPHITE_CONF_DIR='/opt/graphite/conf' - -[program:nginx] -command = /usr/sbin/nginx -stdout_logfile = /var/log/supervisor/%(program_name)s.log -stderr_logfile = /var/log/supervisor/%(program_name)s.log -autorestart = true - -[program:carbon-cache] -;user = www-data -command = /opt/graphite/bin/carbon-cache.py --debug start -stdout_logfile = /var/log/supervisor/%(program_name)s.log -stderr_logfile = /var/log/supervisor/%(program_name)s.log -autorestart = true - -[program:graphite-webapp] -;user = www-data -directory = /opt/graphite/webapp -environment = PYTHONPATH='/opt/graphite/webapp' -command = /usr/bin/gunicorn_django -b127.0.0.1:8000 -w2 graphite/settings.py -stdout_logfile = /var/log/supervisor/%(program_name)s.log -stderr_logfile = /var/log/supervisor/%(program_name)s.log -autorestart = true - diff --git a/devenv/docker/blocks/prometheus_high_card/go.mod b/devenv/docker/blocks/prometheus_high_card/go.mod index 9f135951fa2..6706105c5cc 100644 --- a/devenv/docker/blocks/prometheus_high_card/go.mod +++ b/devenv/docker/blocks/prometheus_high_card/go.mod @@ -1,20 +1,18 @@ module high-card -go 1.24.2 +go 1.24.3 -require ( - github.com/prometheus/client_golang v1.20.2 - golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 -) +require github.com/prometheus/client_golang v1.22.0 require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - golang.org/x/sys v0.32.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + golang.org/x/sys v0.33.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/devenv/docker/blocks/prometheus_high_card/go.sum b/devenv/docker/blocks/prometheus_high_card/go.sum index 9973d412b1b..ebbd18eaeef 100644 --- a/devenv/docker/blocks/prometheus_high_card/go.sum +++ b/devenv/docker/blocks/prometheus_high_card/go.sum @@ -2,25 +2,31 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +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/devenv/docker/blocks/prometheus_high_card/main.go b/devenv/docker/blocks/prometheus_high_card/main.go index dc23a06f711..944fe7fbf09 100644 --- a/devenv/docker/blocks/prometheus_high_card/main.go +++ b/devenv/docker/blocks/prometheus_high_card/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "log" + "math/rand/v2" "net/http" "strconv" "time" @@ -10,7 +11,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" - "golang.org/x/exp/rand" ) func randomValues(max int) func() (string, bool) { @@ -23,7 +23,7 @@ func randomValues(max int) func() (string, bool) { func staticList(input []string) func() string { return func() string { - i := rand.Intn(len(input)) + i := rand.IntN(len(input)) return input[i] } diff --git a/devenv/docker/blocks/prometheus_utf8/go.mod b/devenv/docker/blocks/prometheus_utf8/go.mod index ce04610a6bd..8e05b29a4ff 100644 --- a/devenv/docker/blocks/prometheus_utf8/go.mod +++ b/devenv/docker/blocks/prometheus_utf8/go.mod @@ -1,20 +1,20 @@ module utf8-support -go 1.24.2 +go 1.24.3 require ( - github.com/prometheus/client_golang v1.20.5 - github.com/prometheus/common v0.60.1 - golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 + github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/common v0.62.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - golang.org/x/sys v0.32.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + golang.org/x/sys v0.33.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/devenv/docker/blocks/prometheus_utf8/go.sum b/devenv/docker/blocks/prometheus_utf8/go.sum index 8b05e8f9296..ebbd18eaeef 100644 --- a/devenv/docker/blocks/prometheus_utf8/go.sum +++ b/devenv/docker/blocks/prometheus_utf8/go.sum @@ -2,33 +2,31 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= -github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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/devenv/docker/blocks/prometheus_utf8/main.go b/devenv/docker/blocks/prometheus_utf8/main.go index 8a61eb97bba..8bdfc6d6404 100644 --- a/devenv/docker/blocks/prometheus_utf8/main.go +++ b/devenv/docker/blocks/prometheus_utf8/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "log" + "math/rand/v2" "net/http" "strconv" "time" @@ -11,7 +12,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/model" - "golang.org/x/exp/rand" ) func randomValues(max int) func() (string, bool) { @@ -24,7 +24,7 @@ func randomValues(max int) func() (string, bool) { func staticList(input []string) func() string { return func() string { - i := rand.Intn(len(input)) + i := rand.IntN(len(input)) return input[i] } diff --git a/devenv/docker/blocks/stateful_webhook/Dockerfile b/devenv/docker/blocks/stateful_webhook/Dockerfile index 30d63d2e12f..4cac8ca98dc 100644 --- a/devenv/docker/blocks/stateful_webhook/Dockerfile +++ b/devenv/docker/blocks/stateful_webhook/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24.2 +FROM golang:1.24.3 ADD main.go /go/src/webhook/main.go diff --git a/docs/sources/administration/enterprise-licensing/_index.md b/docs/sources/administration/enterprise-licensing/_index.md index 912ddfe1a4f..f0f1428b719 100644 --- a/docs/sources/administration/enterprise-licensing/_index.md +++ b/docs/sources/administration/enterprise-licensing/_index.md @@ -173,9 +173,9 @@ SAML authentication is not affected by an expired license. Enterprise plugins might stop working. -#### White labeling +#### Custom branding -The white labeling feature is turned off, meaning that any white labeling options will not have any effect. +The custom branding feature is turned off, meaning that any custom branding options will not have any effect. #### Usage insights diff --git a/docs/sources/administration/feature-toggles/index.md b/docs/sources/administration/feature-toggles/index.md deleted file mode 100644 index b79d62fbede..00000000000 --- a/docs/sources/administration/feature-toggles/index.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -description: Learn how authorized users can manage feature toggles -keywords: - - feature - - toggle -labels: - products: - - enterprise - - oss -title: Feature toggles -weight: 900 ---- - -# Feature toggles - -Grafana incorporates feature toggles which let you introduce new functionality safeguarded by control flags. This allows Grafana administrators the flexibility to determine the appropriate timing for enabling or disabling specific features. -For detailed information about particular features and how they operate, refer to [Configure Feature Toggles](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/feature-toggles/). - -## Manage feature toggles - -In the **Administration** page, the **Feature Management** section enables authorized users to view and edit the various features that are active in their Grafana environment. -Authorized users include administrators, and users with the [fixed roles](../roles-and-permissions/access-control/#fixed-roles) `featuremgmt.read` and `featuremgmt.write`. - -There are different options for visibility and customization depending on the development stage of the feature. - -| Stage | Visibility | Editable | -| -------------------- | ---------- | -------- | -| Experimental | Hidden | No | -| Private preview | Hidden | No | -| Public preview | Visible\* | No | -| General availability | Visible\* | Yes\* | -| Deprecated | Visible\* | Yes\* | - -{{< admonition type="note" >}} -Options marked with an asterisk (\*) are defaults for the corresponding feature stage. -Each feature toggle owner can override its default behavior. -{{< /admonition >}} - -## Edit feature toggles - -You can only edit feature toggles if Grafana is configured with the proper feature management settings. -For more information, refer to [Configure feature management](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/feature-toggles/#configure-feature-management). - -Altering the state of a toggle may require restarting your Grafana instance, which can take a few minutes. - -To edit a feature toggle, follow these steps: - -1. Sign in to Grafana as a server administrator or authorized user. -1. In the primary menu, go to **Administration > General > Feature toggles**. -1. Navigate to the list of feature toggles and select your feature state overrides. -1. Click **Save changes** and wait for your Grafana instance to restart with the updated feature toggles. - -{{< admonition type="note" >}} -If you don't have the feature toggle management page, enable the `featureToggleAdminPage` feature toggle. - -Editing feature toggles with the feature toggle management page is available now in all tiers of [Grafana Cloud](/docs/grafana-cloud/). -{{< /admonition >}} diff --git a/docs/sources/administration/plugin-management/index.md b/docs/sources/administration/plugin-management/_index.md similarity index 100% rename from docs/sources/administration/plugin-management/index.md rename to docs/sources/administration/plugin-management/_index.md diff --git a/docs/sources/administration/plugin-management/customize-nav-bar.md b/docs/sources/administration/plugin-management/customize-nav-bar.md new file mode 100644 index 00000000000..3a4e82880c4 --- /dev/null +++ b/docs/sources/administration/plugin-management/customize-nav-bar.md @@ -0,0 +1,89 @@ +--- +title: Customize navigation placement of plugin pages +description: Learn how to relocate Grafana app plugin pages to customize the navigation menu structure. +labels: + products: + - enterprise + - oss + - cloud +keywords: + - grafana + - plugins + - plugin + - navigation + - customize + - configuration + - grafana.ini +weight: 100 +--- + +# Customize navigation placement of app plugin pages + +By default, Grafana app plugins and their pages appear under the "More apps" section in the navigation menu. However, as a Grafana administrator, you might want to improve user experience by relocating specific pages or entire app plugins to more relevant sections of the navigation hierarchy. This guide shows you how to customize the placement of app plugin pages across different parts of your Grafana navigation menu. + +## Customize app and page navigation placement + +You can change the location of your app plugin pages in two ways: + +1. Move the entire app plugin (with all its pages) to a different section +2. Move specific pages from your app plugin to different sections + +### 1. Move an entire app plugin to a different section + +To relocate an entire app plugin to a different navigation section, use the `navigation.app_sections` configuration in your Grafana configuration file: + +```ini +[navigation.app_sections] +org-example-app = explore 100 +``` + +This configuration: + +- Moves the app plugin with ID `org-example-app` +- Places it in the `explore` section +- Assigns it a sort weight of `100` (determining its position within that section) + +### 2. Move individual app pages to different sections + +To move specific pages from an app plugin to different navigation sections, use the `navigation.app_standalone_pages` configuration: + +```ini +[navigation.app_standalone_pages] +/a/org-example-app/dashboard-page = dashboards 200 +/a/org-example-app/monitoring-page = alerting 50 +``` + +This configuration: + +- Moves the page with path `/a/org-example-app/dashboard-page` to the `dashboards` section with sort weight `200` +- Moves the page with path `/a/org-example-app/monitoring-page` to the `alerting` section with sort weight `50` + +## Complete example + +Here's a complete example that configures both the app placement and individual page placement in your Grafana configuration: + +```ini +# Move the entire app to the Explore section +[navigation.app_sections] +org-example-app = explore 50 + +# Move specific pages to their own sections +[navigation.app_standalone_pages] +/a/org-example-app/metrics = dashboards 100 +/a/org-example-app/logs = alerting 75 +``` + +## Understanding page paths + +To move individual pages, you need to know their paths. Page paths in app plugins follow this format: +`/a/PLUGIN_ID/PAGE_PATH` + +You can identify a plugin page path by visiting the page in the browser and observing the URL in the address bar. + +## Troubleshooting + +If your navigation changes don't appear: + +1. Verify your configuration syntax is correct +2. Ensure you've restarted Grafana after making changes +3. Check that the plugin IDs and page paths exactly match what's defined in your plugin diff --git a/docs/sources/administration/recorded-queries/index.md b/docs/sources/administration/recorded-queries/index.md index 7460ee31249..0818c795191 100644 --- a/docs/sources/administration/recorded-queries/index.md +++ b/docs/sources/administration/recorded-queries/index.md @@ -18,7 +18,9 @@ weight: 300 # DEPRECIATED Recorded queries {{% admonition type="warning" %}} -Recorded queries are deprecated. Please use the new [Grafana Managed Recording Rules](/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules) instead. +Recorded queries are deprecated. Please use the new [Grafana-managed recording rules](/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules) instead. + +To learn how to migrate your recorded queries to Grafana-managed recording rules, refer to the migration documentation [here.](/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/migrate-recorded-queries) {{% /admonition %}} Recorded queries allow you to see trends over time by taking a snapshot of a data point on a set interval. This can give you insight into historic trends. 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 f9935338af2..31aeaa1a99a 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 @@ -238,6 +238,28 @@ For more information on Cloud Access Policies and how to use them, see [Access p | `alert.notifications.routes:read` | None | Read notification policies. | | `alert.notifications.routes:write` | None | Create new, update or delete notification policies | +### Grafana Synthetic Monitoring action definitions + +The following list contains role-based access control actions used by Grafana Synthetic Monitoring. + +| Action | Applicable scopes | Description | +| ------------------------------------------------------ | ----------------- | ----------------------------------------------------------- | +| `grafana-synthetic-monitoring-app:read` | None | Read synthetic monitoring app. | +| `grafana-synthetic-monitoring-app:write` | None | Write synthetic monitoring app. | +| `grafana-synthetic-monitoring-app.checks:read` | None | Read checks in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.checks:write` | None | Create and edit checks in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.checks:delete` | None | Delete checks in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.probes:read` | None | Read probes in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.probes:write` | None | Create and edit probes in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.probes:delete` | None | Delete probes in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.alerts:read` | None | Read alerts in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.alerts:write` | None | Create and edit alerts in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.alerts:delete` | None | Delete alerts in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.thresholds:read` | None | Read thresholds in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.thresholds:write` | None | Create and edit thresholds in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.thresholds:delete` | None | Delete thresholds in the Synthetic Monitoring app. | +| `grafana-synthetic-monitoring-app.access-tokens:write` | None | Create and delete synthetic monitoring access tokens. | + ## Scope definitions The following list contains role-based access control scopes. 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 6b00657fd54..10f2af5b991 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md @@ -54,13 +54,13 @@ The following tables list permissions associated with basic and fixed roles. Thi ## Basic role assignments -| Basic role | UID | Associated fixed roles | Description | -| ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`
`fixed:roles:writer`
`fixed:users:reader`
`fixed:users:writer`
`fixed:org.users:reader`
`fixed:org.users:writer`
`fixed:ldap:reader`
`fixed:ldap:writer`
`fixed:stats:reader`
`fixed:settings:reader`
`fixed:settings:writer`
`fixed:provisioning:writer`
`fixed:organization:reader`
`fixed:organization:maintainer`
`fixed:licensing:reader`
`fixed:licensing:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:maintainer`
`fixed:authentication.config:writer`
`fixed:library.panels:creator`
`fixed:library.panels:reader`
`fixed:library.panels:general.reader`
`fixed:library.panels:writer`
`fixed:library.panels:general.writer`
`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. | -| Admin | `basic_admin` | `fixed:reports:reader`
`fixed:reports:writer`
`fixed:datasources:reader`
`fixed:datasources:writer`
`fixed:organization:writer`
`fixed:datasources.permissions:reader`
`fixed:datasources.permissions:writer`
`fixed:teams:writer`
`fixed:dashboards:reader`
`fixed:dashboards:writer`
`fixed:dashboards.permissions:reader`
`fixed:dashboards.permissions:writer`
`fixed:dashboards.public:writer`
`fixed:folders:reader`
`fixed:folders:writer`
`fixed:folders.permissions:reader`
`fixed:folders.permissions:writer`
`fixed:alerting:writer`
`fixed:apikeys:reader`
`fixed:apikeys:writer`
`fixed:alerting.provisioning.secrets:reader`
`fixed:alerting.provisioning:writer`
`fixed:datasources.caching:reader`
`fixed:datasources.caching:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:plugins:writer`
`fixed:library.panels:creator`
`fixed:library.panels:reader`
`fixed:library.panels:general.reader`
`fixed:library.panels:writer`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. | -| Editor | `basic_editor` | `fixed:datasources:explorer`
`fixed:dashboards:creator`
`fixed:folders:creator`
`fixed:annotations:writer`
`fixed:alerting:writer`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:creator`
`fixed:library.panels:general.reader`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. | -| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
`fixed:organization:reader`
`fixed:annotations:reader`
`fixed:annotations.dashboard:writer`
`fixed:alerting:reader`
`fixed:plugins.app:reader`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:general.reader`
`fixed:folders.general:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. | -| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) | +| Basic role | UID | Associated fixed roles | Description | +| ------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Grafana Admin | `basic_grafana_admin` | fixed:roles:writer`
`fixed:users:writer`
`fixed:org.users:writer`
`fixed:ldap:writer`
`fixed:stats:reader`
`fixed:settings:writer`
`fixed:provisioning:writer`
`fixed:organization:maintainer`
`fixed:licensing:writer`
`fixed:plugins:maintainer`
`fixed:authentication.config:writer`
`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. | +| Admin | `basic_admin` | All roles assigned to Editor and `fixed:reports:writer`
`fixed:datasources:writer`
`fixed:organization:writer`
`fixed:datasources.permissions:writer`
`fixed:teams:writer`
`fixed:dashboards:writer`
`fixed:dashboards.permissions:writer`
`fixed:dashboards.public:writer`
`fixed:folders:writer`
`fixed:folders.permissions:writer`
`fixed:alerting:writer`
`fixed:apikeys:writer`
`fixed:alerting.provisioning.secrets:reader`
`fixed:alerting.provisioning:writer`
`fixed:datasources.caching:writer`
`fixed:plugins:writer`
`fixed:library.panels:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. | +| Editor | `basic_editor` | All roles assigned to Viewer and `fixed:datasources:explorer`
`fixed:dashboards:creator`
`fixed:folders:creator`
`fixed:annotations:writer`
`fixed:alerting:writer`
`fixed:library.panels:creator`
`fixed:library.panels:general.writer`
`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. | +| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
`fixed:organization:reader`
`fixed:annotations:reader`
`fixed:annotations.dashboard:writer`
`fixed:alerting:reader`
`fixed:plugins.app:reader`
`fixed:dashboards.insights:reader`
`fixed:datasources.insights:reader`
`fixed:library.panels:general.reader`
`fixed:folders.general:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. | +| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) | ## Fixed role definitions diff --git a/docs/sources/administration/team-management/_index.md b/docs/sources/administration/team-management/_index.md index e83e83ab62c..0a35b764cb7 100644 --- a/docs/sources/administration/team-management/_index.md +++ b/docs/sources/administration/team-management/_index.md @@ -42,6 +42,8 @@ A Team grants permissions to a wide variety of resources including: {{< admonition type="note" >}} All members of a Grafana Team have the same exact permissions. A single Team can't have members with different access levels to resources shared within that Team. + +Additionally, when a user belongs to multiple Teams that have different permission levels for the same resource, the user receives the highest (most permissive) access level from any of their Teams. For example, if a user belongs to two Teams, one with Viewer access and another with Editor access to a folder, the user will have Editor access to that folder. {{< /admonition >}} A user can be a `Member` or an `Administrator` for a given Team. `Members` of a Team inherit permissions from the team, but they don't have team administrator privileges, and can't edit the team itself. Team `Administrators` can add members to a team and update its settings, such as the team name, team member’s team roles, UI preferences, and home dashboard. diff --git a/docs/sources/administration/user-management/server-user-management/change-user-org-permissions/index.md b/docs/sources/administration/user-management/server-user-management/change-user-org-permissions/index.md index 4bf9eac2862..86762102ebf 100644 --- a/docs/sources/administration/user-management/server-user-management/change-user-org-permissions/index.md +++ b/docs/sources/administration/user-management/server-user-management/change-user-org-permissions/index.md @@ -26,3 +26,7 @@ Update organization permissions when you want to enhance or restrict a user's ac 1. In the Organizations section, click **Change role** for the role you want to change 1. Select another role. 1. Click **Save**. + +{{< admonition type="note" >}} +In order for the change to take effect and be reflected within the instance, the account where permissions were altered will need to sign out fully and back in. Role assignment is evaluated during sign in, so if a user has not signed back in after their role was adjusted the instance will continue to reflect their previous role. +{{< /admonition >}} diff --git a/docs/sources/alerting/alerting-rules/alerting-migration.md b/docs/sources/alerting/alerting-rules/alerting-migration.md new file mode 100644 index 00000000000..1d06d31f006 --- /dev/null +++ b/docs/sources/alerting/alerting-rules/alerting-migration.md @@ -0,0 +1,290 @@ +--- +aliases: + - ../alerting-rules/alerting-migration/migration-api/ # /docs/grafana//alerting-rules/alerting-migration/migration-api/ +canonical: https://grafana.com/docs/grafana/latest/alerting-rules/alerting-migration/ +description: Convert alert rules from data sources such as Mimir, Loki, and Prometheus into Grafana-managed alert rules. This enables you to operate and manage these rules using Grafana Alerting. +labels: + products: + - cloud + - enterprise + - oss +title: Import data source-managed rules to Grafana-managed rules +menuTitle: Import to Grafana-managed rules +weight: 450 +refs: + configure-grafana-rule_query_offset: + - pattern: /docs/ + destination: /docs/grafana//setup-grafana/configure-grafana/#rule_query_offset + evaluation-strategies: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/ + missing_series_evaluations_to_resolve: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/#configure-missing-series-evaluations-to-resolve + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/#configure-missing-series-evaluations-to-resolve + configure-recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/ +--- + +# Import data source-managed rules to Grafana-managed rules + +You can convert existing alert rules from data sources such as Mimir, Loki, and Prometheus into Grafana-managed alert rules. This enables you to operate and manage these rules using Grafana Alerting. + +This guide explains two methods for importing data source-managed rules: + +- Using the [Grafana Alerting user interface](#import-rules-with-grafana-alerting) to import rules from connected data sources or Prometheus rule YAML files. + +- Using [command-line tools](#import-rules-with-command-line-tools) like `mimirtool` and `cortextool` to import rules from YAML files. + +Importing rules is a safe operation: the original data source–managed rules remain intact in their original location. During import, the rules are converted into Grafana-managed rules while preserving their configuration and behavior. + +Choose the method that best fits your workflow. As a best practice, test and validate your import process before migrating all alert rules. + +## How it works + +When you use any of the import methods, data source–managed rules are copied to another folder as Grafana-managed rules. + +The original data source–managed rules remain intact in their original location. + +The copied rules are converted to Grafana-managed rules, preserving their behavior by using equivalent Grafana-managed features. The following settings are applied during the conversion: + +- **Unique UIDs** + + The newly created rules are assigned unique UIDs. + If you don’t want a UID to be auto-generated, you can specify one using the `__grafana_alert_rule_uid__` label. + +- **Query offset** + + A query offset is applied to each rule. For example, an offset of `1m` adjusts the query's time range to `To: now-1m`. + + The rule query offset is taken from the `query_offset` value in the rule group configuration. If empty, it defaults to the [`rule_query_offset` configuration setting](ref:configure-grafana-rule_query_offset), which is `1m` by default. + +- **Missing series evaluations to resolve** + + The [Missing series evaluations to resolve](ref:missing_series_evaluations_to_resolve) setting is set to `1` to replicate Prometheus’s alert eviction behavior. + +- **Rule group labels** + + Labels defined at the rule group level are added as labels to each imported rule within the group. + +- **Sequential evaluation** + + Imported rules are evaluated sequentially within each rule group, mirroring Prometheus behavior. This differs from native Grafana-managed alert rules, where the evaluation order is not enforced. For more details, refer to [evaluation strategies](ref:evaluation-strategies). + +- **Feature compatibility** + + The rule group `limit` option and the `query` function within alert rule templates are not currently supported in Grafana-managed rules. If the `limit` option is present, the import fails. However, rules with `query` in templates are imported. + +{{< admonition type="note" >}} +Rules with the label `__grafana_origin` are not included in rule imports. These rules are typically created by apps such as **Kubernetes Monitoring**, **Synthetic Monitoring**, and other **Grafana plugins**. +{{< /admonition >}} + +## Import rules with Grafana Alerting + +You can use the Grafana Alerting user interface to import rules from the following sources: + +- Connected Mimir and Loki data sources with the ruler API enabled +- Prometheus YAML rule files + +Imported rules using this method are editable in the user interface. Like regular Grafana-managed rules, you can later export them for provisioning. + +#### Before you begin + +To use Grafana Alerting to migrate rules, you need the following [RBAC permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/): + +- **Alerting**: `Rules Writer`, `Set provisioning status`. +- **Datasources**: `Reader`. +- **Folders**: `Creator`. + + The Folders permission is optional and only necessary if you want to create new folders for your target namespace. If your account doesn't have permissions to view a namespace, the tool creates a new one. + +To convert data source-managed alert rules to Grafana managed alerts: + +1. Go to **Alerting > Alert rules**. + +2. Navigate to the Data source-managed alert rules section and click **Import to Grafana-managed rules**. + +3. Choose the **Import source** from which you want to import rules: + + - Select **Existing data source-managed rules** to import rules from connected Mimir or Loki data sources with the ruler API enabled. + - Select **Prometheus YAML file** to import rules by uploading a Prometheus YAML rule file. + +4. In the **Data source** dropdown, select the data source that the imported alert rules will query. + +5. (Optional) In Additional settings, select a target folder or designate a new folder to import the rules into. + + If you import the rules into an existing folder, don't choose a folder with existing alert rules, as they could get overwritten. + +6. (Optional) Select a Namespace and/or Group to determine which rules are imported. + +7. (Optional) Turn on **Pause imported alerting rules**. + + Pausing stops alert rule evaluation and doesn’t create any alert instances for the newly created Grafana-managed alert rules. + +8. (Optional) Turn on **Pause imported recording rules**. + + Pausing stops alert rule evaluation behavior for the newly created Grafana-managed alert rules. + +9. (Optional) In the **Target data source** of the **Recording rules** section, you can select the data source that the imported recording rules will query. By default, it is the data source selected in the **Data source** dropdown. + +10. Click **Import**. + + A preview shows the rules that will be imported. If your target folder contains folders with the same name of the imported folders, a warning displays to inform you. You can explore the warning to see a list of folders that might be overwritten. + + Click **Yes, import** to import the rules. + +## Import rules with command-line tools + +You can also use command-line tools to import data source-managed rules as Grafana-managed rules: use `mimirtool` for Mimir and Prometheus rules, and `cortextool` for Loki rules. + +Both tools provide `rules` commands to import rules groups. For example: + +- `rules load` can import rule groups from files. +- `rules sync` can read rule files and applies only the differences compared to existing Grafana-managed rules. This is useful for automation workflows and pipelines. + +By default, rules imported using the API or command-line tools are **Provisioned** and not editable in the user interface. To make them editable, enable the [X-Disable-Provenance](#x-disable-provenance) header. + +#### Before you begin + +You need to have installed [`mimirtool`](/docs/mimir/latest/manage/tools/mimirtool/) or [`cortextool`](https://github.com/grafana/cortex-tools) (version `0.11.3` or later). + +You need a service account with the following [RBAC permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/): + +- **Alerting**: `Rules Reader`, `Rules Writer`, `Set provisioning status`. +- **Datasources**: `Reader`. +- **Folders**: `Creator`, `Reader`, `Writer`. + +You need to a service account token with your service account. For more details, refer to [service accounts and service account tokens](/docs/grafana/latest/administration/service-accounts/). + +### mimirtool + +To convert and import them into a Grafana instance, you can use the `mimirtool rules load` command: + +```bash +MIMIR_ADDRESS=/api/convert/ \ +MIMIR_AUTH_TOKEN= \ +MIMIR_TENANT_ID=1 \ +mimirtool rules load rule_file.yaml \ + --extra-headers "X-Grafana-Alerting-Datasource-UID=" +``` + +This command imports Prometheus alert rules defined in `rule_file.yaml` as Grafana-managed alert rules. It's important to know that: + +1. When using the `/api/convert/` endpoint, `mimirtool` interacts with Grafana—not with a Mimir instance. In this case, `MIMIR_TENANT_ID` must always be set to `1`. +1. The [`X-Grafana-Alerting-Datasource-UID` header](#x-grafana-alerting-datasource-uid) configures the data source that the imported alert rules will query. Use multiple `--extra-headers` flags to include other [optional headers](#optional-headers). + +Similarly, the `rules sync` command can import and update Grafana-managed alert rules. + +```bash +MIMIR_ADDRESS=/api/convert/ \ +MIMIR_AUTH_TOKEN= \ +MIMIR_TENANT_ID=1 \ +mimirtool rules sync rule_file.yaml \ + --extra-headers "X-Grafana-Alerting-Datasource-UID=" \ + --concurrency 1 +``` + +The `--concurrency` flag must be set to `1`, as the default value of `8` may cause API errors. + +This `sync` command reads rules from the file, compares them with the existing Grafana-managed rules in the instance, and applies only the differences—creating, updating, or deleting rules as needed. + +```output +## Sync Summary: 0 Groups Created, 1 Groups Updated, 0 Groups Deleted +``` + +For more information other Mimirtool commands and options, see the [Mimirtool documentation](/docs/mimir/latest/manage/tools/mimirtool/#rules) and the [Mimir HTTP Rule API documentation](/docs/mimir/latest/references/http-api/#ruler-rules:~:text=config/v1/rules-,Get%20rule%20groups%20by%20namespace,DELETE%20%3Cprometheus%2Dhttp%2Dprefix%3E/config/v1/rules/%7Bnamespace%7D,-Delete%20tenant%20configuration). + +### cortextool + +For Loki alert rules, use [`cortextool`](https://github.com/grafana/cortex-tools) (version `0.11.3` or later) with the `--backend=loki` flag. For example: + +```bash +CORTEX_ADDRESS=/api/convert/ \ +CORTEX_AUTH_TOKEN= \ +CORTEX_TENANT_ID=1 \ +cortextool rules load loki_rules.yaml \ + --extra-headers "X-Grafana-Alerting-Datasource-UID=" \ + --backend=loki +``` + +### Optional Headers + +Additional configuration headers for more granular import control include the following: + +#### `X-Disable-Provenance` + +When this header is set to `true`: + +- The imported rules are not marked as provisioned. +- They can then be edited in the Grafana UI. +- They are excluded from the `GET` and `DELETE` operations on the [`/api/convert` endpoints](#compatible-endpoints). + +Do not enable this header when using the `rules sync` command, as it relies on the `GET` and `DELETE` operations to detect and update existing rules. + +#### `X-Grafana-Alerting-Alert-Rules-Paused` + +Set to `true` to import alert rules in paused state. + +#### `X-Grafana-Alerting-Recording-Rules-Paused` + +Set to `true` to import recording rules in paused state. + +#### `X-Grafana-Alerting-Datasource-UID` + +The UID of the data source to use for alert rule queries. + +#### `X-Grafana-Alerting-Target-Datasource-UID` + +The UID of the target data source for recording rules. If not specified, the value from `X-Grafana-Alerting-Datasource-UID` is used. + +#### `X-Grafana-Alerting-Folder-UID` + +Enter the UID of the target destination folder for imported rules. + +#### `X-Grafana-Alerting-Notification-Settings` + +JSON-encoded [`AlertRuleNotificationSettings` object](#alertrulenotificationsettings-object) that allows setting the contact point for the alert rules. + +{{< collapse title="AlertRuleNotificationSettings object" >}} + +##### AlertRuleNotificationSettings object + +When you set `X-Grafana-Alerting-Notification-Settings`, the header value must be a JSON-encoded object with the following keys: + +| Field | Type | Required | Example | Description | +| ----------------------- | ---------- | -------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------- | +| `receiver` | `string` | Yes | `"grafana-default-email"` | Name of the contact point (receiver) to which alerts are routed. Must exist in Grafana before import. | +| `group_by` | `[]string` | No | `["alertname","grafana_folder","cluster"]` | Label set used by Alertmanager to aggregate alerts into a single notification. | +| `group_wait` | `duration` | No | `"30s"` | How long Alertmanager waits before sending the first notification for a new group. | +| `group_interval` | `duration` | No | `"5m"` | Time to wait before adding new alerts to an existing group's next notification. | +| `repeat_interval` | `duration` | No | `"4h"` | Minimum time before a previously-sent notification is repeated. Must not be less than `group_interval`. | +| `mute_time_intervals` | `[]string` | No | `["maintenance"]` | One or more mute time interval names that silence alerts during those windows. | +| `active_time_intervals` | `[]string` | No | `["maintenance"]` | List of active time interval names. Alerts are suppressed unless the current time matches one of them. | + +{{< /collapse >}} + +### Compatible endpoints + +The API endpoints listed in this section are supported in Grafana and are used by mimirtool and cortextool, as shown earlier. + +The `POST` endpoints can be used to import data source–managed alert rules. + +| Endpoint | Method | Summary | +| -------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| POST | /convert/prometheus/config/v1/rules | Create or update multiple rule groups across multiple namespaces. Requires [`X-Grafana-Alerting-Datasource-UID`](#x-grafana-alerting-datasource-uid). | +| POST | /convert/prometheus/config/v1/rules/:namespaceTitle | Create or update a single rule group in a namespace. Requires [`X-Grafana-Alerting-Datasource-UID`](#x-grafana-alerting-datasource-uid). | + +The `GET` and `DELETE` endpoints work only with provisioned and imported alert rules. + +| Endpoint | Method | Summary | +| -------- | ---------------------------------------------------------- | --------------------------------------------------- | +| GET | /convert/prometheus/config/v1/rules | Get all imported rule groups across all namespaces. | +| GET | /convert/prometheus/config/v1/rules/:namespaceTitle | Get imported rule groups in a specific namespace. | +| DELETE | /convert/prometheus/config/v1/rules/:namespaceTitle | Delete all imported alert rules in a namespace. | +| DELETE | /convert/prometheus/config/v1/rules/:namespaceTitle/:group | Delete a specific imported rule group. | diff --git a/docs/sources/alerting/alerting-rules/alerting-migration/_index.md b/docs/sources/alerting/alerting-rules/alerting-migration/_index.md deleted file mode 100644 index 50654a58f1d..00000000000 --- a/docs/sources/alerting/alerting-rules/alerting-migration/_index.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -description: Use the Grafana Alerting import tool to convert your datasource managed alert rules into Grafana managed alert rules -labels: - products: - - cloud - - enterprise - - oss -title: Import data source-managed alert rules -menuTitle: Import to Grafana-managed alert rules -weight: 600 -refs: ---- - -# Import data source-managed alert rules - -Grafana provides an internal tool in Alerting which allows you to import Prometheus and Loki alert rules into Grafana-managed alert rules. - -## Before you begin - -The `alertingMigrationUI` and `grafanaManagedRecordingRulesDatasources` [feature flags](/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles/) needs to be enabled to use this feature. - -To use the migration tool, you need the following [RBAC permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/): - -- Alerting: Rules Writer -- Alerting: Set provisioning status -- Datasources: Reader -- Folders: Creator - {{< admonition type="note" >}} - The Folders permission is optional and only necessary if you want to create new folders for your target namespace. If your account doesn't have permissions to view a namespace, the tool creates a new one. It is a best practice to prepare an import plan before you convert all your alert rules. - {{< /admonition >}} - -## How it works - -When you use the import tool, a folder of data source-managed rules is copied to another folder as Grafana-managed alert rules, preserving the behavior of the rules, and the original alert rules are kept in their original location. - -When data source-managed alert rules are converted to Grafana-managed alert rules, the following are applied to the Grafana-managed alert rules: - -- All rules are given `rule_query_offset` offset value of 1m. -- The `missing_series_evals_to_resolve` is set to 1 for the new rules. -- The newly created rules are given unique UIDs. - -{{< admonition type="note" >}} -Plugin rules that have the label `__grafana_origin` are not included on alert rule imports. -{{< /admonition >}} - -## Import alert rules - -To convert data source-managed alert rules to Grafana managed alerts: - -1. Go to **Alerting > Alert rules**. - -1. Navigate to the Data source-managed alert rules section and click **Import to Grafana-managed rules**. - - The import alert rules page opens. - -1. In the Data source dropdown, select the Loki or Prometheus data source of the alert rules. - -1. In Additional settings, select a target folder or designate a new folder to import the rules into. - - If you import the rules into an existing folder, don't chose a folder with existing alert rules, as they could get overwritten. - -1. (Optional) Select a Namespace and/or Group to determine which rules are imported. - -1. (Optional) Turn on **Pause imported alerting rules**. - - Pausing stops alert rule evaluation and doesn’t create any alert instances for the newly created Grafana-managed alert rules. - -1. (Optional) Turn on **Pause imported recording rules**. - - Pausing stops alert rule evaluation behavior for the newly created Grafana-managed alert rules. - -1. Select which target data source the new recording rule is written to. - -1. Click **Import**. - - A preview shows the rules that will be imported. If your target folder contains folders with the same name of the imported folders, a warning displays to inform you. You can explore the warning to see a list of folders that might be overwritten. - - Click **Yes, import** to import the rules. diff --git a/docs/sources/alerting/alerting-rules/alerting-migration/migration-api.md b/docs/sources/alerting/alerting-rules/alerting-migration/migration-api.md deleted file mode 100644 index 4a623513094..00000000000 --- a/docs/sources/alerting/alerting-rules/alerting-migration/migration-api.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -description: Use the Grafana Alerting API import tool to convert your datasource managed alert rules into Grafana managed alert rules -labels: - products: - - cloud - - enterprise - - oss -title: Import data source-managed alert rules with Grafana Mimirtool -menuTitle: API alert rules import -weight: 601 -refs: ---- - -# Import data source-managed alert rules with Grafana Mimirtool - -You can convert data source-managed alert rules to Grafana-managed alert rules with the Grafana import tool in the Grafana user interface, or you can convert them with the Grafana Mimirtool command-line tool. This guide tells you how to use Mimirtool to import your data source-managed alert rules. - -## Before you begin - -The `grafanaManagedRecordingRulesDatasources` [feature flag](/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles/) needs to be enabled to use this feature. - -To import data source-managed alert rules with Grafana Mimirtool, you need to have the Grafana Mimirtool command-line tool installed. - -You need a service account the following [RBAC permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/): - -- Alerting: Rules Reader -- Alerting: Rules Writer -- Alerting: Set provisioning status -- Datasources: Reader -- Folders: Creator -- Folders: Reader -- Folders: Writer - -You also need to create a service account token with your service account. Refer to the [documentation for more information on service accounts and service account tokens](/docs/grafana/latest/administration/service-accounts/) - -## How it works - -When data source-managed alert rules are converted to Grafana-managed alert rules, the following are applied to the Grafana-managed alert rules: - -- All rules are given `rule_query_offset` offset value of 1m. - Grafana OSS and Enterprise can configure this value in their conf: - ``` - [unified_alerting.prometheus_conversion] - rule_query_offset = 1m - ``` - If this value is set explicitly in a rule group, that value takes precedence over the configuration setting. -- The `missing_series_evals_to_resolve` is set to 1 for the new rules. -- The newly created rules are given unique UIDs. - If you don't want the UID to be automatically generated, you can specify a specific UID with the `__grafana_alert_rule_uid__` label. - -## Import alert rules with Mimirtool or coretextool - -You can use either [Mimirtool](/docs/mimir/latest/manage/tools/mimirtool/) or [`cortextool`](https://github.com/grafana/cortex-tools) (version `0.11.3` or later) to import your alert rules. For more information about Mimirtool commands, see the [Mimirtool documentation](/docs/mimir/latest/manage/tools/mimirtool/#rules). - -To convert your alert rules, use the following command prompt substituting the your URL and your service account token as indicated, followed by your intended Mimirtool command. - -```bash -MIMIR_ADDRESS=https://.grafana-dev.net/api/convert/ MIMIR_AUTH_TOKEN= MIMIR_TENANT_ID=1 -``` - -For coretextool, you need to set `--backend=loki` to import Loki alert rules. For example: - -```bash -CORTEX_ADDRESS=/api/convert/ CORTEX_AUTH_TOKEN= CORTEX_TENANT_ID=1 cortextool rules --backend=loki list -``` - -Headers can be passed to the `mimirtool` or `coretextool` via `--extra-headers`. - -For more information about the Rule API points and examples of Mimirtool commands, see the [Mimir HTTP API documentation](/docs/mimir/latest/references/http-api/#ruler-rules:~:text=config/v1/rules-,Get%20rule%20groups%20by%20namespace,DELETE%20%3Cprometheus%2Dhttp%2Dprefix%3E/config/v1/rules/%7Bnamespace%7D,-Delete%20tenant%20configuration) for more information about the Rule API points and examples of Mimirtool commands. - -{{< admonition type="note" >}} -To use the `mimirtool rules sync` command, you need to set the `--concurrency` parameter to `1` (`--concurrency=1`). The parameter defaults to 8, which may cause the API to return errors. -{{< /admonition >}} - -### Compatible endpoints - -The following are compatible API endpoints: - -**GET** - -``` -GET /convert/prometheus/config/v1/rules - Get all rule groups across all namespaces -GET /convert/prometheus/config/v1/rules/ - Get rule groups in a specific namespace -GET /convert/prometheus/config/v1/rules// - Get a single rule group - -``` - -**POST** - -``` -POST /convert/prometheus/config/v1/rules - Create/update multiple rule groups across multiple namespaces -POST /convert/prometheus/config/v1/rules/ - Create/update a single rule group in a namespace -``` - -Post rules also require the following header: -When posting rules: -`X-Grafana-Alerting-Datasource-UID` - Supply the UID of the data source to use for queries. - -**Delete** - -``` -DELETE /convert/prometheus/config/v1/rules/{NamespaceTitle} - Delete all alert rules in a namespace -DELETE /convert/prometheus/config/v1/rules/{NamespaceTitle}/{Group} - Delete a specific rule group -``` - -**Optional Headers** - -Additional configuration headers for more granular import control include the following: - -- `X-Grafana-Alerting-Recording-Rules-Paused` - Set to "true" to import recording rules in paused state. -- `X-Grafana-Alerting-Alert-Rules-Paused` - Set to "true" to import alert rules in paused state. -- `X-Grafana-Alerting-Target-Datasource-UID` - Enter the UID of the target data source. -- `X-Grafana-Alerting-Folder-UID` - Enter the UID of the target destination folder for imported rules. diff --git a/docs/sources/alerting/alerting-rules/create-data-source-managed-rule.md b/docs/sources/alerting/alerting-rules/create-data-source-managed-rule.md index 44fed1d97cb..d11719cb5cf 100644 --- a/docs/sources/alerting/alerting-rules/create-data-source-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-data-source-managed-rule.md @@ -22,9 +22,9 @@ weight: 200 refs: shared-configure-prometheus-data-source-alerting: - pattern: /docs/grafana/ - destination: /docs/grafana//datasources/prometheus/configure-prometheus-data-source/#alerting + destination: /docs/grafana//datasources/prometheus/configure/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure-prometheus-data-source/#alerting + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure/ configure-grafana-managed-rules: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/alerting-rules/create-grafana-managed-rule/ @@ -85,7 +85,11 @@ The rules are stored within the data source. In a distributed architecture, they We recommend using [Grafana-managed alert rules](ref:configure-grafana-managed-rules) whenever possible and opting for data source-managed alert rules when scaling your alerting setup is necessary. -{{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="" >}} +> Rules from a Prometheus data source appear in the **Data source-managed** section of the **Alert rules** page when [Manage alerts via Alerting UI](ref:shared-configure-prometheus-data-source-alerting) is enabled. +> +> However, Grafana can only create and edit data source-managed rules for Mimir and Loki, not for a Prometheus instance. + +[//]: <> ({{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="" >}}) To create or edit data source-managed alert rules, follow these instructions. @@ -107,7 +111,13 @@ Alert rules for Mimir or Loki instances can be edited or deleted by users with * If you do not want to manage alert rules for a particular data source, go to its settings and clear the **Manage alerts via Alerting UI** checkbox. -{{< docs/shared lookup="alerts/configure-provisioning-before-begin.md" source="grafana" version="" >}} +### Provisioning + +Note that if you delete an alert resource created in the UI, you can no longer retrieve it. + +To backup and manage alert rules, you can [provision alerting resources](ref:shared-provision-alerting-resources) using options such as configuration files, Terraform, or the Alerting API. + +[//]: <> ({{< docs/shared lookup="alerts/configure-provisioning-before-begin.md" source="grafana" version="" >}}) {{< docs/shared lookup="alerts/configure-alert-rule-name.md" source="grafana" version="" >}} 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 a05cf160c9f..bab62def32b 100644 --- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md @@ -24,11 +24,17 @@ refs: destination: /docs/grafana//dashboards/use-dashboards/#time-units-and-relative-ranges - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/dashboards/use-dashboards/#time-units-and-relative-ranges + + configure-missing-series-evaluations-to-resolve: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/#configure-missing-series-evaluations-to-resolve + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/#configure-missing-series-evaluations-to-resolve alert-instance-state: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-instance-state + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-instance-state + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ recovery-threshold: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#recovery-threshold @@ -44,6 +50,11 @@ refs: destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/#pending-period - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/#pending-period + keep-firing-for: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/#keep-firing-for + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/#keep-firing-for alert-rule-evaluation: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/ @@ -145,7 +156,13 @@ Verify that the data sources you plan to query in the alert rule are [compatible Only users with **Edit** permissions for the folder storing the rules can edit or delete Grafana-managed alert rules. Only admins can restore deleted Grafana-managed alert rules. -{{< docs/shared lookup="alerts/configure-provisioning-before-begin.md" source="grafana" version="" >}} +### Provisioning + +Note that if you delete an alert resource created in the UI, you can no longer retrieve it. + +To backup and manage alert rules, you can [provision alerting resources](ref:shared-provision-alerting-resources) using options such as configuration files, Terraform, or the Alerting API. + +[//]: <> ({{< docs/shared lookup="alerts/configure-provisioning-before-begin.md" source="grafana" version="" >}}) ### Default vs Advanced options @@ -235,16 +252,16 @@ To do this, you need to make sure that your alert rule is in the right evaluatio After a condition is met, the alert goes into the **Pending** state. If the condition remains active for the duration specified, the alert transitions to the **Firing** state, else it reverts to the **Normal** state. +1. Optionally, set the [Keep firing for](ref:keep-firing-for) period. + + You can set the minimum amount of time that an alert remains firing after the breached threshold expression no longer returns any results. This sets an alert to a "Recovering" state for the duration of time set here. The Recovering state can be used to reduce noise from flapping alerts. Select "none" stop an alert from firing immediately after the breach threshold is cleared. + 1. Turn on pause alert notifications, if required. You can pause alert rule evaluation to prevent noisy alerting while tuning your alerts. Pausing stops alert rule evaluation and doesn't create any alert instances. This is different to [mute timings](ref:mute-timings), which stop notifications from being delivered, but still allows for alert rule evaluation and the creation of alert instances. -1. Set the time threshold for alerts firing. - -You can set the minimum amount of time that an alert remains firing after the breached threshold expression no longer returns any results. This sets an alert to a "Recovering" state for the duration of time set here. The Recovering state can be used to reduce noise from flapping alerts. Select "none" stop an alert from firing immediately after the breach threshold is cleared. - 1. In **Configure no data and error handling**, you can define the alerting behavior and alerting state for two scenarios: - When the evaluation returns **No data** or all values are null. @@ -256,6 +273,8 @@ You can set the minimum amount of time that an alert remains firing after the br For more details, refer to [alert instance states](ref:alert-instance-state) and [modify the no data or error state](ref:modify-the-no-data-or-error-state). +1. In **Configure no data and error handling**, you can also configure [Missing series evaluations to resolve](ref:configure-missing-series-evaluations-to-resolve): how many consecutive evaluation intervals must pass without data before an alert instance is considered stale. + ## Configure notifications Choose to select a contact point directly from the alert rule form or to use notification policy routing as well as set up mute timings and groupings. @@ -287,15 +306,34 @@ Complete the following steps to set up notifications. 1. Click **See details** to view alert routing details and an email preview. -{{< docs/shared lookup="alerts/configure-notification-message.md" source="grafana" version="" >}} +## Configure notification message -## Permanently delete or restore deleted alert rules +Use [annotations](ref:shared-annotations) to add information to alert messages that can help respond to the alert. -Only users with an Admin role can restore deleted Grafana-managed alert rules. After an alert rule is restored, it is restored with a new, different UID from the one it had before. +Annotations are included by default in notification messages, and can use text or [templates](ref:shared-alert-rule-template) to display dynamic data from queries. -1. Go to **Alerts & IRM > Alerting > Recently deleted**. -1. Click the **Restore** button to restore the alert rule or click **Delete permanently** to delete the alert rule. +Grafana provides several optional annotations. -{{< admonition type="note" >}} -Deleted alert rules are stored for 30 days. Grafana Enterprise and OSS users can adjust the length of time for which the rules are stored can be adjusted in the Grafana configuration file's `[unified_alerting].deleted_rule_retention` field. For an example of how to modify the Grafana configuration file, refer to the [documentation example here](/docs/grafana/latest/alerting/set-up/configure-alert-state-history/#configuring-grafana). -{{< /admonition >}} +1. Optional: Add a summary. + + Short summary of what happened and why. + +1. Optional: Add a description. + + Description of what the alert rule does. + +1. Optional: Add a Runbook URL. + + Webpage where you keep your runbook for the alert + +1. Optional: Add a custom annotation. + + Add any additional information that could help address the alert. + +1. Optional: **Link dashboard and panel**. + + [Link the alert rule to a panel](ref:shared-link-alert-rules-to-panels) to facilitate alert investigation. + +1. Click **Save rule**. + +[//]: <> ({{< docs/shared lookup="alerts/configure-notification-message.md" source="grafana" version="" >}}) diff --git a/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md b/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md index d3b0db3632f..97d49ac5c32 100644 --- a/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md +++ b/docs/sources/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules.md @@ -63,21 +63,18 @@ To configure Grafana-managed recording rules, complete the following steps. This section only applies to Grafana OSS and Grafana Enterprise. -First, enable the `grafanaManagedRecordingRules` [feature flag](ref:configure-feature-toggles). - -Then, enable the feature by setting `enabled = true` in the `[recording_rules]` section of the Grafana config .ini. Provide the URL of your Prometheus-compatible remote-write endpoint in the `url` field, along with optional credentials or headers. +If a rule does not explicitly specify a target data source for writing (for example, if it was created before Grafana 12.1), Grafana will fall back to the `default_datasource_uid` set in the configuration: ``` [recording_rules] -enabled = true -url = http://my-example-prometheus.local:9090/api/prom/push -basic_auth_username = my-user -basic_auth_password = my-pass - -[recording_rules.custom_headers] -X-My-Header = MyValue +default_datasource_uid = my-uid ``` +If you previously configured recording rules using the `url` and `basic_auth_*` configuration options, these are no longer supported. You must either: + +- Set `default_datasource_uid` in the `[recording_rules]` section of the configuration file to point to the target data source +- Or, before upgrading to Grafana 12.1, enable the `grafanaManagedRecordingRulesDatasources` feature flag and update each recording rule individually to include a target data source + ## Add new recording rule To create a new Grafana-managed recording rule: diff --git a/docs/sources/alerting/alerting-rules/create-recording-rules/migrate-recorded-queries.md b/docs/sources/alerting/alerting-rules/create-recording-rules/migrate-recorded-queries.md new file mode 100644 index 00000000000..871aa3e6ae2 --- /dev/null +++ b/docs/sources/alerting/alerting-rules/create-recording-rules/migrate-recorded-queries.md @@ -0,0 +1,51 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-recording-rules/migrate-recorded-queries/ +description: Learn how to migrate your depreciated recorded queries to Grafana-managed recording rules. +keywords: + - grafana + - alerting + - guide + - rules + - recording rules + - recorded queries + - configure +labels: + products: + - cloud + - enterprise + - oss +title: Migrate recorded queries +weight: 402 +--- + +# Migrate recorded queries + +Users can transpose their [now-depreciated recorded queries](/docs/grafana/latest/administration/recorded-queries/) into Grafana-managed recording rules in a few easy steps. The query PromQL for each recorded query has been exposed on the recorded queries list along with the existing datasource, time range, and interval values to simplify the migration process. + +## Migrate your recorded queries to Grafana-managed alert rules + +1. Navigate to **Administration > Plugins and Data > Recorded queries.** + +1. Note the data source, query PromQL, interval, and time range, and copy them somewhere accessible. + + {{< figure alt="Example of relevant recorded query information" src="/media/docs/alerting/rec-query-example.png" max-width="800px" >}} + +1. Now navigate to **Alerting > Alert rules.** + +1. In the Grafana-managed section of the Alert rules page, click **+ New recording**. + + Add a name for your Recording Rule and a name for the new metric that the recording rule generates. + +1. Select your data source and paste your ratio query PromQL into the query builder. + + Click **Options** and validate that the Time Range is the same as your recorded query. + +1. Select the Folder you would like the rule to be created in. + +1. Add any labels to the rule. + +1. Select or create an evaluation group. Set your evaluation group’s evaluation interval to the interval of your recorded query. + +1. Review your rule and click Save **rule and exit** when you are finished. + +1. Update any areas where the recorded query was referenced to use the new Grafana-managed recording rule. diff --git a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md index 4b401cd1d4e..851fbfdbd57 100644 --- a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md +++ b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md @@ -94,5 +94,5 @@ This option is available only in [time series panels](ref:time-series-visualizat 1. Click the **Alert** tab to view existing alert rules or create a new one. {{< admonition type="tip" >}} -For a practical example that links a panel to an alert rule, refer to [Part 5 of our Get Started with Grafana Alerting tutorial](http://www.grafana.com/tutorials/alerting-get-started-pt5/). +For a practical example, refer to our [Getting started: Link alerts to visualizations tutorial](http://www.grafana.com/tutorials/alerting-get-started-pt6/). {{% /admonition %}} diff --git a/docs/sources/alerting/alerting-rules/templates/_index.md b/docs/sources/alerting/alerting-rules/templates/_index.md index d068b4a3390..a2afde85de7 100644 --- a/docs/sources/alerting/alerting-rules/templates/_index.md +++ b/docs/sources/alerting/alerting-rules/templates/_index.md @@ -18,6 +18,11 @@ labels: title: Template annotations and labels weight: 500 refs: + shared-dynamic-label-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/dynamic-labels/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/dynamic-labels/ reference-labels: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/alerting-rules/templates/reference/#labels @@ -190,7 +195,12 @@ low In this example, the value of the `severity` label is determined by the query value, and the possible options are `critical`, `high`, `medium`, or `low`. You can then use the `severity` label to change their notifications—for instance, sending `critical` alerts immediately or routing `low` alerts to a specific team for further review. -{{< docs/shared lookup="alerts/note-dynamic-labels.md" source="grafana" version="" >}} +> **Note:** An alert instance is uniquely identified by its set of labels. +> +> - Avoid displaying query values in labels, as this can create numerous alert instances—one for each distinct label set. Instead, use annotations for query values. +> - If a templated label's value changes, it maps to a different alert instance, and the previous instance is considered **stale**. Learn all the details in this [example using dynamic labels](ref:shared-dynamic-label-example). + +[//]: <> ({{< docs/shared lookup="alerts/note-dynamic-labels.md" source="grafana" version="" >}}) ### How to template a label diff --git a/docs/sources/alerting/alerting-rules/templates/examples.md b/docs/sources/alerting/alerting-rules/templates/examples.md index e9ee1a8015b..c718129b784 100644 --- a/docs/sources/alerting/alerting-rules/templates/examples.md +++ b/docs/sources/alerting/alerting-rules/templates/examples.md @@ -16,6 +16,11 @@ title: Labels and annotations template examples menuTitle: Examples weight: 102 refs: + shared-dynamic-label-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/dynamic-labels/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/dynamic-labels/ labels: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#labels @@ -209,7 +214,12 @@ In this example, the `severity` label is determined by the query value: You can then use the `severity` label to control how alerts are handled. For instance, you could send `critical` alerts immediately, while routing `low` severity alerts to a team for further investigation. -{{< docs/shared lookup="alerts/note-dynamic-labels.md" source="grafana" version="" >}} +> **Note:** An alert instance is uniquely identified by its set of labels. +> +> - Avoid displaying query values in labels, as this can create numerous alert instances—one for each distinct label set. Instead, use annotations for query values. +> - If a templated label's value changes, it maps to a different alert instance, and the previous instance is considered **stale**. Learn all the details in this [example using dynamic labels](ref:shared-dynamic-label-example). + +[//]: <> ({{< docs/shared lookup="alerts/note-dynamic-labels.md" source="grafana" version="" >}}) ### Based on query label diff --git a/docs/sources/alerting/best-practices/_index.md b/docs/sources/alerting/best-practices/_index.md new file mode 100644 index 00000000000..55cfe60dddf --- /dev/null +++ b/docs/sources/alerting/best-practices/_index.md @@ -0,0 +1,20 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/ +description: This section provides a set of guides for useful alerting practices and recommendations +keywords: + - grafana +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Best Practices +title: Grafana Alerting best practices +weight: 170 +--- + +# Grafana Alerting best practices + +This section provides a set of guides and examples of best practices for Grafana Alerting. Here you can learn more about more about how to handle common alert management problems and you can see examples of more advanced usage of Grafana Alerting. + +{{< section >}} diff --git a/docs/sources/alerting/best-practices/connectivity-errors.md b/docs/sources/alerting/best-practices/connectivity-errors.md new file mode 100644 index 00000000000..90360fb4d3a --- /dev/null +++ b/docs/sources/alerting/best-practices/connectivity-errors.md @@ -0,0 +1,234 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/connectivity-errors/ +description: Learn how to detect and handle connectivity issues in alerts using Prometheus, Grafana Alerting, or both. +keywords: + - grafana + - alerting + - guide + - rules + - create +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Handle connectivity errors +title: Handle connectivity errors in alerts +weight: 1010 +refs: + pending-period: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/ + notifications: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/ + no-data-and-error-alerts: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts + configure-nodata-and-error-handling: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state + missing-data-guide: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/missing-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/missing-data/ +--- + +# Handle connectivity errors in alerts + +Connectivity issues are a common cause of misleading alerts or unnoticed failures. + +There could be a number of reasons for these errors. Maybe your target went offline, or Prometheus couldn't scrape it. Or maybe your alert query failed because its target timed out or the network went down. These situations might look similar, but require different considerations in your alerting setup. + +This guide walks through how to detect and handle these types of failures, whether you're writing alert rules in Prometheus, using Grafana Alerting, or combining both. It covers both availability monitoring and alert query failures, and outlines strategies to improve the reliability of your alerts. + +## Understand connectivity issues in alerts + +Typically, connectivity issues fall into a few common scenarios: + +- Servers or containers crashed or were shut down. +- Service overload or timeout. +- Misconfigured authentication or incorrect permissions. +- Network issues like DNS problems or ISP outages. + +When we talk about connectivity errors in alerting, we’re usually referring to one of two use cases: + +1. **Your target is down or unreachable.** + The service crashed, the host was down, or a firewall or DNS issue blocked the connection. These are **availability problems**. + +1. **Your alert query failed.** + The alert couldn’t evaluate its query—maybe because the data source timed out or an invalid query. These are **execution errors**. + +It helps to separate these cases early, because they behave differently and require different strategies. + +Keep in mind that most alert rules don’t hit the target directly. They query metrics from a monitoring system like Prometheus, which scrapes data from your actual infrastructure or application. That gives us two typical alerting setups where connectivity issues can show up: + +1. **Alert rule → Target** + For example, an alert rule querying an external data source like a database. + +2. **Alert rule → Prometheus ← Target** + More common in observability stacks. For instance, Prometheus scrapes a node or container, and the alert rule queries the metrics later. + + In this second setup, you can run into connectivity issues on either side. If Prometheus fails to scrape the target, your alert rule might not fire, even though something is likely wrong. + +## Detect target availability with the Prometheus `up` metric + +Prometheus scrapes metrics from its targets regularly, following the [`scrape_interval`](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config) period. The default scrape interval is 60 seconds, which is generally considered common practice. + +Prometheus provides a built-in metric called `up` for every scrape target, a simple method to indicate whether scraping is successful: + +- `up == 1`: Your target is reachable; Prometheus collected the target metrics as expected. + +- `up == 0`: Prometheus couldn't reach your target—indicating possible downtime or network errors. + +A typical PromQL expression for an alert rule to detect when a target becomes unreachable is: + +`up == 0` + +But this alert rule might result in noisy alerts as one single scrape failure will fire the alert. To reduce noise, you should add a delay: + +`up == 0 for: 5m` + +The `for` option in Prometheus (or [pending period](ref:pending-period) in Grafana) delays the alert until the condition has been true for the full duration. + +In this example, waiting for 5 minutes means the single scrape error won't result in a fired alert. Since Prometheus scrapes metrics every minute by default, the alert only fires after five consecutive failures. + +However, this kind of `up` alert has a few potential downfalls: + +- **Failures can slip between scrape intervals**: An outage that starts and ends between two evaluations go undetected. You could shorten the `for` duration, but this might lead to scrape failures that trigger false alarms. +- **Intermittent recoveries reset the `for` timer**: A single successful scrape resets the alert timer, which masks intermittent outages. + +Brief connectivity drops are common in real-world environments, so expect some flakiness in `up` alerts. For example: + +| Scrape result (`up`) | Alert rule evaluation | +| :------------------- | :---------------------------------------------------- | +| 00:00 `up == 0` | Timer starts | +| 01:00 `up == 0` | Timer continues | +| 02:00 `up == 0` | Timer continues | +| 03:00 `up == 1` | Successful scrape resets timer | +| 04:00 `up == 0` | Timer starts again | +| 05:00 `up == 0` | No alert yet; timer hasn’t reached the `for` duration | + +The longer the period, the more likely this is to happen. + +A single recovery resets the alert, that’s why `up == 0 for: 5m` can sometimes be unreliable. Even if the target is down most of the time, the alert didn't fire, leaving you unaware of a potential persistent issue. + +### Use `avg_over_time` to smooth signal + +One way to work around these issues is to smooth the signal by averaging the `up` metric over a similar or longer period: + +`avg_over_time(up[10m]) < 0.8` + +This alert rule fires when the target is unreachable for more than 20% of the last 10 minutes, rather than looking for consecutive scrape failures. With a one minute scrape interval, three or more failed scrapes within the last 10 minutes now triggers the alert. + +Since this query uses a threshold and time window to control accuracy, you can now lower the `for` duration (or [pending period](ref:pending-period) in Grafana) to something shorter—`0m` or `1m`—so the alert fires faster. + +This approach gives you more flexibility in detecting real crashes or network issues. As always, adjust the threshold and period based on your noise tolerance and how critical the target is. + +### Use synthetic checks to monitor external availability + +Prometheus often runs inside the same network as the target it monitors. That means Prometheus might be able to reach the target, but doesn’t ensure it’s reachable to users on the outside. + +Firewalls, DNS misconfigurations, or other network issues might block public traffic while Prometheus scrapes `up` successfully. + +This is where synthetic monitoring helps. Tools like the [Blackbox Exporter](https://github.com/prometheus/blackbox_exporter) let you continuously verify whether a service is available and reachable from outside your network—not just internally. + +The Blackbox Exporter exposes the results of these checks as metrics, which Prometheus can scrape like any other target. For example, the `probe_success` metric reports whether the probe was able to reach the service. The setup looks like this: + +**Alert rules → Prometheus ← Blackbox Exporter (external probe) → Target** + +To detect when a service isn’t reachable externally, you can define an alert using the `probe_success` metric: + +`probe_success == 0 for: 5m` + +This alert fires when the probe has failed continuously for 5 minutes—indicating that the service couldn’t be reached from the outside. + +You can then combine internal and external checks to make the detection of connectivity errors more reliable. This alert catches when the internal scrape fails or the service is externally unreachable. + +`up == 0 or probe_success == 0` + +As with the `up` metric, you might want to smooth this out using `avg_over_time()` for more robust detection. The smooth version might look like: + +`avg_over_time(up[10m]) < 0.8 or avg_over_time(probe_success[10m]) < 0.8` + +This alert fires when Prometheus couldn't scrape the target successfully for more than 20% of the past 10 minutes, or when the external probes have been failing more than 20% of the time. This smoothing technique can be applied to any binary availability signal. + +## Manage offline hosts + +In many setups, Prometheus scrapes multiple hosts under the same target, such as a fleet of servers or containers behind a common job label. It’s common for one host to go offline while the others continue to report metrics normally. + +If your alert only checks the general `up` metric without breaking it down by labels (like `instance`, `host`, or `pod`), you might miss when a host stops reporting. For example, an alert that looks only at the aggregated status of all instances will likely fail to catch when individual instances go missing. + +This isn't a connectivity error in this context — it’s not that the alert or Prometheus can't reach anything, it’s that one or more specific targets have gone silent. These kinds of problems aren’t caught by `up == 0` alerts. + +For these cases, see the complementary [guide on handling missing data](ref:missing-data-guide) — it covers common scenarios where the alert queries return no data at all, or where only some targets stop reporting. These aren't full availability failures or execution errors, but they can still lead to blind spots in alert detection. + +## Handle query errors in Grafana Alerting + +Not all connectivity issues come from targets going offline. Sometimes, the alert rule fails when querying its target. These aren’t availability problems—they’re query execution errors: maybe the data source timed out, the network dropped, or the query was invalid. + +These errors lead to broken alerts. But they come from a different part of the stack: between the alert rule and the data source, not between the data source (for example, Prometheus) and its target. + +This difference matters. Availability issues are typically handled using metrics like `up` or `probe_success` but execution errors require a different setup. + +Grafana Alerting has built-in handling for execution errors, regardless of the data source. That includes Prometheus, and others like Graphite, InfluxDB, PostgreSQL, etc. By default, Grafana Alerting automatically handles query errors so you don’t miss critical failures. When an alert rule fails to execute, Grafana fires a special `DatasourceError` alert. + +You can configure this behavior depending on how critical the alert is and on whether you already have other alerts detecting the issue. In [**Configure no data and error handling**](ref:configure-nodata-and-error-handling), click **Alert state if execution error or timeout**, and choose the desired option for the alert: + +- **Error (default)**: Triggers a separate `DatasourceError` alert. This default ensures alert rules always inform about query errors but can create noise. +- **Alerting**: Treats the error as if the alert condition is firing. Grafana transitions all existing instances for that rule to the `Alerting` state. +- **Normal**: Ignores the query error and transitions all alert instances to the `Normal` state. This is useful if the error isn’t critical or if you already have other alerts detecting connectivity issues. +- **Keep Last State**: Keeps the previous state until the query succeeds again. Suitable for unstable environments to avoid flapping alerts. + + {{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure error handling` option in Grafana Alerting." max-width="500px" >}} + +This applies even when alert rules query Prometheus itself—not just external data sources. + +### Design alerts for connectivity errors + +In practice, start by deciding if you want to create explicit alert rules — for example, using `up` or `probe_success` — to detect when a target is down or has connectivity issues. + +Then, for each alert rule, choose the error-handling behavior based on whether you already have dedicated connectivity alerts, the stability of the target, and how critical the alert is. Prioritize alerts based on symptom severity rather than just infrastructure signals that might not impact users. + +### Reduce redundant error notifications + +A single data source error can lead to multiple alerts firing simultaneously, sometimes bombarding you with many alerts and generating too much noise. + +As described previously, you can control the error-handling behavior for Grafana alerts. The **Keep Last State** or **Normal** option prevents alerts from firing and helps avoid redundant alerts, especially for services already covered by `up` or `probe_success` alerts. + +When using the default behavior, a single connectivity error will likely trigger multiple `DatasourceError` alerts. + +These alerts are separate from the original alerts—they’re not just a different state of the original alert. They fire immediately, ignore the pending period, and don’t inherit all the labels. This can catch you off guard if you expect them to behave like the original alerts. + +Consider not treating these alerts in the same way as the original alerts, and implement dedicated strategies for their notifications: + +- Reduce duplicate notifications by grouping `DatasourceError` alerts. Use the `datasource_uid` label to group errors from the same data source. + +- Route `DatasourceError` alerts separately, sending them to different teams or channels depending on their impact and urgency. + +For details on how to configure grouping and routing, refer to [handling notifications](ref:notifications) and [`No Data` and `Error` alerts](ref:no-data-and-error-alerts) documentation. + +## Conclusion + +Connectivity issues are one of the common causes of noisy or misleading alerts. This guide covered two distinct types: + +- **Availability issues**, where the target itself is down or unreachable (e.g., due to a crash or network failure). + +- **Query execution errors**, where the alert rule can't reach its data source (e.g., due to timeouts, invalid queries, or data source outages). + +These problems come from different parts of your stack, and require its own techniques. Prometheus and Grafana allow you to detect them, and combining distinct techniques can make your alerts more resilient. + +With Prometheus, avoid relying solely on `up == 0`. Smooth queries to account for intermittent failures, and use synthetic monitoring to detect reachability issues from outside your network. + +In Grafana Alerting, configure error handling explicitly. Not all alerts are equal or have the same urgency. Tune the error-handling behavior based on the reliability and severity of the alerts and whether you already have alerts dedicated to connectivity problems. + +And don’t forget the third case: **missing data**. If only one host from a fleet silently disappears, you might not get alerted. If you're dealing with individual instances that stopped reporting data, see the [Guide on handling missing data](ref:missing-data-guide) to continue exploring this topic. diff --git a/docs/sources/alerting/best-practices/dynamic-labels.md b/docs/sources/alerting/best-practices/dynamic-labels.md new file mode 100644 index 00000000000..4c4078f7e2e --- /dev/null +++ b/docs/sources/alerting/best-practices/dynamic-labels.md @@ -0,0 +1,328 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/dynamic-labels +description: This example shows how to define dynamic labels based on query values, along with important behavior to keep in mind when using them. +keywords: + - grafana + - alerting + - examples +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Examples of dynamic labels +title: Example of dynamic labels in alert instances +weight: 1104 +refs: + missing-data-guide: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/missing-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/missing-data/ + alert-rule-evaluation: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/ + pending-period: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/notification-policies/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/notification-policies/ + view-alert-state-history: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/monitor-status/view-alert-state-history/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/monitor-status/view-alert-state-history/ + stale-alert-instances: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ + notification-policies: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/notification-policies/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/notification-policies/ + templating-labels-annotations: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/templates/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/ + labels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#labels + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/alert-rules/annotation-label/#labels + testdata-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/testdata/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/testdata/ + multi-dimensional-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/multi-dimensional-alerts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/multi-dimensional-alerts/ +--- + +# Example of dynamic labels in alert instances + +Labels are essential for scaling your alerting setup. They define metadata like `severity`, `team`, `category`, or `environment`, which you can use for alert routing. + +A label like `severity="critical"` can be set statically in the alert rule configuration, or dynamically based on a query value such as the current free disk space. Dynamic labels **adjust label values at runtime**, allowing you to reuse the same alert rule across different scenarios. + +This example shows how to define dynamic labels based on query values, along with key behavior to keep in mind when using them. + +First, it's important to understand how Grafana Alerting treats [labels](ref:labels). + +## Alert instances are defined by labels + +Each alert rule creates a separate alert instance for every unique combination of labels. + +This is called [multi-dimensional alerts](ref:multi-dimensional-example): one rule, many instances—**one per unique label set**. + +For example, a rule that queries CPU usage per host might return multiple series (or dimensions): + +- `{alertname="ServerHighCPU", instance="prod-server-1" }` +- `{alertname="ServerHighCPU", instance="prod-server-2" }` +- `{alertname="ServerHighCPU", instance="prod-server-3" }` + +Each unique label combination defines a distinct alert instance, with its own evaluation state and potential notifications. + +The full label set of an alert instance can include: + +- Labels from the query result (e.g., `instance`) +- Auto-generated labels (e.g., `alertname`) +- User-defined labels from the rule configuration + +## User-defined labels + +As shown earlier, alert instances automatically include labels from the query result, such as `instance` or `job`. To add more context or control alert routing, you can define _user-defined labels_ in the alert rule configuration: + +{{< figure src="/media/docs/alerting/example-dynamic-labels-edit-labels-v3.png" max-width="750px" alt="Edit labels UI in the alert rule configuration." >}} + +User-defined labels can be either: + +- **Fixed labels**: These have the same value for every alert instance. They are often used to include common metadata, such as team ownership. + +- **Templated labels**: These calculate their values based on the query result at evaluation time. + +## Templated labels + +Templated labels evaluate their values dynamically, based on the query result. This allows the label value to vary per alert instance. + +Use templated labels to inject additional context into alerts. To learn about syntax and use cases, refer to [Template annotations and labels](ref:templating-labels-annotations). + +You can define templated labels that produce either: + +- A fixed value per alert instance. +- A dynamic value per alert instance that changes based on the last query result. + +### Fixed values per alert instance + +You can use a known label value to enrich the alert with additional metadata not present in existing labels. For example, you can map the `instance` label to an `env` label that represents the deployment environment: + +```go +{{- if eq $labels.instance "prod-server-1" -}}production +{{- else if eq $labels.instance "stag-server-1" -}}staging +{{- else -}}development +{{- end -}} +``` + +This produces alert instances like: + +- `{alertname="ServerHighCPU", instance="prod-server-1", env="production"}` +- `{alertname="ServerHighCPU", instance="stag-server-1", env="staging"}` + +In this example, the `env` label is fixed for each alert instance and does not change during its lifecycle. + +### Dynamic values per alert instance + +You can define a label whose value depends on the numeric result of a query—mapping it to a predefined set of options. This is useful for representing `severity` levels within a single alert rule. + +Instead of defining three separate rules like: + +- _CPU ≥ 90_ → `severity=critical` +- _CPU ≥ 80_ → `severity=warning` +- _CPU ≥ 70_ → `severity=minor` + +You can define a single rule and assign `severity` dynamically using a template: + +```go +{{/* $values.B.Value refers to the numeric result from query B */}} +{{- if gt $values.B.Value 90.0 -}}critical +{{- else if gt $values.B.Value 80.0 -}}warning +{{- else if gt $values.B.Value 70.0 -}}minor +{{- else -}}none +{{- end -}} +``` + +This pattern lets you express multiple alerting scenarios in a single rule, while still routing based on the `severity` label value. + +## Example overview + +In the previous severity template, you can set the alert condition to `$B > 70` to prevent firing when `severity=none`, and then use the `severity` label to route distinct alert instances to different contact points. + +For example, configure a [notification policy](ref:notification-policies) that matches `alertname="ServerHighCPU"` with the following children policies: + +- `severity=critical` → escalate to an incident response and management solution (IRM). +- `severity=warning` → send to the team's Slack channel. +- `severity=minor` → send to a non-urgent queue or log-only dashboard. + +The resulting alerting flow might look like this: + +| Time | $B query | Alert instance | Routed to | +| :--- | :------- | :------------------------------------------------- | :------------------- | +| t1 | 65 | `{alertname="ServerHighCPU", severity="none"}` | `Not firing` | +| t2 | 75 | `{alertname="ServerHighCPU", severity="minor"}` | Non-urgent queue | +| t3 | 85 | `{alertname="ServerHighCPU", severity="warning"}` | Team Slack channel | +| t4 | 95 | `{alertname="ServerHighCPU", severity="critical"}` | IRM escalation chain | + +This alerting setup allows you to: + +- Use a single rule for multiple severity levels. +- Route alerts dynamically using the label value. +- Simplify alert rule maintenance and avoid duplication. + +However, dynamic labels can introduce unexpected behavior when label values change. The next section explains this. + +## Caveat: a label change affects a distinct alert instance + +Remember: **alert instances are defined by their labels**. + +If a dynamic label changes between evaluations, this new value affects a separate alert instance. + +Here's what happens if `severity` changes from `minor` to `warning`: + +1. The instance with `severity="minor"` disappears → it becomes a missing series. +1. A new instance with `severity="warning"` appears → it starts from scratch. +1. After two evaluations without data, the `minor` instance is **resolved and evicted**. + +Here’s a sequence example: + +| Time | Query value | Instance `severity="none"` | Instance `severity="minor"` | Instance `severity="warning"` | +| :--- | :---------- | :------------------------- | :-------------------------- | :---------------------------- | +| t0 | | | | | +| t1 | 75 | | 🔴 📩 | | +| t2 | 85 | | ⚠️ MissingSeries | 🔴 📩 | +| t3 | 85 | | ⚠️ MissingSeries | 🔴 | +| t4 | 50 | 🟢 | 📩 Resolved and evicted | ⚠️ MissingSeries | +| t5 | 50 | 🟢 | | ⚠️ MissingSeries | +| t6 | 50 | 🟢 | | 📩 Resolved and evicted | + +Learn more about this behavior in [Stale alert instances](ref:stale-alert-instances). + +In this example, the `minor` and `warning` alerts likely represent the same underlying issue, but Grafana treats them as distinct alert instances. As a result, this scenario generates two firing notifications and two resolved notifications, one for each instance. + +This behavior is important to keep in mind when dynamic label values change frequently. + +It can lead to multiple notifications firing and resolving in short intervals, resulting in **noisy and confusing notifications**. + +## Try it with TestData + +You can replicate this scenario using the [TestData data source](ref:testdata-data-source) to simulate an unstable signal—like monitoring a noisy sensor. + +This setup reproduces label flapping and shows how dynamic label values affect alert instance behavior. + +1. Add the **TestData** data source through the **Connections** menu. +1. Create an alert rule. + + Navigate to **Alerting** → **Alert rules** and click **New alert rule**. + +1. Simulate a query (`$A`) that returns a noisy signal. + + Select **TestData** as the data source and configure the scenario. + + - Scenario: Random Walk + - Series count: 1 + - Start value: 51 + - Min: 50, Max: 100 + - Spread: 100 (ensures large changes between consecutive data points) + +1. Add an expression. + + - Type: Reduce + - Input: A + - Function: Last (to get the most recent value) + - Name: B + +1. Define the alert condition. + + Use a threshold like `$B >= 50` (it always fires). + +1. Click **Edit Labels** to add a dynamic label. + + Create a new label `severity` and set its value to the following: + + ```go + {{/* $values.B.Value refers to the numeric result from query B */}} + {{- if gt $values.B.Value 90.0 -}}P1 + {{- else if gt $values.B.Value 80.0 -}}P2 + {{- else if gt $values.B.Value 70.0 -}}P3 + {{- else if gt $values.B.Value 60.0 -}}P4 + {{- else if gt $values.B.Value 50.0 -}}P5 + {{- else -}}none + {{- end -}} + ``` + +1. Set evaluation behavior. + + Set a short evaluation interval (e.g., `10s`) to observe quickly label flapping and alert instance transitions in the history. + +1. Preview alert routing to verify the label template. + + In **Configure notifications**, toggle **Advanced options**. + Click **Preview routing** and check the value of the `severity` label: + + {{< figure src="/media/docs/alerting/example-dynamic-labels-preview-label.png" max-width="750px" caption="Preview routing multiple times to verify how label values change over time." >}} + +1. Observe alert state changes. + + Click **Save rule and exit**, and open the [alert history view](ref:view-alert-state-history) to see how changes in `severity` affect the state of distinct alert instances. + + {{< figure src="/media/docs/alerting/example-dynamic-labels-alert-history-page.png" max-width="750px" caption="You can find multiple transitions over time as the label value fluctuates." >}} + + {{< docs/play title="this alert example" url="https://play.grafana.org/alerting/grafana/femr0gkp9vsowe/view" >}} + +## Considerations + +Dynamic labels lets you reuse a single alert rule across multiple escalation scenarios—but it also introduces complexity. When the label value depends on a noisy metric and changes frequently, it can lead to flapping alert instances and excessive notifications. + +These alerts often require tuning to stay reliable and benefit from continuous review. To get the most out of this pattern, consider the following: + +- **Tune evaluation settings and queries for stability** + + Increase the [evaluation interval and pending period](ref:alert-rule-evaluation) to reduce the frequency of state changes. Additionally, consider smoothing metrics with functions like `avg_over_time` to reduce flapping. + +- **Use wider threshold bands** + + Define broader ranges in your label template logic to prevent label switching caused by small value changes. + +- **Disable resolved notifications** + + When labels change frequently and alerts resolve quickly, you can reduce the number of notifications by disabling resolved notifications at the contact point. + +- **Disable the Missing series evaluations setting** + + The [Missing series evaluations setting](ref:stale-alert-instances) (default: 2) defines how many intervals without data are allowed before resolving an instance. Consider disabling it if it's unnecessary for your use case, as it can complicate alert troubleshooting. + +- **Preserve context across related alerts** + + Ensure alert metadata includes enough information to help correlate related alerts during investigation. + +- **Use separate alert rules and static labels when simpler** + + In some cases, defining separate rules with static labels may be easier to manage than one complex dynamic rule. This also allows you to customize alert queries for each specific case. + +## Learn more + +Here's a list of additional resources related to this example: + +- [Multi-dimensional alerting example](ref:multi-dimensional-example) – Explore how Grafana creates separate alert instances for each unique set of labels. +- [Labels](ref:labels) – Learn about the different types of labels and how they define alert instances. +- [Template labels in alert rules](ref:templating-labels-annotations) – Use templating to set label values dynamically based on query results. +- [Stale alert instances](ref:stale-alert-instances) – Understand how Grafana resolves and removes stale alert instances. +- [Handle missing data](ref:missing-data-guide) – Learn how Grafana distinguishes between missing series and `NoData`. +- [Notification policies and routing](ref:notification-policies) – Create multiple notification policies to route alerts based on label values like `severity` or `team`. +- [Dynamic label example in Grafana Play](https://play.grafana.org/alerting/grafana/femr0gkp9vsowe/view) - View this example in Grafana Play to explore alert instances and state transitions with dynamic labels. diff --git a/docs/sources/alerting/best-practices/dynamic-thresholds.md b/docs/sources/alerting/best-practices/dynamic-thresholds.md new file mode 100644 index 00000000000..c70eb8394d2 --- /dev/null +++ b/docs/sources/alerting/best-practices/dynamic-thresholds.md @@ -0,0 +1,229 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/dynamic-thresholds +description: This example shows how to use a distinct threshold value per dimension using multi-dimensional alerts and a Math expression. +keywords: + - grafana + - alerting + - examples +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Examples of dynamic thresholds +title: Example of dynamic thresholds per dimension +weight: 1103 +refs: + testdata-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/testdata/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/testdata/ + math-expression: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#math + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#math + table-data-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/table-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/table-data/ + multi-dimensional-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/multi-dimensional-alerts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/multi-dimensional-alerts/ + recording-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-recording-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/ +--- + +# Example of dynamic thresholds per dimension + +In Grafana Alerting, each alert rule supports only one condition expression. + +That's enough in many cases—most alerts use a fixed numeric threshold like `latency > 3s` or `error_rate > 5%` to determine their state. + +As your alerting setup grows, you may find that different targets require different threshold values. + +Instead of duplicating alert rules, you can assign a **different threshold value to each target**—while keeping the same condition. This simplifies alert maintenance. + +This example shows how to do that using [multi-dimensional alerts](ref:multi-dimensional-example) and a [Math expression](ref:math-expression). + +## Example overview + +You're monitoring latency across multiple API services. Initially, you want to get alerted if the 95th percentile latency (`p95_api_latency`) exceeds 3 seconds, so your alert rule uses a single static threshold: + +``` +p95_api_latency > 3 +``` + +But the team quickly finds that some services require stricter thresholds. For example, latency for payment APIs should stay under 1.5s, while background jobs can tolerate up to 5s. The team establishes different thresholds per service: + +- `p95_api_latency{service="checkout-api"}`: must stay under `1.5s`. +- `p95_api_latency{service="auth-api"}`: also strict, `1.5s`. +- `p95_api_latency{service="catalog-api"}`: less critical, `3s`. +- `p95_api_latency{service="async-tasks"}`: background jobs can tolerate up to `5s`. + +You want to avoid creating one alert rule per service—this is harder to maintain. + +In Grafana Alerting, you can define one alert rule that monitors multiple similar components like this scenario. This is called [multi-dimensional alerts](ref:multi-dimensional-example): one alert rule, many alert instances—**one per unique label set**. + +But there's an issue: Grafana supports only **one alert condition per rule**. + +``` +One alert rule +├─ One condition ( e.g., $A > 3) +│ └─ Applies to all returned series in $A +│ ├─ {service="checkout-api"} +│ ├─ {service="auth-api"} +│ ├─ {service="catalog-api"} +│ └─ {service="async-tasks"} +``` + +To evaluate per-service thresholds, you need a distinct threshold value for each returned series. + +## Dynamic thresholds using a Math expression + +You can create a dynamic alert condition by operating on two queries with a [Math expression](ref:math-expression). + +- `$A` for query results (e.g., `p95_api_latency`). +- `$B` for per-service thresholds (from CSV data or another query). +- `$A > $B` is the _Math_ expression that defines the alert condition. + +Grafana evaluates the _Math_ expression **per series**, by joining series from `$A` and `$B` based on their shared labels before applying the expression. + +Here’s an example of an arithmetic operation: + +{{< docs/shared lookup="alerts/math-example.md" source="grafana" version="" >}} + +In practice, you must align your threshold input with the label sets returned by your alert query. + +The following table illustrates how a per-service threshold is evaluated in the previous example: + +| $A: p95 latency query | $B: threshold value | $C: $A\>$B | State | +| :--------------------------- | :----------------------------- | :--------------------------- | :--------- | +| `{service="checkout-api"} 3` | `{service="checkout-api"} 1.5` | `{service="checkout-api"} 1` | **Firing** | +| `{service="auth-api"} 1` | `{service="auth-api"} 1.5` | `{service="auth-api"} 0` | **Normal** | +| `{service="catalog-api"} 2` | `{service="catalog-api"} 3` | `{service="catalog-api"} 0` | **Normal** | +| `{service="sync-work"} 3` | `{service="sync-work"} 5` | `{service="sync-work"} 0` | **Normal** | + +In this example: + +- `$A` comes from the `p95_api_latency` query. +- `$B` is manually defined with a threshold value for each series in `$A`. +- The alert condition compares `$A>$B` using a _Math_ relational operator (e.g., `>`, `<`, `>=`, `<=`, `==`, `!=`) that joins series by matching labels. +- Grafana evaluates the alert condition and sets the firing state where the condition is true. + +The _Math_ expression works as long as each series in `$A` can be matched with exactly one series in `$B`. They must align in a way that produces a one-to-one match between series in `$A` and `$B`. + +{{% admonition type="caution" %}} + +If a series in one query doesn’t match any series in the other, it’s excluded from the result and a warning message is displayed: + +_1 items **dropped from union(s)**: ["$A > $B": ($B: {service=payment-api})]_ + +{{% /admonition %}} + +**Labels in both series don’t need to be identical**. If labels are a subset of the other, they can join. For example: + +- `$A` returns series `{host="web01", job="event"}` 30 and `{host="web02", job="event"}` 20. +- `$B` returns series `{host="web01"}` 10 and `{host="web02"}` 0. +- `$A` + `$B` returns `{host="web01", job="event"}` 40 and `{host="web02", job="event"}` 20. + +## Try it with TestData + +You can use the [TestData data source](ref:testdata-data-source) to replicate this example: + +1. Add the **TestData** data source through the **Connections** menu. +1. Create an alert rule. + + Navigate to **Alerting** → **Alert rules** and click **New alert rule**. + +1. Simulate a query (`$A`) that returns latencies for each service. + + Select **TestData** as the data source and configure the scenario. + + - Scenario: Random Walk + - Alias: latency + - Labels: service=api-$seriesIndex + - Series count: 4 + - Start value: 1 + - Min: 1, Max: 4 + + This uses `$seriesIndex` to assign unique service labels: `api-0`, `api-1`, etc. + + {{< figure src="/media/docs/alerting/example-dynamic-thresholds-latency-series-v2.png" max-width="750px" alt="TestData data source returns 4 series to simulate latencies for distinct API services." >}} + +1. Define per-service thresholds with static data. + + Add a new query (`$B`) and select **TestData** as the data source. + + From **Scenario**, select **CSV Content** and paste this CSV: + + ``` + service,value + api-0,1.5 + api-1,1.5 + api-2,3 + api-3,5 + ``` + + The `service` column must match the labels from `$A`. + + The `value` column is a numeric value used for the alert comparison. + + For details on CSV format requirements, see [table data examples](ref:table-data-example). + +1. Add a new **Reduce** expression (`$C`). + + - Type: Reduce + - Input: A + - Function: Mean + - Name: C + + This calculates the average latency for each service: `api-0`, `api-1`, etc. + +1. Add a new **Math** expression. + + - Type: Math + - Expression: `$C > $B` + - Set this expression as the **alert condition**. + + This fires if the average latency (`$C`) exceeds the threshold from `$B` for any service. + +1. **Preview** the alert. + + {{< figure src="/media/docs/alerting/example-dynamic-thresholds-preview-v3.png" max-width="750px" caption="Alert preview evaluating multiple series with distinct threshold values" >}} + + {{< docs/play title="this alert example" url="https://play.grafana.org/alerting/grafana/demqzyodxrd34e/view" >}} + +## Other use cases + +This example showed how to build a single alert rule with different thresholds per series using [multi-dimensional alerts](ref:multi-dimensional-example) and [Math expressions](ref:math-expression). + +This approach scales well when monitoring similar components with distinct reliability goals. + +By aligning series from two queries, you can apply a dynamic threshold—one value per label set—without duplicating rules. + +While this example uses static CSV content to define thresholds, the same technique works in other scenarios: + +- **Dynamic thresholds from queries or recording rules**: Fetch threshold values from a real-time query, or from [custom recording rules](ref:recording-rules). +- **Combine multiple conditions**: Build more advanced threshold logic by combining multiple conditions—such as latency, error rate, or traffic volume. + +For example, you can define a PromQL expression that sets a latency threshold which adjusts based on traffic—allowing higher response times during periods of high-load. + +``` +( + // Fires when p95 latency > 2s during usual traffic (≤ 1000 req/s) + service:latency:p95 > 2 and service:request_rate:rate1m <= 1000 +) +or +( + // Fires when p95 latency > 4s during high traffic (> 1000 req/s) + service:latency:p95 > 4 and service:request_rate:rate1m > 1000 +) +``` diff --git a/docs/sources/alerting/best-practices/missing-data.md b/docs/sources/alerting/best-practices/missing-data.md new file mode 100644 index 00000000000..5ee06cfaa4f --- /dev/null +++ b/docs/sources/alerting/best-practices/missing-data.md @@ -0,0 +1,206 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/missing-data/ +description: Learn how to detect missing metrics and design alerts that handle gaps in data in Prometheus and Grafana Alerting. +keywords: + - grafana + - alerting + - guide + - rules + - create +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Handle missing data +title: Handle missing data in Grafana Alerting +weight: 1020 +refs: + connectivity-errors-guide: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/connectivity-errors/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/connectivity-errors/ + connectivity-errors-reduce-alert-fatigue: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/connectivity-errors/#reducing-notification-fatigue-from-datasourceerror-alerts + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/connectivity-errors/ + alert-history: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/monitor-status/view-alert-state-history/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/monitor-status/view-alert-state-history/ + configure-nodata-and-error-handling: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state + stale-alert-instances: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#stale-alert-instances-missingseries + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#stale-alert-instances-missingseries + no-data-and-error-alerts: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts +--- + +# Handle missing data in Grafana Alerting + +Missing data from when a target stops reporting metric data can be one of the most common issues when troubleshooting alerts. In cloud-native environments, this happens all the time. Pods or nodes scale down to match demand, or an entire job quietly disappears. + +When this happens, alerts won’t fire, and you might not notice the system has stopped reporting. + +Sometimes it's just a lack of data from a few instances. Other times, it's a connectivity issue where the entire target is unreachable. + +This guide covers different scenarios where the underlying data is missing and shows how to design your alerts to act on those cases. If you're troubleshooting an unreachable host or a network failure, see the [Handle connectivity errors documentation](ref:connectivity-errors-guide) as well. + +## No Data vs. Missing Series + +There are a few common causes when an instance stops reporting data, similar to [connectivity errors](ref:connectivity-errors-guide): + +- Host crash: The system is down, and Prometheus stops scraping the target. +- Temporary network failures: Intermittent scrape failures cause data gaps. +- Deployment changes: Decommissioning, Kubernetes pod eviction, or scaling down resources. +- Ephemeral workloads: Metrics intentionally stop reporting. +- And more. + +The first thing to understand is the difference between a query failure (or connectivity error), _No Data_, and a _Missing Series_. + +Alert queries often return multiple time series — one per instance, pod, region, or label combination. This is known as a **multi-dimensional alert**, meaning a single alert rule can trigger multiple alert instances (alerts). + +For example, imagine a recorded metric, `http_request_latency_seconds`, that reports latency per second in the regions where the application is deployed. The query returns one series per region — for instance, `region1` and `region2` — and generates only two alert instances. In this scenario, you may experience: + +- **Connectivity Error** if the alert rule query fails. +- **No Data** if the query runs successfully but returns no data at all. +- **Missing Series** if one or more specific series, which previously returned data, are missing, but other series still return data. + +In both _No Data_ and _Missing Series_ cases, the query still technically "works", but the alert won’t fire unless you explicitly configure it to handle these situations. + +The following tables illustrate both scenarios using the previous example, with an alert that triggers if the latency exceeds 2 seconds in any region: `avg_over_time(http_request_latency_seconds[5m]) > 2`. + +**No Data Scenario:** The query returns no data for any series: + +| Time | region1 | region2 | Alert triggered | +| :---- | :--------- | :--------- | :--------------------------- | +| 00:00 | 1.5s 🟢 | 1s 🟢 | ✅ No Alert | +| 01:00 | No Data ⚠️ | No Data ⚠️ | ⚠️ No Alert (Silent Failure) | +| 02:00 | 1.4s 🟢 | 1s 🟢 | ✅ No Alert | + +**MissingSeries Scenario:** Only a specific series (`region2`) disappears: + +| Time | region1 | region2 | Alert triggered | +| :---- | :------ | :---------------- | :--------------------------- | +| 00:00 | 1.5s 🟢 | 1s 🟢 | ✅ No Alert | +| 01:00 | 1.6s 🟢 | Missing Series ⚠️ | ⚠️ No Alert (Silent Failure) | +| 02:00 | 1.4s 🟢 | 1s 🟢 | ✅ No Alert | + +In both cases, something broke silently. + +## Detect missing data in Prometheus + +Prometheus doesn't fire alerts when the query returns no data. It simply assumes there was nothing to report, like with query errors. Missing data won’t trigger existing alerts unless you explicitly check for it. + +In Prometheus, a common way to catch missing data is by to use the `absent_over_time` function. + +`absent_over_time(http_request_latency_seconds[5m]) == 1` + +This triggers when all series for `http_request_latency_seconds` are absent for 5 minutes — catching the _No Data_ case when the entire metric disappears. + +However, `absent_over_time()` can’t detect which specific series are missing since it doesn’t preserve labels. The alert won’t tell you which series stopped reporting, only that the query returns no data. + +If you want to check for missing data per-region or label, you can specify the label in the alert query as follows: + +`absent_over_time(http_request_latency_seconds{region="region1"}[5m]) == 1` +`or` +`absent_over_time(http_request_latency_seconds{region="region2"}[5m]) == 1` + +But this doesn't scale well. It is unreliable to have hard-coded queries for each label set, especially in dynamic cloud environments where instances can appear or disappear at any time. + +## Manage No Data issues in Grafana alerts + +While Prometheus provides functions like `absent_over_time()` to detect missing data, not all data sources — like Graphite, InfluxDB, PostgreSQL, and others — available to Grafana alerts support a similar function. + +To handle this, Grafana Alerting implements a built-in `No Data` state logic, so you don’t need to detect missing data with `absent_*` queries. Instead, you can configure in the alert rule settings how alerts behave when no data is returned. + +Similar to error handling, Grafana triggers a special _No data_ alert by default and lets you control this behavior. In [**Configure no data and error handling**](ref:configure-nodata-and-error-handling), click **Alert state if no data or all values are null**, and choose one of the following options: + +- **No Data (default):** Triggers a new `DatasourceNoData` alert, treating _No data_ as a specific problem. +- **Alerting:** Transition each existing alert instance into the `Alerting` state when data disappears. +- **Normal:** Ignores missing data and transitions all instances to the `Normal` state. Useful when receiving intermittent data, such as from experimental services, sporadic actions, or periodic reports. +- **Keep Last State:** Leaves the alert in its previous state until the data returns. This is common in environments where brief metric gaps happen regularly, like with flaky exporters or noisy environments. + + {{< figure src="/media/docs/alerting/alert-rule-configure-no-data.png" alt="A screenshot of the `Configure no data handling` option in Grafana Alerting." max-width="500px" >}} + +### Manage DatasourceNoData notifications + +When Grafana triggers a [NoData alert](ref:no-data-and-error-alerts), it creates a distinct alert instance, separate from the original alert instance. These alerts behave differently: + +- They use a dedicated `alertname: DatasourceNoData`. +- They don’t inherit all the labels from the original alert instances. +- They trigger immediately, ignoring the pending period. + +Because of this, `DatasourceNoData` alerts might require a dedicated setup to handle their notifications. For general recommendations, see [Reduce redundant DatasourceError alerts](ref:connectivity-errors-reduce-alert-fatigue) — similar practices can apply to _NoData_ alerts. + +## Evict alert instances for missing series + +_MissingSeries_ occurs when only some series disappear but not all. This case is subtle, but important. + +Grafana marks missing series as [**stale**](ref:stale-alert-instances) after two evaluation intervals and triggers the alert instance eviction process. Here’s what happens under the hood: + +- Alert instances with missing data keep their last state for two evaluation intervals. +- If the data is still missing after that: + - Grafana adds the annotation `grafana_state_reason: MissingSeries`. + - The alert instance transitions to the `Normal` state. + - A **resolved notification** is sent if the alert was previously firing. + - The **alert instance is removed** from the Grafana UI. + +If an alert instance becomes stale, you’ll find it in the [alert history](ref:alert-history) as `Normal (Missing Series)` before it disappears. This table shows the eviction process from the previous example: + +| Time | region1 | region2 | Alert triggered | +| :---- | :-------------------- | :------------------------------------ | :----------------------------------------------------------------------- | +| 00:00 | 1.5s 🟢 | 1s 🟢 | 🟢🟢 No Alerts | +| 01:00 | 3s 🔴
`Alerting` | 3s 🔴
`Alerting` | 🔴🔴 Alert instances triggered for both regions | +| 02:00 | 1.6s 🟢 | `(MissingSeries)`⚠️
`Alerting` ️ | 🟢🔴 Region2 missing, state maintained. | +| 03:00 | 1.4s 🟢 | `(MissingSeries)`
`Normal` | 🟢🟢 `region2` was resolved, 📩 notification sent, and instance evicted. | +| 04:00 | 1.4s 🟢 | — | 🟢 No Alerts. `region2` was evicted. | + +### + +### Why doesn’t MissingSeries match No Data behavior? + +In dynamic environments, such as autoscaling groups, ephemeral pods, spot instances, series naturally come and go. **MissingSeries** normally signals infrastructure or deployment changes. + +By default, **No Data** triggers an alert to indicate a potential problem. + +The eviction process for **MissingSeries** is designed to prevent alert flapping when a pod or instance disappears, reducing alert noise. + +In environments with frequent scale events, prioritize symptom-based alerts over individual infrastructure signals and use aggregate alerts unless you explicitly need to track individual instances. + +### Handle MissingSeries notifications + +A stale alert instance triggers a **resolved notification** if it transitions from a firing state (such as `Alerting`, `No Data`, or `Error`) to `Normal`. + +You can display the `MissingSeries` annotation in notifications to indicate the alert wasn’t resolved by recovery but evicted due to series data going missing. + +Review these notifications to confirm whether something broke or if the alert was unnecessary. To reduce noise: + +- Silence or mute alerts during planned maintenance or rollouts. +- Adjust alert rules to avoid triggering on series you expect to come and go, and use aggregated alerts instead. + +## Conclusion + +Missing data isn’t always a failure. It’s a common scenario in dynamic environments when certain targets stop reporting. + +Grafana Alerting handles distinct scenarios automatically. Here’s how to think about it: + +- Understand `DatasourceNoData` and `MissingSeries` notifications, since they don’t behave like regular alerts. +- Use Grafana’s _No Data_ handling options to define what happens when a query returns nothing. +- When _NoData_ is not an issue, consider rewriting the query to always return data — for example, in Prometheus, use `your_metric_query OR on() vector(0)` to return `0` when `your_metric_query` returns nothing. +- Use `absent()` or `absent_over_time()` in Prometheus for fine-grained detection when a metric or label disappears entirely. +- Don’t alert on every instance by default. In dynamic environments, it’s better to aggregate and alert on symptoms — unless a missing individual instance directly impacts users. +- If you’re getting too much noise from disappearing data, consider adjusting alerts, using `Keep Last State`, or routing those alerts differently. +- For connectivity issues involving alert query failures, see the sibling guide: [Handling connectivity errors in Grafana Alerting](ref:connectivity-errors-guide). diff --git a/docs/sources/alerting/best-practices/multi-dimensional-alerts.md b/docs/sources/alerting/best-practices/multi-dimensional-alerts.md new file mode 100644 index 00000000000..1d3c1a9129c --- /dev/null +++ b/docs/sources/alerting/best-practices/multi-dimensional-alerts.md @@ -0,0 +1,159 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/multi-dimensional-alerts/ +description: This example shows how a single alert rule can generate multiple alert instances using time series data. +keywords: + - grafana +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Examples of multi-dimensional alerts +title: Example of multi-dimensional alerts on time series data +weight: 1101 +refs: + testdata-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/testdata/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/testdata/ + table-data-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/multi-dimensional-alerts/table-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/multi-dimensional-alerts/table-data/ + annotations: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/annotation-label/#annotations + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/#annotations + reduce-expression: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#reduce + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#reduce + alert-grouping: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/group-alert-notifications/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/ +--- + +# Example of multi-dimensional alerts on time series data + +This example shows how a single alert rule can generate multiple alert instances — one for each label set (or time series). This is called **multi-dimensional alerting**: one alert rule, many alert instances. + +In Prometheus, each unique combination of labels defines a distinct time series. Grafana Alerting uses the same model: each label set is evaluated independently, and a separate alert instance is created for each series. + +This pattern is common in dynamic environments when monitoring a group of components like multiple CPUs, containers, or per-host availability. Instead of defining individual alert rules or aggregated alerts, you alert on _each dimension_ — so you can detect particular issues and include that level of detail in notifications. + +For example, a query returns one series per CPU: + +| `cpu` label value | CPU percent usage | +| :---------------- | :---------------- | +| cpu-0 | 95 | +| cpu-1 | 30 | +| cpu-2 | 85 | + +With a threshold of `> 80`, this would trigger two alert instances for `cpu-0` and one for `cpu-2`. + +## Examples overview + +Imagine you want to trigger alerts when CPU usage goes above 80%, and you want to track each CPU core independently. + +You can use a Prometheus query like this: + +``` +sum by(cpu) ( + rate(node_cpu_seconds_total{mode!="idle"}[1m]) +) +``` + +This query returns the active CPU usage rate per CPU core, averaged over the past minute. + +| CPU core | Active usage rate | +| :------- | :---------------- | +| cpu-0 | 95 | +| cpu-1 | 30 | +| cpu-2 | 85 | + +This produces one series for each existing CPU. + +When Grafana Alerting evaluates the query, it creates an individual alert instance for each returned series. + +| Alert instance | Value | +| :------------- | :---- | +| {cpu="cpu-0"} | 95 | +| {cpu="cpu-1"} | 30 | +| {cpu="cpu-2"} | 85 | + +With a threshold condition like `$A > 80`, Grafana evaluates each instance separately and fires alerts only where the condition is met: + +| Alert instance | Value | State | +| :------------- | :---- | :----- | +| {cpu="cpu-0"} | 95 | Firing | +| {cpu="cpu-1"} | 30 | Normal | +| {cpu="cpu-2"} | 85 | Firing | + +Multi-dimensional alerts help you surface issues on individual components—problems that might be missed when alerting on aggregated data (like total CPU usage). + +Each alert instance targets a specific component, identified by its unique label set. This makes alerts more specific and actionable. For example, you can set a [`summary` annotation](ref:annotations) in your alert rule that identifies the affected CPU: + +``` +High CPU usage on {{$labels.cpu}} +``` + +In the previous example, the two firing alert instances would display summaries indicating the affected CPUs: + +- High CPU usage on `cpu-0` +- High CPU usage on `cpu-2` + +## Try it with TestData + +You can quickly experiment with multi-dimensional alerts using the [**TestData** data source](ref:testdata-data-source), which can generate multiple random time series. + +1. Add the **TestData** data source through the **Connections** menu. +1. Go to **Alerting** and create an alert rule +1. Select **TestData** as the data source. +1. Configure the TestData scenario + + - Scenario: **Random Walk** + - Labels: `cpu=cpu-$seriesIndex` + - Series count: 3 + - Min: 70, Max: 100 + - Spread: 2 + + {{< figure src="/media/docs/alerting/testdata-random-series-v2.png" max-width="750px" alt="Generating random time series data using the TestData data source" >}} + +## Reduce time series data for comparison + +The example returns three time series like shown above with values across the selected time range. + +To alert on each series, you need to reduce the time series to a single value that the alert condition can evaluate and determine the alert instance state. + +Grafana Alerting provides several ways to reduce time series data: + +- **Data source query functions**. The earlier example used the Prometheus `sum` function to sum the rate results by `cpu,`producing a single value per CPU core. +- **Reduce expression**. In the query and condition section, Grafana provides the `Reduce` expression to aggregate time series data. + - In **Default mode**, the **When** input selects a reducer (like `last`, `mean`, or `min`), and the threshold compares that reduced value. + - In **Advanced mode**, you can add the [**Reduce** expression](ref:reduce-expression) (e.g., `last()`, `mean()`) before defining the threshold (alert condition). + +For demo purposes, this example uses the **Advanced mode** with a **Reduce** expression: + +1. Toggle **Advanced mode** in the top right section of the query panel to enable adding additional expressions. +1. Add the **Reduce** expression using a function like `mean()` to reduce each time series to a single value. +1. Define the alert condition using a **Threshold** like `$reducer > 80` +1. Click **Preview** to evaluate the alert rule. + + {{< figure src="/media/docs/alerting/using-expressions-with-multiple-series.png" max-width="750px" caption="The alert condition evaluates the reduced value for each alert instance and shows whether each instance is Firing or Normal." alt="Alert preview using a Reduce expression and a threshold condition" >}} + + {{< docs/play title="this alert example" url="https://play.grafana.org/alerting/grafana/cemqwfn334npce/view" >}} + +## Learn more + +This example shows how Grafana Alerting implements a multi-dimensional alerting model: one rule, many alert instances and why reducing time series data to a single value is required for evaluation. + +For additional learning resources, check out: + +- [Get started with Grafana Alerting – Part 2](https://grafana.com/tutorials/alerting-get-started-pt2/) +- [Example of alerting on tabular data](ref:table-data-example) diff --git a/docs/sources/alerting/best-practices/table-data.md b/docs/sources/alerting/best-practices/table-data.md new file mode 100644 index 00000000000..60a6cb341a9 --- /dev/null +++ b/docs/sources/alerting/best-practices/table-data.md @@ -0,0 +1,143 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/table-data +description: This example shows how to create an alert rule using table data. +keywords: + - grafana +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Examples of table data +title: Example of alerting on tabular data +weight: 1102 +refs: + testdata-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/testdata/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/testdata/ + multi-dimensional-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/multi-dimensional-alerts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/multi-dimensional-alerts/ + infinity-csv: + - pattern: /docs/grafana/ + destination: /docs/plugins/yesoreyeram-infinity-datasource/latest/csv/ +--- + +# Example of alerting on tabular data + +Not all data sources return time series data. SQL databases, CSV files, and some APIs often return results as rows or arrays of columns or fields — commonly referred to as tabular data. + +This example shows how to create an alert rule using data in table format. Grafana treats each row as a separate alert instance, as long as the data meets the expected format. + +## How Grafana Alerting evaluates tabular data + +When a query returns data in table format, Grafana transforms each row into a separate alert instance. + +To evaluate each row (alert instance), it expects: + +1. **Only one numeric column.** This is the value used for evaluating the alert condition. +1. **Non-numeric columns.** These columns defines the label set. The column name becomes a label name; and the cell value becomes the label value. +1. **Unique label sets per row.** Each row must be uniquely identifiable by its labels. This ensures each row represents a distinct alert instance. + +{{< admonition type="caution" >}} +These three conditions must be met—otherwise, Grafana can’t evaluate the table data and the rule will fail. +{{< /admonition >}} + +## Example overview + +Imagine you store disk usage in a `DiskSpace` table and you want to trigger alerts when the available space drops below 5%. + +| Time | Host | Disk | PercentFree | +| ---------- | ---- | ---- | ----------- | +| 2021-06-07 | web1 | /etc | 3 | +| 2021-06-07 | web2 | /var | 4 | +| 2021-06-07 | web3 | /var | 8 | + +To calculate the free space per Host and Disk in this case, you can use `$__timeFilter` to filter by time but without returning the date to Grafana: + +```sql +SELECT + Host, + Disk, + AVG(PercentFree) AS PercentFree +FROM DiskSpace +WHERE $__timeFilter(Time) +GROUP BY Host, Disk +``` + +This query returns the following table response: + +| Host | Disk | PercentFree | +| ---- | ---- | ----------- | +| web1 | /etc | 3 | +| web2 | /var | 4 | +| web3 | /var | 8 | + +When Alerting evaluates the query response, the data is transformed into three alert instances as previously detailed: + +- The numeric column becomes the value for the alert condition. +- Additional columns define the label set for each alert instance. + +| Alert instance | Value | +| ---------------------------- | ----- | +| `{Host="web1", Disk="/etc"}` | 3 | +| `{Host="web2", Disk="/var"}` | 4 | +| `{Host="web3", Disk="/var"}` | 8 | + +Finally, an alert condition that checks for less than 5% of free space (`$A < 5`) would result in two alert instances firing: + +| Alert instance | Value | State | +| ---------------------------- | ----- | ------ | +| `{Host="web1", Disk="/etc"}` | 3 | Firing | +| `{Host="web2", Disk="/var"}` | 4 | Firing | +| `{Host="web3", Disk="/var"}` | 8 | Normal | + +## Try it with TestData + +To test this quickly, you can simulate the table using the [**TestData** data source](ref:testdata-data-source): + +1. Add the **TestData** data source through the **Connections** menu. +1. Go to **Alerting** and create an alert rule +1. Select **TestData** as the data source. +1. From **Scenario**, select **CSV Content** and paste this CSV: + + ```bash + host, disk, percentFree + web1, /etc, 3 + web2, /var, 4 + web3, /var, 8 + ``` + +1. Set a condition like `$A < 5` and **Preview** the alert. + + Grafana evaluates the table data and fires the two first alert instances. + + {{< figure src="/media/docs/alerting/example-table-data-preview.png" max-width="750px" alt="Alert preview with tabular data using the TestData data source" >}} + + {{< docs/play title="this alert example" url="https://play.grafana.org/alerting/grafana/eemqylh1l8tfkf/view" >}} + +## CSV data with Infinity + +Note that when the [Infinity plugin fetches CSV data](ref:infinity-csv), all the columns are parsed and returned as strings. By default, this causes the query expression to fail in Alerting. + +To make it work, you need to format the CSV data as [expected by Grafana Alerting](#how-grafana-alerting-evaluates-tabular-data). + +In the query editor, specify the column names and their types to ensure that only one column is treated as a number. + +{{< figure src="/media/docs/alerting/example-table-data-infinity-csv-data.png" max-width="750px" alt="Using the Infinity data source plugin to fetch CSV data in Alerting" >}} + +## Differences with time series data + +Working with time series is similar—each series is treated as a separate alert instance, based on its label set. + +The key difference is the data format: + +- **Time series data** contains multiple values over time, each with its own timestamp. + To evaluate the alert condition, alert rules **must reduce each series to a single number** using a function like `last()`, `avg()`, or `max()`. +- **Tabular data** doesn’t require reduction, as each row contains only a single numeric value used to evaluate the alert condition. + +For comparison, see the [multi-dimensional time series data example](ref:multi-dimensional-example). diff --git a/docs/sources/alerting/best-practices/tutorials.md b/docs/sources/alerting/best-practices/tutorials.md new file mode 100644 index 00000000000..fbef5f36797 --- /dev/null +++ b/docs/sources/alerting/best-practices/tutorials.md @@ -0,0 +1,33 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/tutorials/ +description: This section provides a set of step-by-step tutorials guides to get started with Grafana Aletings. +keywords: + - grafana + - tutorials +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Tutorials +title: Grafana Alerting tutorials +weight: 1800 +--- + +# Grafana Alerting tutorials + +This section provides step-by-step tutorials to help you learn Grafana Alerting and explore key features through practical, easy-to-follow examples. + +## Get started with Grafana Alerting + +- [Create and receive your first alert](https://grafana.com/tutorials/alerting-get-started/) +- [Create multi-dimensional alerts and route them](https://grafana.com/tutorials/alerting-get-started-pt2/) +- [Group alert notifications](https://grafana.com/tutorials/alerting-get-started-pt3/) +- [Template your alert notifications](https://grafana.com/tutorials/alerting-get-started-pt4/) + +## Additional tutorials + +- [Route alerts using dynamic labels](https://grafana.com/tutorials/alerting-get-started-pt5/) +- [Link alerts to visualizations](https://grafana.com/tutorials/alerting-get-started-pt6/) +- [Create alerts with log data](https://grafana.com/tutorials/create-alerts-with-logs/) +- [Create alerts with InfluxDB and Flux queries](https://grafana.com/tutorials/create-alerts-from-flux-queries/) diff --git a/docs/sources/alerting/configure-notifications/create-silence.md b/docs/sources/alerting/configure-notifications/create-silence.md index 5cf6f4a9c04..ba08f790d4d 100644 --- a/docs/sources/alerting/configure-notifications/create-silence.md +++ b/docs/sources/alerting/configure-notifications/create-silence.md @@ -67,7 +67,18 @@ Silences stop notifications from being created for a specified time window but d Silences are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and only suppress notifications for alerts managed by that Alertmanager. {{< /admonition >}} -{{< docs/shared lookup="alerts/mute-timings-vs-silences.md" source="grafana" version="" >}} +## Mute timings vs silences + +[Mute timings](ref:shared-mute-timings) and [silences](ref:shared-silences) are distinct methods to suppress notifications. They do not prevent alert rules from being evaluated or stop alert instances from appearing in the user interface; they only prevent notifications from being created. + +The following table highlights the key differences between mute timings and silences. + +| | Mute timing | Silence | +| ---------- | ----------------------------------------------------------- | ---------------------------------------------------------------- | +| **Setup** | Created and then added to notification policies | Matches alerts using labels to determine whether to silence them | +| **Period** | Uses time interval definitions that can repeat periodically | Has a fixed start and end time | + +[//]: <> ({{< docs/shared lookup="alerts/mute-timings-vs-silences.md" source="grafana" version="" >}}) ## Add silences @@ -81,9 +92,60 @@ To add a silence, complete the following steps. 1. Optionally, in **Duration**, specify how long the silence is enforced. This automatically updates the end time in the **Silence start and end** field. 1. In the **Label** and **Value** fields, enter one or more _Matching Labels_ to determine which alerts the silence applies to. - {{< docs/shared lookup="alerts/how_label_matching_works.md" source="grafana" version="" >}} + {{< collapse title="How label matching works" >}} - Any matching alerts (in the firing state only) display under **Affected alert instances**. +Use [labels](ref:shared-alert-labels) and label matchers to link alert rules to [notification policies](ref:shared-notification-policies) and [silences](ref:shared-silences). This allows for a flexible way to manage your alert instances, specify which policy should handle them, and which alerts to silence. + +A label matchers consists of 3 distinct parts, the **label**, the **value** and the **operator**. + +- The **Label** field is the name of the label to match. It must exactly match the label name. + +- The **Value** field matches against the corresponding value for the specified **Label** name. How it matches depends on the **Operator** value. + +- The **Operator** field is the operator to match against the label value. The available operators are: + + | Operator | Description | + | -------- | -------------------------------------------------- | + | `=` | Select labels that are exactly equal to the value. | + | `!=` | Select labels that are not equal to the value. | + | `=~` | Select labels that regex-match the value. | + | `!~` | Select labels that do not regex-match the value. | + +{{% admonition type="note" %}} +If you are using multiple label matchers, they are combined using the AND logical operator. This means that all matchers must match in order to link a rule to a policy. +{{% /admonition %}} + +**Label matching example** + +If you define the following set of labels for your alert: + +`{ foo=bar, baz=qux, id=12 }` + +then: + +- A label matcher defined as `foo=bar` matches this alert rule. +- A label matcher defined as `foo!=bar` does _not_ match this alert rule. +- A label matcher defined as `id=~[0-9]+` matches this alert rule. +- A label matcher defined as `baz!~[0-9]+` matches this alert rule. +- Two label matchers defined as `foo=bar` and `id=~[0-9]+` match this alert rule. + +**Exclude labels** + +You can also write label matchers to exclude labels. + +Here is an example that shows how to exclude the label `Team`. You can choose between any of the values below to exclude labels. + +| Label | Operator | Value | +| ------ | -------- | ----- | +| `team` | `=` | `""` | +| `team` | `!~` | `.+` | +| `team` | `=~` | `^$` | + + {{< /collapse >}} + +[//]: <> ({{< docs/shared lookup="alerts/how_label_matching_works.md" source="grafana" version="" >}}) + +Any matching alerts (in the firing state only) display under **Affected alert instances**. 1. In **Comment**, add details about the silence. 1. Click **Submit**. diff --git a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md index feb4d406caa..339a725d641 100644 --- a/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md +++ b/docs/sources/alerting/configure-notifications/manage-contact-points/integrations/configure-opsgenie.md @@ -41,10 +41,12 @@ To create your Opsgenie integration in Grafana Alerting, complete the following 1. Enter a contact point name. 1. From the **Integration** list, select **Opsgenie**. 1. In the **API key** field, paste in your API key. -1. In the **Alert API URL**, enter `https://api.opsgenie.com/v2/alerts`. +1. Configure the **Alert API URL**. + 1. For Grafana Alertmanager, enter `https://api.opsgenie.com/v2/alerts`. + 1. For other Alertmanagers, enter the host for sending Opsgenie API requests, depending on the hosted region. 1. Click **Test** to check that your integration works. - ** For Grafana Alertmanager only.** + **For Grafana Alertmanager only.** A test alert notification is sent to the Alerts page in Opsgenie. diff --git a/docs/sources/alerting/configure-notifications/mute-timings.md b/docs/sources/alerting/configure-notifications/mute-timings.md index 79be30654e0..e0394f4b70b 100644 --- a/docs/sources/alerting/configure-notifications/mute-timings.md +++ b/docs/sources/alerting/configure-notifications/mute-timings.md @@ -47,7 +47,18 @@ Use mute timings to temporarily pause notifications for a specific recurring per Mute timings are assigned to a [specific Alertmanager](ref:alertmanager-architecture) and only suppress notifications for alerts managed by that Alertmanager. {{< /admonition >}} -{{< docs/shared lookup="alerts/mute-timings-vs-silences.md" source="grafana" version="" >}} +## Mute timings vs silences + +[Mute timings](ref:shared-mute-timings) and [silences](ref:shared-silences) are distinct methods to suppress notifications. They do not prevent alert rules from being evaluated or stop alert instances from appearing in the user interface; they only prevent notifications from being created. + +The following table highlights the key differences between mute timings and silences. + +| | Mute timing | Silence | +| ---------- | ----------------------------------------------------------- | ---------------------------------------------------------------- | +| **Setup** | Created and then added to notification policies | Matches alerts using labels to determine whether to silence them | +| **Period** | Uses time interval definitions that can repeat periodically | Has a fixed start and end time | + +[//]: <> ({{< docs/shared lookup="alerts/mute-timings-vs-silences.md" source="grafana" version="" >}}) ## Add mute timings diff --git a/docs/sources/alerting/configure-notifications/template-notifications/examples.md b/docs/sources/alerting/configure-notifications/template-notifications/examples.md index 7e916c5f60a..c94de3628a8 100644 --- a/docs/sources/alerting/configure-notifications/template-notifications/examples.md +++ b/docs/sources/alerting/configure-notifications/template-notifications/examples.md @@ -55,6 +55,11 @@ refs: destination: /docs/grafana//alerting/fundamentals/notifications/group-alert-notifications/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/ + link-alert-rules-to-panels: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/link-alert-rules-to-panels/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/ custom-payload-webhook: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/configure-notifications/manage-contact-points/integrations/webhook-notifier/#custom-payload @@ -347,16 +352,16 @@ Pass the dot (`.`) to execute the template: ```template_output 1 resolved alert(s) -- Dashboard: https://example.com/d/ -- Panel: https://example.com/d/ +- Dashboard: https://example.com/d/uiyahbsdaubsd?from=1740070380000&orgId=1&to=1740074106395 +- Panel: https://example.com/d/uiyahbsdaubsd?from=1740070380000&orgId=1&to=1740074106395&viewPanel=31 - AlertGenerator: ?orgId=1 - Silence: https://example.com/alerting/silence/new - RunbookURL: https://example.com/on-call/db_server_disk_space 1 firing alert(s) -- Dashboard: https://example.com/d/ -- Panel: https://example.com/d/ +- Dashboard: https://example.com/d/uiyahbsdaubsd?from=1740070380000&orgId=1&to=1740074106395 +- Panel: https://example.com/d/uiyahbsdaubsd?from=1740070380000&orgId=1&to=1740074106395&viewPanel=31 - AlertGenerator: ?orgId=1 - Silence: https://example.com/alerting/silence/new - RunbookURL: https://example.com/on-call/web_server_http_errors @@ -408,6 +413,75 @@ Execute the template by passing the dot (`.`): [FIRING:1, RESOLVED:1] api warning (sql_db) ``` +## Print a link to a dashboard with time range + +You can include a link to a dashboard or panel in your alert notifications. This is useful when the alert rule is created from a dashboard panel or monitors a target visualized in an existing dashboard. + +Including a dashboard link in the notification helps responders quickly navigate to the relevant context for investigation. + +Use one of the following methods to include a dashboard link with the correct time range in the alert notification: + +1. You can [link the alert rule to a panel](ref:link-alert-rules-to-panels). This includes the dashboard and panel URLs via `{{.Alert.DashboardURL}}` and `{{.Alert.PanelURL}}`. + + ```go + {{ define "custom.link_to_dashboard" -}} + {{ range .Alerts -}} + Dashboard: {{.DashboardURL}} + Panel: {{ .PanelURL }} + {{ end -}} + {{ end -}} + ``` + + Run the template using: + + ```go + {{ template "custom.link_to_dashboard" . }} + ``` + + ```template_output + Dashboard: https://example.com/d/uiyahbsdaubsd?from=1740070380000&orgId=1&to=1740074106395 + Panel: https://example.com/d/uiyahbsdaubsd?from=1740070380000&orgId=1&to=1740074106395&viewPanel=31 + ``` + + These URLs include a time range based on the alert’s timing: + + - `from`: One hour before the alert started. + - `to`: The current time if the alert is firing, or the alert’s end time if resolved. + +1. Alternatively, you can use a custom annotation to set the dashboard URL and build the full URL using the `from` and `to` query parameters derived from `{{.Alert.StartsAt}}` and `{{.Alert.EndsAt}}`. + + ```go + {{ define "custom.my_dashboard_url_annotation" -}} + {{ range .Alerts -}} + + {{/* StartsAt - 1h */}} + {{- $from := (.StartsAt.Add -3600000000000).UnixMilli }} + + {{- $to := "" }} + {{- if eq .Status "resolved" }} + {{- $to = (.EndsAt).UnixMilli }} + {{- else -}} + {{/* Use current time if alert is firing */}} + {{- $to = (time.Now).UnixMilli }} + {{- end -}} + + Dashboard: {{.Annotations.MyDashboardURL}}?from={{$from}}&to={{$to}} + {{ end }} + {{ end }} + ``` + + To use this template, define a custom annotation named `MyDashboardURL` that contains the base dashboard URL without `from` and `to` parameters. For example: `http://localhost:3000/d/uiyahbsdaubsd`. + + Run the template using: + + ```go + {{ template "custom.my_dashboard_url_annotation" . }} + ``` + + ```template_output + Dashboard: http://localhost:3000/d/uiyahbsdaubsd?from=1740070380000&to=1740071880000 + ``` + ## Custom JSON payload The [custom payload option](ref:custom-payload-webhook) in the webhook contact point allows you to customize the payload of webhook notifications using a custom template. diff --git a/docs/sources/alerting/configure-notifications/template-notifications/reference.md b/docs/sources/alerting/configure-notifications/template-notifications/reference.md index d9d5cc75fa4..02cd0f92770 100644 --- a/docs/sources/alerting/configure-notifications/template-notifications/reference.md +++ b/docs/sources/alerting/configure-notifications/template-notifications/reference.md @@ -110,14 +110,14 @@ You can execute this template by passing the dot (`.`): Grafana-managed alerts include these additional properties: -| Name | Type | Description | -| -------------- | --------- | -------------------------------------------------------------------------------------------------- | -| `DashboardURL` | string | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation. | -| `PanelURL` | string | A link to the panel if the alert has a Panel ID annotation. | -| `SilenceURL` | string | A link to silence the alert. | -| `Values` | [KV](#kv) | The values of expressions used to evaluate the alert condition. Only relevant values are included. | -| `ValueString` | string | A string that contains the labels and value of each reduced expression in the alert. | -| `OrgID` | integer | The ID of the organization that owns the alert. | +| Name | Type | Description | +| -------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `DashboardURL` | string | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation, with time range from `1h` before alert start to end (or now if firing). | +| `PanelURL` | string | A link to the panel if the alert has a Panel ID annotation, with time range from `1h` before alert start to end (or now if firing). | +| `SilenceURL` | string | A link to silence the alert. | +| `Values` | [KV](#kv) | The values of expressions used to evaluate the alert condition. Only relevant values are included. | +| `ValueString` | string | A string that contains the labels and value of each reduced expression in the alert. | +| `OrgID` | integer | The ID of the organization that owns the alert. | This example iterates over the list of firing and resolved alerts (`.Alerts`) in the notification and prints the data for each alert: diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md index 24e71878bc7..af2efc8aa46 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md @@ -29,10 +29,11 @@ refs: # Alert rule evaluation -The criteria determining when an alert rule fires are based on two settings: +The criteria determining when an alert rule fires are based on three settings: - [Evaluation group](#evaluation-group): how frequently the alert rule is evaluated. - [Pending period](#pending-period): how long the condition must be met to start firing. +- [Keep firing for](#pending-period): how long the alert continues to fire after the condition is no longer met. {{< figure src="/media/docs/alerting/alert-rule-evaluation-2.png" max-width="750px" alt="Set the evaluation behavior of the alert rule in Grafana." caption="Set alert rule evaluation" >}} @@ -50,7 +51,7 @@ Rules in different groups can be evaluated simultaneously. - **Data source-managed** rules within the same group are evaluated sequentially, one after the other—this is useful to ensure that recording rules are evaluated before alert rules. -- **Grafana-managed rules [imported from data source-managed rules](ref:import-ds-rules)** are evaluated sequentially, like data source-managed rules. +- **Grafana-managed rules [imported from data source-managed rules](ref:import-ds-rules)** are also evaluated sequentially. ## Pending period diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances.md new file mode 100644 index 00000000000..50983640285 --- /dev/null +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances.md @@ -0,0 +1,79 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ +description: An alert instance is considered stale when its series disappears for a number of consecutive evaluation intervals. Learn how Grafana resolves them. +keywords: + - grafana + - alerting + - guide + - state +labels: + products: + - cloud + - enterprise + - oss +title: Stale alert instances +weight: 110 +refs: + no-data-state: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-state + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-state + no-data-and-error-handling: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/alerting-rules/create-grafana-managed-rule/#configure-no-data-and-error-handling + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/#configure-no-data-and-error-handling + guide-missing-data: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/missing-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/missing-data/ +--- + +# Stale alert instances + +An alert instance is considered **stale** if the alert rule query returns data but its dimension (or series) has disappeared for a number of consecutive evaluation intervals (2 by default). + +This is different from the [**No Data** state](ref:no-data-state), which occurs when the alert rule query runs successfully but returns no dimensions (or series) at all. + +A stale alert instance transitions to the **Normal (MissingSeries)** state as **Resolved**, and is then evicted: + +| Eval. Interval | 1 | 2 | 3 | 4 | +| :--------------- | :-- | :-------------- | :--------------------------------------- | :-- | +| Alert instance A | ✔ | ✔ | ✔ | ✔ | +| Alert instance B | ✔ | `MissingSeries` | ️`Normal(MissingSeries)` 📩\* | | + +{{< admonition type="note" >}} + +Stale alert instances are supported only for Grafana-managed alert rules. + +{{< /admonition >}} + +## How Grafana handles stale alert instances + +The process for handling stale alert instances is as follows: + +1. The alert rule runs and returns data for some label sets. + +1. An alert instance that previously existed is now missing. + +1. Grafana keeps the previous state of the alert instance for the number of evaluation intervals specified in [Missing series evaluations to resolve](#configure-missing-series-evaluations-to-resolve). + +1. If it remains missing after the specified number of evaluation intervals (2 by default), it transitions to the **Normal** state and sets **MissingSeries** in the `grafana_state_reason` annotation. + + Stale alert instances in the **Alerting**, **No Data**, or **Error** states transition to the **Normal** state as **Resolved**, and are routed for notifications like other resolved alerts. + +1. The alert instance is removed from the UI. + +{{< admonition type="tip" >}} + +For common examples and practical guidance on handling **No Data** and **stale** alert scenarios, see [Handling missing data](ref:guide-missing-data). + +{{< /admonition >}} + +## Configure Missing series evaluations to resolve + +In [Configure no data and error handling > Missing series evaluations to resolve](ref:no-data-and-error-handling), you can set how many consecutive evaluation intervals must pass without data for a given dimension before the alert instance is marked as stale and resolved. + +If you don't specify a value, Grafana uses the **default of 2 evaluation intervals**. diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md index b7987e29828..046f9336cd8 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/state-and-health.md @@ -19,6 +19,17 @@ labels: title: State and health of alerts weight: 109 refs: + evaluation_timeout: + - pattern: /docs/ + destination: /docs/grafana//setup-grafana/configure-grafana/#evaluation_timeout + max_attempts: + - pattern: /docs/ + destination: /docs/grafana//setup-grafana/configure-grafana/#max_attempts + stale-alert-instances: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ pending-period: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/#pending-period @@ -39,6 +50,21 @@ refs: destination: /docs/grafana//alerting/fundamentals/notifications/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/ + notification-policies: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/notifications/notification-policies/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/notification-policies/ + guide-connectivity-errors: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/connectivity-errors/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/connectivity-errors/ + guide-missing-data: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/missing-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/missing-data/ --- # State and health of alerts @@ -49,14 +75,14 @@ There are three key components that help you understand how your alerts behave d An alert instance can be in either of the following states: -| State | Description | -| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Normal** | The state of an alert when the condition (threshold) is not met. | -| **Pending** | The state of an alert that has breached the threshold but for less than the [pending period](ref:pending-period). | -| **Alerting** | The state of an alert that has breached the threshold for longer than the [pending period](ref:pending-period). | -| **Recovering** | The state of an alert that has been configured to keep [firing for a duration after it is triggered](ref:keep-firing). | -| **No Data\*** | The state of an alert whose query returns no data or all values are null.
An alert in this state generates a new [DatasourceNoData alert](#no-data-and-error-alerts). You can [modify the default behavior of the no data state](#modify-the-no-data-or-error-state). | -| **Error\*** | The state of an alert when an error or timeout occurred evaluating the alert rule.
An alert in this state generates a new [DatasourceError alert](#no-data-and-error-alerts). You can [modify the default behavior of the error state](#modify-the-no-data-or-error-state). | +| State | Description | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Normal** | The state of an alert when the condition (threshold) is not met. | +| **Pending** | The state of an alert that has breached the threshold but for less than the [pending period](ref:pending-period). | +| **Alerting** | The state of an alert that has breached the threshold for longer than the [pending period](ref:pending-period). | +| **Recovering** | The state of an alert that has been configured to keep [firing for a duration after it is triggered](ref:keep-firing). | +| **Error\*** | The state of an alert when an error or timeout occurred evaluating the alert rule.
You can customize the behavior of the [Error state](#error-state), which by default triggers a different alert. | +| **No Data\*** | The state of an alert whose query returns no data or all values are null.
You can customize the behavior of the [No Data state](#no-data-state), which by default triggers a different alert. | If an alert rule changes (except for updates to annotations, the evaluation interval, or other internal fields), its alert instances reset to the `Normal` state. The alert instance state then updates accordingly during the next evaluation. @@ -74,25 +100,37 @@ Alert instances will be routed for [notifications](ref:notifications) when they {{< figure src="/media/docs/alerting/alert-rule-evaluation-overview-statediagram-v2.png" alt="A diagram of the alert instance states and when to route their notifications." max-width="750px" >}} -### Stale alert instances (MissingSeries) +### `Error` state -The `No Data` state occurs when the alert rule query runs successfully but returns no data points at all. +The **Error** state is triggered when the alert rule fails to evaluate its query or queries successfully. -An alert instance is considered stale if the query returns data but its dimension or series has disappeared for two evaluation intervals. In this case, the alert instance transitions to the **Normal (MissingSeries)** state as resolved, and is then evicted. +This can occur due to evaluation timeouts (default: `30s`) or three repeated failures when querying the data source. The [`evaluation_timeout`](ref:evaluation_timeout) and [`max_attempts`](ref:max_attempts) options control these settings. -The process for handling stale alert instances is as follows: +When an alert instance enters the **Error** state, Grafana, by default, triggers a new [`DatasourceError` alert](#no-data-and-error-alerts). You can control this behavior based on the desired outcome of your alert rule in [Modify the `No Data` or `Error` state](#modify-the-no-data-or-error-state). -1. The alert rule runs and returns data for some label sets. +### `No Data` state -1. An alert instance that previously existed is now missing. +The **No Data** state occurs when the alert rule query runs successfully but returns no data points at all. -1. Grafana keeps the previous state of the alert instance for two evaluation intervals. +When an alert instance enters the **No Data** state, Grafana, by default, triggers a new [`DatasourceNoData` alert](#no-data-and-error-alerts). You can control this behavior based on the desired outcome of your alert rule in [Modify the `No Data` or `Error` state](#modify-the-no-data-or-error-state). -1. If it remains missing after two intervals, it transitions to the **Normal** state and sets **MissingSeries** in the `grafana_state_reason` annotation. +## Modify the `No Data` or `Error` state -1. Stale alert instances in the **Alerting**, **No Data**, or **Error** states transition to the **Normal** state as **Resolved**, and are routed for notifications like other resolved alerts. +These states are supported only for Grafana-managed alert rules. -1. The alert instance is removed from the UI. +In [Configure no data and error handling](ref:no-data-and-error-handling), you can change the default behavior when the evaluation returns no data or an error. You can set the alert instance state to `Alerting`, `Normal`, `Error`, or `Keep Last State`. + +{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} + +{{< docs/shared lookup="alerts/table-configure-no-data-and-error.md" source="grafana" version="" >}} + +Note that when you configure the **No Data** or **Error** behavior to `Alerting` or `Normal`, Grafana attempts to keep a stable set of fields under notification `Values`. If your query returns no data or an error, Grafana re-uses the latest known set of fields in `Values`, but will use `-1` in place of the measured value. + +### Keep last state + +The "Keep Last State" option helps mitigate temporary data source issues, preventing alerts from unintentionally firing, resolving, and re-firing. + +However, in situations where strict monitoring is critical, relying solely on the "Keep Last State" option may not be appropriate. Instead, consider using an alternative or implementing additional alert rules to ensure that issues with prolonged data source disruptions are detected. ### `No Data` and `Error` alerts @@ -108,18 +146,6 @@ You can manage these alerts like regular ones by using their labels to apply act If the alert rule is configured to send notifications directly to a selected contact point (instead of using notification policies), the `DatasourceNoData` and `DatasourceError` alerts are also sent to that contact point. Any additional notification settings defined in the alert rule, such as muting or grouping, are preserved. -## Modify the `No Data` or `Error` state - -These states are supported only for Grafana-managed alert rules. - -In [Configure no data and error handling](ref:no-data-and-error-handling), you can change the default behaviour when the evaluation returns no data or an error. You can set the alert instance state to `Alerting`, `Normal`, `Error`, or `Keep Last State`. - -{{< figure src="/media/docs/alerting/alert-rule-configure-no-data-and-error-v2.png" alt="A screenshot of the `Configure no data and error handling` option in Grafana Alerting." max-width="500px" >}} - -{{< docs/shared lookup="alerts/table-configure-no-data-and-error.md" source="grafana" version="" >}} - -Note that when you configure the **No Data** or **Error** behavior to `Alerting` or `Normal`, Grafana attempts to keep a stable set of fields under notification `Values`. If your query returns no data or an error, Grafana re-uses the latest known set of fields in `Values`, but will use `-1` in place of the measured value. - ### Reduce `No Data` or `Error` alerts To minimize the number of **No Data** or **Error** state alerts received, try the following. @@ -129,30 +155,34 @@ To minimize the number of **No Data** or **Error** state alerts received, try th To minimize timeouts resulting in the **Error** state, reduce the time range to request less data every evaluation cycle. -1. Change the default [evaluation time out](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#evaluation_timeout). The default is set at 30 seconds. To increase the default evaluation timeout, open a support ticket from the [Cloud Portal](https://grafana.com/docs/grafana-cloud/account-management/support/#grafana-cloud-support-options). Note that this should be a last resort, because it may affect the performance of all alert rules and cause missed evaluations if the timeout is too long. +1. Change the default [evaluation time out](ref:evaluation_timeout). The default is set at 30 seconds. To increase the default evaluation timeout, open a support ticket from the [Cloud Portal](https://grafana.com/docs/grafana-cloud/account-management/support/#grafana-cloud-support-options). Note that this should be a last resort, because it may affect the performance of all alert rules and cause missed evaluations if the timeout is too long. -### Keep last state +1. To reduce multiple notifications from **Error** alerts, define a [notification policy](ref:notification-policies) to handle all related alerts with `alertname=DatasourceError`, and filter and group errors from the same data source using the `datasource_uid` label. -The "Keep Last State" option helps mitigate temporary data source issues, preventing alerts from unintentionally firing, resolving, and re-firing. +{{< admonition type="tip" >}} -However, in situations where strict monitoring is critical, relying solely on the "Keep Last State" option may not be appropriate. Instead, consider using an alternative or implementing additional alert rules to ensure that issues with prolonged data source disruptions are detected. +For common examples and practical guidance on handling **Error**, **No Data**, and **stale** alert scenarios, see the following related guides: + +- [Handling connectivity errors](ref:guide-connectivity-errors) +- [Handling missing data](ref:guide-missing-data) + {{< /admonition >}} ## `grafana_state_reason` for troubleshooting Occasionally, an alert instance may be in a state that isn't immediately clear to everyone. For example: -- [Stale alert instances](#stale-alert-instances-missingseries) in the `Alerting` state transition to the `Normal` state when the series disappear. - If "no data" handling is configured to transition to a state other than `No Data`. - If "error" handling is configured to transition to a state other than `Error`. - If the alert rule is deleted, paused, or updated in some cases, the alert instance also transitions to the `Normal` state. +- [Stale alert instances](ref:stale-alert-instances) in the `Alerting` state transition to the `Normal` state when the series disappear. In these situations, the evaluation state may differ from the alert state, and it might be necessary to understand the reason for being in that state when receiving the notification. The `grafana_state_reason` annotation is included in these situations, providing the reason that explains why the alert instance transitioned to its current state. For example: -- [Stale alert instances](#stale-alert-instances-missingseries) in the `Normal` state include the `grafana_state_reason` annotation with the value **MissingSeries**. - If "no data" or "error" handling transitions to the `Normal` state, the `grafana_state_reason` annotation is included with the value **No Data** or **Error**, respectively. - If the alert rule is deleted or paused, the `grafana_state_reason` is set to **Paused** or **RuleDeleted**. For some updates, it is set to **Updated**. +- [Stale alert instances](ref:stale-alert-instances) in the `Normal` state include the `grafana_state_reason` annotation with the value **MissingSeries**. ## Alert rule state diff --git a/docs/sources/alerting/fundamentals/alert-rules/_index.md b/docs/sources/alerting/fundamentals/alert-rules/_index.md index 439c56b130c..59d15cb930a 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rules/_index.md @@ -20,9 +20,9 @@ weight: 100 refs: shared-configure-prometheus-data-source-alerting: - pattern: /docs/grafana/ - destination: /docs/grafana//datasources/prometheus/configure-prometheus-data-source/#alerting + destination: /docs/grafana//datasources/prometheus/configure/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure-prometheus-data-source/#alerting + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure/ queries-and-conditions: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rules/queries-conditions/#data-source-queries @@ -102,7 +102,11 @@ Data source-managed alert rules can only be created using Grafana Mimir or Grafa 1. Alert rules are evaluated by the Alert Rule Evaluation Engine within the data source. 1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications). -{{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="" >}} +> Rules from a Prometheus data source appear in the **Data source-managed** section of the **Alert rules** page when [Manage alerts via Alerting UI](ref:shared-configure-prometheus-data-source-alerting) is enabled. +> +> However, Grafana can only create and edit data source-managed rules for Mimir and Loki, not for a Prometheus instance. + +[//]: <> ({{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="" >}}) ## Comparison between alert rule types diff --git a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md index dde43d6a6aa..e958f5e1caa 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md +++ b/docs/sources/alerting/fundamentals/alert-rules/queries-conditions.md @@ -17,11 +17,16 @@ labels: title: Queries and conditions weight: 104 refs: - data-source-alerting: + dynamic-threshold-example: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rules/#supported-data-sources + destination: /docs/grafana//alerting/best-practices/dynamic-thresholds/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/#supported-data-sources + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/dynamic-thresholds/ + alert-instance: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/#alert-instances + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/#alert-instances state-and-health: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/state-and-health/ @@ -37,6 +42,21 @@ refs: destination: /docs/grafana//panels-visualizations/query-transform-data/expression-queries/#math - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/expression-queries/#math + resample-operation: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/expression-queries/#resample + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/expression-queries/#resample + reduce-operation: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/expression-queries/#reduce + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/expression-queries/#reduce + table-data-example: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/best-practices/table-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/table-data/ --- # Queries and conditions @@ -53,13 +73,18 @@ Alerting periodically runs the queries and expressions, evaluating the condition ## Data source queries -Alerting queries are the same as the queries used in Grafana panels, but Grafana-managed alerts are limited to querying [data sources that have Alerting enabled](ref:data-source-alerting). +Alerting queries are the same as the queries used in Grafana panels, but Grafana-managed alerts are limited to querying [data sources that have Alerting enabled](/grafana/plugins/data-source-plugins/?features=alerting). -Queries in Grafana can be applied in various ways, depending on the data source and query language being used. Each data source’s query editor provides a customized user interface to help you write queries that take advantage of its unique capabilities. +Queries in Grafana can be applied in various ways, depending on the data source and query language being used. Each data source’s query editor provides a customized user interface to help you write queries that take advantage of its unique capabilities. For details about query editors and syntax in Grafana, refer to [Query and transform data](ref:query-transform-data). -For more details about queries in Grafana, refer to [Query and transform data](ref:query-transform-data). +Alerting can work with two types of data: -{{< figure src="/media/docs/alerting/alerting-query-conditions-default-options.png" max-width="750px" caption="Define alert query and alert condition" >}} +1. **Time series data** — The query returns a collection of time series, where each series must be [reduced](#reduce) to a single numeric value for evaluating the alert condition. +1. **Tabular data** — The query must return data in a table format with only one numeric column. Each row must have a value in that column, used to evaluate the alert condition. See a [tabular data example](ref:table-data-example). + +Each time series or table row is evaluated as a separate [alert instance](ref:alert-instance). + +{{< figure src="/media/docs/alerting/alerting-query-conditions-default-options.png" max-width="750px" caption="Alert query using the Prometheus query editor and alert condition" >}} ## Alert condition @@ -89,7 +114,7 @@ Aggregates time series values within the selected time range into a single numbe Reduce takes one or more time series and transform each series into a single number, which can then be compared in the alert condition. -The following aggregations functions are included: `Min`, `Max`, `Mean`, `Mediam`, `Sum`, `Count`, and `Last`. +The following aggregations functions are included: `Min`, `Max`, `Mean`, `Mediam`, `Sum`, `Count`, and `Last`. For more details, refer to the [Reduce documentation](ref:reduce-operation). ### Math @@ -97,9 +122,7 @@ Performs free-form math functions/operations on time series data and numbers. Fo If queries being compared have **multiple series in their results**, series from different queries are matched(joined) if they have the same labels. For example: -- `$A` returns series `{host=web01} 30` and `{host=web02} 20` -- `$B` returns series `{host=web01} 10` and `{host=web02} 0` -- `$A + $B` returns `{host=web01} 40` and `{host=web02} 20`. +{{< docs/shared lookup="alerts/math-example.md" source="grafana" version="" >}} In this case, only series with matching labels are joined, and the operation is calculated between them. @@ -109,11 +132,14 @@ You can also use a Math expression to define the **alert condition**. For exampl - `$B > 70` should fire if the value of B (query or expression) is more than 70. - `$B < $C * 100` should fire if the value of B is less than the value of C multiplied by 100. +- Compare matching series from two queries, as shown in the [dynamic threshold example](ref:dynamic-threshold-example). ### Resample Realigns a time range to a new set of timestamps, this is useful when comparing time series data from different data sources where the timestamps would otherwise not align. +For more details, refer to the [Resample documentation](ref:resample-operation). + ### Threshold Compares single numbers from previous queries or expressions (e.g., `$A`, `$B`) to a specified condition. It's often used to define the alert condition. @@ -192,63 +218,3 @@ The following aggregation functions are also available to further refine your qu | `count_non_null` | Displays a count of values in the result set that aren't `null` | {{< /collapse >}} - -## Alert on numeric data - -Among certain data sources numeric data that is not time series can be directly alerted on, or passed into Server Side Expressions (SSE). This allows for more processing and resulting efficiency within the data source, and it can also simplify alert rules. -When alerting on numeric data instead of time series data, there is no need to [reduce](#reduce) each labeled time series into a single number. Instead labeled numbers are returned to Grafana instead. - -#### Tabular Data - -This feature is supported with backend data sources that query tabular data: - -- SQL data sources such as MySQL, Postgres, MSSQL, and Oracle. -- The Azure Kusto based services: Azure Monitor (Logs), Azure Monitor (Azure Resource Graph), and Azure Data Explorer. - -A query with Grafana managed alerts or SSE is considered numeric with these data sources, if: - -- The "Format AS" option is set to "Table" in the data source query. -- The table response returned to Grafana from the query includes only one numeric (e.g. int, double, float) column, and optionally additional string columns. - -If there are string columns then those columns become labels. The name of column becomes the label name, and the value for each row becomes the value of the corresponding label. If multiple rows are returned, then each row should be uniquely identified their labels. - -**Example** - -For a MySQL table called "DiskSpace": - -| Time | Host | Disk | PercentFree | -| ----------- | ---- | ---- | ----------- | -| 2021-June-7 | web1 | /etc | 3 | -| 2021-June-7 | web2 | /var | 4 | -| 2021-June-7 | web3 | /var | 8 | - -You can query the data filtering on time, but without returning the time series to Grafana. For example, an alert that would trigger per Host, Disk when there is less than 5% free space: - -```sql -SELECT Host, Disk, CASE WHEN PercentFree < 5.0 THEN PercentFree ELSE 0 END FROM ( - SELECT - Host, - Disk, - Avg(PercentFree) - FROM DiskSpace - Group By - Host, - Disk - Where __timeFilter(Time) -``` - -This query returns the following Table response to Grafana: - -| Host | Disk | PercentFree | -| ---- | ---- | ----------- | -| web1 | /etc | 3 | -| web2 | /var | 4 | -| web3 | /var | 0 | - -When this query is used as the **condition** in an alert rule, then the non-zero is alerting. As a result, three alert instances are produced: - -| Labels | Status | -| --------------------- | -------- | -| {Host=web1,disk=/etc} | Alerting | -| {Host=web2,disk=/var} | Alerting | -| {Host=web3,disk=/var} | Normal | diff --git a/docs/sources/alerting/fundamentals/notifications/notification-policies.md b/docs/sources/alerting/fundamentals/notifications/notification-policies.md index ae8d53f1520..2182796cdcc 100644 --- a/docs/sources/alerting/fundamentals/notifications/notification-policies.md +++ b/docs/sources/alerting/fundamentals/notifications/notification-policies.md @@ -71,7 +71,58 @@ Notification policies are _not_ a list, but rather are structured according to a Each policy consists of a set of label matchers (0 or more) that specify which alerts they are or aren't interested in handling. A matching policy refers to a notification policy with label matchers that match the alert instance’s labels. -{{< docs/shared lookup="alerts/how_label_matching_works.md" source="grafana" version="" >}} +{{< collapse title="How label matching works" >}} + +Use [labels](ref:shared-alert-labels) and label matchers to link alert rules to [notification policies](ref:shared-notification-policies) and [silences](ref:shared-silences). This allows for a flexible way to manage your alert instances, specify which policy should handle them, and which alerts to silence. + +A label matchers consists of 3 distinct parts, the **label**, the **value** and the **operator**. + +- The **Label** field is the name of the label to match. It must exactly match the label name. + +- The **Value** field matches against the corresponding value for the specified **Label** name. How it matches depends on the **Operator** value. + +- The **Operator** field is the operator to match against the label value. The available operators are: + + | Operator | Description | + | -------- | -------------------------------------------------- | + | `=` | Select labels that are exactly equal to the value. | + | `!=` | Select labels that are not equal to the value. | + | `=~` | Select labels that regex-match the value. | + | `!~` | Select labels that do not regex-match the value. | + +{{% admonition type="note" %}} +If you are using multiple label matchers, they are combined using the AND logical operator. This means that all matchers must match in order to link a rule to a policy. +{{% /admonition %}} + +**Label matching example** + +If you define the following set of labels for your alert: + +`{ foo=bar, baz=qux, id=12 }` + +then: + +- A label matcher defined as `foo=bar` matches this alert rule. +- A label matcher defined as `foo!=bar` does _not_ match this alert rule. +- A label matcher defined as `id=~[0-9]+` matches this alert rule. +- A label matcher defined as `baz!~[0-9]+` matches this alert rule. +- Two label matchers defined as `foo=bar` and `id=~[0-9]+` match this alert rule. + +**Exclude labels** + +You can also write label matchers to exclude labels. + +Here is an example that shows how to exclude the label `Team`. You can choose between any of the values below to exclude labels. + +| Label | Operator | Value | +| ------ | -------- | ----- | +| `team` | `=` | `""` | +| `team` | `!~` | `.+` | +| `team` | `=~` | `^$` | + +{{< /collapse >}} + +[//]: <> ({{< docs/shared lookup="alerts/how_label_matching_works.md" source="grafana" version="" >}}) {{< figure src="/media/docs/alerting/notification-routing.png" max-width="750px" caption="Matching alert instances with notification policies" alt="Example of a notification policy tree" >}} diff --git a/docs/sources/alerting/monitor-status/view-alert-rules.md b/docs/sources/alerting/monitor-status/view-alert-rules.md index 199941c9578..34869a84364 100644 --- a/docs/sources/alerting/monitor-status/view-alert-rules.md +++ b/docs/sources/alerting/monitor-status/view-alert-rules.md @@ -76,3 +76,22 @@ To view or restore previous versions for an alert rule, complete the following s On the Alert rule's Versions page you can view, compare and restore the previous rule versions. {{< figure src="/media/docs/alerting/screenshot-grafana-alerting-version-history-v3.png" max-width="750px" alt="View alert rule history to compare and restore previous alert rules." >}} + +## Bulk pause or resume alert rules evaluations within a folder + +Admin users can pause or resume all of the alert rules evaluations within a folder. To pause or resume all the alert rules evaluations in a folder, click the menu icon and select **Pause all rule evaluation** or **Resume all rule evaluation**. + +## Bulk delete all alert rules within a folder + +Admin users can delete all of the alert rules within a folder. To delete all the alert rules in a folder, click the menu icon and select **Delete**. Then type "Delete" into the field and click **Delete** to confirm the bulk deletion. + +## Permanently delete or restore deleted alert rules + +Only users with an Admin role can restore deleted Grafana-managed alert rules. After an alert rule is restored, it is restored with a new, different UID from the one it had before. + +1. Go to **Alerts & IRM > Alerting > Recently deleted**. +1. Click the **Restore** button to restore the alert rule or click **Delete permanently** to delete the alert rule. + +{{< admonition type="note" >}} +Deleted alert rules are stored for 30 days. Grafana Enterprise and OSS users can adjust the length of time for which the rules are stored can be adjusted in the Grafana configuration file's `[unified_alerting].deleted_rule_retention` field. For an example of how to modify the Grafana configuration file, refer to the [documentation example here](/docs/grafana/latest/alerting/set-up/configure-alert-state-history/#configuring-grafana). +{{< /admonition >}} diff --git a/docs/sources/alerting/monitor-status/view-alert-state.md b/docs/sources/alerting/monitor-status/view-alert-state.md index 7725361f9de..6450bb08b6e 100644 --- a/docs/sources/alerting/monitor-status/view-alert-state.md +++ b/docs/sources/alerting/monitor-status/view-alert-state.md @@ -42,14 +42,9 @@ refs: destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-rule-state alert-instance-state: - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-instance-state + destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-instance-state - alert-instance-state: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-instance-state - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-instance-state + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/ alert-rule-health: - pattern: /docs/grafana/ destination: /docs/grafana//alerting/fundamentals/alert-rule-evaluation/state-and-health/ diff --git a/docs/sources/alerting/set-up/configure-high-availability/_index.md b/docs/sources/alerting/set-up/configure-high-availability/_index.md index 63dce1c6a7b..556843b4e50 100644 --- a/docs/sources/alerting/set-up/configure-high-availability/_index.md +++ b/docs/sources/alerting/set-up/configure-high-availability/_index.md @@ -67,13 +67,21 @@ For a demo, see this [example using Docker Compose](https://github.com/grafana/a ## Enable alerting high availability using Redis -As an alternative to Memberlist, you can use Redis for high availability. This is useful if you want to have a central -database for HA and cannot support the meshing of all Grafana servers. +As an alternative to Memberlist, you can configure Redis to enable high availability. Redis standalone, Redis Cluster and Redis Sentinel modes are supported. + +{{% admonition type="note" %}} + +Memberlist is the preferred option for high availability. Use Redis only in environments where direct communication between Grafana servers is not possible, such as when TCP or UDP ports are blocked. + +{{% /admonition %}} 1. Make sure you have a Redis server that supports pub/sub. If you use a proxy in front of your Redis cluster, make sure the proxy supports pub/sub. 1. In your custom configuration file ($WORKING_DIR/conf/custom.ini), go to the `[unified_alerting]` section. -1. Set `ha_redis_address` to the Redis server address Grafana should connect to. +1. Set `ha_redis_address` to the Redis server address or addresses Grafana should connect to. It can be a single Redis address if using Redis standalone, or a list of comma-separated addresses if using Redis Cluster or Sentinel. +1. Optional: Set `ha_redis_cluster_mode_enabled` to `true` if you are using Redis Cluster. +1. Optional: Set `ha_redis_sentinel_mode_enabled` to `true` if you are using Redis Sentinel. Also set `ha_redis_sentinel_master_name` to the Redis Sentinel master name. 1. Optional: Set the username and password if authentication is enabled on the Redis server using `ha_redis_username` and `ha_redis_password`. +1. Optional: Set the username and password if authentication is enabled on Redis Sentinel using `ha_redis_sentinel_username` and `ha_redis_sentinel_password`. 1. Optional: Set `ha_redis_prefix` to something unique if you plan to share the Redis server with multiple Grafana instances. 1. Optional: Set `ha_redis_tls_enabled` to `true` and configure the corresponding `ha_redis_tls_*` fields to secure communications between Grafana and Redis with Transport Layer Security (TLS). 1. Set `[ha_advertise_address]` to `ha_advertise_address = "${POD_IP}:9094"` This is required if the instance doesn't have an IP address that is part of RFC 6890 with a default route. diff --git a/docs/sources/dashboards/_index.md b/docs/sources/dashboards/_index.md index c24b99d34cc..69696e00405 100644 --- a/docs/sources/dashboards/_index.md +++ b/docs/sources/dashboards/_index.md @@ -59,9 +59,9 @@ refs: {{< shared id="dashboard-overview" >}} -A Grafana dashboard is a set of one or more [panels](ref:panels), organized and arranged into one or more rows, that provide an at-a-glance view of related information. These panels are created using components that query and transform raw data from a data source into charts, graphs, and other visualizations. +A Grafana dashboard is a set of one or more [panels](ref:panels), organized and arranged into one or more rows, that provide an at-a-glance view of related information. These panels are created using components that query and transform raw data from a data source into visualizations. -A data source can be an SQL database, Grafana Loki, Grafana Mimir, or a JSON-based API. It can even be a basic CSV file. Data source plugins take a query you want answered, retrieve the data from the data source, and reconcile the differences between the data model of the data source and the data model of Grafana dashboards. +A data source can be an SQL database, Grafana Loki, Grafana Mimir, or an API endpoint. It can even be a basic CSV file. Data source plugins take a query you want answered, retrieve the data from the data source, and reconcile the differences between the data model of the data source and the data model of Grafana dashboards. {{< /shared >}} diff --git a/docs/sources/dashboards/build-dashboards/create-dynamic-dashboard/index.md b/docs/sources/dashboards/build-dashboards/create-dynamic-dashboard/index.md new file mode 100644 index 00000000000..47c4492204e --- /dev/null +++ b/docs/sources/dashboards/build-dashboards/create-dynamic-dashboard/index.md @@ -0,0 +1,375 @@ +--- +labels: + products: + - cloud + - enterprise + - oss + stage: + - experimental +_build: + list: false +noindex: true +title: Create a dynamic dashboard +description: Create and edit a dynamic dashboard +weight: 900 +refs: + built-in-special-data-sources: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/#special-data-sources + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/#special-data-sources + visualization-specific-options: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/ + configure-standard-options: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-standard-options/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-standard-options/ + configure-value-mappings: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-value-mappings/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-value-mappings/ + generative-ai-features: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/manage-dashboards/#set-up-generative-ai-features-for-dashboards + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/manage-dashboards/#set-up-generative-ai-features-for-dashboards + configure-thresholds: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-thresholds/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-thresholds/ + data-sources: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/ + add-a-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/#add-a-data-source + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//datasources/#add-a-data-source + about-users-and-permissions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/roles-and-permissions/ + visualizations-options: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//panels-visualizations/visualizations/ + configure-repeating-panels: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-panel-options/#configure-repeating-panels + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-panel-options/#configure-repeating-panels + override-field-values: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-overrides/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-overrides/ +--- + +# Create and edit dynamic dashboards + +{{< docs/experimental product="Dynamic dashboards" featureFlag="dashboardNewLayouts" >}} + +Dashboards and panels allow you to show your data in visual form. Each panel needs at least one query to display a visualization. + +## Before you begin + +- Ensure that you have the proper permissions. For more information about permissions, refer to [About users and permissions](ref:about-users-and-permissions). +- Identify the dashboard to which you want to add the panel. +- Understand the query language of the target data source. +- Ensure that data source for which you are writing a query has been added. For more information about adding a data source, refer to [Add a data source](ref:add-a-data-source) if you need instructions. + +## Create a dashboard + +To create a dashboard, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Click **New** and select **New Dashboard**. +1. In the edit pane, enter the dashboard title and description. + + {{< figure src="/media/docs/grafana/dashboards/screenshot-new-dashboard-v12.png" max-width="750px" alt="New dashboard" >}} + +1. Under **Panel layout**, choose one of the following options: + + - **Custom** - Position and size panels manually. The default selection. + - **Auto grid** - Panels are automatically resized to create a uniform grid based on the column and row settings. + +1. Click **+ Add visualization**. +1. In the dialog box that opens, do one of the following: + + - Select one of your existing data sources. + - Select one of the Grafana [built-in special data sources](ref:built-in-special-data-sources). + - Click **Configure a new data source** to set up a new one (Admins only). + + {{< figure class="float-right" src="/media/docs/grafana/dashboards/screenshot-data-source-selector-10.0.png" max-width="800px" alt="Select data source modal" >}} + + The **Edit panel** view opens with your data source selected. + You can change the panel data source later using the drop-down in the **Query** tab of the panel editor if needed. + + For more information about data sources, refer to [Data sources](ref:data-sources) for specific guidelines. + +1. Write or construct a query in the query language of your data source. +1. Click **Refresh** to query the data source. +1. In the visualization list, select a visualization type. + + {{< figure src="/media/docs/grafana/dashboards/screenshot-select-visualization-v12.png" max-width="350px" alt="Visualization selector" >}} + + Grafana displays a preview of your query results with the visualization applied. + + For more information about configuring individual visualizations, refer to [Visualizations options](ref:visualizations-options). + +1. Under **Panel options**, enter a title and description for your panel or have Grafana create them using [generative AI features](ref:generative-ai-features). +1. Refer to the following documentation for ways you can adjust panel settings. + + While not required, most visualizations need some adjustment before they properly display the information that you need. + + - [Configure value mappings](ref:configure-value-mappings) + - [Visualization-specific options](ref:visualization-specific-options) + - [Override field values](ref:override-field-values) + - [Configure thresholds](ref:configure-thresholds) + - [Configure standard options](ref:configure-standard-options) + +1. When you've finished editing your panel, click **Save**. + + Alternatively, click **Back to dashboard** if you want to see your changes applied to the dashboard first. Then click **Save** when you're ready. + +1. Enter a title and description for your dashboard if you haven't already or have Grafana create them using [generative AI features](ref:generative-ai-features). +1. Select a folder, if applicable. +1. (Optional) Enter a description of the changes you've made. +1. Click **Save**. +1. To add more panels to the dashboard, click **Back to dashboard** and at the bottom-left corner of the dashboard, click **+ Add panel**. + + {{< figure src="/media/docs/grafana/dashboards/screenshot-add-panel-v12.png" max-width="500px" alt="Add panel button" >}} + +1. (Optional) In the edit pane, enter a title and description for the panel and set the panel transparency and repeat options, if applicable. +1. Click **Configure** in either the edit pane or on the panel to the configuration process. +1. When you've saved all the changes you want to make to the dashboard, click **Back to dashboard**. +1. Toggle off the edit mode switch. + +## Group panels + +To help create meaningful sections in your dashboard, you can group panels into rows or tabs. +Rows and tabs let you break up big dashboards or make one dashboard out of several smaller ones. +You can nest tabs and rows within each other or themselves. +Also, tabs are included in the dashboard URL. + +The following sections describe the configuration options for adding tabs and rows. +While grouping is meant for multiple panels, you can start a grouping with just one panel. + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. +1. At the bottom-left corner of the dashboard, click **Group panels**. +1. Select **Group into row** or **Group into tab**. + + A dotted line surrounds the panels and the **Row** or **Tab** edit pane is displayed on the right side of the dashboard. + +1. Set the [grouping configuration options](#grouping-configuration-options). +1. When you're finished, click **Save** at the top-right corner of the dashboard. +1. (Optional) Enter a description of the changes you've made. +1. Click **Save**. + +### Grouping configuration options + +The following table describes the options you can set for a row. + + + +| Option | Description | +| ------ | ----------- | +| Title | Title of the row or tab. | +| Fill screen | Toggle the switch on to make the row fill the screen. Only applies to rows. | +| Hide row header | Toggle the switch on to hide the header. In edit mode, the row header is visible, but crossed out with the hidden icon next to it. Only applies to rows. | +| Group layout | Select the grouping option, between **Rows** and **Tabs**. Only available when there's a nested grouping and applies to the nested grouping. | +| Panel layout | Select whether panels are sized and positioned manually, **Custom**, or automatically, **Auto grid**. Only available when a grouping contains panels. | +| Repeat options > [Repeat by variable](#configure-repeat-options) | Configure the dashboard to dynamically add rows or tabs based on the value of a variable. | +| Show / hide rules > [Row/Tab visibility](#configure-showhide-rules) | Control whether or not rows or tabs are displayed based on variables or a time range. | + + + +## Configure repeat options + + + +You can configure Grafana to dynamically add panels, rows, or tabs to a dashboard based on the value of that variable. +Variables dynamically change your queries across all rows in a dashboard. + +This only applies to queries that include a multi-value variable. + + + +To configure repeats, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. + + The **Dashboard** edit pane opens on the right side of the dashboard. + +1. Click in the panel, row, or tab you want to work with to bring it into focus and display the associated options in the edit pane. +1. Expand the **Repeat options** section. +1. Select the **Repeat by variable**. +1. For panels only, set the following options: + + - Under **Repeat direction**, choose one of the following: + + - **Horizontal** - Arrange panels side-by-side. Grafana adjusts the width of a repeated panel. You can’t mix other panels on a row with a repeated panel. + - **Vertical** - Arrange panels in a column. The width of repeated panels is the same as the original, repeated panel. + + - If you selected **Horizontal**, select a value in the **Max per row** drop-down list to control the maximum number of panels that can be in a row. + +1. (Optional) To provide context to dashboard users, add the variable name to the panel, row, or tab title. +1. When you've finished setting the repeat option, click **Save**. +1. (Optional) Enter a description of the changes you've made. +1. Click **Save**. +1. Toggle off the edit mode switch. + +### Repeating rows and the Dashboard special data source + + + +If a row includes panels using the special [Dashboard data source](ref:built-in-special-data-sources)—the data source that uses a result set from another panel in the same dashboard—then corresponding panels in repeated rows will reference the panel in the original row, not the ones in the repeated rows. + +For example, in a dashboard: + +- `Row 1` includes `Panel 1A` and `Panel 1B` +- `Panel 1B` uses the results from `Panel 1A` by way of the `-- Dashboard --` data source +- Repeating row, `Row 2`, includes `Panel 2A` and `Panel 2B` +- `Panel 2B` references `Panel 1A`, not `Panel 2A` + +## Configure show/hide rules + +You can configure panels, rows, and tabs to be shown or hidden based on rules. +For example, you might want to set a panel to be hidden if there's no data returned by a query or a tab to only be shown based on a variable being present. + +{{< admonition type="note" >}} +You can only configure show/hide rules for panels when the dashboard is using the **Auto grid** panel layout. +{{< /admonition >}} + +To configure show/hide rules, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. + + The **Dashboard** edit pane opens on the right side of the dashboard. + +1. Click in the panel, row, or tab you want to work with to bring it into focus and display the associated options in the edit pane. +1. Expand the **Show / hide rules** section. +1. Select **Show** or **Hide** to set whether the panel, row, or tab is shown or hidden based on the rules outcome. +1. Click **+ Add rule**. +1. Select a rule type: + + - **Query result** - Show or hide a panel based on query results. Choose from **Has data** and **No data**. For panels only. + - **Template variable** - Show or hide the panel, row, or tab dynamically based on the variable value. Select a variable and operator and enter a value. + - **Time range less than** - Show or hide the panel, row, or tab if the dashboard time range is shorter than the selected time frame. Select or enter a time range. + +1. Configure the rule. +1. Under **Match rules**, select one of the following: + + - **Match all** - The panel, row, or tab is shown or hidden only if _all_ the rules are matched. + - **Match any** - The panel, row, or tab is shown or hidden if _any_ of the rules are matched. + + This option is only displayed if you add multiple rules. + +1. When you've finished setting rules, click **Save**. +1. (Optional) Enter a description of the changes you've made. +1. Click **Save**. +1. Toggle off the edit mode switch. + +## Edit dashboards + +When the dashboard is in edit mode, the edit pane that opens displays options associated with the part of the dashboard that it's in focus. +For example, if you click in the area of a panel, row, or tab, that area comes into focus and the edit pane shows the options for that area: + +{{< figure src="/media/docs/grafana/dashboards/screenshot-edit-pane-focus-v12.png" max-width="750px" alt="Dashboard with a panel in focus" >}} + +- For rows and tabs, all of the available options are in the edit pane. +- For panels, high-level options are in the edit pane and further configuration options are in the **Edit panel** view. +- For dashboards, high-level options are in the edit pane and further configuration options are in the **Settings** page. + +To edit dashboards, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. + + The **Dashboard** edit pane opens on the right side of the dashboard. + +1. Click in the area you want to work with to bring it into focus and display the associated options in the edit pane. +1. Do one of the following: + + - For rows or tabs, make the required changes using the edit pane. + - For panels, update the panel title, description, repeat options or show/hide rules in the edit pane. For more changes, click **Configure** and continue in **Edit panel** view. + - For dashboards, update the dashboard title, description, grouping or panel layout. For more changes, click the settings (gear) icon in the top-right corner. + +1. When you've finished making changes, click **Save**. +1. (Optional) Enter a description of the changes you've made. +1. Click **Save**. +1. Toggle off the edit mode switch. + +## Move or resize a panel + + + +When you're dashboard has a **Custom** layout, you can resize or move a panel to any location on the dashboard. + +To move or resize, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. +1. Do one of the following: + + - Click the panel title and drag the panel to the new location. + - Click and drag the lower-right corner of the panel to change the size of the panel. + +1. Click **Save**. +1. (Optional) Enter a description of the changes you've made. +1. Click **Save**. +1. Toggle off the edit mode switch. + +## Navigate using the dashboard outline + +The dashboard **Outline** provides a tree-like structure that shows you all of the parts of your dashboard and their relationships to each other including panels, rows, tabs, and variables. +The outline also lets you quickly navigate the dashboard so that you don't have to spend time finding a particular element to work with it. +By default, the outline is collapsed except for the part that's currently in focus. + +{{< figure src="/media/docs/grafana/dashboards/screenshot-dashboard-outline-v12.png" max-width="750px" alt="Dashboard with outline open showing panel in focus" >}} + +To navigate the dashboard using the outline, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. + + The **Dashboard** edit pane opens on the right side of the dashboard. + +1. In the edit pane, expand the **Outline** section. +1. Expand the outline to find the dashboard part to which you want to navigate. +1. Click the tree item to navigate that part of the dashboard. + +## Copy a dashboard + +To make a copy of a dashboard, follow these steps: + +1. Click **Dashboards** in the main menu. +1. Navigate to the dashboard you want to update. +1. Toggle on the edit mode switch. +1. Click the **Save** drop-down and select **Save as copy**. +1. (Optional) Specify the name, folder, description, and whether or not to copy the original dashboard tags for the copied dashboard. + + By default, the copied dashboard has the same name as the original dashboard with the word "Copy" appended and is in the same folder. + +1. Click **Save**. diff --git a/docs/sources/dashboards/variables/variable-syntax/index.md b/docs/sources/dashboards/variables/variable-syntax/index.md index 644b67c01d4..fd876b86847 100644 --- a/docs/sources/dashboards/variables/variable-syntax/index.md +++ b/docs/sources/dashboards/variables/variable-syntax/index.md @@ -136,12 +136,23 @@ Interpolation result: 'test1.|test2' ### Raw -The raw format for a data source variable returns the UID (unique identifier) of the data source, rather than its name. +Doesn't apply any data source-specific formatting to the variable. + +For example, in this case, there's a dashboard with a Prometheus data source and a multi-value variable. +Grafana typically converts the variable values as follows to accommodate Prometheus: ```bash -datasourceVariable = 'd7bbe725-9e48-4af8-a0cb-6cb255d873a3' -String to interpolate: '${datasourceVariable:raw}' -Interpolation result: 'd7bbe725-9e48-4af8-a0cb-6cb255d873a3' +servers = ['test1.', 'test2'] +String to interpolate: '${servers}' +Interpolation result: '(test1 | test2)' +``` + +Using the raw format, the values are returned without that formatting: + +```bash +servers = ['test1.', 'test2'] +String to interpolate: '${servers:raw}' +Interpolation result: 'test1,test2' ``` ### Regex diff --git a/docs/sources/datasources/influxdb/query-editor/index.md b/docs/sources/datasources/influxdb/query-editor/index.md index 14b3c1294d4..7095ba3f997 100644 --- a/docs/sources/datasources/influxdb/query-editor/index.md +++ b/docs/sources/datasources/influxdb/query-editor/index.md @@ -31,7 +31,7 @@ refs: - pattern: /docs/grafana/ destination: /docs/grafana//panels-visualizations/visualizations/logs/ - pattern: /docs/grafana-cloud/ - destination: grafana-cloud/visualizations/panels-visualizations/visualizations/logs/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/logs/ query-editor: - pattern: /docs/grafana/ destination: /docs/grafana//panels-visualizations/query-transform-data/#query-editors diff --git a/docs/sources/datasources/prometheus/_index.md b/docs/sources/datasources/prometheus/_index.md index 29ec89bfe74..2e01187dd7e 100644 --- a/docs/sources/datasources/prometheus/_index.md +++ b/docs/sources/datasources/prometheus/_index.md @@ -26,21 +26,11 @@ refs: destination: /docs/grafana//getting-started/get-started-grafana-prometheus/#get-started-with-grafana-and-prometheus - pattern: /docs/grafana-cloud/ destination: /docs/grafana//getting-started/get-started-grafana-prometheus/#get-started-with-grafana-and-prometheus - configure-grafana-configuration-file-location: - - pattern: /docs/grafana/ - destination: /docs/grafana//setup-grafana/configure-grafana/#configuration-file-location - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//setup-grafana/configure-grafana/#configuration-file-location provisioning-data-sources: - pattern: /docs/grafana/ destination: /docs/grafana//administration/provisioning/#data-sources - pattern: /docs/grafana-cloud/ destination: /docs/grafana//administration/provisioning/#data-sources - explore: - - pattern: /docs/grafana/ - destination: /docs/grafana//explore/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//explore/ set-up-grafana-monitoring: - pattern: /docs/grafana/ destination: /docs/grafana//setup-grafana/set-up-grafana-monitoring/ @@ -73,145 +63,103 @@ refs: destination: /docs/grafana//fundamentals/intro-to-prometheus/ configure-prometheus-data-source: - pattern: /docs/grafana/ - destination: /docs/grafana//datasources/prometheus/configure-prometheus-data-source/ + destination: /docs/grafana//datasources/prometheus/configure - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//datasources/prometheus/configure-prometheus-data-source/ + destination: /docs/grafana//datasources/prometheus/configure + annotate-visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/annotate-visualizations/ + recorded-queries: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/recorded-queries/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/recorded-queries/ + transformations: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/transform-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/transform-data/ + alerting: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/ + visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/ + variables: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/variables/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/variables/ + --- # Prometheus data source -Prometheus is an open source database that uses a telemetry collector agent to scrape and store metrics used for monitoring and alerting. Grafana provides native support for Prometheus. If you are just getting started with Prometheus, see [What is Prometheus?](ref:intro-to-prometheus). +Prometheus is an open source database that uses a telemetry collector agent to scrape and store metrics used for monitoring and alerting. -{{% admonition type="tip" %}} -For instructions on downloading Prometheus see [Get started with Grafana and Prometheus](ref:get-started-prometheus). +Grafana provides native support for Prometheus, so you don't need to install a plugin. -If you’re ready to start visualizing your metrics, check out our Prometheus Learning Journeys: - -- [Connect to a Prometheus data source in Grafana Cloud](https://www.grafana.com/docs/learning-journeys/prometheus/) to visualize your metrics directly from where they are stored. -- [Send metrics to Grafana Cloud using Prometheus remote write](https://www.grafana.com/docs/learning-journeys/prom-remote-write/) to explore Grafana Cloud without making significant changes to your existing configuration. - {{% /admonition %}} - -For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:administration-documentation). -Only users with the organization `administrator` role can add data sources and edit existing data sources. -Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system. - -Once you've added the Prometheus data source, you can [configure it](ref:configure-prometheus-data-source) so that your Grafana instance's users can create queries in its [query editor](query-editor/) when they [build dashboards](ref:build-dashboards), use [Explore](ref:explore), and [annotate visualizations](ref:annotate-visualizations). - -The following guides will help you get started with the Prometheus data source: +The following documentation will help you get started working with Prometheus and Grafana: +- [What is Prometheus?](ref:intro-to-prometheus) +- [Prometheus data model](https://prometheus.io/docs/concepts/data_model/) +- [Getting started](https://prometheus.io/docs/prometheus/latest/getting_started/) - [Configure the Prometheus data source](ref:configure-prometheus-data-source) - [Prometheus query editor](query-editor/) - [Template variables](template-variables/) +## Exemplars + +In Prometheus, an **exemplar** is a specific trace that represents a measurement taken within a given time interval. While metrics provide an aggregated view of your system, and traces offer a detailed view of individual requests, exemplars serve as a bridge between the two, linking high-level metrics to specific traces for deeper insights. + +Exemplars associate higher-cardinality metadata from a specific event with traditional time series data. Refer to [Introduction to exemplars](ref:exemplars) in the Prometheus documentation for detailed information on how they work. + +Grafana can show exemplar data alongside a metric both in Explore and in Dashboards. + +{{< figure src="/static/img/docs/v74/exemplars.png" class="docs-image--no-shadow" caption="Exemplar window" >}} + +You add exemplars when you configure the Prometheus data source. + +{{< figure src="/static/img/docs/prometheus/exemplars-10-1.png" max-width="500px" class="docs-image--no-shadow" >}} + ## Prometheus API The Prometheus data source also works with other projects that implement the [Prometheus querying API](https://prometheus.io/docs/prometheus/latest/querying/api/). -For more information on how to query other Prometheus-compatible projects from Grafana, refer to the specific project's documentation: +For more information on how to query other Prometheus-compatible projects from Grafana, refer to the specific product's documentation: - [Grafana Mimir](/docs/mimir/latest/) - [Thanos](https://thanos.io/tip/components/query.md/) -## Provision the data source - -You can define and configure the data source in YAML files as part of Grafana's provisioning system. -For more information about provisioning, and for available configuration options, refer to [Provisioning Grafana](ref:provisioning-data-sources). - -{{% admonition type="note" %}} -Once you have provisioned a data source you cannot edit it. -{{% /admonition %}} - -### Provisioning example - -```yaml -apiVersion: 1 - -datasources: - - name: Prometheus - type: prometheus - access: proxy - # Access mode - proxy (server in the UI) or direct (browser in the UI). - url: http://localhost:9090 - jsonData: - httpMethod: POST - manageAlerts: true - prometheusType: Prometheus - prometheusVersion: 2.44.0 - cacheLevel: 'High' - disableRecordingRules: false - incrementalQueryOverlapWindow: 10m - exemplarTraceIdDestinations: - # Field with internal link pointing to data source in Grafana. - # datasourceUid value can be anything, but it should be unique across all defined data source uids. - - datasourceUid: my_jaeger_uid - name: traceID - - # Field with external link. - - name: traceID - url: 'http://localhost:3000/explore?orgId=1&left=%5B%22now-1h%22,%22now%22,%22Jaeger%22,%7B%22query%22:%22$${__value.raw}%22%7D%5D' -``` - ## View Grafana metrics with Prometheus -Grafana exposes metrics for Prometheus on the `/metrics` endpoint. -We also bundle a dashboard within Grafana so you can start viewing your metrics faster. +Grafana exposes metrics for Prometheus on the `/metrics` endpoint and includes a pre-built dashboard to help you start visualizing your metrics immediately. -**To import the bundled dashboard:** +Complete the following steps to import the pre-built dashboard: -1. Navigate to the data source's [configuration page](ref:configure-prometheus-data-source). -1. Select the **Dashboards** tab. - - This displays dashboards for Grafana and Prometheus. - -1. Select **Import** for the dashboard to import. +1. Navigate to the Prometheus [configuration page](ref:configure-prometheus-data-source). +1. Click the **Dashboards** tab. +1. Locate the **Grafana metrics** dashboard in the list and click **Import**. For details about these metrics, refer to [Internal Grafana metrics](ref:set-up-grafana-monitoring). ## Amazon Managed Service for Prometheus -The Prometheus data source with Amazon Managed Service for Prometheus is deprecated. Please use the [Amazon Managed service for Prometheus data source](https://grafana.com/grafana/plugins/grafana-amazonprometheus-datasource/). Migrations steps are detailed in the link. +Grafana has deprecated the Prometheus data source for Amazon Managed Service for Prometheus. Use the [Amazon Managed Service for Prometheus data source](https://grafana.com/grafana/plugins/grafana-amazonprometheus-datasource/) instead. The linked documentation outlines the migration steps. -## Azure authentication settings +## Get the most out of the Prometheus data source -The Prometheus data source works with Azure authentication. To configure Azure authentication see [Configure Azure Active Directory (AD) authentication](/docs/grafana/latest/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication). +After you install and configure Prometheus you can: -In Grafana Enterprise, update the .ini configuration file: [Configure Grafana](ref:configure-grafana). Depending on your setup, the .ini file is located [here](ref:configure-grafana-configuration-file-location). -Add the following setting in the **[auth]** section : - -```bash -[auth] -azure_auth_enabled = true -``` - -{{% admonition type="note" %}} -If you are using Azure authentication settings do not enable `Forward OAuth identity`. Both use the same HTTP authorization headers. Azure settings will get overwritten by the Oauth token. -{{% /admonition %}} - -## Exemplars - -Exemplars associate higher-cardinality metadata from a specific event with traditional time series data. See [Introduction to exemplars](ref:exemplars) in Prometheus documentation for detailed information on how they work. - -{{% admonition type="note" %}} -Available in Prometheus v2.26 and higher with Grafana v7.4 and higher. -{{% /admonition %}} - -Grafana can show exemplars data alongside a metric both in Explore and in Dashboards. - -{{< figure src="/static/img/docs/v74/exemplars.png" class="docs-image--no-shadow" caption="Screenshot showing the detail window of an Exemplar" >}} - -See the Exemplars section in [Configure Prometheus data source](ref:configure-prometheus-data-source). - -{{< figure src="/static/img/docs/prometheus/exemplars-10-1.png" max-width="500px" class="docs-image--no-shadow" caption="Exemplars" >}} - -## Incremental dashboard queries (beta) - -As of Grafana 10, the Prometheus data source can be configured to query live dashboards incrementally, instead of re-querying the entire duration on each dashboard refresh. - -This can be toggled on or off in the data source configuration or provisioning file (under `incrementalQuerying` in jsonData). -Additionally, the amount of overlap between incremental queries can be configured using the `incrementalQueryOverlapWindow` jsonData field, the default value is `10m` (10 minutes). - -Increasing the duration of the `incrementalQueryOverlapWindow` will increase the size of every incremental query, but might be helpful for instances that have inconsistent results for recent data. - -## Recording Rules (beta) - -The Prometheus data source can be configured to disable recording rules under the data source configuration or provisioning file (under `disableRecordingRules` in jsonData). +- Create a wide variety of [visualizations](ref:visualizations) +- Configure and use [templates and variables](ref:variables) +- Add [transformations](ref:transformations) +- Add [annotations](ref:annotate-visualizations) +- Set up [alerting](ref:alerting) +- Create [recorded queries](ref:recorded-queries) diff --git a/docs/sources/datasources/prometheus/configure-prometheus-data-source.md b/docs/sources/datasources/prometheus/configure-prometheus-data-source.md deleted file mode 100644 index a0888659638..00000000000 --- a/docs/sources/datasources/prometheus/configure-prometheus-data-source.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -aliases: - - ../data-sources/prometheus/ - - ../features/datasources/prometheus/ -description: Guide for configuring Prometheus in Grafana -keywords: - - grafana - - prometheus - - guide -labels: - products: - - cloud - - enterprise - - oss -menuTitle: Configure Prometheus -title: Configure the Prometheus data source -weight: 200 -refs: - intro-to-prometheus: - - pattern: /docs/grafana/ - destination: /docs/grafana//fundamentals/intro-to-prometheus/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//fundamentals/intro-to-prometheus/ - exemplars: - - pattern: /docs/grafana/ - destination: /docs/grafana//fundamentals/exemplars/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//fundamentals/exemplars/ - configure-data-links-value-variables: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/configure-data-links/#value-variables - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//panels-visualizations/configure-data-links/#value-variables - alerting-alert-rules: - - pattern: /docs/grafana/ - destination: /docs/grafana//alerting/fundamentals/alert-rules/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/ ---- - -# Configure Prometheus - -Grafana ships with built-in support for Prometheus. If you are new to Prometheus the following documentation will help you get started working with Prometheus and Grafana: - -- [What is Prometheus?](ref:intro-to-prometheus) -- [Prometheus data model](https://prometheus.io/docs/concepts/data_model/) -- [Getting started](https://prometheus.io/docs/prometheus/latest/getting_started/) - -## Configure the data source - -{{< shared id="add-prom-data-source" >}} - -To add the Prometheus data source, complete the following steps: - -1. Click **Connections** in the left-side menu. -1. Under **Connections**, click **Add new connection**. -1. Enter `Prometheus` in the search bar. -1. Click **Prometheus data source**. -1. Click **Add new data source** in the upper right. -1. Enter a name for the data source. - -{{< /shared >}} - -You will be taken to the **Settings** tab where you will set up your Prometheus configuration. - -## Configuration options - -The following is a list of configuration options for Prometheus. - -The first option to configure is the name of your connection: - -- **Name** - The data source name. This is how you refer to the data source in panels and queries. Examples: prometheus-1, prom-metrics. - -- **Default** - Toggle to select as the default name in dashboard panels. When you go to a dashboard panel this will be the default selected data source. - -### Connection section - -- **Prometheus server URL** - The URL of your Prometheus server. {{< shared id="prom-data-source-url" >}} - If your Prometheus server is local, use `http://localhost:9090`. If it's on a server within a network, this is the URL with the port where you are running Prometheus. Example: `http://prometheus.example.orgname:9090`. - -{{< admonition type="note" >}} - -If you're running Grafana and Prometheus together in different container environments, each localhost refers to its own container - if the server URL is localhost:9090, that means port 9090 inside the Grafana container, not port 9090 on the host machine. - -You should use the IP address of the Prometheus container, or the hostname if you are using Docker Compose. Alternatively, you can consider `http://host.docker.internal:9090`. - -{{< /admonition >}} - -{{< /shared >}} - -### Authentication section - -There are several authentication methods you can choose in the Authentication section. - -{{% admonition type="note" %}} - -Use TLS (Transport Layer Security) for an additional layer of security when working with Prometheus. For information on setting up TLS encryption with Prometheus see [Securing Prometheus API and UI Endpoints Using TLS Encryption](https://prometheus.io/docs/guides/tls-encryption/). You must add TLS settings to your Prometheus configuration file **prior** to setting these options in Grafana. - -{{% /admonition %}} - -- **Basic authentication** - The most common authentication method. Use your `data source` user name and `data source` password to connect. - -- **With credentials** - Toggle on to enable credentials such as cookies or auth headers to be sent with cross-site requests. - -- **TLS client authentication** - Toggle on to use client authentication. When enabled, add the `Server name`, `Client cert` and `Client key`. The client provides a certificate that is validated by the server to establish the client's trusted identity. The client key encrypts the data between client and server. - -- **With CA cert** - Authenticate with a CA certificate. Follow the instructions of the CA (Certificate Authority) to download the certificate file. - -- **Skip TLS verify** - Toggle on to bypass TLS certificate validation. - -- **Forward OAuth identity** - Forward the OAuth access token (and also the OIDC ID token if available) of the user querying the data source. - -### Custom HTTP headers - -- **Header** - Add a custom header. This allows custom headers to be passed based on the needs of your Prometheus instance. - -- **Value** - The value of the header. - -## Advanced settings - -Following are additional configuration options. - -### Advanced HTTP settings - -- **Allowed cookies** - Specify cookies by name that should be forwarded to the data source. The Grafana proxy deletes all forwarded cookies by default. - -- **Timeout** - The HTTP request timeout. This must be in seconds. The default is 30 seconds. - -### Alerting - -- **Manage alerts via Alerting UI** - Toggle to enable [data source-managed rules in Grafana Alerting](ref:alerting-alert-rules) for this data source. For `Mimir`, it enables managing data source-managed rules and alerts. For `Prometheus`, it only supports viewing existing rules and alerts, which are displayed as data source-managed. - -{{% admonition type="note" %}} - -The **Manage alerts via Alerting UI** toggle is enabled by default. You can change this behavior by setting the [default_manage_alerts_ui_toggle](../../../setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle) option in the Grafana configuration file. - -{{% /admonition %}} - -### Interval behavior - -- **Scrape interval** - Set to the typical scrape and evaluation interval configured in Prometheus. The default is `15s`. - -- **Query timeout** - The default is `60s`. - -### Query editor - -- **Default editor** - Sets a default editor. Options are `Builder` or `Code`. For information on query editor types see [Prometheus query editor](../query-editor/). - -- **Disable metrics lookup** - Toggle on to disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with large Prometheus instances. - -### Performance - -- **Prometheus type** - The type of your Prometheus server. There are four options: `Prometheus`, `Cortex`, `Mimir`, and `Thanos`. - -- **Cache level** - The browser caching level for editor queries. There are four options: `Low`, `Medium`, `High`, or `None`. - -- **Incremental querying (beta)** - Changes the default behavior of relative queries to always request fresh data from the Prometheus instance. Enable this option to decrease database and network load. - -- **Disable recording rules (beta)** - Toggle on to disable the recording rules. Enable this option to improve dashboard performance. - -### Other - -- **Custom query parameters** - Add custom parameters to the Prometheus query URL. For example `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be concatenated together with an '&'. - -- **HTTP method** - Use either `POST` or `GET` HTTP method to query your data source. `POST` is the recommended and pre-selected method as it allows bigger queries. Change to `GET` if you have a Prometheus version older than 2.1 or if `POST` requests are restricted in your network. - -### Exemplars - -Support for exemplars is available only for the Prometheus data source. If this is your first time working with exemplars see [Introduction to exemplars](ref:exemplars). An exemplar is a specific trace representative of measurement taken in a given time interval. - -- **Internal link** - Toggle on to enable an internal link. When enabled, reveals the data source selector. Select the backend tracing data store for your exemplar data. - -- **URL** - _(Visible if you **disable** `Internal link`)_ Defines the external link's URL trace backend. You can interpolate the value from the field by using the [`${__value.raw}` macro](ref:configure-data-links-value-variables). - -- **Data source** - _(Visible if you **enable** `Internal link`)_ The data source the exemplar will navigate to. - -- **URL label** - Adds a custom display label to override the value of the `Label name` field. - -- **Label name** - The name of the field in the `labels` object used to obtain the traceID property. - -- **Remove exemplar link** - Click to remove existing links. - -### Troubleshooting - -Refer to the following troubleshooting information, as required. - -#### Data doesn't appear in Explore metrics - - - -If metric data doesn't appear in Explore after you've successfully tested a connection to a Prometheus data source or sent -metrics to Grafana Cloud, ensure that you've selected the correct data source in the **Data source** drop-down menu. If -you've used remote_write to send metrics to Grafana Cloud, the data source name follows the convention -`grafanacloud-stackname-prom`. - - - -The following image shows the **Data source** field in Explore metrics. - -![Image that shows Prometheus metrics in Explore](/media/docs/grafana/data-sources/prometheus/troubleshoot-connection-1.png) diff --git a/docs/sources/datasources/prometheus/configure/_index.md b/docs/sources/datasources/prometheus/configure/_index.md new file mode 100644 index 00000000000..a1b3b641061 --- /dev/null +++ b/docs/sources/datasources/prometheus/configure/_index.md @@ -0,0 +1,301 @@ +--- +aliases: + - ../data-sources/prometheus/ + - ../features/datasources/prometheus/ +description: Guide for configuring Prometheus in Grafana +keywords: + - grafana + - prometheus + - guide +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Configure the Prometheus data source +title: Configure the Prometheus data source +weight: 200 +refs: + intro-to-prometheus: + - pattern: /docs/grafana/ + destination: /docs/grafana//fundamentals/intro-to-prometheus/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//fundamentals/intro-to-prometheus/ + exemplars: + - pattern: /docs/grafana/ + destination: /docs/grafana//fundamentals/exemplars/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//fundamentals/exemplars/ + configure-data-links-value-variables: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-data-links/#value-variables + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//panels-visualizations/configure-data-links/#value-variables + alerting-alert-rules: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/fundamentals/alert-rules/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/ + add-a-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/#add-a-data-source + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//datasources/#add-a-data-source + prom-query-editor: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/prometheus/query-editor + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//datasources/prometheus/query-editor + default-manage-alerts-ui-toggle: + - pattern: /docs/grafana/ + destination: /docs/grafana//setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle + provision-grafana: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/provisioning/ + manage-alerts-toggle: + - pattern: /docs/grafana/ + destination: /docs/grafana//setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle + private-data-source-connect: + - pattern: /docs/grafana/ + destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/ + - pattern: /docs/grafana-cloud/ + destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/ + configure-pdc: + - pattern: /docs/grafana/ + destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc + azure-active-directory: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/azure-monitor/#configure-azure-active-directory-ad-authentication + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//datasources/azure-monitor/#configure-azure-active-directory-ad-authentication + configure-grafana-configuration-file-location: + - pattern: /docs/grafana/ + destination: /docs/grafana//setup-grafana/configure-grafana/#configuration-file-location + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//setup-grafana/configure-grafana/#configuration-file-location +--- + +# Configure the Prometheus data source + +This document provides instructions for configuring the Prometheus data source and explains the available configuration options. Grafana includes built-in support for Prometheus, so you don't need to install a plugin. For general information on adding a data source to Grafana, refer to [Add a data source](ref:add-a-data-source). + +## Before you begin + +- You must have the `Organization administrator` role to add a data source. Administrators can also configure a data source via YAML files. + +- Know which Prometheus-compatible database you are using. + +- Familiarize yourself with your Prometheus security configuration and gather any necessary security certificates and client keys. + +- Verify that data from Prometheus is being written to your Grafana instance. + +## Configure the data source using the UI + +{{< shared id="add-prom-data-source" >}} + +To add the Prometheus data source, complete the following steps: + +1. Click **Connections** in the left-side menu. +1. Under **Connections**, click **Add new connection**. +1. Enter `Prometheus` in the search bar. +1. Click **Prometheus data source**. +1. Click **Add new data source** in the upper right. + +{{< /shared >}} + +Grafana takes you to the **Settings** tab where you will set up your Prometheus configuration. + +## Configuration options + +Following is a list of configuration options for Prometheus. + +- **Name** - The data source name. Sets the name you use to refer to the data source in panels and queries. Examples: prometheus-1, prom-metrics. +- **Default** - Toggle to select as the default name in dashboard panels. When you go to a dashboard panel this will be the default selected data source. + +**Connection:** + +- **Prometheus server URL** - The URL of your Prometheus server. {{< shared id="prom-data-source-url" >}} + If Prometheus is running locally, use `http://localhost:9090`. If it's hosted on a networked server, provide the server’s URL along with the port where Prometheus is running. Example: `http://prometheus.example.orgname:9090`. + +{{< admonition type="note" >}} +When running Grafana and Prometheus in separate containers, localhost refers to each container’s own network namespace. This means that `localhost:9090` points to port 9090 inside the Grafana container, not on the host machine. + +Use the IP address of the Prometheus container, or the hostname if you are using Docker Compose. Alternatively, you can use `http://host.docker.internal:9090` to reference the host machine. +{{< /admonition >}} + +{{< /shared >}} + +**Authentication:** + +There are three authentication options for the Prometheus data source. + +- **Basic authentication** - The most common authentication method. + + - **User** - The username you use to connect to the data source. + - **Password** - The password you use to connect to the data source. + +- **Forward OAuth identity** - Forward the OAuth access token (and also the OIDC ID token if available) of the user querying the data source. + +- **No authentication** - Allows access to the data source without any authentication. + +**TLS settings:** + +{{< admonition type="note" >}} +Use TLS (Transport Layer Security) for an additional layer of security when working with Prometheus. For information on setting up TLS encryption with Prometheus refer to [Securing Prometheus API and UI Endpoints Using TLS Encryption](https://prometheus.io/docs/guides/tls-encryption/). You must add TLS settings to your Prometheus configuration file **prior** to setting these options in Grafana. +{{< /admonition >}} + +- **Add self-signed certificate** - Check the box to authenticate with a CA certificate. Follow the instructions of the CA (Certificate Authority) to download the certificate file. Required for verifying self-signed TLS certificates. + - **CA certificate** - Add your certificate. +- **TLS client authentication** - Check the box to enable TLS client authentication. + - **Server name** - Add the server name, which is used to verify the hostname on the returned certificate. + - **Client certificate** - The client certificate is generated from a Certificate Authority or its self-signed. Follow the instructions of the CA (Certificate Authority) to download the certificate file. + - **Client key** - Add your client key, which can also be generated from a Certificate Authority (CA) or be self-signed. The client key encrypts data between the client and server. +- **Skip TLS verify** - Toggle on to bypass TLS certificate validation. Skipping TLS certificate validation is not recommended unless absolutely necessary or for testing purposes. + +**HTTP headers:** + +Pass along additional information and metadata about the request or response. + +- **Header** - Add a custom header. This allows custom headers to be passed based on the needs of your Prometheus instance. +- **Value** - The value of the header. + +**Advanced settings:** + +Following are optional configuration settings you can configure for more control over your data source. + +- **Advanced HTTP settings:** + - **Allowed cookies** - Specify cookies by name that should be forwarded to the data source. The Grafana proxy deletes all forwarded cookies by default. + - **Timeout** - The HTTP request timeout, must be in seconds. + +**Alerting:** + +- **Manage alerts via Alerting UI** -Toggled on by default. This enables [data source-managed rules in Grafana Alerting](ref:alerting-alert-rules) for this data source. For `Mimir`, it enables managing data source-managed rules and alerts. For `Prometheus`, it only supports viewing existing rules and alerts, which are displayed as data source-managed. Change this by setting the [`default_manage_alerts_ui_toggle`](ref:manage-alerts-toggle) option in the `grafana.ini` configuration file. + +**Interval behavior:** + +- **Scrape interval** - Sets the standard scrape and evaluation interval in Prometheus. The default is `15s`. This interval determines how often Prometheus scrapes targets. Set it to match the typical scrape and evaluation interval in your Prometheus configuration file. If you set a higher value than your Prometheus configuration, Grafana will evaluate data at this interval, resulting in fewer data points. +- **Query timeout** - Sets the Prometheus query timeout. The default is `60s`. Without a timeout, complex or inefficient queries can run indefinitely, consuming CPU and memory resources. + +**Query editor:** + +- **Default editor** - Sets the default query editor. Options are `Builder` or `Code`. `Builder` mode helps you build queries using a visual interface. `Code` mode is geared for the experienced Prometheus user with prior expertise in PromQL. For more details on editor types refer to [Prometheus query editor](ref:prom-query-editor). You can switch easily code editors in the Query editor UI. +- **Disable metrics lookup** - Toggle on to disable the metrics chooser and metric and label support in the query field's autocomplete. This can improve performance for large Prometheus instances. + +**Performance:** + +- **Prometheus type** - Select the type of your Prometheus-compatible database, such as Prometheus, Cortex, Mimir, or Thanos. Changing this setting will save your current configuration. Different database types support different APIs. For example, some allow `regex` matching for label queries to improve performance, while others provide a metadata API. Setting this incorrectly may cause unexpected behavior when querying metrics and labels. Refer to your Prometheus documentation to ensure you select the correct type. +- **Cache level** - Sets the browser caching level for editor queries. There are four options: `Low`, `Medium`, `High`, or `None`. Higher cache settings are recommended for high cardinality data sources. +- **Incremental querying (beta)** - Toggle on to enable incremental querying. Enabling this feature changes the default behavior of relative queries. Instead of always requesting fresh data from the Prometheus instance, Grafana will cache query results and only fetch new records. This helps reduce database and network load. + - **Query overlap window** - If you are using incremental querying, specify a duration (e.g., 10m, 120s, or 0s). The default is `10m`. This is a buffer of time added to incremental queries and this value is added to the duration of each incremental request. +- **Disable recording rules (beta)** - Toggle on to disable the recording rules. When recording rules are disabled, Grafana won't fetch and parse recording rules from Prometheus, improving dashboard performance by reducing processing overhead.. + +**Other settings:** + +- **Custom query parameters** - Add custom parameters to the Prometheus query URL, which allow for more control over how queries are executed. Examples: `timeout`, `partial_response`, `dedup`, or `max_source_resolution`. Multiple parameters should be joined using `&`. +- **HTTP method** - Select either the `POST` or `GET` HTTP method to query your data source. `POST`is recommended and selected by default, as it supports larger queries. Select `GET` if you're using Prometheus version 2.1 or older, or if your network restricts `POST` requests. + Toggle on +- **Use series endpoint** - Enabling this option makes Grafana use the series endpoint (/api/v1/series) with the match[] parameter instead of the label values endpoint (/api/v1/label//values). While the label values endpoint is generally more performant, some users may prefer the series endpoint because it supports the `POST` method, whereas the label values endpoint only allows `GET` requests. + +**Exemplars:** + +Support for exemplars is available only for the Prometheus data source. For more information on exemplars refer to [Introduction to exemplars](ref:exemplars). An exemplar is a trace that represents a specific measurement taken within a given time interval. + +Click the **+ sign** to add exemplars. + +- **Internal link** - Toggle on to enable an internal link. This will display the data source selector, where you can choose the backend tracing data store for your exemplar data. +- **URL** - _(Visible if you **disable** `Internal link`)_ Defines the external link's URL trace backend. You can interpolate the value from the field by using the [`${__value.raw}` macro](ref:configure-data-links-value-variables). +- **Data source** - _(Visible when`Internal link` is enabled.)_ Select the data source that the exemplar will link to from the drop-down. +- **URL label** - Adds a custom display label to override the value of the `Label name` field. +- **Label name** - The name of the field in the `labels` object used to obtain the traceID property. +- **Remove exemplar link** - Click the **X** to remove existing links. + +You can add multiple exemplars. + +- **Private data source connect** - _Only for Grafana Cloud users._ Private data source connect, or PDC, allows you to establish a private, secured connection between a Grafana Cloud instance, or stack, and data sources secured within a private network. Click the drop-down to locate the URL for PDC. For more information regarding Grafana PDC refer to [Private data source connect (PDC)](ref:private-data-source-connect) and [Configure Grafana private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc) for steps on setting up a PDC connection. + +Click **Manage private data source connect** to be taken to your PDC connection page, where you’ll find your PDC configuration details. + +After you have configured your Prometheus data source options, click **Save & test** at the bottom to test out your data source connection. + +You should see a confirmation dialog box that says: + +**Successfully queried the Prometheus API.** + +**Next, you can start to visualize data by building a dashboard, or by querying data in the Explore view.** + +You can also remove a connection by clicking **Delete**. + +## Provision the Prometheus data source + +You can define and configure the data source in YAML files as part of the Grafana provisioning system. For more information about provisioning, and for available configuration options, refer to [Provision Grafana](ref:provision-grafana). + +{{< admonition type="note" >}} +After you have provisioned a data source you cannot edit it. +{{< /admonition >}} + +**Example of a Prometheus data source configuration:** + + ```yaml + apiVersion: 1 + + datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://localhost:9090 + jsonData: + httpMethod: POST + manageAlerts: true + prometheusType: Prometheus + prometheusVersion: 3.3.0 + cacheLevel: 'High' + disableRecordingRules: false + timeInterval: 10s # Prometheus scrape interval + incrementalQueryOverlapWindow: 10m + exemplarTraceIdDestinations: + # Field with internal link pointing to data source in Grafana. + # datasourceUid value can be anything, but it should be unique across all defined data source uids. + - datasourceUid: my_jaeger_uid + name: traceID + + # Field with external link. + - name: traceID + url: 'http://localhost:3000/explore?orgId=1&left=%5B%22now-1h%22,%22now%22,%22Jaeger%22,%7B%22query%22:%22$${__value.raw}%22%7D%5D' + ``` + +## Azure authentication settings + +The Prometheus data source works with Azure authentication. To configure Azure authentication refer to [Configure Azure Active Directory (AD) authentication](ref:azure-active-directory). + +In Grafana Enterprise, you need to update the .ini configuration file. Refer to [Configuration file location](ref:configure-grafana-configuration-file-location) to locate your .ini file. + +Add the following setting in the **[auth]** section of the .ini configuration file: + +```bash +[auth] +azure_auth_enabled = true +``` + +{{% admonition type="note" %}} +If you are using Azure authentication, don't enable `Forward OAuth identity`. Both methods use the same HTTP authorization headers, and the OAuth token will override your Azure credentials. +{{% /admonition %}} + +## Recording rules (beta) + +You can configure the Prometheus data source to disable recording rules in the data source configuration or provisioning file under `disableRecordingRules` in jsonData. + +## Troubleshooting configuration issues + +Refer to the following troubleshooting information as needed. + +**Data doesn't appear in Metrics Drilldown:** + +If you have successfully tested the connection to a Prometheus data source or are sending metrics to Grafana Cloud and there is no metric data appearing in Explore, make sure you've selected the correct data source from the data source drop-down menu. When using `remote_write` to send metrics to Grafana Cloud, the data source name follows the convention `grafanacloud-stackname-prom`. diff --git a/docs/sources/datasources/prometheus/query-editor/_index.md b/docs/sources/datasources/prometheus/query-editor/_index.md new file mode 100644 index 00000000000..5a4b9894c4b --- /dev/null +++ b/docs/sources/datasources/prometheus/query-editor/_index.md @@ -0,0 +1,238 @@ +--- +aliases: + - ../../data-sources/prometheus/query-editor/ +description: Guide for using the Prometheus data source's query editor +keywords: + - grafana + - prometheus + - logs + - queries +labels: + products: + - cloud + - enterprise + - oss +menuTitle: Prometheus query editor +title: Prometheus query editor +weight: 300 +refs: + query-transform-data: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/query-transform-data/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//panels-visualizations/query-transform-data/ + table: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/table/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//panels-visualizations/visualizations/table/ + exemplars: + - pattern: /docs/grafana/ + destination: /docs/grafana//fundamentals/exemplars/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//fundamentals/exemplars/ + heatmap: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/heatmap/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//panels-visualizations/visualizations/heatmap/ + time-series-transform: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/visualizations/time-series/#transform + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//panels-visualizations/visualizations/time-series/#transform + explore: + - pattern: /docs/grafana/ + destination: /docs/grafana//explore/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//explore/ +--- + +# Prometheus query editor + +Grafana provides a query editor for the Prometheus data source to create queries in PromQL. For more information about PromQL, see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). The Prometheus query editor is located on the [Explore page](ref:explore). You can also access the PostgreSQL query editor from a dashboard panel. Click the ellipsis in the upper right of the panel and select **Edit**. + +For general documentation on querying data sources in Grafana, refer to [Query and transform data](ref:query-transform-data). For options and functions common to all query editors, refer to [Query editors](ref:query-transform-data). + +The Prometheus query editor has two modes: + +- [Builder mode](#builder-mode) +- [Code mode](#code-mode) + +![Query editor mode](/media/docs/prometheus/builder-code-v11-mode.png) + +Grafana synchronizes both modes, allowing you to switch between them. Grafana also displays a warning message if it detects an issue with the query while switching modes. + +You can configure Prometheus-specific options in the query editor by setting several options regardless of mode. + +{{< figure src="/static/img/docs/prometheus/options.png" max-width="500px" class="docs-image--no-shadow" caption="Options" >}} + +## Builder mode + +**Builder mode** helps you build queries using a visual interface. This option is best for users who have limited experience working with Prometheus and PromQL. + +The following video demonstrates how to use the visual Prometheus query builder: + +{{< vimeo 720004179 >}} + +Builder mode contains the following components: + +- **Kick start your query** - Click to view a list of predefined operation patterns that help you quickly build queries with multiple operations. These include: + + - Rate query starters + - Histogram query starters + - Binary query starters + +Click the arrow next to each to see the available options to add to your query. + +- **Explain** - Toggle on to display a step-by-step explanation of all query components and operations. + +{{< figure src="/static/img/docs/prometheus/explain-results.png" max-width="500px" class="docs-image--no-shadow" caption="Explain results" >}} + +- **Builder/Code** - Click the corresponding **Builder** or **Code** tab on the toolbar to select an editor mode. + +If you select Builder mode you will see the following options: + +- **Metric** - Select a metric from the drop-down. Click the icon to open Metrics explorer, where you can search for metrics by name and filter by type if your instance has a large number of metrics. Refer to [Metrics explorer](#metrics-explorer) for more detail on using this feature. +- **Label filters** - Select label filters from the drop-down. Select an operator and a value. + Select desired labels and their values from the drop-down list. + When a metric is selected, the data source requests available labels and their values from the server. + Use the `+` button to add a label, and the `x` button to remove a label. + +Click **+ Operations** to select from a list of operations including Aggregations, Range functions, Functions, Binary operations, Trigonometric and Time functions. You can select multiple operations. Refer to [Operations](#operations) for more detail. + +**Options:** + +- **Legend**- Lets you customize the name for the time series. You can use a predefined or custom format. + + - **Auto** - Displays unique labels. Also displays all overlapping labels if a series has multiple labels. + - **Verbose** - Displays all label names. + - **Custom** - Lets you customize the legend using label templates. For example, `{{hostname}}` is replaced with the value of the `hostname` label. To switch to a different legend mode, clear the input and click outside the field. + +- **Min step** - Sets the minimum interval between data points returned by the query. For example, setting this to `1h` suggests that data is collected or displayed at hourly intervals. This setting supports the `$__interval` and `$__rate_interval` macros. Note that the time range of the query is aligned to this step size, which may adjust the actual start and end times of the returned data. + +- **Format** - Determines how the data from your Prometheus query is interpreted and visualized in a panel. Choose from the following format options: + + - **Time series** - The default format. Refer to [Time series kind formats](https://grafana.com/developers/dataplane/timeseries/) for information on time series data frames and how time and value fields are structured. + - **Table** - Displays data in table format. This format works only in a [Table panel](ref:table). + - **Heatmap** - Displays Histogram-type metrics in a [Heatmap panel](ref:heatmap) by converting cumulative histograms to regular ones and sorting the series by the bucket bound. Converts cumulative histogram data into regular histogram format and sorts the series by bucket boundaries for proper display. + +- **Type** - This setting determines the query type. These include: + - **Both** - The default option. Returns results for both a **Range** query and an **Instant** query. + - **Range** - Returns a range vector - a set of time series + a set of time series where each series includes multiple data points over a period of time. You can choose to visualize the data as lines, bars, points, stacked lines, or stacked bars. + - **Instant** - Returns a single data point per series — the most recent value within the selected time range. The results can be displayed in a table or as raw data. To visualize instant query results in a time series panel, start by adding field override, then add a property to the override called `Transform`, and set the Transform value to `Constant` in the drop-down. For more information, refer to the [Time Series Transform option documentation](ref:time-series-transform). + +{{% admonition type="note" %}} +Grafana adjusts the query time range to align with the dynamically calculated step interval. This alignment ensures consistent metric visualization and supports Prometheus's result caching requirements. However, this alignment can cause minor visual differences, such as a slight gap at the graph's right edge or a shifted start time. For example, a `15s` step aligns timestamps to Unix times divisible by 15 seconds. A `1w` `minstep` aligns the range to the start of the week, which for Prometheus is Thursday at 00:00 UTC. +{{% /admonition %}} + +- **Exemplars** - Toggle on to run a query that includes exemplars in the graph. Exemplars are unique to Prometheus. For more information see [Introduction to exemplars](ref:exemplars). + +{{% admonition type="note" %}} +There is no option to add exemplars with an **Instant** query type. +{{% /admonition %}} + +### Filter metrics + +{{< figure src="/static/img/docs/prometheus/metrics-and-labels.png" max-width="500px" class="docs-image--no-shadow" caption="Metric and label filters" >}} + +When you are ready to create a query, you can choose the specific metric name from the drop-down list under **Metric**. +The data source provides the list of available metrics based on the selected time range. +You can also enter text into the selector when the drop-down is open to search and filter the list. + +#### Metrics explorer in Builder mode + +{{< figure src="/static/img/docs/prometheus/screenshot-grafana-prometheus-metrics-explorer-2.png" max-width="500px" class="docs-image--no-shadow" caption="Metrics explorer" >}} + +Click the **Open book icon** to open the Metrics explorer, where you can search for and filter all the metrics in your instance. + +If you would like to explore your metrics in the query builder further, you can open the **Metrics explorer** by clicking the first option in the metric select component of the query builder. + +The Metrics explorer displays all metrics in a paginated table list. The list shows the total number of metrics, as well as the name, type, and description for each metric. You can enter text into the search input to filter results. +You can also filter by type. + +The following options are included under the **Additional Settings** drop-down: + +- **Include description in search**: Toggle on to search by both name and description. +- **Include results with no metadata**: Toggle on to include metrics that lack type or description metadata. +- **Disable text wrap**: Toggle on to disable text wrapping. +- **Enable regex search**: Toggle on to filter metric names by regex search, which uses an additional call on the Prometheus API. + +{{% admonition type="note" %}} +The Metrics explorer (Builder mode) and [Metrics browser (Code mode)](#metrics-browser-in-code-mode) are separate elements. The Metrics explorer does not have the ability to browse labels yet, but the Metrics browser can display all labels on a metric name. +{{% /admonition %}} + +### Operations + +{{< figure src="/static/img/docs/prometheus/operations.png" max-width="500px" class="docs-image--no-shadow" caption="Operations" >}} + +Select the **+ Operations** button to add operations to your query. + +The query editor groups operations into the following sections: + +- Aggregations - for additional information see [Aggregation operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators). +- Range functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). +- Functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). +- Binary operations - for additional information see [Binary operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operators). +- Trigonometric - for additional information see [Trigonometric functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#trigonometric-functions). +- Time functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). + +All operations have function parameters under the operation header. Click the `operator` to see a full list of supported functions. Some operations allow you to apply specific labels to functions. + +{{< figure src="/static/img/docs/prometheus/use-function-by-label-9-5.png" max-width="500px" class="docs-image--no-shadow" caption="Functions and labels" >}} + +Some operations are only valid when used in a specific order. If you add an operation in a way that would create an invalid or illogical query, the query editor automatically places it in the correct position to maintain a valid query structure. + +### Hints + +The query editor can detect which operations are most appropriate for certain selected metrics. +When it does, it displays a hint next to the **+ Operations** button. + +To add the operation to your query, click the **Hint**. + +{{< figure src="/static/img/docs/prometheus/hint-example.png" max-width="500px" class="docs-image--no-shadow" caption="Hint" >}} + +When you are satisfied with your query, click **Run query**. + +## Code mode + +**Code mode** is for the experienced Prometheus user with prior expertise in PromQL, Prometheus' query language. The Code mode editor allows you to create queries just as you would in Prometheus. For more information about PromQL see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). + +{{< figure src="/static/img/docs/prometheus/code-mode.png" max-width="500px" class="docs-image--no-shadow" caption="Code mode" >}} + +The user interface (UI) also lets you select metrics, labels, filters, and operations. + +You can write complex queries using the text editor with autocompletion features and syntax highlighting. Code mode's autocomplete feature works automatically while typing. The query editor can autocomplete static functions, aggregations, keywords, and also dynamic items like metrics and labels. The autocompletion drop-down includes documentation for the suggested items where available. + +It also contains a [Metrics browser](#metrics-browser-in-code-mode) to further help you write queries. To open the Metrics browser, click the arrow next to **Metrics browser**. + +### Metrics browser in Code mode + +The Metrics browser locates metrics and selects relevant labels to help you build basic queries. +When you click **Metrics browser** in `Code` mode, it displays all available metrics and labels. +If supported by your Prometheus instance, each metric also displays its `HELP` and `TYPE` as a tooltip. + +{{< figure alt="Prometheus query editor metrics browser" src="/media/docs/prometheus/Metrics-browser-V10-prom-query-editor.png" caption="Metrics browser" >}} + +When you select a metric under **Step 1**, the browser narrows down the available labels to show only the ones applicable to the metric. +You can then select one or more labels shown in **Step 2**. +Select one or more values in **Step 3** for each label to tighten your query scope. +In **Step 4**, you can select **Use query** to run the query, **Use as rate query** to add the rate operation to your query (`$__rate_interval`), **Validate selector** to verify the selector is valid and show the number of series found, or **Clear** to clear your selections and start over. + +{{% admonition type="note" %}} +If you don't remember the exact metric name, you can start by selecting a few labels to filter the list. This helps you find relevant label values and narrow down your options. +{{% /admonition %}} + +All lists in the Metrics browser include a search field to quickly filter metrics or labels by keyword. +In the **Values** section, there's a single search field that filters across all selected labels, making it easier to find matching values. For example, if you have labels like `app`, `job`, and `job_name`, only one of them might contain the value you're looking for. + +When you are satisfied with your query, click **Run query**. + +## Incremental dashboard queries (beta) + +Starting with Grafana v10, the Prometheus data source supports incremental querying for live dashboards. Instead of re-querying the entire time range on each refresh, Grafana can fetch only new data since the last query. + +You can enable or disable this feature in the data source configuration or provisioning file using the `incrementalQuerying` field in `jsonData`. + +You can also control the overlap between consecutive incremental queries using the `incrementalQueryOverlapWindow` field in `jsonData`. By default, this is set to `10m` (10 minutes). Increasing the `incrementalQueryOverlapWindow` value increases the time range covered by each incremental query. This can help in environments where the most recent data may be delayed or incomplete. diff --git a/docs/sources/datasources/prometheus/query-editor/index.md b/docs/sources/datasources/prometheus/query-editor/index.md deleted file mode 100644 index 34d51182048..00000000000 --- a/docs/sources/datasources/prometheus/query-editor/index.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -aliases: - - ../../data-sources/prometheus/query-editor/ -description: Guide for using the Prometheus data source's query editor -keywords: - - grafana - - prometheus - - logs - - queries -labels: - products: - - cloud - - enterprise - - oss -menuTitle: Query editor -title: Prometheus query editor -weight: 300 -refs: - query-transform-data: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/query-transform-data/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//panels-visualizations/query-transform-data/ - table: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/table/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//panels-visualizations/visualizations/table/ - exemplars: - - pattern: /docs/grafana/ - destination: /docs/grafana//fundamentals/exemplars/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//fundamentals/exemplars/ - heatmap: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/heatmap/ - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//panels-visualizations/visualizations/heatmap/ - time-series-transform: - - pattern: /docs/grafana/ - destination: /docs/grafana//panels-visualizations/visualizations/time-series/#transform - - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//panels-visualizations/visualizations/time-series/#transform ---- - -# Prometheus query editor - -Grafana provides a query editor for the Prometheus data source to create queries in PromQL. For more information about PromQL, see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). - -For general documentation on querying data sources in Grafana, see [Query and transform data](ref:query-transform-data). - -For options and functions common to all query editors, see [Query editors](ref:query-transform-data). - -## Choose a query editing mode - -The Prometheus query editor has two modes: - -- [Builder mode](#builder-mode) -- [Code mode](#code-mode) - -Each mode is explained in greater detail below. - -{{< figure src="/static/img/docs/prometheus/editing-mode.png" max-width="500px" class="docs-image--no-shadow" caption="Query editor mode" >}} - -Both modes are synchronized, so you can switch between them. However, if there is an issue with the query while switching modes, a warning message will appear. - -## Toolbar elements - -The query editor toolbar contains the following elements: - -- **Kick start your query** - Click to see a list of operation patterns that help you quickly get started adding multiple operations to your query. These include: - - - Rate query starters - - Histogram query starters - - Binary query starters - -Click the arrow next to each to see available options to add to your query. - -- **Explain** - Toggle to display a step-by-step explanation of all query components and operations. - -{{< figure src="/static/img/docs/prometheus/explain-results.png" max-width="500px" class="docs-image--no-shadow" caption="Explain results" >}} - -- **Builder/Code** - Click the corresponding **Builder** or **Code** tab on the toolbar to select a editor mode. - -## Configure common options - -You can configure Prometheus-specific options in the query editor by setting several options regardless of mode. - -{{< figure src="/static/img/docs/prometheus/options.png" max-width="500px" class="docs-image--no-shadow" caption="Options" >}} - -### Legend - -The **Legend** setting defines the time series's name. You can use a predefined or custom format. - -- **Auto** - Displays unique labels. Also displays all overlapping labels if a series has multiple labels. -- **Verbose** - Displays all label names. -- **Custom** - Uses templating to select which labels will be included. For example, `{{hostname}}` is replaced by the label value for the label `hostname`. Clear the input and click outside of it to select another mode. - -### Min step - -The **Min step** setting defines the lower bounds on the interval between data points. -For example, set this to `1h` to hint that measurements are taken hourly. -This setting supports the `$__interval` and `$__rate_interval` macros. Be aware that the query range dates are aligned to the step and this can change the start and end of the range. - -### Format - -Switch between the following format options: - -- **Time series** - The default time series format. See [Time series kind formats](https://grafana.com/developers/dataplane/timeseries/) for information on time series data frames and how time and value fields are structured. -- **Table** - This works only in a [Table panel](ref:table). -- **Heatmap** - Displays metrics of the Histogram type on a [Heatmap panel](ref:heatmap) by converting cumulative histograms to regular ones and sorting the series by the bucket bound. - -### Type - -The **Type** setting sets the query type. These include: - -- **Both** - The default option. Returns results for both a **Range** query and an **Instant** query. -- **Range** - Returns a range vector consisting of a set of time series data containing a range of data points over time for each time series. You can choose lines, bars, points, stacked lines or stacked bars -- **Instant** - Returns one data point per query and only the most recent point in the time range provided. The results can be shown in table format or as raw data. To depict instant query results in the time series panel, first add a field override, next add a property to the override named `Transform`, and finally select `Constant` from the **Transform** dropdown. - -For more information, refer to the [Time Series Transform option documentation](ref:time-series-transform). - -{{% admonition type="note" %}} -Grafana modifies the request dates for queries to align them with the dynamically calculated step. -This ensures a consistent display of metrics data and Prometheus requires this for caching results. But, aligning the range with the step can result in a small gap of data at the right edge of a graph or change the start date of the range. For example, a 15s step aligns the range to Unix time divisible by 15s and a 1w minstep aligns the range to the start of the week on a Thursday. -{{% /admonition %}} - -### Exemplars - -Toggle **Exemplars** to run a query that includes exemplars in the graph. Exemplars are unique to Prometheus. For more information see [Introduction to exemplars](ref:exemplars). - -{{% admonition type="note" %}} -There is no option to add exemplars with an **Instant** query type. -{{% /admonition %}} - -### Inspector - -Click **Inspector** to get detailed statistics regarding your query. Inspector functions as a kind of debugging tool that "inspects" your query. It provides query statistics under **Stats**, request response time under **Query**, data frame details under **{} JSON**, and the shape of your data under **Data**. - -{{< figure src="/static/img/docs/prometheus/insepctor-9-5.png" max-width="500px" class="docs-image--no-shadow" caption="Inspector" >}} - -## Builder mode - -**Builder mode** helps you build queries using a visual interface. This option is best for users who have limited or no previous experience working with Prometheus and PromQL. - -This video demonstrates how to use the visual Prometheus query builder: - -{{< vimeo 720004179 >}} - -
- -### Metrics - -{{< figure src="/static/img/docs/prometheus/metrics-and-labels.png" max-width="500px" class="docs-image--no-shadow" caption="Metric and label filters" >}} - -When you are ready to create a query, you can choose the specific metric name from the dropdown list under **Metric**. -The data source requests the list of available metrics from the Prometheus server based on the selected time rage. -You can also enter text into the selector when the dropdown is open to search and filter the list. - -#### Metrics explorer - -{{< figure src="/static/img/docs/prometheus/screenshot-grafana-prometheus-metrics-explorer-2.png" max-width="500px" class="docs-image--no-shadow" caption="Metrics explorer" >}} - -If you would like to explore your metrics in the query builder further, you can open the **Metrics Explorer** by clicking the first option in the metric select component of the query builder. - -The metrics explorer is different than the metrics browser. The metrics explorer is only found in the query builder section. The metrics browser is only found in the code editor. The metrics explorer does not have the ability to browse labels yet, but the metrics browser can display all labels on a metric name. - -The metrics explorer displays all metrics in a paginated table list. The list shows the total number of metrics, as well as the name, type and description for each metric. You can enter text into the search input to filter results. -You can also filter by type. - -There are also additional settings for the following items: - -- Include description in search. Search by name **and** description -- Include results with no metadata. Many Prometheus metrics have no metadata. This allows users to include metrics with undefined type and description. -- Disable text wrap. -- Enable regex search. This uses the Prometheus API to enable regex search for the metric name. - -### Label filters - -Select desired labels and their values from the dropdown list. -When a metric is selected, the data source requests available labels and their values from the server. -Use the `+` button to add a label, and the `x` button to remove a label. - -### Operations - -{{< figure src="/static/img/docs/prometheus/operations.png" max-width="500px" class="docs-image--no-shadow" caption="Operations" >}} - -Select the **+ Operations** button to add operations to your query. - -The query editor groups operations into the following sections: - -- Aggregations - for additional information see [Aggregation operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators). -- Range functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). -- Functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). -- Binary operations - for additional information see [Binary operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operators). -- Trigonometric - for additional information see [Trigonometric functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#trigonometric-functions). -- Time functions - for additional information see [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/#functions). - -All operations have function parameters under the operation header. Click the `operator` to see a full list of supported functions. Some operations allow you to apply specific labels to functions. - -{{< figure src="/static/img/docs/prometheus/use-function-by-label-9-5.png" max-width="500px" class="docs-image--no-shadow" caption="Functions and labels" >}} - -Some operations make sense only when used in a specific order. -If adding an operation would result in nonsensical query, the query editor adds the operation to the correct place. - -#### Hints - -{{< figure src="/static/img/docs/prometheus/hint-example.png" max-width="500px" class="docs-image--no-shadow" caption="Hint" >}} - -The query editor can detect which operations are most appropriate for some selected metrics. -If it does, it displays a hint next to the **+ Operations** button. - -To add the operation to your query, click the **Hint**. - -Once you are satisfied with your query, click **Run query**. - -## Code mode - -**Code mode** is for the experienced Prometheus user with prior expertise in PromQL, Prometheus' query language. The Code mode editor allows you to create queries just as you would in Prometheus. For more information about PromQL see [Querying Prometheus](http://prometheus.io/docs/querying/basics/). - -{{< figure src="/static/img/docs/prometheus/code-mode.png" max-width="500px" class="docs-image--no-shadow" caption="Code mode" >}} - -The user interface (UI) also lets you select metrics, labels, filters and operations. - -You can write complex queries using the text editor with autocompletion features and syntax highlighting. -It also contains a [Metrics browser](#metrics-browser) to further help you write queries. - -### Use autocomplete - -Code mode's autocomplete feature works automatically while typing. The query editor can autocomplete static functions, aggregations, keywords, and also dynamic items like metrics and labels. -The autocompletion dropdown includes documentation for the suggested items where available. - -### Metrics browser - -The metrics browser locates metrics and selects relevant labels to help you build basic queries. -When you click **Metrics browser** in `Code` mode, it displays all available metrics and labels. -If supported by your Prometheus instance, each metric also displays its `HELP` and `TYPE` as a tooltip. - -{{< figure src="/static/img/docs/prometheus/metric-browser.png" max-width="500px" class="docs-image--no-shadow" caption="Metrics browser" >}} - -When you select a metric under Step 1, the browser narrows down the available labels to show only the ones applicable to the metric. -You can then select one or more labels shown in Step 2. -Select one or more values in Step 3 for each label to tighten your query scope. -In Step 4, you can select **Use query** to run the query, **Use as rate query** to add the rate operation to your query (`$__rate_interval`), **Validate selector** to verify the selector is valid and show the number of series found, or **Clear** to clear your selections and start over. - -{{% admonition type="note" %}} -If you do not remember a metric name, you can also select a few labels to narrow down the list, then find relevant label values. -{{% /admonition %}} - -All lists in the metrics browser have a search field above them to quickly filter for metrics or labels that match a certain string. -The values section has only one search field, and its filtering applies to all labels to help you find values across labels once selected. - -For example, among your labels `app`, `job`, `job_name` only one might have the value you are looking for. - -Once you are satisfied with your query, click **Run query**. diff --git a/docs/sources/datasources/prometheus/template-variables/index.md b/docs/sources/datasources/prometheus/template-variables/_index.md similarity index 68% rename from docs/sources/datasources/prometheus/template-variables/index.md rename to docs/sources/datasources/prometheus/template-variables/_index.md index 19511570ca0..f1f6ca5bb92 100644 --- a/docs/sources/datasources/prometheus/template-variables/index.md +++ b/docs/sources/datasources/prometheus/template-variables/_index.md @@ -13,7 +13,7 @@ labels: - cloud - enterprise - oss -menuTitle: Template variables +menuTitle: Prometheus template variables title: Prometheus template variables weight: 400 refs: @@ -42,13 +42,13 @@ refs: # Prometheus template variables Instead of hard-coding details such as server, application, and sensor names in metric queries, you can use variables. Grafana refers to such variables as **template** variables. -Grafana lists these variables in dropdown select boxes at the top of the dashboard to help you change the data displayed in your dashboard. +Grafana lists these variables in dropdown select boxes at the top of the dashboard to help you change the displayed data. -For an introduction to templating and template variables, see [Templating](ref:variables) and [Add and manage variables](ref:add-template-variables). +For an introduction to templating and template variables, refer to [Templating](ref:variables) and [Add and manage variables](ref:add-template-variables). ## Use query variables -You have the option to use several different variable types, but variables of the type `Query` will query Prometheus for a list of metrics, labels, label values, a query result or a series. +Grafana supports several types of variables, but Query variables are specifically used to query Prometheus. They can return a list of metrics, labels, label values, query results, or series. Select a Prometheus data source query type and enter the required inputs: @@ -61,18 +61,18 @@ Select a Prometheus data source query type and enter the required inputs: | `Series query` | `metric`, `label` or both | Returns a list of time series associated with the entered data. | /api/v1/series | | `Classic query` | classic query string | Deprecated, classic version of variable query editor. Enter a string with the query type using a syntax like the following: `label_values(,