merge main

This commit is contained in:
Ryan McKinley
2025-10-08 12:54:38 +03:00
3695 changed files with 359702 additions and 98026 deletions
+4 -4
View File
@@ -1,13 +1,13 @@
[build]
bin = "./bin/grafana"
bin = "./bin/grafana-air"
args_bin = ["server", "-profile", "-profile-addr=127.0.0.1", "-profile-port=6000", "-profile-block-rate=1", "-profile-mutex-rate=5", "-packaging=dev", "cfg:app_mode=development"]
cmd = "make GO_BUILD_DEV=1 build-backend"
cmd = "make GO_BUILD_DEV=1 build-air"
exclude_regex = ["_test.go", "_gen.go"]
exclude_unchanged = true
follow_symlink = true
include_dir = ["apps", "conf", "devenv/dev-dashboards", "pkg", "public/views"]
include_dir = ["apps", "conf", "pkg", "public/views"]
include_ext = ["go", "ini", "toml", "html", "json"]
stop_on_error = false
stop_on_error = true
send_interrupt = true
kill_delay = 500
-181
View File
@@ -1,181 +0,0 @@
// @ts-check
const emotionPlugin = require('@emotion/eslint-plugin');
const importPlugin = require('eslint-plugin-import');
const jestPlugin = require('eslint-plugin-jest');
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const lodashPlugin = require('eslint-plugin-lodash');
const barrelPlugin = require('eslint-plugin-no-barrel-files');
const reactPlugin = require('eslint-plugin-react');
const testingLibraryPlugin = require('eslint-plugin-testing-library');
const grafanaConfig = require('@grafana/eslint-config/flat');
const grafanaPlugin = require('@grafana/eslint-plugin');
const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin');
// Include the base Grafana configs and remove the rules,
// as we just want to pull in all of the necessary configuration but not run the rules
// (this should only be concerned with checking rules that we want to improve,
// so there's no need to try and run the rules that will be linted properly anyway)
const mappedBaseConfigs = grafanaConfig.map((/** @type {import('eslint').Linter.Config} */ config) => {
const { rules, ...baseConfig } = config;
return baseConfig;
});
/**
* @type {Array<import('eslint').Linter.Config>}
*/
module.exports = [
{
name: 'grafana/betterer-ignores',
ignores: [
'.github',
'.yarn',
'**/.*',
'**/*.gen.ts',
'**/build/',
'**/compiled/',
'**/dist/',
'data/',
'deployment_tools_config.json',
'devenv',
'e2e-playwright/test-plugins',
'e2e/tmp',
'packages/grafana-ui/src/components/Icon/iconBundle.ts',
'pkg',
'playwright-report',
'public/lib/monaco/',
'public/locales/_build',
'public/locales/**/*.js',
'public/vendor/',
'scripts/grafana-server/tmp',
'!.betterer.eslint.config.js',
],
},
{
name: 'react/jsx-runtime-rules',
rules: reactPlugin.configs.flat['jsx-runtime'].rules,
},
...mappedBaseConfigs,
{
files: ['**/*.{ts,tsx,js}'],
plugins: {
'@emotion': emotionPlugin,
lodash: lodashPlugin,
jest: jestPlugin,
import: importPlugin,
'jsx-a11y': jsxA11yPlugin,
'no-barrel-files': barrelPlugin,
'@grafana': grafanaPlugin,
'testing-library': testingLibraryPlugin,
'@grafana/i18n': grafanaI18nPlugin,
},
linterOptions: {
// This reports unused disable directives that we can clean up but
// it also conflicts with the betterer eslint rules so disabled
reportUnusedDisableDirectives: false,
},
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@grafana/no-aria-label-selectors': 'error',
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@grafana/ui*', '*/Layout/*'],
importNames: ['Layout', 'HorizontalGroup', 'VerticalGroup'],
message: 'Use Stack component instead.',
},
{
group: ['@grafana/ui/src/*', '@grafana/runtime/src/*', '@grafana/data/src/*'],
message: 'Import from the public export instead.',
},
],
},
],
},
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
ignores: [
'**/*.{test,spec}.{ts,tsx}',
'**/__mocks__/**',
'**/public/test/**',
'**/mocks.{ts,tsx}',
'**/mocks/**/*.{ts,tsx}',
'**/spec/**/*.{ts,tsx}',
],
rules: {
'@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.',
},
{
selector: 'CallExpression[callee.type="MemberExpression"][callee.property.name="localeCompare"]',
message:
'Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.',
},
],
},
},
{
files: ['public/app/**/*.{ts,tsx}'],
rules: {
'no-barrel-files/no-barrel-files': 'error',
},
},
{
// custom rule for Table to avoid performance regressions
files: ['packages/grafana-ui/src/components/Table/TableNG/Cells/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['**/themes/ThemeContext'],
importNames: ['useStyles2', 'useTheme2'],
message:
'Do not use "useStyles2" or "useTheme2" in a cell directly. Instead, provide styles to cells via `getDefaultCellStyles` or `getCellSpecificStyles`.',
},
],
},
],
},
},
];
-4764
View File
File diff suppressed because it is too large Load Diff
-119
View File
@@ -1,119 +0,0 @@
import { BettererFileTest } from '@betterer/betterer';
import { ESLint } from 'eslint';
import { promises as fs } from 'fs';
// Why are we ignoring these?
// They're all deprecated/being removed so doesn't make sense to fix types
const eslintPathsToIgnore = [
'packages/grafana-ui/src/graveyard', // will be removed alongside angular in Grafana 12
'public/app/angular', // will be removed in Grafana 12
'public/app/plugins/panel/graph', // will be removed alongside angular in Grafana 12
'public/app/plugins/panel/table-old', // will be removed alongside angular in Grafana 12
];
// Avoid using functions that report the position of the issues, as this causes a lot of merge conflicts
export default {
'better eslint': () =>
countEslintErrors()
.include('**/*.{ts,tsx}')
.exclude(new RegExp(eslintPathsToIgnore.join('|'))),
'no undocumented stories': () => countUndocumentedStories().include('**/*.story.tsx'),
'no skipping a11y tests in stories': () => countSkippedA11yTestStories().include('**/*.story.tsx'),
'no gf-form usage': () =>
regexp(/gf-form/gm, 'gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.')
.include('**/*.{ts,tsx,html}')
.exclude(new RegExp('packages/grafana-ui/src/themes/GlobalStyles')),
};
function countSkippedA11yTestStories() {
return new BettererFileTest(async (filePaths, fileTestResult) => {
await Promise.all(
filePaths.map(async (filePath) => {
// look for skipped a11y tests
const skipRegex = new RegExp("a11y: { test: 'off' }", 'gm');
const fileText = await fs.readFile(filePath, 'utf8');
const hasSkip = skipRegex.test(fileText);
if (hasSkip) {
// In this case the file contents don't matter:
const file = fileTestResult.addFile(filePath, '');
// Add the issue to the first character of the file:
file.addIssue(0, 0, 'No skipping of a11y tests in stories. Please fix the component or story instead.');
}
})
);
});
}
function countUndocumentedStories() {
return new BettererFileTest(async (filePaths, fileTestResult) => {
await Promise.all(
filePaths.map(async (filePath) => {
// look for .mdx import in the story file
const mdxImportRegex = new RegExp("^import.*\\.mdx';$", 'gm');
// Looks for the "autodocs" string in the file
const autodocsStringRegex = /autodocs/;
const fileText = await fs.readFile(filePath, 'utf8');
const hasMdxImport = mdxImportRegex.test(fileText);
const hasAutodocsString = autodocsStringRegex.test(fileText);
// If both .mdx import and autodocs string are missing, add an issue
if (!hasMdxImport && !hasAutodocsString) {
// In this case the file contents don't matter:
const file = fileTestResult.addFile(filePath, '');
// Add the issue to the first character of the file:
file.addIssue(0, 0, 'No undocumented stories are allowed, please add an .mdx file with some documentation');
}
})
);
});
}
/**
* Generic regexp pattern matcher, similar to @betterer/regexp.
* The only difference is that the positions of the errors are not reported, as this may cause a lot of merge conflicts.
*/
function regexp(pattern: RegExp, issueMessage: string) {
return new BettererFileTest(async (filePaths, fileTestResult) => {
await Promise.all(
filePaths.map(async (filePath) => {
const fileText = await fs.readFile(filePath, 'utf8');
const matches = fileText.match(pattern);
if (matches) {
// File contents doesn't matter, since we're not reporting the position
const file = fileTestResult.addFile(filePath, '');
matches.forEach(() => {
file.addIssue(0, 0, issueMessage);
});
}
})
);
});
}
function countEslintErrors() {
return new BettererFileTest(async (filePaths, fileTestResult) => {
// Just bail early if there's no files to test. Prevents trying to get the base config from failing
if (filePaths.length === 0) {
return;
}
const runner = new ESLint({
overrideConfigFile: './.betterer.eslint.config.js',
warnIgnored: false,
});
const lintResults = await runner.lintFiles(Array.from(filePaths));
lintResults.forEach(({ messages, filePath }) => {
const file = fileTestResult.addFile(filePath, '');
messages
.sort((a, b) => (a.message > b.message ? 1 : -1))
.forEach((message, index) => {
file.addIssue(0, 0, message.message, `${index}`);
});
});
});
}
+1
View File
@@ -4,6 +4,7 @@
.gitignore
.vscode
bin
!bin/grafana-linux-k8s
data*
dist
docker
+40 -8
View File
@@ -32,7 +32,9 @@
/devenv/README.md @grafana/docs-grafana
# START Technical documentation
/.vale.ini @grafana/docs-tooling
/.vale.ini @grafana/docs-tooling
/AGENTS.md @grafana/docs-tooling
# `make docs` procedure and related workflows are owned @grafana/docs-tooling. Slack #docs.
/docs/ @grafana/docs-tooling
@@ -72,6 +74,7 @@
# Git Sync / App Platform Provisioning
/apps/provisioning/ @grafana/grafana-git-ui-sync-team
/pkg/operators @grafana/grafana-git-ui-sync-team
/public/app/features/provisioning @grafana/grafana-git-ui-sync-team
/pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team
/pkg/tests/apis/provisioning @grafana/grafana-git-ui-sync-team
@@ -85,10 +88,12 @@
/apps/preferences/ @grafana/grafana-app-platform-squad @grafana/grafana-frontend-platform
/apps/shorturl/ @grafana/sharing-squad
/apps/secret/ @grafana/grafana-operator-experience-squad
/apps/scope/ @grafana/grafana-operator-experience-squad
/apps/investigations/ @fcjack @matryer @svennergr
/apps/advisor/ @grafana/plugins-platform-backend
/apps/iam/ @grafana/access-squad
/apps/sdk.mk @grafana/grafana-app-platform-squad
/apps/correlations @grafana/datapro
/pkg/api/ @grafana/grafana-backend-group
/pkg/apis/ @grafana/grafana-app-platform-squad
/pkg/apis/query @grafana/grafana-datasources-core-services
@@ -138,6 +143,7 @@
/pkg/apimachinery @grafana/grafana-app-platform-squad
/pkg/apimachinery/identity/ @grafana/identity-squad
/pkg/apimachinery/errutil/ @grafana/grafana-backend-group
/pkg/operators/iam @grafana/access-squad
/pkg/promlib @grafana/oss-big-tent
/pkg/storage/ @grafana/grafana-search-and-storage
/pkg/storage/secret/ @grafana/grafana-operator-experience-squad
@@ -185,7 +191,7 @@
/pkg/setting/ @grafana/grafana-backend-services-squad
/pkg/tests/ @grafana/grafana-backend-services-squad
/pkg/tests/apis/ @grafana/grafana-app-platform-squad
/pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend
/pkg/tests/apis/alerting @grafana/alerting-backend
/pkg/tests/apis/features @grafana/grafana-backend-services-squad
/pkg/tests/apis/folder @grafana/grafana-search-and-storage
/pkg/tests/apis/iam @grafana/identity-access-team
@@ -307,6 +313,7 @@
/devenv/docker/blocks/webdav/ @grafana/alerting-backend
/devenv/docker/buildcontainer/ @bergquist
/devenv/docker/compose_header.yml @grafana/grafana-backend-services-squad
/devenv/docker/compose_volume_section.yml @grafana/grafana-backend-services-squad
/devenv/docker/debtest/ @bergquist
/devenv/docker/ha-test-unified-alerting/ @grafana/alerting-backend
/devenv/docker/ha_test/ @grafana/grafana-backend-services-squad
@@ -409,12 +416,20 @@
/e2e/ @grafana/grafana-frontend-platform
/e2e-playwright/cloud-plugins-suite/ @grafana/partner-datasources
/e2e-playwright/dashboard-new-layouts/ @grafana/dashboards-squad
/e2e-playwright/dashboard-cujs/ @grafana/dashboards-squad
/e2e-playwright/dashboards-search-suite/ @grafana/dashboards-squad
/e2e-playwright/dashboards/cujs/ @grafana/dashboards-squad
/e2e-playwright/dashboards/DashboardForConditionalRendering.json @grafana/dashboards-squad
/e2e-playwright/dashboards/DashboardWithAllConditionalRendering.json @grafana/dashboards-squad
/e2e-playwright/dashboards/README.md @grafana/dashboards-squad
/e2e-playwright/dashboards/AdHocFilterTest.json @grafana/datapro
/e2e-playwright/dashboards/DashboardLiveTest.json @grafana/dashboards-squad
/e2e-playwright/dashboards/DataLinkWithoutSlugTest.json @grafana/dashboards-squad
/e2e-playwright/dashboards/PanelSandboxDashboard.json @grafana/plugins-platform-frontend
/e2e-playwright/dashboards/TestDashboard.json @grafana/dashboards-squad @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards/TestV2Dashboard.json @grafana/dashboards-squad
/e2e-playwright/dashboards/V2DashWithRepeats.json @grafana/dashboards-squad
/e2e-playwright/dashboards-suite/adhoc-filter-from-panel.spec.ts @grafana/datapro
/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts @grafana/sharing-squad
@@ -451,6 +466,8 @@
/e2e-playwright/fixtures/exemplars-query-response.json @grafana/observability-traces-and-profiling
/e2e-playwright/fixtures/long-trace-response.json @grafana/observability-traces-and-profiling
/e2e-playwright/fixtures/tempo-response.json @grafana/oss-big-tent
/e2e-playwright/fixtures/prometheus-response.json @grafana/datapro
/e2e-playwright/panels-suite/canvas-scene.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/dashlist.spec.ts @grafana/grafana-search-navigate-organise
/e2e-playwright/panels-suite/datagrid-data-change.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/datagrid-editing-features.spec.ts @grafana/dataviz-squad
@@ -461,9 +478,11 @@
/e2e-playwright/panels-suite/panelEdit_base.spec.ts @grafana/dashboards-squad
/e2e-playwright/panels-suite/panelEdit_queries.spec.ts @grafana/dashboards-squad
/e2e-playwright/panels-suite/panelEdit_transforms.spec.ts @grafana/datapro
/e2e-playwright/panels-suite/table-footer.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-kitchenSink.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-markdown.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-sparkline.spec.ts @grafana/dataviz-squad
/e2e-playwright/panels-suite/table-utils.ts @grafana/dataviz-squad
/e2e-playwright/plugin-e2e/ @grafana/oss-big-tent @grafana/partner-datasources
/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
/e2e-playwright/smoke-tests-suite/ @grafana/grafana-frontend-platform
@@ -611,6 +630,7 @@
/packages/grafana-runtime/rollup.config.ts @grafana/grafana-frontend-platform
/packages/grafana-runtime/src/index.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
/packages/grafana-runtime/src/internal/index.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
/packages/grafana-runtime/src/internal/openFeature @grafana/grafana-frontend-platform
/packages/grafana-runtime/src/unstable.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
/packages/grafana-runtime/tsconfig.build.json @grafana/grafana-frontend-platform
/packages/grafana-runtime/tsconfig.json @grafana/grafana-frontend-platform
@@ -727,7 +747,6 @@
/scripts/tsconfig.base.json @grafana/frontend-ops
/.editorconfig @grafana/frontend-ops
/eslint.config.js @grafana/frontend-ops
/.betterer.eslint.config.js @grafana/frontend-ops
/.gitattributes @grafana/frontend-ops
/.gitignore @grafana/frontend-ops
/.ignore @grafana/frontend-ops
@@ -753,6 +772,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
# public folder
/public/app/api/ @grafana/grafana-frontend-platform
/public/app/api/clients/folder/ @grafana/grafana-search-navigate-organise
/public/app/core/actions/ @grafana/grafana-frontend-platform
/public/app/core/app_events.ts @grafana/grafana-frontend-platform
/public/app/core/components/AccessControl/ @grafana/identity-access-team
@@ -857,6 +877,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/core/utils/accessControl.ts @grafana/identity-access-team
/public/app/core/utils/applyStateChanges.ts @grafana/dashboards-squad
/public/app/core/utils/arrayMove.ts @grafana/grafana-frontend-platform
/public/app/core/utils/isFrontendService.ts @grafana/grafana-frontend-platform
/public/app/core/utils/auth.ts @grafana/identity-access-team
/public/app/core/utils/browser* @grafana/grafana-frontend-platform
/public/app/core/utils/colors.ts @grafana/grafana-frontend-platform
@@ -910,6 +931,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/features/inspector/ @grafana/dashboards-squad
/public/app/features/logs/ @grafana/observability-logs
/public/app/features/live/ @grafana/dashboards-squad
/public/app/features/stars/ @grafana/grafana-search-navigate-organise
/public/app/features/apiserver/ @grafana/grafana-app-platform-squad
/public/app/features/manage-dashboards/ @grafana/dashboards-squad
/public/app/features/migrate-to-cloud @grafana/grafana-operator-experience-squad
@@ -934,6 +956,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/features/variables/ @grafana/dashboards-squad
/public/app/features/preferences/ @grafana/grafana-frontend-platform
/public/app/features/bookmarks/ @grafana/grafana-search-navigate-organise
/public/app/plugins/panel/* @grafana/dataviz-squad
/public/app/plugins/panel/alertlist/ @grafana/alerting-frontend
/public/app/plugins/panel/annolist/ @grafana/dashboards-squad
/public/app/plugins/panel/barchart/ @grafana/dataviz-squad
@@ -946,7 +969,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/public/app/plugins/panel/heatmap/ @grafana/dataviz-squad
/public/app/plugins/panel/histogram/ @grafana/dataviz-squad
/public/app/plugins/panel/logs/ @grafana/observability-logs
/public/app/plugins/panel/logs-new/ @grafana/observability-logs
/public/app/plugins/panel/nodeGraph/ @grafana/observability-traces-and-profiling @grafana/app-o11y-visualizations
/public/app/plugins/panel/traces/ @grafana/observability-traces-and-profiling
/public/app/plugins/panel/flamegraph/ @grafana/observability-traces-and-profiling
@@ -1003,7 +1025,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/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
/scripts/benchmark-access-control.sh @grafana/access-squad
/scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend
@@ -1011,7 +1032,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/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 @grafana/datapro
/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/frontend-ops
/scripts/cli/ @grafana/grafana-frontend-platform
/scripts/clean-git-or-error.sh @grafana/grafana-as-code
/scripts/grafana-server/ @grafana/grafana-frontend-platform
@@ -1037,11 +1058,14 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
/scripts/codemods/explicit-barrel-imports.cjs @grafana/frontend-ops
/scripts/rtk-client-generator/ @grafana/grafana-search-navigate-organise
/scripts/codeowners-manifest/ @grafana/dataviz-squad
/scripts/test-coverage-by-codeowner.js @grafana/dataviz-squad
/jest.config.codeowner.js @grafana/dataviz-squad
/scripts/**/generate-transformations* @grafana/datapro
/scripts/webpack/ @grafana/frontend-ops
/scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform
.betterer.results @grafanabot
.betterer.ts @grafana/grafana-frontend-platform
eslint-suppressions.json @grafanabot
# Design system
/public/img/icons/unicons/ @grafana/design-system
@@ -1129,6 +1153,8 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
# Feature toggles
/pkg/services/featuremgmt/ @grafana/grafana-backend-services-squad
# Data source migrations
/pkg/services/promtypemigration/ @grafana/partner-datasources @grafana/aws-datasources
# Kind definitions
/kinds/dashboard @grafana/dashboards-squad
@@ -1234,6 +1260,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/i18n-verify.yml @grafana/grafana-frontend-platform
/.github/workflows/deploy-storybook-preview.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform
/.github/workflows/scripts/publish-frontend-metrics.mts @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
@@ -1243,6 +1270,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/changelog.yml @zserge
/.github/workflows/shellcheck.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/cleanup-branches.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/publish-artifact.yml @grafana/grafana-developer-enablement-squad
/.github/actions/changelog @zserge
/.github/workflows/swagger-gen.yml @grafana/grafana-backend-group
@@ -1252,10 +1280,14 @@ embed.go @grafana/grafana-as-code
/.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform
/.github/workflows/frontend-perf-tests.yaml @grafana/grafana-frontend-platform
/.github/workflows/release-npm.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/determine-npm-tag.sh @grafana/grafana-frontend-platform
/.github/workflows/scripts/validate-commit-in-head.sh @grafana/grafana-frontend-platform
/.github/zizmor.yml @grafana/grafana-developer-enablement-squad
/.github/license_finder.yaml @bergquist
/.github/actionlint.yaml @grafana/grafana-developer-enablement-squad
/.github/workflows/pr-test-docker.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/update-schema-types.yml @grafana/plugins-platform-frontend
# Generated files not requiring owner approval
/packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot
+1
View File
@@ -139,6 +139,7 @@ runs:
with:
verb: run
dagger-flags: --verbose=0
version: 0.18.8
args: go run -C ${GRAFANA_PATH} ./pkg/build/cmd artifacts --artifacts ${ARTIFACTS} --grafana-dir=${GRAFANA_PATH} --alpine-base=${ALPINE_BASE} --ubuntu-base=${UBUNTU_BASE} --enterprise-dir=${ENTERPRISE_PATH} --version=${VERSION} --patches-repo=${PATCHES_REPO} --patches-ref=${PATCHES_REF} --patches-path=${PATCHES_PATH} --build-id=${BUILD_ID} --tag-format="${TAG_FORMAT}" --ubuntu-tag-format="${UBUNTU_TAG_FORMAT}" --org=${DOCKER_ORG} --registry=${DOCKER_REGISTRY} --checksum=${CHECKSUM} --verify=${VERIFY} > $OUTFILE
- id: output
shell: bash
@@ -86,7 +86,6 @@ runs:
- '.github/actions/change-detection/**'
- '**.cue'
- '.prettier*'
- '.betterer*'
- '.yarnrc.yml'
- 'eslint.config.js'
- 'jest.config.js'
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
persist-credentials: false
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 2
persist-credentials: false
+4 -4
View File
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4 # 4.2.2
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Check if update branch exists
@@ -60,10 +60,10 @@ jobs:
run: |
FROM_COMMIT="${{ steps.current-commit.outputs.from_commit }}"
TO_COMMIT="${{ steps.latest-commit.outputs.to_commit }}"
# Compare just the length of the shorter hash
SHORT_TO_COMMIT="${TO_COMMIT:0:${#FROM_COMMIT}}"
if [ "$FROM_COMMIT" = "$SHORT_TO_COMMIT" ]; then
echo "Current version ($FROM_COMMIT) is already at latest ($SHORT_TO_COMMIT). No update needed."
exit 0
@@ -157,4 +157,4 @@ jobs:
{
"channel": "C076RNRRZ2N",
"text": "Update alerting module in Grafana ${{ steps.create-pr.outputs.pull-request-url }}"
}
}
@@ -12,7 +12,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
+3 -2
View File
@@ -23,7 +23,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -42,7 +42,7 @@ jobs:
name: Validate Backend Configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@@ -58,6 +58,7 @@ jobs:
run: |
CODEGEN_VERIFY=1 make gen-cue
CODEGEN_VERIFY=1 make gen-jsonnet
CODEGEN_VERIFY=1 make gen-apps
- name: Validate go.mod
run: go run scripts/modowners/modowners.go check go.mod
+5 -5
View File
@@ -24,7 +24,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -55,7 +55,7 @@ jobs:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@@ -68,7 +68,7 @@ jobs:
run: |
set -euo pipefail
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")"
go test -short -timeout=30m "${PACKAGES[@]}"
CGO_ENABLED=0 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)
@@ -90,7 +90,7 @@ jobs:
steps:
# Set up repository clone
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@@ -118,7 +118,7 @@ jobs:
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")"
# This tee requires pipefail to be set, otherwise `go test`'s exit code is thrown away.
# That means having no `-o pipefail` => failing tests => exit code 0, which is wrong.
go test -short -timeout=30m "${PACKAGES[@]}"
CGO_ENABLED=0 go test -short -timeout=30m "${PACKAGES[@]}"
# This is the job that is actually required by rulesets.
# We need to require EITHER the OSS or the Enterprise job to pass.
+1 -1
View File
@@ -62,7 +62,7 @@ jobs:
echo "PR number: $PR_NUMBER"
- name: Checkout Grafana
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 2
+20 -8
View File
@@ -13,17 +13,29 @@ on:
required: false
permissions:
contents: write
pull-requests: write
id-token: write
contents: read
jobs:
bump-version:
runs-on: ubuntu-latest
steps:
- name: Checkout Grafana
uses: actions/checkout@v4
- uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
persist-credentials: false
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_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"contents": "write", "pull_requests": "write", "workflows": "write"}'
- name: Checkout Grafana
uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Update package.json versions
uses: ./pkg/build/actions/bump-version
with:
@@ -35,10 +47,10 @@ jobs:
DRY_RUN: ${{ inputs.dry_run }}
REF_NAME: ${{ github.ref_name }}
RUN_ID: ${{ github.run_id }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.generate_token.outputs.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 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
git checkout -b "bump-version/${RUN_ID}/${VERSION}"
git add .
+1 -1
View File
@@ -84,7 +84,7 @@ jobs:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
ref: main
sparse-checkout: |
+18
View File
@@ -0,0 +1,18 @@
name: Clean up orphaned branches
on:
workflow_dispatch:
schedule:
- cron: "0 9 * * 1"
jobs:
cleanup-branches:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- uses: actions/checkout@v5
- uses: grafana/shared-workflows/actions/cleanup-branches@cleanup-branches/v1.0.0
with:
dry-run: true
max-date: "1 month ago"
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
contents: read
steps:
# Checks-out your repository, which is validated in the next step
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: GitHub CODEOWNERS Validator
+2 -2
View File
@@ -33,7 +33,7 @@ jobs:
go: ${{ steps.detect-changes.outputs.backend }}
actions: 'true'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -63,7 +63,7 @@ jobs:
steps:
- name: Checkout repository
if: needs.detect-changes.outputs[matrix.language] == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }}
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions
@@ -43,7 +43,7 @@ jobs:
version: ${{ steps.build_frontend.outputs.version }}
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Verify inputs
@@ -62,7 +62,7 @@ jobs:
with:
credentials_json: '${{ env.PLUGINS_GOOGLE_CREDENTIALS }}'
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a'
uses: 'google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db'
- name: Setup nodejs environment
uses: actions/setup-node@v4
with:
+1 -1
View File
@@ -61,7 +61,7 @@ jobs:
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
repository: ${{ inputs.repository }}
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@@ -29,7 +29,7 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: './pr'
persist-credentials: false
@@ -80,7 +80,7 @@ jobs:
working-directory: './base'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: './base'
ref: ${{ github.event.pull_request.base.ref }}
@@ -132,7 +132,7 @@ jobs:
id-token: 'write'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@@ -165,7 +165,7 @@ jobs:
project_id: 'grafanalabs-global'
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a'
uses: 'google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db'
if: github.event.pull_request.head.repo.full_name == github.repository
with:
version: '>= 363.0.0'
@@ -220,7 +220,7 @@ jobs:
app-id: ${{ env.GITHUB_APP_ID }}
private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }}
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@@ -29,7 +29,7 @@ jobs:
if: github.event.pull_request.head.repo.full_name == github.repository
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
container:
image: grafana/vale:latest # zizmor: ignore[unpinned-images]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: grafana/writers-toolkit/vale-action@vale-action/v1 # zizmor: ignore[unpinned-uses]
@@ -10,7 +10,7 @@ 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' }}
if: ${{ github.repository_owner == 'grafana' && ((github.event.issue.pull_request && startsWith(github.event.comment.body, '/deploy-to-hg')) || github.event.action == 'closed') }}
runs-on:
labels: ubuntu-x64-xlarge
continue-on-error: true
@@ -42,7 +42,7 @@ jobs:
private_key: ${{ env.APP_PEM }}
- name: Checkout ephemeral instances repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
repository: grafana/ephemeral-grafana-instances-github-action
token: ${{ steps.generate_token.outputs.token }}
+3 -1
View File
@@ -11,13 +11,15 @@ permissions: {}
jobs:
test:
name: Feature toggles documentation is in sync with source
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
+7 -26
View File
@@ -18,7 +18,7 @@ jobs:
changed: ${{ steps.detect-changes.outputs.frontend }}
prettier: ${{ steps.detect-changes.outputs.frontend == 'true' || steps.detect-changes.outputs.docs == 'true' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -39,7 +39,7 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@@ -60,7 +60,7 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@@ -86,7 +86,7 @@ jobs:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@@ -106,7 +106,7 @@ jobs:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@@ -120,25 +120,6 @@ jobs:
github-app-name: 'grafana-ci-bot'
- run: yarn install --immutable --check-cache
- run: yarn run typecheck
lint-frontend-betterer:
needs: detect-changes
permissions:
contents: read
id-token: write
if: needs.detect-changes.outputs.changed == 'true'
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'
cache: 'yarn'
cache-dependency-path: 'yarn.lock'
- run: yarn install --immutable --check-cache
- run: yarn run betterer:ci
lint-frontend-api-clients:
permissions:
contents: read
@@ -149,7 +130,7 @@ jobs:
name: Verify API clients
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
@@ -180,7 +161,7 @@ jobs:
name: Verify API clients (enterprise)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
+4 -4
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@@ -58,7 +58,7 @@ jobs:
GRAFANA_ADMIN_USER: ${{ env.FSPERFBASELINE_USERNAME }}
GRAFANA_ADMIN_PASSWORD: ${{ env.FSPERFBASELINE_PASSWORD }}
run: yarn e2e:playwright --grep @performance --reporter json
- name: Run Playwright tests (fsperf)
id: pw-fsperf
continue-on-error: true
@@ -82,7 +82,7 @@ jobs:
-e PROMETHEUS_URL="$PROMETHEUS_URL" \
-e PROMETHEUS_USER="$PROMETHEUS_USER" \
-e PROMETHEUS_PASSWORD="$PROMETHEUS_TOKEN" \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.0 report \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.1 report \
--grafana-url "http://fsperfbaseline.grafana-dev.net" \
--test-suite-name "FrontendPerfTests" \
--report-input playwright \
@@ -104,7 +104,7 @@ jobs:
-e PROMETHEUS_URL="$PROMETHEUS_URL" \
-e PROMETHEUS_USER="$PROMETHEUS_USER" \
-e PROMETHEUS_PASSWORD="$PROMETHEUS_TOKEN" \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.0 report \
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.1 report \
--grafana-url "http://fsperf.grafana-dev.net" \
--test-suite-name "FrontendPerfTests" \
--report-input playwright \
+3 -3
View File
@@ -23,7 +23,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -38,7 +38,7 @@ jobs:
if: needs.detect-changes.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-go@v5.5.0
@@ -59,7 +59,7 @@ jobs:
if: needs.detect-changes.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-go@v5.5.0
+4 -4
View File
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions
@@ -97,7 +97,7 @@ jobs:
ACTOR: ${{ github.actor }}
- name: Checkout
if: steps.check-if-grafana-org-member.outputs.is_grafana_org_member != 'true' && github.event.issue.author_association != 'MEMBER' && github.event.issue.author_association != 'OWNER'
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
persist-credentials: false
sparse-checkout: |
@@ -144,7 +144,7 @@ jobs:
# Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_triager path in Vault
repo_secrets: |
GITHUB_APP_ID=plugins_platform_issue_triager_github_bot:app_id
GITHUB_APP_PRIVATE_KEY=plugins_platform_issue_triager_github_bot:app_pem
GITHUB_APP_PRIVATE_KEY=plugins_platform_issue_triager_github_bot:app_pem
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
@@ -166,4 +166,4 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
LABELS: internal
LABELS: internal
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions
uses: actions/checkout@v4 # v4.2.2
uses: actions/checkout@v5
with:
repository: "grafana/grafana-github-actions"
path: ./actions
@@ -37,7 +37,7 @@ jobs:
private-key: ${{ env.PRIVATE_KEY }}
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
+65 -27
View File
@@ -30,7 +30,7 @@ jobs:
changed: ${{ steps.detect-changes.outputs.e2e }}
cloud_plugins_changed: ${{ steps.detect-changes.outputs.e2e-cloud-plugins }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -48,7 +48,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@@ -67,6 +67,7 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a docker:grafana:linux/amd64 --grafana-dir="${PWD}" > out.txt
- name: Cat built artifact
@@ -117,7 +118,7 @@ jobs:
outputs:
artifact: ${{ steps.artifact.outputs.artifact }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@@ -148,19 +149,11 @@ jobs:
needs:
- build-grafana
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
- id: get-github-token
name: "create github app token"
uses: grafana/shared-workflows/actions/create-github-app-token@eb02241ed0a92aff205feab8ac3afcdf51c757c8 # create-github-app-token-v0.2.0
with:
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_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"checks": "write"}'
github_app: "delivery-bot-app"
- uses: grafana/shared-workflows/actions/login-to-gar@main
id: login-to-gar
with:
@@ -183,7 +176,7 @@ jobs:
echo "IMAGE=${DOCKER_IMAGE}" >> "$GITHUB_ENV"
- name: Add PR status check
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
GH_TOKEN: ${{ steps.get-github-token.outputs.token }}
SHA: ${{ github.event.pull_request.head.sha }}
run: |
gh api \
@@ -207,8 +200,6 @@ jobs:
fail-fast: false
matrix:
include:
- suite: various-suite
path: e2e/various-suite
- suite: various-suite (old arch)
path: e2e/old-arch/various-suite
flags: --flags="--env dashboardScene=false"
@@ -227,7 +218,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/download-artifact@v4
@@ -241,6 +232,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e --package=grafana.tar.gz
--suite=${{ matrix.path }}
@@ -270,7 +262,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@@ -303,7 +295,7 @@ jobs:
shardTotal: [8]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/download-artifact@v4
@@ -312,6 +304,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e-playwright --package=grafana.tar.gz --shard=${{ matrix.shard }}/${{ matrix.shardTotal }} --blob-dir=./blob-report
- uses: actions/upload-artifact@v4
@@ -322,7 +315,7 @@ jobs:
retention-days: 1
run-azure-monitor-e2e:
if: needs.detect-changes.outputs.cloud_plugins_changed == 'true' && github.event.pull_request.head.repo.fork == false
if: needs.detect-changes.outputs.cloud_plugins_changed == 'true' && github.event.pull_request.head.repo.fork == false && github.event_name == 'pull_request'
runs-on: ubuntu-x64-large
needs:
- build-grafana
@@ -331,7 +324,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@@ -374,6 +367,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e-playwright --package=grafana.tar.gz --playwright-command="yarn e2e:playwright:cloud-plugins" --cloud-plugin-creds=/tmp/outputs.json
@@ -397,7 +391,7 @@ jobs:
name: All Playwright tests complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
@@ -482,7 +476,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/download-artifact@v4
@@ -492,14 +486,58 @@ jobs:
if: github.event_name == 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz
- name: Run non-PR a11y test
if: github.event_name != 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail --results=./pa11y-ci-results.json
- name: Upload pa11y results
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
retention-days: 1
name: pa11y-ci-results
path: pa11y-ci-results.json
publish-metrics:
needs:
- run-a11y-test
name: Publish metrics
# Run on `grafana/grafana` main branch only
if: github.event_name == 'push' && github.repository == 'grafana/grafana' && github.ref_name == 'main'
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
repo_secrets: |
GRAFANA_MISC_STATS_API_KEY=grafana-misc-stats:api_key
- name: Checkout code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Install dependencies
run: yarn install --immutable
- name: Get pa11y results
uses: actions/download-artifact@v4
with:
name: pa11y-ci-results
- name: Extract and publish metrics
run: ./scripts/ci-frontend-metrics.sh | node --experimental-strip-types .github/workflows/scripts/publish-frontend-metrics.mts
env:
GRAFANA_MISC_STATS_API_KEY: ${{ env.GRAFANA_MISC_STATS_API_KEY}}
# This is the job that is actually required by rulesets.
# We want to only require one job instead of all the individual tests.
@@ -518,7 +556,7 @@ jobs:
name: All E2E tests complete
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
+6 -6
View File
@@ -19,7 +19,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -46,7 +46,7 @@ jobs:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
total: [16]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
@@ -78,7 +78,7 @@ jobs:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
total: [16]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Enterprise
@@ -107,7 +107,7 @@ jobs:
runs-on: ubuntu-x64-large
name: "Decoupled plugin tests"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
@@ -128,7 +128,7 @@ jobs:
runs-on: ubuntu-x64-large
name: "Packages unit tests"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Node.js
@@ -160,7 +160,7 @@ jobs:
name: All frontend unit tests complete
runs-on: ubuntu-x64-small
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Check test suites
+4 -4
View File
@@ -16,7 +16,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@@ -71,7 +71,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@@ -123,7 +123,7 @@ jobs:
echo "No changes in generated Go files"
else
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then
echo "> !!! Please link Enterprise and run 'make gen-go', then commit the changes to both repositories."
echo "> !!! Please synchronize the grafana OSS and grafana enterprise code bases as defined in the enterprise readme, then run 'make gen-go' in the OSS folder and commit the changes to both repositories."
else
echo "> !!! Please run 'make gen-go' and commit the changes."
fi
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend || steps.detect-changes.outputs.frontend || steps.detect-changes.outputs.dockerfile }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -31,7 +31,7 @@ jobs:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 # v4
+37 -5
View File
@@ -28,7 +28,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -37,7 +37,6 @@ jobs:
uses: ./.github/actions/change-detection
with:
self: .github/workflows/pr-test-integration.yml
sqlite:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
@@ -55,7 +54,7 @@ jobs:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@@ -70,6 +69,39 @@ jobs:
set -euo pipefail
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=8m -run '^TestIntegration' "${PACKAGES[@]}"
sqlite_nocgo:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
strategy:
matrix:
# We don't need more than this since it has to wait for the other tests.
shard: [
1/4, 2/4, 3/4, 4/4,
]
fail-fast: false
name: Sqlite Without CGo (${{ matrix.shard }})
runs-on: ubuntu-x64-large-io
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
uses: actions/setup-go@v5.5.0
with:
go-version-file: go.mod
cache: true
- name: Run tests
env:
SHARD: ${{ matrix.shard }}
run: |
set -euo pipefail
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)"
CGO_ENABLED=0 go test -tags=sqlite -timeout=8m -run '^TestIntegration' "${PACKAGES[@]}"
mysql:
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
@@ -103,7 +135,7 @@ jobs:
- 3306:3306
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
@@ -152,7 +184,7 @@ jobs:
- 5432:5432
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
+5 -1
View File
@@ -33,9 +33,13 @@ on:
type: string
required: false
default: github-prerelease-writer@grafanalabs-workload-identity.iam.gserviceaccount.com
runs-on:
type: string
required: false
default: github-hosted-ubuntu-x64-small
jobs:
publish:
runs-on: github-hosted-ubuntu-x64-small
runs-on: ${{ inputs.runs-on }}
name: Publish
permissions:
id-token: write
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
fetch-depth: 0
persist-credentials: false
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
# required for the `grafana/grafana-github-actions/has-matching-release-tag` action to work
fetch-depth: 0
@@ -38,7 +38,7 @@ jobs:
run: go run .github/workflows/scripts/kinds/verify-kinds.go
- name: "Checkout Actions library"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
repository: "grafana/grafana-github-actions"
path: "./actions"
@@ -15,7 +15,7 @@ jobs:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 # zizmor: ignore[unpinned-uses]
@@ -17,7 +17,7 @@ jobs:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
persist-credentials: false
+79 -3
View File
@@ -56,7 +56,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up version (Release Branches)
@@ -140,7 +140,7 @@ jobs:
# The downside to this is that the frontend will be built for each one when it could be reused for all of them.
# This could be a future improvement.
include:
- name: linux-amd64
- name: linux-amd64 # publish-npm relies on this step building npm packages
artifacts: targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook
verify: true
- name: linux-arm64
@@ -169,7 +169,7 @@ jobs:
verify: true
steps:
- uses: grafana/shared-workflows/actions/dockerhub-login@dockerhub-login/v1.0.2
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up QEMU
@@ -197,6 +197,7 @@ jobs:
name: artifacts-${{ matrix.name }}
path: ${{ steps.build.outputs.dist-dir }}
retention-days: 1
publish-artifacts:
name: Upload artifacts
uses: grafana/grafana/.github/workflows/publish-artifact.yml@main
@@ -211,6 +212,7 @@ jobs:
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}_${{ github.run_id }}
environment: prod
publish-dockerhub:
if: github.ref_name == 'main'
permissions:
@@ -268,3 +270,77 @@ jobs:
docker manifest push grafana/grafana:main-ubuntu
docker manifest push "grafana/grafana-dev:${VERSION}"
docker manifest push "grafana/grafana-dev:${VERSION}-ubuntu"
dispatch-npm-canaries:
if: github.ref_name == 'main'
name: Dispatch publish NPM canaries
permissions:
actions: write
contents: read
runs-on: ubuntu-x64-small
needs:
- setup
steps:
- name: Dispatch action
env:
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
VERSION: ${{ needs.setup.outputs.version }}
BUILD_ID: ${{ github.run_id }}
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run release-npm.yml \
--repo grafana/grafana \
--ref main \
--field grafana_commit="$GRAFANA_COMMIT" \
--field version="$VERSION" \
--field build_id="$BUILD_ID"\
--field version_type="canary"
# notify-pr creates (or updates) a comment in a pull request to link to this workflow where the release artifacts are
# being built.
notify-pr:
runs-on: ubuntu-x64-small
permissions:
contents: read
id-token: write
needs:
- setup
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
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_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"issues": "write", "pull_requests": "write", "contents": "read"}'
- name: Find PR
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
run: echo "ISSUE_NUMBER=$(gh api "/repos/grafana/grafana/commits/${GRAFANA_COMMIT}/pulls" | jq -r '.[0].number')" >> "$GITHUB_ENV"
- name: Find Comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
id: fc
with:
issue-number: ${{ env.ISSUE_NUMBER }}
comment-author: 'grafana-delivery-bot[bot]'
body-includes: GitHub Actions Build
token: ${{ steps.generate_token.outputs.token }}
- name: Create or update comment
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
token: ${{ steps.generate_token.outputs.token }}
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ env.ISSUE_NUMBER }}
body: |
:rocket: Your submission is now being built and packaged.
- [GitHub Actions Build](https://github.com/grafana/grafana/actions/runs/${{ github.run_id }})
- Version: ${{ needs.setup.outputs.version }}
edit-mode: replace
+134
View File
@@ -0,0 +1,134 @@
name: Release NPM packages
run-name: Publish NPM ${{ inputs.version_type }} ${{ inputs.version }}
on:
workflow_call:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
type: string
version:
description: 'Version to publish as'
required: true
type: string
build_id:
description: 'Run ID from the original release-build workflow'
required: true
type: string
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
type: string
workflow_dispatch:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
version:
description: 'Version to publish as'
required: true
build_id:
description: 'Run ID from the original release-build workflow'
required: true
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
permissions: {}
jobs:
# If called with version_type 'canary' or 'stable', build + publish to NPM
# If called with version_type 'nightly', do nothing (we're not yet tagging them with the nightly tag)
publish:
name: Publish NPM packages
runs-on: github-hosted-ubuntu-x64-small
if: inputs.version_type == 'canary' || inputs.version_type == 'stable'
# Required for this workflow to have permission to publish NPM packages
environment: npm-publish
permissions:
contents: read
id-token: write
steps:
- name: Info
env:
GITHUB_REF: ${{ github.ref }}
GRAFANA_COMMIT: ${{ inputs.grafana_commit }}
run: |
echo "GRAFANA_COMMIT: $GRAFANA_COMMIT"
echo "github.ref: $GITHUB_REF"
- name: Checkout workflow ref
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 100
fetch-tags: false
# this will fail with "{commit} is not a valid commit" if the commit is valid but
# not in the last 100 commits.
- name: Verify commit is in workflow HEAD
env:
GIT_COMMIT: ${{ inputs.grafana_commit }}
run: ./.github/workflows/scripts/validate-commit-in-head.sh
shell: bash
- name: Map version type to NPM tag
id: npm-tag
env:
VERSION: ${{ inputs.version }}
VERSION_TYPE: ${{ inputs.version_type }}
REFERENCE_PKG: "@grafana/runtime"
run: |
TAG=$(./.github/workflows/scripts/determine-npm-tag.sh)
echo "NPM_TAG=$TAG" >> "$GITHUB_OUTPUT"
shell: bash
- name: Checkout build commit
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ inputs.grafana_commit }}
- name: Setup Node
uses: ./.github/actions/setup-node
# Trusted Publishing is only available in npm v11.5.1 and later
- name: Update npm
run: npm install -g npm@^11.5.1
- name: Install dependencies
run: yarn install --immutable
- name: Typecheck packages
run: yarn run packages:typecheck
- name: Version, build, and pack packages
env:
VERSION: ${{ inputs.version }}
run: |
yarn run packages:build
yarn lerna version "$VERSION" \
--exact \
--no-git-tag-version \
--no-push \
--force-publish \
--yes
yarn run packages:pack
- name: Debug packed files
run: tree -a ./npm-artifacts
- name: Validate packages
run: ./scripts/validate-npm-packages.sh
- name: Debug OIDC Claims
uses: github/actions-oidc-debugger@2e9ba5d3f4bebaad1f91a2cede055115738b7ae8
with:
audience: '${{ github.server_url }}/${{ github.repository_owner }}'
- name: Publish packages
env:
NPM_TAG: ${{ steps.npm-tag.outputs.NPM_TAG }}
run: ./scripts/publish-npm-packages.sh --dist-tag "$NPM_TAG" --registry 'https://registry.npmjs.org/'
+3 -2
View File
@@ -118,14 +118,14 @@ jobs:
permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}"
- run: echo "RELEASE_BRANCH=release-${VERSION}" >> "$GITHUB_ENV"
- name: Checkout Grafana
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ steps.generate_changelog_token.outputs.token }}
ref: ${{ env.RELEASE_BRANCH }}
fetch-tags: true
fetch-depth: 0
- name: Checkout Grafana (main)
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ steps.generate_changelog_token.outputs.token }}
ref: main
@@ -198,6 +198,7 @@ jobs:
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run -C .grafana-main ./pkg/build/actions/bump-version -version="patch"
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@@ -25,7 +25,7 @@ jobs:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Pin Go version to mod file
@@ -96,7 +96,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Restore Cached Node Modules
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Pin Go version to mod file
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
fail() { echo "Error: $*" >&2; exit 1; }
# Ensure required variables are set
if [[ -z "${REFERENCE_PKG}" || -z "${VERSION_TYPE}" || -z "${VERSION}" ]]; then
fail "Missing required environment variables: REFERENCE_PKG, VERSION_TYPE, VERSION"
fi
semver_cmp () {
IFS='.' read -r -a arr_a <<< "$1"
IFS='.' read -r -a arr_b <<< "$2"
for i in 0 1 2; do
local aa=${arr_a[i]:-0}
local bb=${arr_b[i]:-0}
# shellcheck disable=SC2004
if (( 10#$aa > 10#$bb )); then echo gt; return 0; fi
if (( 10#$aa < 10#$bb )); then echo lt; return 0; fi
done
echo "eq"
}
STABLE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)$' # x.y.z
PRE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)$' # x.y.z-123456
# Validate that the VERSION matches VERSION_TYPE
# - stable must be x.y.z
# - nightly/canary must be x.y.z-123456
case "$VERSION_TYPE" in
stable)
[[ $VERSION =~ $STABLE_REGEX ]] || fail "For 'stable', version must match x.y.z" ;;
nightly|canary)
[[ $VERSION =~ $PRE_REGEX ]] || fail "For '$VERSION_TYPE', version must match x.y.z-123456" ;;
*)
fail "Unknown version_type '$VERSION_TYPE'" ;;
esac
# Extract major, minor from VERSION
IFS=.- read -r major minor patch _ <<< "$VERSION"
# Determine NPM tag
case "$VERSION_TYPE" in
canary) TAG="canary" ;;
nightly) TAG="nightly" ;;
stable)
# Use npm dist-tag "latest" as the reference
LATEST="$(npm view --silent "$REFERENCE_PKG" dist-tags.latest 2>/dev/null || true)"
echo "Latest for $REFERENCE_PKG is ${LATEST:-<none>}" >&2
if [[ -z ${LATEST:-} ]]; then
TAG="latest" # first ever publish
else
case "$(semver_cmp "$VERSION" "$LATEST")" in
gt) TAG="latest" ;; # newer than reference -> latest
lt|eq) TAG="v${major}.${minor}-latest" ;; # older or equal -> vX.Y-latest
esac
fi
;;
esac
echo "Resolved NPM_TAG=$TAG (VERSION=$VERSION, current latest=${LATEST:-none})" 1>&2 # stderr
printf '%s' "$TAG"
@@ -0,0 +1,75 @@
import fs from 'node:fs'
interface Payload {
name: string;
value: number;
interval: number;
mtype: string;
time: number;
}
console.log("Publishing metrics");
// Get API key from environment variable
const key = process.env.GRAFANA_MISC_STATS_API_KEY;
if (!key) {
throw new Error("API key is required. Provide it via the GRAFANA_MISC_STATS_API_KEY environment variable");
}
const unixTimestamp = Math.floor(Date.now() / 1000);
const data: Payload[] = [];
const input = fs.readFileSync(0, "utf-8");
// parse metrics from input
const regexp = /^Metrics: (\{.+\})/ms;
const matches = input.match(regexp);
if (!matches) {
throw new Error("No metrics found");
}
console.log('matches[0]', matches[0])
console.log('matches[1]', matches[1])
const metrics: Record<string, string> = JSON.parse(matches[1]);
// Convert metrics to payload format
for (const [metricName, valueStr] of Object.entries(metrics)) {
const value = parseInt(valueStr, 10);
if (isNaN(value)) {
throw new Error(`Metric "${metricName}" has invalid value format: "${valueStr}"`);
}
data.push({
name: metricName,
value: value,
interval: 60,
mtype: "gauge",
time: unixTimestamp,
});
}
const jsonPayload = JSON.stringify(data);
console.log(`Publishing metrics to https://graphite-us-central1.grafana.net/metrics, JSON: ${jsonPayload}`);
const url = 'https://graphite-us-central1.grafana.net/metrics';
const username = '6371';
const headers = new Headers();
headers.set("Content-Type", "application/json");
headers.set('Authorization', 'Basic ' + Buffer.from(username + ":" + key).toString('base64'));
try {
const response = await fetch(url, {
method: "POST",
headers,
body: jsonPayload,
});
if (!response.ok) {
throw new Error(`Metrics publishing failed with status code ${response.status}`);
}
console.log("Metrics successfully published");
} catch (error) {
throw new Error(`Metrics publishing failed: ${error instanceof Error ? error.message : String(error)}`);
}
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -z "${GIT_COMMIT:-}" ]]; then
echo "Error: Environment variable GIT_COMMIT is required"
exit 1
fi
if git merge-base --is-ancestor "$GIT_COMMIT" HEAD; then
echo "Commit $GIT_COMMIT is contained in HEAD"
else
echo "Error: Commit $GIT_COMMIT is not contained in HEAD"
exit 1
fi
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Run Shellcheck
+36 -5
View File
@@ -17,7 +17,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -27,7 +27,7 @@ jobs:
with:
self: .github/workflows/storybook-a11y.yml
test-storybook-a11y:
test-storybook-a11y-light:
runs-on: ubuntu-latest-8-cores
permissions:
contents: read
@@ -36,17 +36,48 @@ jobs:
if: needs.detect-changes.outputs.changed == 'true'
name: "Run Storybook a11y tests"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
package-manager-cache: false # too large for GH's cache limits :-(
- run: yarn install --immutable --check-cache
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Start Storybook
run: yarn storybook &
run: STORYBOOK_THEME=light yarn storybook &
- name: Run tests
# the chromium browser used by Playwright sets its locale to "en_US@posix" by default
# this is not a valid language code, and causes some stories to fail to load!
# instead, we set the LANG environment variable to en_US to override this
# see https://github.com/microsoft/playwright/issues/34046
env:
LANG: en_US
run: npx wait-on --timeout 120000 http://localhost:9001 && yarn test:storybook
test-storybook-a11y-dark:
runs-on: ubuntu-latest-8-cores
permissions:
contents: read
id-token: write
needs: detect-changes
if: needs.detect-changes.outputs.changed == 'true'
name: "Run Storybook a11y tests"
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
package-manager-cache: false # too large for GH's cache limits :-(
- run: yarn install --immutable --check-cache
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Start Storybook
run: STORYBOOK_THEME=dark yarn storybook &
- name: Run tests
# the chromium browser used by Playwright sets its locale to "en_US@posix" by default
# this is not a valid language code, and causes some stories to fail to load!
+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
outputs:
changed: ${{ steps.detect-changes.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: true # required to get more history in the changed-files action
fetch-depth: 2
@@ -45,7 +45,7 @@ jobs:
steps:
# Set up repository clone
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Setup Go
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
trivy-scan:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Install Trivy
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
shell: bash
run: echo "fetch_depth=$(( ${{ github.event.pull_request.commits }} + 2 ))" >> "$GITHUB_OUTPUT"
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
fetch-depth: ${{ steps.fetch_depth.outputs.fetch_depth }}
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
if: github.repository == 'grafana/grafana'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: grafana/writers-toolkit/update-make-docs@update-make-docs/v1 # zizmor: ignore[unpinned-uses]
+22
View File
@@ -0,0 +1,22 @@
name: Update Schema Types
on:
push:
branches:
- main
paths:
- docs/sources/developers/plugins/plugin.schema.json
workflow_dispatch:
# These permissions are needed to assume roles from Github's OIDC.
permissions:
contents: read
id-token: write
jobs:
bundle-schema-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/plugin-actions/bundle-schema-types@main
+5 -1
View File
@@ -6,12 +6,16 @@ on:
paths:
- '**/*.cue'
permissions: {}
jobs:
main:
runs-on: "ubuntu-latest"
permissions:
contents: read # clone repository
steps:
- name: "Checkout Grafana repo"
uses: "actions/checkout@v4"
uses: "actions/checkout@v5"
with:
fetch-depth: 0
persist-credentials: false
+14 -1
View File
@@ -42,6 +42,7 @@ __debug_bin*
/devenv/docker/blocks/auth/saml-enterprise
/devenv/docker/blocks/auth/signer
/devenv/docker/blocks/mt-db
/devenv/mt-tilt
/tmp
tools/phantomjs/phantomjs
@@ -94,6 +95,8 @@ example-apiserver/
/devenv/docker/blocks/auth/openldap/certs/
conf/custom.ini
conf/operator.ini
conf/storage.ini
/conf/provisioning/**/*.yaml
!/conf/provisioning/**/sample.yaml
@@ -127,6 +130,9 @@ profile.cov
/public/app/extensions
!/public/app/extensions/.keep
# Enterprise operators
/pkg/operators/enterprise_*
/pkg/operators/**/enterprise_*
debug.test
/examples/*/dist
@@ -189,6 +195,8 @@ compilation-stats.json
/e2e/build_results.zip
/e2e/extensions
!/e2e/extensions/.keep
/e2e-playwright/extensions
!/e2e-playwright/extensions/.keep
/e2e/extensions-suite
/test-results/
/playwright-report/
@@ -218,7 +226,6 @@ public/api-spec.json
deployment_tools_config.json
.betterer.cache
.nx
# Temporary file for backporting PRs
@@ -242,3 +249,9 @@ public/mockServiceWorker.js
/e2e-playwright/test-plugins/*/dist
/apps/provisioning/cmd/job-controller/bin/
# Ignore unified storage kv store files
/grafana-kv-data
/codeowners-manifest/
+2
View File
@@ -101,6 +101,8 @@ linters:
- '**/pkg/tsdb/azuremonitor/**/*'
- '**/pkg/tsdb/cloud-monitoring/*'
- '**/pkg/tsdb/cloud-monitoring/**/*'
- '**/pkg/tsdb/graphite/*'
- '**/pkg/tsdb/graphite/**/*'
- '**/pkg/tsdb/mysql/*'
- '**/pkg/tsdb/mysql/**/*'
- '**/pkg/tsdb/parca/*'
+6 -2
View File
@@ -11,10 +11,12 @@ node_modules
pkg
public/lib/monaco
public/sass/*.generated.scss
scripts/cli/bettererIssueTemplate.md
scripts/grafana-server/tmp
vendor
/coverage
/codeowners-manifest
# TS generate from cue by cuetsy
**/*.gen.ts
@@ -42,4 +44,6 @@ public/mockServiceWorker.js
# Playwright results
test-results
playwright-report
playwright-report
eslint-suppressions.json
+9
View File
@@ -120,6 +120,15 @@
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Debug ESLint with stats reporter",
"type": "node",
"request": "launch",
"runtimeExecutable": "yarn",
"runtimeArgs": ["run", "eslint", "${file}", "--format", "./scripts/cli/eslint-stats-reporter.mjs"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Debug Go test",
"type": "go",
File diff suppressed because one or more lines are too long
+3 -1
View File
@@ -25,4 +25,6 @@ plugins:
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.9.2.cjs
yarnPath: .yarn/releases/yarn-4.9.4.cjs
enableScripts: false
+297
View File
@@ -0,0 +1,297 @@
# AGENTS.md
<!-- docs-ai-begin -->
<!-- version: 1.1.0 -->
## Documentation
Instructions for documentation authoring in Markdown files.
DOCS.md contains all the Docs AI toolkit docs in one file.
## Role
Act as an experienced software engineer and technical writer for Grafana Labs.
Write for software developers and engineers who understand general programming concepts.
Focus on practical implementation and clear problem-solving guidance.
### Grafana
Use full product names on first mention, then short names:
- Grafana Alloy (full), Alloy (short)
- Grafana Beyla (full), Beyla (short)
Use "OpenTelemetry Collector" on first mention, then "Collector" for subsequent references.
Keep full name for distributions, headings, and links.
Always use "Grafana Cloud" in full.
Use complete terms:
- "OpenTelemetry" (not "OTel")
- "Kubernetes" (not "K8s")
Present observability signals in order: metrics, logs, traces, and profiles.
Focus content on Grafana solutions when discussing integrations or migrations.
## Style
### Structure
Structure articles into sections with headings.
Leave Markdown front matter content between two triple dashes `---`.
The front matter YAML `title` and the content h1 (#) heading should be the same.
Make sure there's an h1 heading in the content; this redundancy is required.
Always include copy after a heading or between headings, for example:
```markdown
## Heading
Immediately followed by copy and not another heading.
## Sub heading
```
The immediate copy after a heading should introduce and provide an overview of what's covered in the section.
Start articles with an introduction that covers the goal of the article. Example goals:
- Learn concepts
- Set up or install something
- Configure something
- Use a product to solve a business problem
- Troubleshoot a problem
- Integrate with other software or systems
- Migrate from one thing to another
- Refer to APIs or reference documentation
Follow the goal with a list of prerequisites, for example:
```markdown
Before you begin, ensure you have the following:
- <Prerequisite 1>
- <Prerequisite 2>
- ...
```
Suggest and link to next steps and related resources at the end of the article, for example:
- Learn more about A, B, C
- Configure X
- Use X to achieve Y
- Use X to achieve Z
- Project homepage or documentation
- Project repository (for example, GitHub, GitLab)
- Project package (for example, pip or NPM)
You don't need to use the "Refer to..." syntax for next steps; use the link text directly.
### Copy
Write simple, direct copy with short sentences and paragraphs.
Use contractions:
- it's, isn't, that's, you're, don't
Choose simple words:
- use (not utilize)
- help (not assist)
- show (not demonstrate)
Write with verbs and nouns. Use minimal adjectives except when describing Grafana Labs products.
## Tense
Write in present simple tense.
Avoid present continuous tense.
Only write in future tense to show future actions.
### Voice
Always write in an active voice.
Change passive voice to active voice.
### Perspective
Address users as "you".
Use second person perspective consistently.
### Wordlist
Use allowlist/blocklist instead of whitelist/blacklist.
Use primary/secondary instead of master/slave.
Use "refer to" instead of "see", "consult", "check out", and other phrases.
### Formatting
Use sentence case for titles and headings.
Use inline Markdown links: [Link text](https://example.com).
Link to other sections using descriptive phrases that include the section name:
"For setup details, refer to the [Lists](#lists) section."
Bold text with two asterisks: **bold**
Emphasize text with one underscore: _italics_
Format UI elements using sentence case as they appear:
- Click **Submit**.
- Navigate to **User settings**.
- Configure **Alerting rules**.
### Lists
Write complete sentences for lists:
- Works with all languages and frameworks (correct)
- All languages and frameworks (incorrect)
Use dashes for unordered lists.
Bold keywords at list start and follow with a colon.
### Images
Include descriptive alt text that conveys the essential information or purpose.
Write alt text without "Image of..." or "Picture of..." prefixes.
### Code
Use single code backticks for:
- user input
- placeholders in markdown, for example _`<PLACEHOLDER_NAME>`_
- files and directories, for example `/opt/file.md`
- source code keywords and identifiers,
for example variables, function and class names
- configuration options and values, for example `PORT` and `80`
- status codes, for example `404`
Use triple code backticks followed by the syntax for code blocks, for example:
```javascript
console.log('Hello World!');
```
Introduce each code block with a short description.
End the introduction with a colon if the code sample follows it, for example:
```markdown
The code sample outputs "Hello World!" to the browser console:
<CODE_BLOCK>
```
Use descriptive placeholder names in code samples.
Use uppercase letters with underscores to separate words in placeholders,
for example:
```sh
OTEL_RESOURCE_ATTRIBUTES="service.name=<SERVICE_NAME>
OTEL_EXPORTER_OTLP_ENDPOINT=<OTLP_ENDPOINT>
```
The placeholder includes the name and the less than and greater than symbols,
for example <PLACEHOLDER_NAME>.
If the placeholder is markdown emphasize it with underscores,
for example _`<PLACEHOLDER_NAME>`_.
In code blocks use the placeholder without additional backticks or emphasis,
for example <PLACEHOLDER_NAME>.
Provide an explanation for each placeholder,
typically in the text following the code block or in a configuration section.
Follow code samples with an explanation
and configuration options for placeholders, for example:
```markdown
<CODE_BLOCK>
This code sets required environment variables
to send OTLP data to an OTLP endpoint.
To configure the code refer to the configuration section.
<CONFIGURATION>
```
Put configuration for a code block after the code block.
## APIs
When documenting API endpoints specify the HTTP method,
for example `GET`, `POST`, `PUT`, `DELETE`.
Provide the full request path, using backticks.
Use backticks for parameter names and example values.
Use placeholders like `{userId}` for path parameters, for example:
- To retrieve user details, make a `GET` request to `/api/v1/users/{userId}`.
### CLI commands
When presenting CLI commands and their output,
introduce the command with a brief explanation of its purpose.
Clearly distinguish the command from its output.
For commands, use `sh` to specify the code block language.
For output, use a generic specifier like `text`, `console`,
or `json`/`yaml` if the output is structured.
For example:
```markdown
To list all running pods in the `default` namespace, use the following command:
<CODE_BLOCK>
```
The output will resemble the following:
```text
NAME READY STATUS RESTARTS AGE
my-app-deployment-7fdb6c5f65-abcde 1/1 Running 0 2d1h
another-service-pod-xyz123 2/2 Running 0 5h30m
```
### Shortcodes
Leave Hugo shortcodes in the content when editing.
Use our custom admonition Hugo shortcode for notes, cautions, or warnings,
with `<TYPE>` as "note", "caution", or "warning":
```markdown
{{< admonition type="<TYPE>" >}}
...
{{< /admonition >}}
```
Use admonitions sparingly.
Only include exceptional information in admonitions.
<!-- docs-ai-end -->
+222
View File
@@ -1,3 +1,224 @@
<!-- 12.2.0 START -->
# 12.2.0 (2025-09-23)
### Features and enhancements
- ** Alerting:** Add feedback buttons for the new AI helpers (Enterprise)
- **Access:** Remove plugin app access in plugin basic role seeder (Enterprise)
- **Actions:** Infinity authentication [#109493](https://github.com/grafana/grafana/pull/109493), [@adela-almasan](https://github.com/adela-almasan)
- **Alerting:** Add GMA export to the new list page [#109784](https://github.com/grafana/grafana/pull/109784), [@konrad147](https://github.com/konrad147)
- **Alerting:** Add alerting AI buttons for cloud (Enterprise)
- **Alerting:** Add contact point filter to Active Notifications page [#109775](https://github.com/grafana/grafana/pull/109775), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Add enrichment per rule extension component (Enterprise)
- **Alerting:** Add extension point link from alert rule to grafana-metricsdrilldown-app [#108566](https://github.com/grafana/grafana/pull/108566), [@bohandley](https://github.com/bohandley)
- **Alerting:** Add feature toggle and extension point [#110141](https://github.com/grafana/grafana/pull/110141), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
- **Alerting:** Add keepFiringFor and missing_series_evals_to_resolve to file provisioning [#109699](https://github.com/grafana/grafana/pull/109699), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Add observability to enrichment UI (Enterprise)
- **Alerting:** Add tooltips in enrichment list for enrichment type (Enterprise)
- **Alerting:** Alert enrichment list page (Enterprise)
- **Alerting:** Allow filter by rule source in Filter V2 [#110336](https://github.com/grafana/grafana/pull/110336), [@laurenashleigh](https://github.com/laurenashleigh)
- **Alerting:** Auto refresh contact points in the rule form [#109539](https://github.com/grafana/grafana/pull/109539), [@konrad147](https://github.com/konrad147)
- **Alerting:** Check if TimeInterval is used in ActiveTimings when deleting [#110691](https://github.com/grafana/grafana/pull/110691), [@fayzal-g](https://github.com/fayzal-g)
- **Alerting:** Disable group consistency check for GMA rules [#109599](https://github.com/grafana/grafana/pull/109599), [@konrad147](https://github.com/konrad147)
- **Alerting:** Display Error Message in Alert History View [#110123](https://github.com/grafana/grafana/pull/110123), [@laurenashleigh](https://github.com/laurenashleigh)
- **Alerting:** Enrichment Config Form (Enterprise)
- **Alerting:** Filter out private labels before writing recording rules [#109295](https://github.com/grafana/grafana/pull/109295), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** List V2 - Add a group link to the rule list item [#108960](https://github.com/grafana/grafana/pull/108960), [@konrad147](https://github.com/konrad147)
- **Alerting:** List V2 - datasource icons for rules [#109033](https://github.com/grafana/grafana/pull/109033), [@konrad147](https://github.com/konrad147)
- **Alerting:** Load labels in drop-downs without blocking the interaction with the form inputs [#110648](https://github.com/grafana/grafana/pull/110648), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
- **Alerting:** Mark Prometheus to Grafana conversion API as stable [#103499](https://github.com/grafana/grafana/pull/103499), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Move alerting file to an alerting folder [#110257](https://github.com/grafana/grafana/pull/110257), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
- **Alerting:** Support JSON responses in the Prometheus conversion API [#109070](https://github.com/grafana/grafana/pull/109070), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Support extra labels in the Prometheus conversion API [#109136](https://github.com/grafana/grafana/pull/109136), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Support retry with backoff in alert rule evaluation [#99710](https://github.com/grafana/grafana/pull/99710), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Triage alert history with Assistant if available (Enterprise)
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auth:** Add setting to disable username based brute force login protection [#109152](https://github.com/grafana/grafana/pull/109152), [@TheoBrigitte](https://github.com/TheoBrigitte)
- **Auth:** Support JWT configs `tls_client_ca` and `jwk_set_bearer_token_file` [#109095](https://github.com/grafana/grafana/pull/109095), [@Baarsgaard](https://github.com/Baarsgaard)
- **Azure:** Resource picker improvements (#109458) [#109520](https://github.com/grafana/grafana/pull/109520), [@aangelisc](https://github.com/aangelisc)
- **Azure:** Show resource group in picker [#110442](https://github.com/grafana/grafana/pull/110442), [@aangelisc](https://github.com/aangelisc)
- **Canvas:** Add option to disable tooltips for one-click elements [#109937](https://github.com/grafana/grafana/pull/109937), [@adela-almasan](https://github.com/adela-almasan)
- **Canvas:** Dynamic connection direction [#108423](https://github.com/grafana/grafana/pull/108423), [@adela-almasan](https://github.com/adela-almasan)
- **Chore:** Remove prometheusCodeModeMetricNamesSearch feature toggle [#109024](https://github.com/grafana/grafana/pull/109024), [@itsmylife](https://github.com/itsmylife)
- **Chore:** Removes HideAngularDeprecation configuration [#110665](https://github.com/grafana/grafana/pull/110665), [@hugohaggmark](https://github.com/hugohaggmark)
- **CloudConfig:** Add config from defaults.ini to StackInfo (Enterprise)
- **CloudWatch:** Append query type to the request id [#109068](https://github.com/grafana/grafana/pull/109068), [@idastambuk](https://github.com/idastambuk)
- **CloudWatch:** Use default region when query region is unset [#109089](https://github.com/grafana/grafana/pull/109089), [@iwysiu](https://github.com/iwysiu)
- **CloudWatch:** Use the correct metric name for errors per function panel in the AWS Lambda sample dashboard [#110718](https://github.com/grafana/grafana/pull/110718), [@kevinwcyu](https://github.com/kevinwcyu)
- **CommandPalette:** Use fuzzySearch util from grafana/data [#108884](https://github.com/grafana/grafana/pull/108884), [@Clarity-89](https://github.com/Clarity-89)
- **Dashboard:** Inspect drawer can no longer be opened with url or linked to [#109617](https://github.com/grafana/grafana/pull/109617), [@torkelo](https://github.com/torkelo)
- **Dashboards:** Add support for full screen panel view and embedded (solo panel) route to repeated panels and new layouts (via new SoloPanelContex) [#107375](https://github.com/grafana/grafana/pull/107375), [@torkelo](https://github.com/torkelo)
- **Dashboards:** Conserve timestamp on time range copy-paste across timezones [#109769](https://github.com/grafana/grafana/pull/109769), [@alik-r](https://github.com/alik-r)
- **Dashboards:** Enable kubernetesDashboards by default [#107618](https://github.com/grafana/grafana/pull/107618), [@dprokop](https://github.com/dprokop)
- **Dashboards:** Make it possible to render variables under a drop-down [#109225](https://github.com/grafana/grafana/pull/109225), [@leventebalogh](https://github.com/leventebalogh)
- **Database:** Add primary key to Settings table (Enterprise)
- **Database:** Add primary key to settings table (Enterprise)
- **Dependencies:** Bump Go to v1.24.5 (Enterprise)
- **Docs:** Deprecate `grafana/grafana-oss` docker repo in favor of `grafana/grafana` [#110065](https://github.com/grafana/grafana/pull/110065), [@kminehart](https://github.com/kminehart)
- **Flame Graph:** Analyze with Grafana Assistant [#108684](https://github.com/grafana/grafana/pull/108684), [@ifrost](https://github.com/ifrost)
- **Folders:** Add team folders feature toggle [#109389](https://github.com/grafana/grafana/pull/109389), [@tomratcliffe](https://github.com/tomratcliffe)
- **Folders:** Update folder using app platform APIs [#110449](https://github.com/grafana/grafana/pull/110449), [@tomratcliffe](https://github.com/tomratcliffe)
- **Folders:** Use app platform search endpoint and update tests [#108814](https://github.com/grafana/grafana/pull/108814), [@tomratcliffe](https://github.com/tomratcliffe)
- **Go:** Update to 1.24.6 [#109313](https://github.com/grafana/grafana/pull/109313), [@Proximyst](https://github.com/Proximyst)
- **InfluxDB:** Ad hoc filters support for expressions [#109344](https://github.com/grafana/grafana/pull/109344), [@aangelisc](https://github.com/aangelisc)
- **Metrics:** Add http_response_size_bytes metric [#110428](https://github.com/grafana/grafana/pull/110428), [@joshhunt](https://github.com/joshhunt)
- **Nested folders:** Remove feature flag [#109212](https://github.com/grafana/grafana/pull/109212), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
- **NestedFolderPicker:** Add rootFolderUID prop [#109991](https://github.com/grafana/grafana/pull/109991), [@ywzheng1](https://github.com/ywzheng1)
- **P2P Filter:** Add adhoc filter option toggle [#110160](https://github.com/grafana/grafana/pull/110160), [@Develer](https://github.com/Develer)
- **PieChart:** Add panel options for ascending/descending sort, and no sorting [#109564](https://github.com/grafana/grafana/pull/109564), [@cglukas](https://github.com/cglukas)
- **Plugin Extensions:** DataSource Configuration Components [#108350](https://github.com/grafana/grafana/pull/108350), [@shelldandy](https://github.com/shelldandy)
- **Plugins:** Add Connections homepage [#108316](https://github.com/grafana/grafana/pull/108316), [@oshirohugo](https://github.com/oshirohugo)
- **Plugins:** Record plugin version in request metrics [#110210](https://github.com/grafana/grafana/pull/110210), [@njvrzm](https://github.com/njvrzm)
- **Preferences:** Move codegen to apps [#109178](https://github.com/grafana/grafana/pull/109178), [@ryantxu](https://github.com/ryantxu)
- **Prometheus data source:** Migration service [#107364](https://github.com/grafana/grafana/pull/107364), [@bossinc](https://github.com/bossinc)
- **Prometheus:** Refactor metrics modal to handle high cardinality metrics [#108437](https://github.com/grafana/grafana/pull/108437), [@itsmylife](https://github.com/itsmylife)
- **Pyroscope:** Process and display sampling annotations [#109707](https://github.com/grafana/grafana/pull/109707), [@aleks-p](https://github.com/aleks-p)
- **Reporting:** Permit valid but weird emails (Enterprise)
- **Reporting:** Show correct recipient count (Enterprise)
- **Revert:** DataSource: Support config CRUD from apiservers (#106996) [#110342](https://github.com/grafana/grafana/pull/110342), [@njvrzm](https://github.com/njvrzm)
- **Revert:** DataSource: Support config CRUD from apiservers (#8860) (Enterprise)
- **SCIM:** Add flag for rejecting non provisioned users from logging in (Enterprise)
- **SCIM:** Allow empty externalId on update operation (Enterprise)
- **SCIM:** Delete user instead of disabling it on SCIM DELETE user request (Enterprise)
- **SQL Expressions:** Switch feature toggle to public preview [#110473](https://github.com/grafana/grafana/pull/110473), [@kylebrandt](https://github.com/kylebrandt)
- **Table:** Frozen columns [#109276](https://github.com/grafana/grafana/pull/109276), [@fastfrwrd](https://github.com/fastfrwrd)
- **Table:** Max row height for variable height rows [#109639](https://github.com/grafana/grafana/pull/109639), [@fastfrwrd](https://github.com/fastfrwrd)
- **Table:** Tooltip from Field [#109428](https://github.com/grafana/grafana/pull/109428), [@fastfrwrd](https://github.com/fastfrwrd)
- **Table:** Update UX for uniform-reducer case in new footer and overflow [#110493](https://github.com/grafana/grafana/pull/110493), [@fastfrwrd](https://github.com/fastfrwrd)
- **TableNG:** Footer enhancements [#102948](https://github.com/grafana/grafana/pull/102948), [@alexjonspencer1](https://github.com/alexjonspencer1)
- **Text:** Add Inter italic font variants to Grafana UI [#110313](https://github.com/grafana/grafana/pull/110313), [@kapowaz](https://github.com/kapowaz)
- **TraceView:** Refine UI visual hierarchy inside details section [#108929](https://github.com/grafana/grafana/pull/108929), [@ifrost](https://github.com/ifrost)
- **Transformations:** Add empty values options to Transpose [#108421](https://github.com/grafana/grafana/pull/108421), [@gelicia](https://github.com/gelicia)
- **Trend/TimeSeries:** Add "Show values" option [#108090](https://github.com/grafana/grafana/pull/108090), [@HasithDeAlwis](https://github.com/HasithDeAlwis)
- **Trend:** Add support for a logarithmic x axis [#101433](https://github.com/grafana/grafana/pull/101433), [@gelicia](https://github.com/gelicia)
- **Variables:** shows warning when user tries to save erroneous variables [#110154](https://github.com/grafana/grafana/pull/110154), [@hugohaggmark](https://github.com/hugohaggmark)
- **VizTooltip:** Replace `ExemplarHoverView` with `VizTooltip` components [#109369](https://github.com/grafana/grafana/pull/109369), [@adela-almasan](https://github.com/adela-almasan)
### Bug fixes
- **Alerting:** Fix bug where rules with identical mute/active intervals produced conflicting routes [#110971](https://github.com/grafana/grafana/pull/110971), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Fix copying of recording rule fields [#110311](https://github.com/grafana/grafana/pull/110311), [@moustafab](https://github.com/moustafab)
- **Alerting:** Fix field names on webhook HMAC/TLS config HCL export [#110722](https://github.com/grafana/grafana/pull/110722), [@JacobsonMT](https://github.com/JacobsonMT)
- **Alerting:** Fix newly created alert rules not immediately showing up in folder view [#109584](https://github.com/grafana/grafana/pull/109584), [@tomratcliffe](https://github.com/tomratcliffe)
- **Alerting:** Fix permission checks for the Import to GMA [#109950](https://github.com/grafana/grafana/pull/109950), [@konrad147](https://github.com/konrad147)
- **Alerting:** Fix permissions for enrichment routes (Enterprise)
- **Alerting:** Fix subpath handling in the alerting package [#109448](https://github.com/grafana/grafana/pull/109448), [@konrad147](https://github.com/konrad147)
- **Alerting:** Fix wrong import (Enterprise)
- **Alerting:** Hide list view loader if we don't have anything yet [#110464](https://github.com/grafana/grafana/pull/110464), [@gillesdemey](https://github.com/gillesdemey)
- **Alerting:** Set dataSourceName to GRAFANA_RULES_SOURCE_NAME when switch… [#109900](https://github.com/grafana/grafana/pull/109900), [@laurenashleigh](https://github.com/laurenashleigh)
- **Alerting:** Update alerting module to 10915888e4f099586ad37bea5f4a70f45101d2f5 [#109989](https://github.com/grafana/grafana/pull/109989), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Azure:** Fix logs editor rendering [#109491](https://github.com/grafana/grafana/pull/109491), [@aangelisc](https://github.com/aangelisc)
- **Canvas:** Fix element selection being cleared on panel resize [#110010](https://github.com/grafana/grafana/pull/110010), [@adela-almasan](https://github.com/adela-almasan)
- **CloudConfig:** Fix panic in defaults.ini merge (Enterprise)
- **CloudWatch:** Fix handling region for legacy alerts [#109217](https://github.com/grafana/grafana/pull/109217), [@iwysiu](https://github.com/iwysiu)
- **CloudWatch:** Fix logs query requestId to prevent setting undefined-logs as a requestId [#109930](https://github.com/grafana/grafana/pull/109930), [@kevinwcyu](https://github.com/kevinwcyu)
- **CloudWatch:** Update grafana/aws-sdk-go with STS endpoint bugfix [#109120](https://github.com/grafana/grafana/pull/109120), [@idastambuk](https://github.com/idastambuk)
- **Config:** Fix date_formats options being moved to a different section [#109339](https://github.com/grafana/grafana/pull/109339), [@joshhunt](https://github.com/joshhunt)
- **Dashboard List:** Fix how link query part is created when variables are included [#109861](https://github.com/grafana/grafana/pull/109861), [@aocenas](https://github.com/aocenas)
- **Dashboard versions:** Fix list for large dashboards [#109433](https://github.com/grafana/grafana/pull/109433), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
- **Dashboard:** Fix AngularJS deprecation in grafana-overview dashboard [#106462](https://github.com/grafana/grafana/pull/106462), [@schoen2](https://github.com/schoen2)
- **Dashboard:** Fixes url links to embedded panels in scene based dashboards [#109837](https://github.com/grafana/grafana/pull/109837), [@torkelo](https://github.com/torkelo)
- **Dashboards:** Fix UTF-8 characters not working with excel downloads by replacing download for excel with excel compatibility mode. [#110099](https://github.com/grafana/grafana/pull/110099), [@oscarkilhed](https://github.com/oscarkilhed)
- **Dashboards:** Fix issue where the time range picker would seemingly be hidden behind the side menu if it was set to always open. [#108607](https://github.com/grafana/grafana/pull/108607), [@oscarkilhed](https://github.com/oscarkilhed)
- **Dashboards:** Fix kiosk mode not persisting through refresh [#110284](https://github.com/grafana/grafana/pull/110284), [@oscarkilhed](https://github.com/oscarkilhed)
- **Dashboards:** Fixing saving and viewing snapshots for repeated panels [#109856](https://github.com/grafana/grafana/pull/109856), [@torkelo](https://github.com/torkelo)
- **Explore:** Fix units overflow for trace durations [#108515](https://github.com/grafana/grafana/pull/108515), [@martincostello](https://github.com/martincostello)
- **Fix:** Install plugins when they have no plugin archive info(catalog en… [#109200](https://github.com/grafana/grafana/pull/109200), [@s4kh](https://github.com/s4kh)
- **InfluxDB:** Fix Unable to use self-signed CA for adding influxdb data source [#105586](https://github.com/grafana/grafana/pull/105586), [@geekeryy](https://github.com/geekeryy)
- **Prometheus:** Don't use incremental querying if one of the queries has $\_\_range variable [#108823](https://github.com/grafana/grafana/pull/108823), [@itsmylife](https://github.com/itsmylife)
- **Prometheus:** Fix eager auto completion [#109128](https://github.com/grafana/grafana/pull/109128), [@itsmylife](https://github.com/itsmylife)
- **Prometheus:** QueryEditor fix error when switching from code to builder for undefined aggregation operations [#110179](https://github.com/grafana/grafana/pull/110179), [@jcolladokuri](https://github.com/jcolladokuri)
- **Pyroscope:** Add start and end date to profiletypes call [#110277](https://github.com/grafana/grafana/pull/110277), [@zoltanbedi](https://github.com/zoltanbedi)
- **Pyroscope:** Fix incorrect rate calculation from flamegraph totals [#110470](https://github.com/grafana/grafana/pull/110470), [@marcsanmi](https://github.com/marcsanmi)
- **Service Accounts:** Fix typo on page indicating none are present [#109560](https://github.com/grafana/grafana/pull/109560), [@eamonryan](https://github.com/eamonryan)
- **Tempo:** Fix instant query streaming [#108924](https://github.com/grafana/grafana/pull/108924), [@adrapereira](https://github.com/adrapereira)
- **TimeSeries:** Use exported time shift and fix time comparison tooltip [#109947](https://github.com/grafana/grafana/pull/109947), [@drew08t](https://github.com/drew08t)
- **Transformations:** Account for group by / count when assessing if calculation is needed [#110546](https://github.com/grafana/grafana/pull/110546), [@gelicia](https://github.com/gelicia)
- **Transforms:** GroupToMatrix transform should retain keyRowField config [#109066](https://github.com/grafana/grafana/pull/109066), [@fastfrwrd](https://github.com/fastfrwrd)
### Breaking changes
- **Alerting:** Enable alertingSaveStateCompressed by default [#109390](https://github.com/grafana/grafana/pull/109390), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Dashboards:** Repeating with no clone keys [#109839](https://github.com/grafana/grafana/pull/109839), [@torkelo](https://github.com/torkelo)
- **Provisioning:** Use inline secrets for gitsync [#109908](https://github.com/grafana/grafana/pull/109908), [@ryantxu](https://github.com/ryantxu)
- **Stars:** Remove deprecated internal ID apis [#110499](https://github.com/grafana/grafana/pull/110499), [@ryantxu](https://github.com/ryantxu)
### Plugin development fixes & changes
- **Drawer:** Truncate Drawer title to just one line [#109540](https://github.com/grafana/grafana/pull/109540), [@joshhunt](https://github.com/joshhunt)
- **Modal:** Center modals at smaller screen heights [#109256](https://github.com/grafana/grafana/pull/109256), [@ashharrison90](https://github.com/ashharrison90)
- **MultiCombobox:** Fix async options to being able to be removed [#109473](https://github.com/grafana/grafana/pull/109473), [@joshhunt](https://github.com/joshhunt)
- **MultiCombobox:** Fix select all when only a single option is available [#109910](https://github.com/grafana/grafana/pull/109910), [@aangelisc](https://github.com/aangelisc)
<!-- 12.2.0 END -->
<!-- 12.1.2 START -->
# 12.1.2 (2025-09-23)
### Features and enhancements
- **Alerting:** Update alerting module [#109999](https://github.com/grafana/grafana/pull/109999), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auditing:** Document new options for recording datasource query request/response body [#109981](https://github.com/grafana/grafana/pull/109981), [@macabu](https://github.com/macabu)
- **Chore:** Don't show a "Not found" for public-dashboard fetches if the service is disabled via config [#110144](https://github.com/grafana/grafana/pull/110144), [@mmandrus](https://github.com/mmandrus)
- **CloudWatch:** Use default region when query region is unset [#111079](https://github.com/grafana/grafana/pull/111079), [@iwysiu](https://github.com/iwysiu)
### Bug fixes
- **Alerting:** Fix bug where rules with identical mute/active intervals produced conflicting routes [#110973](https://github.com/grafana/grafana/pull/110973), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
- **Alerting:** Fix copying of recording rule fields [#110312](https://github.com/grafana/grafana/pull/110312), [@moustafab](https://github.com/moustafab)
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111097](https://github.com/grafana/grafana/pull/111097), [@mgyongyosi](https://github.com/mgyongyosi)
### Plugin development fixes & changes
- **Fix:** Prevent Rollup from treeshaking NPM packages [#108570](https://github.com/grafana/grafana/pull/108570), [@jackw](https://github.com/jackw)
<!-- 12.1.2 END -->
<!-- 12.0.5 START -->
# 12.0.5 (2025-09-23)
### Features and enhancements
- **Alerting:** Update alerting module [#110000](https://github.com/grafana/grafana/pull/110000), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auditing:** Document new options for recording datasource query request/response body [#109980](https://github.com/grafana/grafana/pull/109980), [@macabu](https://github.com/macabu)
### Bug fixes
- **Alerting:** Fix copying of recording rule fields [#110346](https://github.com/grafana/grafana/pull/110346), [@moustafab](https://github.com/moustafab)
- **Azure:** Fix time management field [#108481](https://github.com/grafana/grafana/pull/108481), [@aangelisc](https://github.com/aangelisc)
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111156](https://github.com/grafana/grafana/pull/111156), [@mgyongyosi](https://github.com/mgyongyosi)
### Plugin development fixes & changes
- **Fix:** Prevent Rollup from treeshaking NPM packages [#110523](https://github.com/grafana/grafana/pull/110523), [@jackw](https://github.com/jackw)
<!-- 12.0.5 END -->
<!-- 11.6.6 START -->
# 11.6.6 (2025-09-23)
### Features and enhancements
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
<!-- 11.6.6 END -->
<!-- 11.5.9 START -->
# 11.5.9 (2025-09-23)
### Features and enhancements
- **Auditing:** Add settings to control recording of datasource query request and response body (Enterprise)
- **Auditing:** Document new options for recording datasource query request/response body [#109976](https://github.com/grafana/grafana/pull/109976), [@macabu](https://github.com/macabu)
### Bug fixes
- **Fix:** Fix redirection after login when Grafana is served from subpath [#111099](https://github.com/grafana/grafana/pull/111099), [@mgyongyosi](https://github.com/mgyongyosi)
<!-- 11.5.9 END -->
<!-- 12.1.1 START -->
# 12.1.1 (2025-08-13)
@@ -13,6 +234,7 @@
- **Alerting:** Fix active time intervals when time interval is renamed [#108547](https://github.com/grafana/grafana/pull/108547), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Alerting:** Fix subpath handling in the alerting package [#109505](https://github.com/grafana/grafana/pull/109505), [@konrad147](https://github.com/konrad147)
- **Config:** Fix date_formats options being moved to a different section [#109366](https://github.com/grafana/grafana/pull/109366), [@joshhunt](https://github.com/joshhunt)
- **Pyroscope:** Fix flamegraph totals showing incorrect values after rate aggregation changes [#110470](https://github.com/grafana/grafana/pull/110470), [@marcsanmiquel](https://github.com/marcsanmiquel)
<!-- 12.1.1 END -->
<!-- 12.0.4 START -->
+1 -1
View File
@@ -2,7 +2,7 @@
Thank you for your interest in contributing to Grafana! We welcome all people who want to contribute in a healthy and constructive manner within our community. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](CODE_OF_CONDUCT.md).
This document is a guide to help you through the process of making technical contributions to Grafana.
This document is a guide to help you through the process of contributing to Grafana. Be sure to check out the [Grafana Champions program](https://grafana.com/community/champions/?src=github&camp=community-cross-platform-engagement) as you start to contribute- it’s designed to recognize and empower individuals who are actively contributing to the growth and success of the Grafana ecosystem.
Whether you're a new contributer or a seasoned veteran we hope these resources help you connect with the community:
+4
View File
@@ -95,9 +95,11 @@ COPY pkg/aggregator pkg/aggregator
COPY apps/playlist apps/playlist
COPY apps/plugins apps/plugins
COPY apps/shorturl apps/shorturl
COPY apps/correlations apps/correlations
COPY apps/preferences apps/preferences
COPY apps/provisioning apps/provisioning
COPY apps/secret apps/secret
COPY apps/scope apps/scope
COPY apps/investigations apps/investigations
COPY apps/advisor apps/advisor
COPY apps/dashboard apps/dashboard
@@ -106,7 +108,9 @@ COPY apps/preferences apps/preferences
COPY apps/iam apps/iam
COPY apps apps
COPY kindsv2 kindsv2
COPY apps/alerting/alertenrichment apps/alerting/alertenrichment
COPY apps/alerting/notifications apps/alerting/notifications
COPY apps/alerting/rules apps/alerting/rules
COPY pkg/codegen pkg/codegen
COPY pkg/plugins/codegen pkg/plugins/codegen
+21 -2
View File
@@ -17,6 +17,7 @@ GO_RACE_FLAG := $(if $(GO_RACE),-race)
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_BUILD_FLAGS += $(if $(GO_BUILD_CGO),-cgo-enabled=$(GO_BUILD_CGO))
GO_TEST_FLAGS += $(if $(GO_BUILD_TAGS),-tags=$(GO_BUILD_TAGS))
GIT_BASE = remotes/origin/main
@@ -70,6 +71,7 @@ swagger-oss-gen: ## Generate API Swagger specification
-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" \
-x "github.com/docker/docker" \
-i pkg/api/swagger_tags.json \
--exclude-tag=alpha \
--exclude-tag=enterprise
@@ -88,6 +90,7 @@ swagger-enterprise-gen: ## Generate API Swagger specification
-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" \
-x "github.com/docker/docker" \
-i pkg/api/swagger_tags.json \
-t enterprise \
--exclude-tag=alpha \
@@ -171,7 +174,19 @@ gen-cuev2: ## Do all CUE code generation
APPS_DIRS := ./apps/dashboard ./apps/folder ./apps/alerting/notifications
.PHONY: gen-apps
gen-apps: ## Generate code for Grafana App SDK apps
gen-apps: do-gen-apps gofmt ## Generate code for Grafana App SDK apps and run gofmt
@if [ -n "$$CODEGEN_VERIFY" ]; then \
echo "Verifying generated code is up to date..."; \
if ! git diff --quiet; then \
echo "Error: Generated apps code is not up to date. Please run 'make gen-apps' to regenerate."; \
git diff --name-only; \
exit 1; \
fi; \
echo "Generated apps code is up to date."; \
fi
.PHONY: do-gen-apps
do-gen-apps: ## Generate code for Grafana App SDK apps
for dir in $(APPS_DIRS); do \
$(MAKE) -C $$dir generate; \
done
@@ -231,6 +246,10 @@ build-backend: ## Build Grafana backend.
@echo "build backend"
$(GO) run build.go $(GO_BUILD_FLAGS) build-backend
.PHONY: build-air
build-air: build-backend
@cp ./bin/grafana ./bin/grafana-air
.PHONY: build-server
build-server: ## Build Grafana server.
@echo "build server"
@@ -386,7 +405,7 @@ lint-go-diff:
.PHONY: gofmt
gofmt: ## Run gofmt for all Go files.
gofmt -s -w .
@go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}' | xargs gofmt -s -w 2>&1 | grep -v '/pkg/build/' || true
# with disabled SC1071 we are ignored some TCL,Expect `/usr/bin/env expect` scripts
.PHONY: shellcheck
+9 -10
View File
@@ -8,7 +8,7 @@ require (
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43
github.com/grafana/grafana v0.0.0-00010101000000-000000000000
github.com/grafana/grafana-app-sdk v0.40.2
github.com/grafana/grafana-app-sdk/logging v0.40.1
github.com/grafana/grafana-app-sdk/logging v0.40.2
github.com/grafana/grafana-plugin-sdk-go v0.278.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2
github.com/stretchr/testify v1.10.0
@@ -244,14 +244,14 @@ require (
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
@@ -291,10 +291,9 @@ require (
modernc.org/libc v1.65.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.10.0 // indirect
modernc.org/sqlite v1.37.0 // indirect
modernc.org/sqlite v1.38.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
xorm.io/builder v0.3.6 // indirect
)
+2 -4
View File
@@ -665,8 +665,8 @@ github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E=
github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF8gFLL7wWU=
github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0=
github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4=
github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/grafana-app-sdk/logging v0.40.2 h1:HQ1+y9Od92iMbWWB54QxiYpNtCvYGUVpyxvxZ7ywB1k=
github.com/grafana/grafana-app-sdk/logging v0.40.2/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/grafana-aws-sdk v1.1.0 h1:G0fvwbQmHw14c5RXPd7Gnw9ZQcgzl139LtMDoe0KhmE=
github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8=
github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA=
@@ -1135,8 +1135,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
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.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
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/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4=
github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM=
+5 -2
View File
@@ -7,6 +7,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/operator"
"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"
@@ -48,8 +49,10 @@ func New(cfg app.Config) (app.App, error) {
Name: "advisor",
KubeConfig: cfg.KubeConfig,
InformerConfig: simple.AppInformerConfig{
ErrorHandler: func(ctx context.Context, err error) {
log.WithContext(ctx).Error("Informer processing error", "error", err)
InformerOptions: operator.InformerOptions{
ErrorHandler: func(ctx context.Context, err error) {
log.WithContext(ctx).Error("Informer processing error", "error", err)
},
},
},
ManagedKinds: []simple.AppManagedKind{
@@ -5,6 +5,8 @@ import (
"testing"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/stretchr/testify/assert"
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/repo"
@@ -12,7 +14,6 @@ import (
"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"
)
func TestRun(t *testing.T) {
@@ -22,7 +23,7 @@ func TestRun(t *testing.T) {
pluginInfo []repo.PluginInfo
pluginPreinstalled []string
pluginManaged []string
pluginProvisioned []string
pluginProvisioned []provisionedplugins.Plugin
pluginErrors []*plugins.Error
expectedFailures []advisor.CheckReportFailure
}{
@@ -117,7 +118,7 @@ func TestRun(t *testing.T) {
pluginInfo: []repo.PluginInfo{
{Status: "deprecated", Slug: "plugin5", Version: "1.1.0"}, // This should be ignored
},
pluginProvisioned: []string{"plugin5"},
pluginProvisioned: []provisionedplugins.Plugin{{ID: "plugin5"}},
expectedFailures: []advisor.CheckReportFailure{},
},
{
@@ -281,10 +282,10 @@ func (m *mockManagedPlugins) ManagedPlugins(ctx context.Context) []string {
type mockProvisionedPlugins struct {
provisionedplugins.Manager
provisioned []string
provisioned []provisionedplugins.Plugin
}
func (m *mockProvisionedPlugins) ProvisionedPlugins(ctx context.Context) ([]string, error) {
func (m *mockProvisionedPlugins) ProvisionedPlugins(_ context.Context) ([]provisionedplugins.Plugin, error) {
return m.provisioned, nil
}
@@ -7,6 +7,8 @@ import (
"strings"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/k8s"
"github.com/grafana/grafana-app-sdk/logging"
@@ -60,38 +62,6 @@ func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
}, nil
}
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
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
log.Info("Error updating check type, ignoring", "error", err)
}
return nil
}
return err
}
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() {
@@ -121,26 +91,139 @@ func (r *Runner) Run(ctx context.Context) error {
Steps: stepTypes,
},
}
for i := 0; i < r.retryAttempts; i++ {
err := r.createOrUpdate(context.WithoutCancel(ctx), logger, obj)
if err != nil {
if strings.Contains(err.Error(), "apiserver is shutting down") {
logger.Debug("Error creating check type, not retrying", "error", err)
return nil
}
logger.Debug("Error creating check type, retrying", "error", err, "attempt", i+1)
if i == r.retryAttempts-1 {
logger.Error("Unable to register check type", "check_type", t.ID(), "error", err)
} else {
// Calculate exponential backoff delay: baseDelay * 2^attempt
delay := r.retryDelay * time.Duration(1<<i)
time.Sleep(delay)
}
continue
}
logger.Debug("Check type registered successfully", "check_type", t.ID())
break
err := r.registerCheckType(ctx, logger, t.ID(), obj)
if err != nil {
return err
}
}
return nil
}
func (r *Runner) registerCheckType(ctx context.Context, logger logging.Logger, checkType string, obj resource.Object) error {
for i := 0; i < r.retryAttempts; i++ {
current, err := r.client.Get(ctx, obj.GetStaticMetadata().Identifier())
if err != nil {
if errors.IsNotFound(err) {
// Check type does not exist, create it
err = r.create(context.WithoutCancel(ctx), logger, obj)
if err != nil {
if !r.shouldRetry(err, logger, i+1, checkType) {
return nil
}
// Retry
continue
}
// Success
logger.Debug("Check type created successfully", "check_type", checkType)
break
}
if !r.shouldRetry(err, logger, i+1, checkType) {
return nil
}
// Retry
continue
}
// Check type already exists, check if it's the same and update if needed
logger.Debug("Check type already exists, checking if it's the same", "identifier", obj.GetStaticMetadata().Identifier())
if r.needsUpdate(current, obj, logger) {
err = r.update(context.WithoutCancel(ctx), logger, obj, current)
if err != nil {
if !r.shouldRetry(err, logger, i+1, checkType) {
return nil
}
// Retry
continue
}
// Success
logger.Debug("Check type updated successfully", "check_type", checkType)
break
}
// Check type is the same, no need to update
logger.Debug("Check type already registered", "check_type", checkType)
break
}
return nil
}
func (r *Runner) shouldRetry(err error, logger logging.Logger, attempt int, checkType string) bool {
logger.Debug("Error storing check type", "error", err, "attempt", attempt)
if isAPIServerShuttingDown(err, logger) {
return false
}
if attempt == r.retryAttempts-1 {
logger.Error("Unable to register check type", "check_type", checkType, "error", err)
return false
}
// Calculate exponential backoff delay: baseDelay * 2^attempt
delay := r.retryDelay * time.Duration(1<<attempt)
time.Sleep(delay)
return true
}
func (r *Runner) create(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 {
return err
}
log.Debug("Check type created successfully", "identifier", id)
return nil
}
func (r *Runner) needsUpdate(current, newObj resource.Object, log logging.Logger) bool {
needsUpdate := false
// Check if the object annotations exist in the current object
currentAnnotations := current.GetAnnotations()
if currentAnnotations == nil {
currentAnnotations = make(map[string]string)
}
annotations := newObj.GetAnnotations()
for k, v := range annotations {
if currentAnnotations[k] != v {
needsUpdate = true
}
}
// Compare checktype spec steps with current steps
currentCheckType := current.(*advisorv0alpha1.CheckType)
newCheckType := newObj.(*advisorv0alpha1.CheckType)
newSteps := newCheckType.Spec.Steps
currentSteps := currentCheckType.Spec.Steps
if !cmp.Equal(newSteps, currentSteps, cmpopts.SortSlices(func(a, b advisorv0alpha1.CheckTypeStep) bool {
return a.StepID < b.StepID
})) {
log.Debug("Check type step mismatch, updating", "identifier", newObj.GetStaticMetadata().Identifier())
needsUpdate = true
}
return needsUpdate
}
func (r *Runner) update(ctx context.Context, log logging.Logger, obj resource.Object, current resource.Object) error {
id := obj.GetStaticMetadata().Identifier()
log.Debug("Updating check type", "identifier", id)
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
log.Info("Error updating check type, ignoring", "error", err)
}
log.Debug("Check type updated successfully", "identifier", id)
return nil
}
func isAPIServerShuttingDown(err error, logger logging.Logger) bool {
if strings.Contains(err.Error(), "apiserver is shutting down") {
logger.Debug("Error creating check type, not retrying", "error", err)
return true
}
return false
}
@@ -16,6 +16,54 @@ import (
)
func TestCheckTypesRegisterer_Run(t *testing.T) {
newMockCheck := &mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
}
existingObjectDifferentAnnotations := &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.NameAnnotation: "existing-name", // Different to trigger update
},
},
Spec: advisorv0alpha1.CheckTypeSpec{
Name: "check1",
Steps: []advisorv0alpha1.CheckTypeStep{
{StepID: "step1", Title: "Step 1", Description: "Description 1"},
},
},
}
existingObjectDifferentSteps := &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.NameAnnotation: "mock", // Same as check name
},
},
Spec: advisorv0alpha1.CheckTypeSpec{
Name: "check1",
Steps: []advisorv0alpha1.CheckTypeStep{
{StepID: "step2", Title: "Step 2", Description: "Description 2"}, // Different step
},
},
}
existingObjectSameContent := &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.NameAnnotation: "mock", // Same as check name
},
},
Spec: advisorv0alpha1.CheckTypeSpec{
Name: "check1",
Steps: []advisorv0alpha1.CheckTypeStep{
{StepID: "step1", Title: "Step 1", Description: "Description 1"},
},
},
}
tests := []struct {
name string
checks []checks.Check
@@ -25,14 +73,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr error
}{
{
name: "successful create",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
name: "successful create",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return nil, k8sErrs.NewNotFound(schema.GroupResource{}, id.Name)
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return obj, nil
@@ -41,17 +85,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: nil,
},
{
name: "create already exists, successful update",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
name: "resource exists with different annotations, should update",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return obj, nil
@@ -59,27 +96,32 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: nil,
},
{
name: "create already exists, with custom annotations",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
name: "resource exists with different steps, should update",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return &advisorv0alpha1.CheckType{
ObjectMeta: metav1.ObjectMeta{
Name: "check1",
Annotations: map[string]string{
checks.IgnoreStepsAnnotationList: "step1",
},
},
}, nil
return existingObjectDifferentSteps, nil
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return obj, nil
},
expectedErr: nil,
},
{
name: "resource exists with same annotations and steps, should not update",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectSameContent, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("updateFunc should not be called")
},
expectedErr: nil,
},
{
name: "resource exists, with custom annotations preserved",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
if obj.GetAnnotations()[checks.IgnoreStepsAnnotationList] != "step1" {
@@ -90,14 +132,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: nil,
},
{
name: "create error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
name: "create error",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return nil, k8sErrs.NewNotFound(schema.GroupResource{}, id.Name)
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, errors.New("create error")
@@ -106,17 +144,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: errors.New("create error"),
},
{
name: "update error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
name: "update error",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("update error")
@@ -124,17 +155,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: errors.New("update error"),
},
{
name: "shutting down error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
name: "shutting down error",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("apiserver is shutting down")
@@ -142,14 +166,10 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
expectedErr: nil,
},
{
name: "custom namespace",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
name: "custom namespace",
checks: []checks.Check{newMockCheck},
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
return existingObjectDifferentAnnotations, nil
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
if obj.GetNamespace() != "custom-namespace" {
@@ -262,13 +282,19 @@ func (m *mockClient) Get(ctx context.Context, id resource.Identifier) (resource.
if m.getFunc != nil {
return m.getFunc(ctx, id)
}
return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil
return nil, errors.New("not implemented")
}
func (m *mockClient) Create(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return m.createFunc(ctx, id, obj, opts)
if m.createFunc != nil {
return m.createFunc(ctx, id, obj, opts)
}
return nil, errors.New("not implemented")
}
func (m *mockClient) Update(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return m.updateFunc(ctx, id, obj, opts)
if m.updateFunc != nil {
return m.updateFunc(ctx, id, obj, opts)
}
return nil, errors.New("not implemented")
}
+24 -1
View File
@@ -14,6 +14,7 @@ import (
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/services/contexthandler"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
)
var retryAnnotationPollingInterval = 1 * time.Second
@@ -81,7 +82,11 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
}
return fmt.Errorf("error running steps: %w", err)
}
// Wait for the item to be persisted before patching the object
err = waitForItem(ctx, log, client, obj)
if err != nil {
return err
}
report := &advisorv0alpha1.CheckReport{
Failures: failures,
Count: int64(len(items)),
@@ -264,6 +269,24 @@ func retryAnnotationChanged(oldObj, newObj resource.Object) bool {
oldAnnotations[checks.RetryAnnotation] != newAnnotations[checks.RetryAnnotation]
}
func waitForItem(ctx context.Context, log logging.Logger, client resource.Client, obj resource.Object) error {
_, err := client.Get(ctx, resource.Identifier{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
})
retries := 0
for err != nil && k8serrors.IsNotFound(err) && retries < 5 {
log.Debug("Waiting for item to be persisted", "check", obj.GetName(), "retries", retries)
time.Sleep(retryAnnotationPollingInterval)
retries++
_, err = client.Get(ctx, resource.Identifier{
Namespace: obj.GetNamespace(),
Name: obj.GetName(),
})
}
return err
}
// waitForRetryAnnotation waits for the retry annotation to match the item to retry
func waitForRetryAnnotation(ctx context.Context, log logging.Logger, client resource.Client, obj resource.Object, itemToRetry string) error {
currentObj, err := client.Get(ctx, resource.Identifier{
+39
View File
@@ -0,0 +1,39 @@
module github.com/grafana/grafana/apps/alerting/alertenrichment
go 1.24.6
require (
github.com/grafana/grafana-app-sdk v0.46.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28
k8s.io/apimachinery v0.34.1
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
)
require (
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/text v0.29.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
)
+118
View File
@@ -0,0 +1,118 @@
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.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/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/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.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
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/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28 h1:PgMfX4OPENz/iXmtDDIW9+poZY4UD0hhmXm7flVclDo=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28/go.mod h1:av5N0Naq+8VV9MLF7zAkihy/mVq5UbS2EvRSJukDHlY=
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/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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
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/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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/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.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
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.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
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.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
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/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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
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.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
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/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/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
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-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
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/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
@@ -0,0 +1,24 @@
package v1beta1
import (
"encoding/json"
"io"
"github.com/grafana/grafana-app-sdk/resource"
)
// AlertEnrichmentJSONCodec is a JSON codec for AlertEnrichment resources
type AlertEnrichmentJSONCodec struct{}
// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
func (*AlertEnrichmentJSONCodec) 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 (*AlertEnrichmentJSONCodec) Write(writer io.Writer, from resource.Object) error {
return json.NewEncoder(writer).Encode(from)
}
// Interface compliance checks
var _ resource.Codec = &AlertEnrichmentJSONCodec{}
@@ -0,0 +1,18 @@
package v1beta1
import "k8s.io/apimachinery/pkg/runtime/schema"
const (
// APIGroup is the API group used by all kinds in this package
APIGroup = "alertenrichment.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: APIGroup,
Version: APIVersion,
}
)
@@ -0,0 +1,6 @@
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=alertenrichment.grafana.app
package v1beta1
@@ -0,0 +1,207 @@
package v1beta1
import (
"fmt"
"time"
"github.com/grafana/grafana-app-sdk/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)
// App Platform resource.Object interface methods for AlertEnrichment
func (o *AlertEnrichment) GetSpec() any {
return o.Spec
}
func (o *AlertEnrichment) SetSpec(spec any) error {
cast, ok := spec.(AlertEnrichmentSpec)
if !ok {
return fmt.Errorf("cannot set spec type %#v, not of type AlertEnrichmentSpec", spec)
}
o.Spec = cast
return nil
}
func (o *AlertEnrichment) GetSubresources() map[string]any {
return map[string]any{}
}
func (o *AlertEnrichment) GetSubresource(name string) (any, bool) {
return nil, false
}
func (o *AlertEnrichment) SetSubresource(name string, value any) error {
return fmt.Errorf("subresource %s does not exist", name)
}
func (o *AlertEnrichment) Copy() resource.Object {
return resource.CopyObject(o)
}
func (o *AlertEnrichment) GetStaticMetadata() resource.StaticMetadata {
gvk := o.GroupVersionKind()
return resource.StaticMetadata{
Name: o.Name,
Namespace: o.Namespace,
Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind,
}
}
func (o *AlertEnrichment) 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 *AlertEnrichment) 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 *AlertEnrichment) 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"].(map[string]string); ok {
o.Annotations = annotations
}
if managedFields, ok := metadata.ExtraFields["managedFields"].([]metav1.ManagedFieldsEntry); ok {
o.ManagedFields = managedFields
}
if ownerReferences, ok := metadata.ExtraFields["ownerReferences"].([]metav1.OwnerReference); ok {
o.OwnerReferences = ownerReferences
}
}
}
func (o *AlertEnrichment) GetCreatedBy() string {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
return o.Annotations["grafana.com/createdBy"]
}
func (o *AlertEnrichment) SetCreatedBy(createdBy string) {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
o.Annotations["grafana.com/createdBy"] = createdBy
}
func (o *AlertEnrichment) GetUpdateTimestamp() time.Time {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
parsed, _ := time.Parse(time.RFC3339, o.Annotations["grafana.com/updateTimestamp"])
return parsed
}
func (o *AlertEnrichment) SetUpdateTimestamp(updateTimestamp time.Time) {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
o.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
}
func (o *AlertEnrichment) GetUpdatedBy() string {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
return o.Annotations["grafana.com/updatedBy"]
}
func (o *AlertEnrichment) SetUpdatedBy(updatedBy string) {
if o.Annotations == nil {
o.Annotations = make(map[string]string)
}
o.Annotations["grafana.com/updatedBy"] = updatedBy
}
// AlertEnrichmentList also needs to implement resource.ListObject
func (o *AlertEnrichmentList) Copy() resource.ListObject {
cpy := &AlertEnrichmentList{
TypeMeta: o.TypeMeta,
Items: make([]AlertEnrichment, len(o.Items)),
}
o.ListMeta.DeepCopyInto(&cpy.ListMeta)
for i := 0; i < len(o.Items); i++ {
o.Items[i].DeepCopyInto(&cpy.Items[i])
}
return cpy
}
func (o *AlertEnrichmentList) GetItems() []resource.Object {
items := make([]resource.Object, len(o.Items))
for i, item := range o.Items {
items[i] = &item
}
return items
}
func (o *AlertEnrichmentList) SetItems(items []resource.Object) {
o.Items = make([]AlertEnrichment, len(items))
for i, item := range items {
if ae, ok := item.(*AlertEnrichment); ok {
o.Items[i] = *ae
}
}
}
@@ -0,0 +1,27 @@
package v1beta1
import (
"github.com/grafana/grafana-app-sdk/resource"
)
// schema is unexported to prevent accidental overwrites
var (
schemaAlertEnrichment = resource.NewSimpleSchema(APIGroup, APIVersion, &AlertEnrichment{}, &AlertEnrichmentList{}, resource.WithKind("AlertEnrichment"),
resource.WithPlural("alert-enrichments"), resource.WithScope(resource.NamespacedScope))
kindAlertEnrichment = resource.Kind{
Schema: schemaAlertEnrichment,
Codecs: map[resource.KindEncoding]resource.Codec{
resource.KindEncodingJSON: &AlertEnrichmentJSONCodec{},
},
}
)
// AlertEnrichmentKind returns a resource.Kind for this Schema with a JSON codec
func AlertEnrichmentKind() resource.Kind {
return kindAlertEnrichment
}
// AlertEnrichmentSchema returns a resource.SimpleSchema representation of AlertEnrichment
func AlertEnrichmentSchema() *resource.SimpleSchema {
return schemaAlertEnrichment
}
@@ -0,0 +1,240 @@
package v1beta1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
// JSONSchema descriptions help the enrichment suggest API to generate enrichment configurations.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AlertEnrichment struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec AlertEnrichmentSpec `json:"spec,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AlertEnrichmentList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AlertEnrichment `json:"items,omitempty"`
}
// AlertEnrichmentSpec specifies an alert enrichment pipeline.
type AlertEnrichmentSpec struct {
// Title of the alert enrichment.
// +kubebuilder:validation:Required
Title string `json:"title" yaml:"title" jsonschema:"description=Title of the alert enrichment"`
// Description of the alert enrichment.
Description string `json:"description,omitempty" yaml:"description,omitempty" jsonschema:"description=Human‑readable description"`
// Alert rules for which to run the enrichment for.
// If not set, the enrichment runs for all alert rules.
// +listType=set
AlertRuleUIDs []string `json:"alertRuleUids,omitempty" yaml:"alertRuleUids,omitempty" jsonschema:"description=UIDs of alert rules this enrichment applies to (empty = all)"`
// LabelMatchers optionally restricts when this enrichment runs.
LabelMatchers []Matcher `json:"labelMatchers,omitempty" yaml:"labelMatchers,omitempty" jsonschema:"description=Label matchers that must be satisfied by the alert for this enrichment to run"`
// AnnotationMatchers optionally restricts when this enrichment runs.
AnnotationMatchers []Matcher `json:"annotationMatchers,omitempty" yaml:"annotationMatchers,omitempty" jsonschema:"description=Annotation matchers that must be satisfied by the alert for this enrichment to run"`
// Receivers optionally restricts the enrichment to one or more receiver names.
// If not set, the enrichment runs for alerts coming from all receivers.
// +listType=set
Receivers []string `json:"receivers,omitempty" yaml:"receivers,omitempty" jsonschema:"description=Alertmanager receiver names to match (empty = all)"`
// Steps of the enrichment pipeline.
Steps []Step `json:"steps" yaml:"steps" jsonschema:"description=Ordered list of enricher steps"`
}
// Type of comparison performed by the matcher. This mimics Alertmanager matchers.
// +enum
type StepType string
// Defines values for MatchType.
const (
StepTypeEnricher StepType = "enricher"
StepTypeConditional StepType = "conditional"
)
// Step represent an invocation of a single enricher.
type Step struct {
Type StepType `json:"type" yaml:"type" jsonschema:"description=Step kind: 'enricher' or 'conditional'"`
// Timeout is the maximum about of time this specific enrichment is allowed to take.
Timeout metav1.Duration `json:"timeout" yaml:"timeout" jsonschema:"description=Maximum execution duration for this step, for example '5s'"`
// Enricher specifies what enricher to run and it's configuration.
Enricher *EnricherConfig `json:"enricher,omitempty" yaml:"enricher,omitempty" jsonschema:"description=Enricher configuration"`
// Conditional allows branching to specifies what enricher to run and it's configuration.
Conditional *Conditional `json:"conditional,omitempty" yaml:"conditional,omitempty" jsonschema:"description=Conditional enricher configuration that branches based on the condition"`
}
type Conditional struct {
// If is the condition to evaluate.
If Condition `json:"if" yaml:"if" jsonschema:"description=Condition to evaluate before running the enrichment steps"`
// Then is the enrichment steps to perform if all the conditions above are true.
Then []Step `json:"then" yaml:"then" jsonschema:"description=Steps executed when the condition is true"`
// Else is the enrichment steps to perform otherwise.
Else []Step `json:"else,omitempty" yaml:"else,omitempty" jsonschema:"description=Steps executed when the condition is false"`
}
type Condition struct {
// LabelMatchers optionally specifies the condition to require matching label values.
LabelMatchers []Matcher `json:"labelMatchers,omitempty" yaml:"labelMatchers,omitempty" jsonschema:"description=Label matchers that must be satisfied"`
// AnnotationMatchers optionally restricts when the per-alert enrichments are run.
AnnotationMatchers []Matcher `json:"annotationMatchers,omitempty" yaml:"annotationMatchers,omitempty" jsonschema:"description=Annotation matchers that must be satisfied"`
// DataSourceQuery is a data source query to run. If the query returns a non-zero value,
// then the condition is taken to be true.
DataSourceQuery *RawDataSourceQuery `json:"dataSourceQuery,omitempty" yaml:"dataSourceQuery,omitempty" jsonschema:"description=Data source query to run to evaluate the condition"`
}
// Matcher is used to match label (or annotation) values.
type Matcher struct {
Type MatchType `json:"type" yaml:"type" jsonschema:"description=Comparison operator ('=', '!=', '=~', '!~')"`
Name string `json:"name" yaml:"name" jsonschema:"description=Label/annotation key"`
Value string `json:"value" yaml:"value" jsonschema:"description=Value or regex pattern to match"`
}
// Type of comparison performed by the matcher. This mimics Alertmanager matchers.
// +enum
type MatchType string
// Defines values for MatchType.
const (
MatchTypeEqual MatchType = "="
MatchTypeNotEqual MatchType = "!="
MatchTypeRegexp MatchType = "=~"
MatchNotRegexp MatchType = "!~"
)
// Type of enricher
// +enum
type EnricherType string
// Defines values for EnricherType.
const (
EnricherTypeAssign EnricherType = "assign"
EnricherTypeExternal EnricherType = "external"
EnricherTypeDataSourceQuery EnricherType = "dsquery"
EnricherTypeSift EnricherType = "sift"
EnricherTypeAsserts EnricherType = "asserts"
EnricherTypeExplain EnricherType = "explain"
EnricherTypeLoop EnricherType = "loop"
EnricherTypeAssistant EnricherType = "assistant"
)
// EnricherConfig is a discriminated union of enricher configurations.
type EnricherConfig struct {
Type EnricherType `json:"type" yaml:"type" jsonschema:"description=Enricher type ('assign', 'external', 'dsquery', 'sift', 'asserts', 'explain', 'loop')"`
Assign *AssignEnricher `json:"assign,omitempty" yaml:"assign,omitempty" jsonschema:"description=Assign enricher settings"`
External *ExternalEnricher `json:"external,omitempty" yaml:"external,omitempty" jsonschema:"description=External HTTP enricher settings"`
DataSource *DataSourceEnricher `json:"dataSource,omitempty" yaml:"dataSource,omitempty" jsonschema:"description=Data source query enricher settings"`
Sift *SiftEnricher `json:"sift,omitempty" yaml:"sift,omitempty" jsonschema:"description=Sift enricher settings"`
Asserts *AssertsEnricher `json:"asserts,omitempty" yaml:"asserts,omitempty" jsonschema:"description=Asserts enricher settings"`
Explain *ExplainEnricher `json:"explain,omitempty" yaml:"explain,omitempty" jsonschema:"description=Explain enricher settings"`
Loop *LoopEnricher `json:"loop,omitempty" yaml:"loop,omitempty" jsonschema:"description=Loop enricher settings"`
Assistant *AssistantEnricher `json:"assistant,omitempty" yaml:"assistant,omitempty" jsonschema:"description=Assistant enricher settings"`
}
// AssignEnricher configures an enricher which assigns annotations.
type AssignEnricher struct {
// Annotations to change and values to set them to.
// +listType=map
// +listMapKey=name
Annotations []Assignment `json:"annotations" yaml:"annotations" jsonschema:"description=Annotations to set on the alert"`
}
type Assignment struct {
// Name of the annotation to assign.
Name string `json:"name" yaml:"name" jsonschema:"description=Annotation key"`
// Value to assign to the annotation. Can use Go template format, with access to
// annotations and labels via e.g. {{$annotations.x}}
Value string `json:"value" yaml:"value" jsonschema:"description=Template value to apply, for example '{{ $labels.instance }} is down'"`
}
// ExternalEnricher configures an enricher which calls an external service.
type ExternalEnricher struct {
// URL of the external HTTP service to call out to.
URL string `json:"url" yaml:"url" jsonschema:"description=HTTP endpoint to call for enrichment"`
}
// Type of data source query
// +enum
type DataSourceQueryType string
// Defines values for EnricherType.
const (
DataSourceQueryTypeRaw DataSourceQueryType = "raw"
DataSourceQueryTypeLogs DataSourceQueryType = "logs"
)
// DataSourceEnricher configures an enricher which calls an external service.
type DataSourceEnricher struct {
Type DataSourceQueryType `json:"type" yaml:"type" jsonschema:"description=Data source query type ('raw', 'logs')"`
Raw *RawDataSourceQuery `json:"raw,omitempty" yaml:"raw,omitempty" jsonschema:"description=Raw query definition"`
Logs *LogsDataSourceQuery `json:"logs,omitempty" yaml:"logs,omitempty" jsonschema:"description=Logs query definition"`
}
// RawDataSourceQuery allows defining the entire query request
type RawDataSourceQuery struct {
// The data source request to perform.
Request common.Unstructured `json:"request,omitempty" yaml:"request,omitempty" jsonschema:"description=Grafana data source request payload"`
// The RefID of the response to use. Not required if only a single query is given.
RefID string `json:"refId,omitempty" yaml:"refId,omitempty" jsonschema:"description=RefID of the response to use, needed if multiple queries are given"`
}
// LogsDataSourceQuery is a simplified method of describing a logs query,
// typically those that return data frames with a "Line" field.
type LogsDataSourceQuery struct {
// The datasource plugin type
DataSourceType string `json:"dataSourceType" yaml:"dataSourceType" jsonschema:"description=Data source plugin type (e.g. 'prometheus', 'loki')"`
// Datasource UID
DataSourceUID string `json:"dataSourceUid,omitempty" yaml:"dataSourceUid,omitempty" jsonschema:"description=UID of the data source to query"`
// The logs query to run.
Expr string `json:"expr" yaml:"expr" jsonschema:"description=Log query expression"`
// Number of log lines to add to the alert. Defaults to 3.
MaxLines int `json:"maxLines,omitempty" yaml:"maxLines,omitempty" jsonschema:"description=Maximum number of log lines to include, defaults to 3"`
}
// SiftEnricher configures an enricher which calls into Sift.
type SiftEnricher struct {
// In the future, there may be configuration options.
}
// AssertsEnricher configures an enricher which calls into Asserts.
type AssertsEnricher struct {
// In the future, there may be configuration options.
}
// ExplainEnricher uses LLM to generate explanations for alerts.
type ExplainEnricher struct {
Annotation string `json:"annotation" yaml:"annotation" jsonschema:"description=Annotation name to set the explanation in, by default '__enriched_ai_explanation'"`
}
// LoopEnricher configures an enricher which calls into Loop.
type LoopEnricher struct {
// In the future, there may be configuration options.
}
// AssistantEnricher configures an enricher which calls into Assistant.
type AssistantEnricher struct {
// In the future, there may be configuration options.
}
@@ -0,0 +1,484 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// SPDX-License-Identifier: AGPL-3.0-only
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertEnrichment) DeepCopyInto(out *AlertEnrichment) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertEnrichment.
func (in *AlertEnrichment) DeepCopy() *AlertEnrichment {
if in == nil {
return nil
}
out := new(AlertEnrichment)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AlertEnrichment) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertEnrichmentJSONCodec) DeepCopyInto(out *AlertEnrichmentJSONCodec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertEnrichmentJSONCodec.
func (in *AlertEnrichmentJSONCodec) DeepCopy() *AlertEnrichmentJSONCodec {
if in == nil {
return nil
}
out := new(AlertEnrichmentJSONCodec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertEnrichmentList) DeepCopyInto(out *AlertEnrichmentList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]AlertEnrichment, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertEnrichmentList.
func (in *AlertEnrichmentList) DeepCopy() *AlertEnrichmentList {
if in == nil {
return nil
}
out := new(AlertEnrichmentList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AlertEnrichmentList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AlertEnrichmentSpec) DeepCopyInto(out *AlertEnrichmentSpec) {
*out = *in
if in.AlertRuleUIDs != nil {
in, out := &in.AlertRuleUIDs, &out.AlertRuleUIDs
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.LabelMatchers != nil {
in, out := &in.LabelMatchers, &out.LabelMatchers
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.AnnotationMatchers != nil {
in, out := &in.AnnotationMatchers, &out.AnnotationMatchers
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.Receivers != nil {
in, out := &in.Receivers, &out.Receivers
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Steps != nil {
in, out := &in.Steps, &out.Steps
*out = make([]Step, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AlertEnrichmentSpec.
func (in *AlertEnrichmentSpec) DeepCopy() *AlertEnrichmentSpec {
if in == nil {
return nil
}
out := new(AlertEnrichmentSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AssertsEnricher) DeepCopyInto(out *AssertsEnricher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AssertsEnricher.
func (in *AssertsEnricher) DeepCopy() *AssertsEnricher {
if in == nil {
return nil
}
out := new(AssertsEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AssignEnricher) DeepCopyInto(out *AssignEnricher) {
*out = *in
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make([]Assignment, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AssignEnricher.
func (in *AssignEnricher) DeepCopy() *AssignEnricher {
if in == nil {
return nil
}
out := new(AssignEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Assignment) DeepCopyInto(out *Assignment) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Assignment.
func (in *Assignment) DeepCopy() *Assignment {
if in == nil {
return nil
}
out := new(Assignment)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AssistantEnricher) DeepCopyInto(out *AssistantEnricher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AssistantEnricher.
func (in *AssistantEnricher) DeepCopy() *AssistantEnricher {
if in == nil {
return nil
}
out := new(AssistantEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in
if in.LabelMatchers != nil {
in, out := &in.LabelMatchers, &out.LabelMatchers
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.AnnotationMatchers != nil {
in, out := &in.AnnotationMatchers, &out.AnnotationMatchers
*out = make([]Matcher, len(*in))
copy(*out, *in)
}
if in.DataSourceQuery != nil {
in, out := &in.DataSourceQuery, &out.DataSourceQuery
*out = new(RawDataSourceQuery)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
if in == nil {
return nil
}
out := new(Condition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Conditional) DeepCopyInto(out *Conditional) {
*out = *in
in.If.DeepCopyInto(&out.If)
if in.Then != nil {
in, out := &in.Then, &out.Then
*out = make([]Step, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Else != nil {
in, out := &in.Else, &out.Else
*out = make([]Step, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditional.
func (in *Conditional) DeepCopy() *Conditional {
if in == nil {
return nil
}
out := new(Conditional)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DataSourceEnricher) DeepCopyInto(out *DataSourceEnricher) {
*out = *in
if in.Raw != nil {
in, out := &in.Raw, &out.Raw
*out = new(RawDataSourceQuery)
(*in).DeepCopyInto(*out)
}
if in.Logs != nil {
in, out := &in.Logs, &out.Logs
*out = new(LogsDataSourceQuery)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSourceEnricher.
func (in *DataSourceEnricher) DeepCopy() *DataSourceEnricher {
if in == nil {
return nil
}
out := new(DataSourceEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnricherConfig) DeepCopyInto(out *EnricherConfig) {
*out = *in
if in.Assign != nil {
in, out := &in.Assign, &out.Assign
*out = new(AssignEnricher)
(*in).DeepCopyInto(*out)
}
if in.External != nil {
in, out := &in.External, &out.External
*out = new(ExternalEnricher)
**out = **in
}
if in.DataSource != nil {
in, out := &in.DataSource, &out.DataSource
*out = new(DataSourceEnricher)
(*in).DeepCopyInto(*out)
}
if in.Sift != nil {
in, out := &in.Sift, &out.Sift
*out = new(SiftEnricher)
**out = **in
}
if in.Asserts != nil {
in, out := &in.Asserts, &out.Asserts
*out = new(AssertsEnricher)
**out = **in
}
if in.Explain != nil {
in, out := &in.Explain, &out.Explain
*out = new(ExplainEnricher)
**out = **in
}
if in.Loop != nil {
in, out := &in.Loop, &out.Loop
*out = new(LoopEnricher)
**out = **in
}
if in.Assistant != nil {
in, out := &in.Assistant, &out.Assistant
*out = new(AssistantEnricher)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnricherConfig.
func (in *EnricherConfig) DeepCopy() *EnricherConfig {
if in == nil {
return nil
}
out := new(EnricherConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExplainEnricher) DeepCopyInto(out *ExplainEnricher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExplainEnricher.
func (in *ExplainEnricher) DeepCopy() *ExplainEnricher {
if in == nil {
return nil
}
out := new(ExplainEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExternalEnricher) DeepCopyInto(out *ExternalEnricher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalEnricher.
func (in *ExternalEnricher) DeepCopy() *ExternalEnricher {
if in == nil {
return nil
}
out := new(ExternalEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LogsDataSourceQuery) DeepCopyInto(out *LogsDataSourceQuery) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogsDataSourceQuery.
func (in *LogsDataSourceQuery) DeepCopy() *LogsDataSourceQuery {
if in == nil {
return nil
}
out := new(LogsDataSourceQuery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LoopEnricher) DeepCopyInto(out *LoopEnricher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoopEnricher.
func (in *LoopEnricher) DeepCopy() *LoopEnricher {
if in == nil {
return nil
}
out := new(LoopEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Matcher) DeepCopyInto(out *Matcher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Matcher.
func (in *Matcher) DeepCopy() *Matcher {
if in == nil {
return nil
}
out := new(Matcher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RawDataSourceQuery) DeepCopyInto(out *RawDataSourceQuery) {
*out = *in
in.Request.DeepCopyInto(&out.Request)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RawDataSourceQuery.
func (in *RawDataSourceQuery) DeepCopy() *RawDataSourceQuery {
if in == nil {
return nil
}
out := new(RawDataSourceQuery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SiftEnricher) DeepCopyInto(out *SiftEnricher) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SiftEnricher.
func (in *SiftEnricher) DeepCopy() *SiftEnricher {
if in == nil {
return nil
}
out := new(SiftEnricher)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Step) DeepCopyInto(out *Step) {
*out = *in
out.Timeout = in.Timeout
if in.Enricher != nil {
in, out := &in.Enricher, &out.Enricher
*out = new(EnricherConfig)
(*in).DeepCopyInto(*out)
}
if in.Conditional != nil {
in, out := &in.Conditional, &out.Conditional
*out = new(Conditional)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Step.
func (in *Step) DeepCopy() *Step {
if in == nil {
return nil
}
out := new(Step)
in.DeepCopyInto(out)
return out
}
@@ -0,0 +1,19 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// SPDX-License-Identifier: AGPL-3.0-only
// Code generated by defaulter-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}
@@ -0,0 +1,753 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// SPDX-License-Identifier: AGPL-3.0-only
// Code generated by openapi-gen. DO NOT EDIT.
package v1beta1
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/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichment": schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichment(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichmentJSONCodec": schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichmentJSONCodec(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichmentList": schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichmentList(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichmentSpec": schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichmentSpec(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssertsEnricher": schema_pkg_apis_alertenrichment_v1beta1_AssertsEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssignEnricher": schema_pkg_apis_alertenrichment_v1beta1_AssignEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Assignment": schema_pkg_apis_alertenrichment_v1beta1_Assignment(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssistantEnricher": schema_pkg_apis_alertenrichment_v1beta1_AssistantEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Condition": schema_pkg_apis_alertenrichment_v1beta1_Condition(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Conditional": schema_pkg_apis_alertenrichment_v1beta1_Conditional(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.DataSourceEnricher": schema_pkg_apis_alertenrichment_v1beta1_DataSourceEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.EnricherConfig": schema_pkg_apis_alertenrichment_v1beta1_EnricherConfig(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.ExplainEnricher": schema_pkg_apis_alertenrichment_v1beta1_ExplainEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.ExternalEnricher": schema_pkg_apis_alertenrichment_v1beta1_ExternalEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.LogsDataSourceQuery": schema_pkg_apis_alertenrichment_v1beta1_LogsDataSourceQuery(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.LoopEnricher": schema_pkg_apis_alertenrichment_v1beta1_LoopEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher": schema_pkg_apis_alertenrichment_v1beta1_Matcher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.RawDataSourceQuery": schema_pkg_apis_alertenrichment_v1beta1_RawDataSourceQuery(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.SiftEnricher": schema_pkg_apis_alertenrichment_v1beta1_SiftEnricher(ref),
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Step": schema_pkg_apis_alertenrichment_v1beta1_Step(ref),
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichment(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichmentSpec"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichmentSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichmentJSONCodec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AlertEnrichmentJSONCodec is a JSON codec for AlertEnrichment resources",
Type: []string{"object"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichmentList(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/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichment"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AlertEnrichment", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AlertEnrichmentSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AlertEnrichmentSpec specifies an alert enrichment pipeline.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"title": {
SchemaProps: spec.SchemaProps{
Description: "Title of the alert enrichment.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"description": {
SchemaProps: spec.SchemaProps{
Description: "Description of the alert enrichment.",
Type: []string{"string"},
Format: "",
},
},
"alertRuleUids": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "set",
},
},
SchemaProps: spec.SchemaProps{
Description: "Alert rules for which to run the enrichment for. If not set, the enrichment runs for all alert rules.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"labelMatchers": {
SchemaProps: spec.SchemaProps{
Description: "LabelMatchers optionally restricts when this enrichment runs.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher"),
},
},
},
},
},
"annotationMatchers": {
SchemaProps: spec.SchemaProps{
Description: "AnnotationMatchers optionally restricts when this enrichment runs.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher"),
},
},
},
},
},
"receivers": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "set",
},
},
SchemaProps: spec.SchemaProps{
Description: "Receivers optionally restricts the enrichment to one or more receiver names. If not set, the enrichment runs for alerts coming from all receivers.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"steps": {
SchemaProps: spec.SchemaProps{
Description: "Steps of the enrichment pipeline.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Step"),
},
},
},
},
},
},
Required: []string{"title", "steps"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Step"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AssertsEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AssertsEnricher configures an enricher which calls into Asserts.",
Type: []string{"object"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AssignEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AssignEnricher configures an enricher which assigns annotations.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"annotations": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"name",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "Annotations to change and values to set them to.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Assignment"),
},
},
},
},
},
},
Required: []string{"annotations"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Assignment"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_Assignment(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{
Description: "Name of the annotation to assign.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"value": {
SchemaProps: spec.SchemaProps{
Description: "Value to assign to the annotation. Can use Go template format, with access to annotations and labels via e.g. {{$annotations.x}}",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"name", "value"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_AssistantEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AssistantEnricher configures an enricher which calls into Assistant.",
Type: []string{"object"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"labelMatchers": {
SchemaProps: spec.SchemaProps{
Description: "LabelMatchers optionally specifies the condition to require matching label values.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher"),
},
},
},
},
},
"annotationMatchers": {
SchemaProps: spec.SchemaProps{
Description: "AnnotationMatchers optionally restricts when the per-alert enrichments are run.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher"),
},
},
},
},
},
"dataSourceQuery": {
SchemaProps: spec.SchemaProps{
Description: "DataSourceQuery is a data source query to run. If the query returns a non-zero value, then the condition is taken to be true.",
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.RawDataSourceQuery"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Matcher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.RawDataSourceQuery"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_Conditional(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"if": {
SchemaProps: spec.SchemaProps{
Description: "If is the condition to evaluate.",
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Condition"),
},
},
"then": {
SchemaProps: spec.SchemaProps{
Description: "Then is the enrichment steps to perform if all the conditions above are true.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Step"),
},
},
},
},
},
"else": {
SchemaProps: spec.SchemaProps{
Description: "Else is the enrichment steps to perform otherwise.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Step"),
},
},
},
},
},
},
Required: []string{"if", "then"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Condition", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Step"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_DataSourceEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "DataSourceEnricher configures an enricher which calls an external service.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"logs\"`\n - `\"raw\"`",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"logs", "raw"},
},
},
"raw": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.RawDataSourceQuery"),
},
},
"logs": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.LogsDataSourceQuery"),
},
},
},
Required: []string{"type"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.LogsDataSourceQuery", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.RawDataSourceQuery"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_EnricherConfig(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "EnricherConfig is a discriminated union of enricher configurations.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"asserts\"`\n - `\"assign\"`\n - `\"assistant\"`\n - `\"dsquery\"`\n - `\"explain\"`\n - `\"external\"`\n - `\"loop\"`\n - `\"sift\"`",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"asserts", "assign", "assistant", "dsquery", "explain", "external", "loop", "sift"},
},
},
"assign": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssignEnricher"),
},
},
"external": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.ExternalEnricher"),
},
},
"dataSource": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.DataSourceEnricher"),
},
},
"sift": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.SiftEnricher"),
},
},
"asserts": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssertsEnricher"),
},
},
"explain": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.ExplainEnricher"),
},
},
"loop": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.LoopEnricher"),
},
},
"assistant": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssistantEnricher"),
},
},
},
Required: []string{"type"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssertsEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssignEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.AssistantEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.DataSourceEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.ExplainEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.ExternalEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.LoopEnricher", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.SiftEnricher"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_ExplainEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ExplainEnricher uses LLM to generate explanations for alerts.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"annotation": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"annotation"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_ExternalEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ExternalEnricher configures an enricher which calls an external service.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"url": {
SchemaProps: spec.SchemaProps{
Description: "URL of the external HTTP service to call out to.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"url"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_LogsDataSourceQuery(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "LogsDataSourceQuery is a simplified method of describing a logs query, typically those that return data frames with a \"Line\" field.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"dataSourceType": {
SchemaProps: spec.SchemaProps{
Description: "The datasource plugin type",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"dataSourceUid": {
SchemaProps: spec.SchemaProps{
Description: "Datasource UID",
Type: []string{"string"},
Format: "",
},
},
"expr": {
SchemaProps: spec.SchemaProps{
Description: "The logs query to run.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"maxLines": {
SchemaProps: spec.SchemaProps{
Description: "Number of log lines to add to the alert. Defaults to 3.",
Type: []string{"integer"},
Format: "int32",
},
},
},
Required: []string{"dataSourceType", "expr"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_LoopEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "LoopEnricher configures an enricher which calls into Loop.",
Type: []string{"object"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_Matcher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Matcher is used to match label (or annotation) values.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"!=\"`\n - `\"!~\"`\n - `\"=\"`\n - `\"=~\"`",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"!=", "!~", "=", "=~"},
},
},
"name": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"value": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"type", "name", "value"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_RawDataSourceQuery(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "RawDataSourceQuery allows defining the entire query request",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"request": {
SchemaProps: spec.SchemaProps{
Description: "The data source request to perform.",
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
},
},
"refId": {
SchemaProps: spec.SchemaProps{
Description: "The RefID of the response to use. Not required if only a single query is given.",
Type: []string{"string"},
Format: "",
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_SiftEnricher(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "SiftEnricher configures an enricher which calls into Sift.",
Type: []string{"object"},
},
},
}
}
func schema_pkg_apis_alertenrichment_v1beta1_Step(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Step represent an invocation of a single enricher.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"conditional\"`\n - `\"enricher\"`",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"conditional", "enricher"},
},
},
"timeout": {
SchemaProps: spec.SchemaProps{
Description: "Timeout is the maximum about of time this specific enrichment is allowed to take.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
},
},
"enricher": {
SchemaProps: spec.SchemaProps{
Description: "Enricher specifies what enricher to run and it's configuration.",
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.EnricherConfig"),
},
},
"conditional": {
SchemaProps: spec.SchemaProps{
Description: "Conditional allows branching to specifies what enricher to run and it's configuration.",
Ref: ref("github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Conditional"),
},
},
},
Required: []string{"type", "timeout"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.Conditional", "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1.EnricherConfig", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"},
}
}
@@ -0,0 +1,11 @@
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,AlertEnrichmentSpec,AnnotationMatchers
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,AlertEnrichmentSpec,LabelMatchers
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,AlertEnrichmentSpec,Steps
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,Condition,AnnotationMatchers
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,Condition,LabelMatchers
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,Conditional,Else
API rule violation: list_type_missing,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,Conditional,Then
API rule violation: names_match,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,AlertEnrichmentSpec,AlertRuleUIDs
API rule violation: names_match,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,LogsDataSourceQuery,DataSourceUID
API rule violation: names_match,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,RawDataSourceQuery,RefID
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1,AlertEnrichmentList,Items
+1 -2
View File
@@ -5,5 +5,4 @@ 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 --useoldmanifestkinds
@$(APP_SDK_BIN) generate --grouping=group --gogenpath=./pkg/apis --defencoding=yaml --postprocess
@@ -0,0 +1,433 @@
apiVersion: apps.grafana.com/v1alpha2
kind: AppManifest
metadata:
name: alerting-notifications
spec:
appName: alerting-notifications
group: notifications.alerting.grafana.app
preferredVersion: v0alpha1
versions:
- kinds:
- conversion: false
kind: Receiver
plural: Receivers
schemas:
Integration:
additionalProperties: false
properties:
disableResolveMessage:
type: boolean
secureFields:
additionalProperties:
type: boolean
type: object
settings:
additionalProperties:
additionalProperties: {}
type: object
type: object
type:
type: string
uid:
type: string
version:
type: string
required:
- type
- version
- settings
type: object
OperatorState:
additionalProperties: false
properties:
descriptiveState:
description: descriptiveState is an optional more descriptive state
field which has no requirements on format
type: string
details:
additionalProperties:
additionalProperties: {}
type: object
description: details contains any extra information that is operator-specific
type: object
lastEvaluation:
description: lastEvaluation is the ResourceVersion last evaluated
type: string
state:
description: |-
state describes the state of the lastEvaluation.
It is limited to three possible states for machine evaluation.
enum:
- success
- in_progress
- failed
type: string
required:
- lastEvaluation
- state
type: object
Receiver:
properties:
spec:
$ref: '#/components/schemas/spec'
status:
$ref: '#/components/schemas/status'
required:
- spec
spec:
additionalProperties: false
properties:
integrations:
items:
$ref: '#/components/schemas/Integration'
type: array
title:
type: string
required:
- title
- integrations
type: object
status:
additionalProperties: false
properties:
additionalFields:
additionalProperties:
additionalProperties: {}
type: object
description: additionalFields is reserved for future use
type: object
operatorStates:
additionalProperties:
$ref: '#/components/schemas/OperatorState'
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: object
type: object
scope: Namespaced
- conversion: false
kind: RoutingTree
plural: RoutingTrees
schemas:
Matcher:
additionalProperties: false
properties:
label:
type: string
type:
enum:
- =
- '!='
- =~
- '!~'
type: string
value:
type: string
required:
- type
- label
- value
type: object
OperatorState:
additionalProperties: false
properties:
descriptiveState:
description: descriptiveState is an optional more descriptive state
field which has no requirements on format
type: string
details:
additionalProperties:
additionalProperties: {}
type: object
description: details contains any extra information that is operator-specific
type: object
lastEvaluation:
description: lastEvaluation is the ResourceVersion last evaluated
type: string
state:
description: |-
state describes the state of the lastEvaluation.
It is limited to three possible states for machine evaluation.
enum:
- success
- in_progress
- failed
type: string
required:
- lastEvaluation
- state
type: object
Route:
additionalProperties: false
properties:
active_time_intervals:
items:
type: string
type: array
continue:
type: boolean
group_by:
items:
type: string
type: array
group_interval:
type: string
group_wait:
type: string
matchers:
items:
$ref: '#/components/schemas/Matcher'
type: array
mute_time_intervals:
items:
type: string
type: array
receiver:
type: string
repeat_interval:
type: string
routes:
items:
$ref: '#/components/schemas/Route'
type: array
required:
- continue
type: object
RouteDefaults:
additionalProperties: false
properties:
group_by:
items:
type: string
type: array
group_interval:
type: string
group_wait:
type: string
receiver:
type: string
repeat_interval:
type: string
required:
- receiver
type: object
RoutingTree:
properties:
spec:
$ref: '#/components/schemas/spec'
status:
$ref: '#/components/schemas/status'
required:
- spec
spec:
additionalProperties: false
properties:
defaults:
$ref: '#/components/schemas/RouteDefaults'
routes:
items:
$ref: '#/components/schemas/Route'
type: array
required:
- defaults
- routes
type: object
status:
additionalProperties: false
properties:
additionalFields:
additionalProperties:
additionalProperties: {}
type: object
description: additionalFields is reserved for future use
type: object
operatorStates:
additionalProperties:
$ref: '#/components/schemas/OperatorState'
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: object
type: object
scope: Namespaced
- conversion: false
kind: TemplateGroup
plural: TemplateGroups
schemas:
OperatorState:
additionalProperties: false
properties:
descriptiveState:
description: descriptiveState is an optional more descriptive state
field which has no requirements on format
type: string
details:
additionalProperties:
additionalProperties: {}
type: object
description: details contains any extra information that is operator-specific
type: object
lastEvaluation:
description: lastEvaluation is the ResourceVersion last evaluated
type: string
state:
description: |-
state describes the state of the lastEvaluation.
It is limited to three possible states for machine evaluation.
enum:
- success
- in_progress
- failed
type: string
required:
- lastEvaluation
- state
type: object
TemplateGroup:
properties:
spec:
$ref: '#/components/schemas/spec'
status:
$ref: '#/components/schemas/status'
required:
- spec
spec:
additionalProperties: false
properties:
content:
type: string
title:
type: string
required:
- title
- content
type: object
status:
additionalProperties: false
properties:
additionalFields:
additionalProperties:
additionalProperties: {}
type: object
description: additionalFields is reserved for future use
type: object
operatorStates:
additionalProperties:
$ref: '#/components/schemas/OperatorState'
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: object
type: object
scope: Namespaced
- conversion: false
kind: TimeInterval
plural: TimeIntervals
schemas:
Interval:
additionalProperties: false
properties:
days_of_month:
items:
type: string
type: array
location:
type: string
months:
items:
type: string
type: array
times:
items:
$ref: '#/components/schemas/TimeRange'
type: array
weekdays:
items:
type: string
type: array
years:
items:
type: string
type: array
type: object
OperatorState:
additionalProperties: false
properties:
descriptiveState:
description: descriptiveState is an optional more descriptive state
field which has no requirements on format
type: string
details:
additionalProperties:
additionalProperties: {}
type: object
description: details contains any extra information that is operator-specific
type: object
lastEvaluation:
description: lastEvaluation is the ResourceVersion last evaluated
type: string
state:
description: |-
state describes the state of the lastEvaluation.
It is limited to three possible states for machine evaluation.
enum:
- success
- in_progress
- failed
type: string
required:
- lastEvaluation
- state
type: object
TimeInterval:
properties:
spec:
$ref: '#/components/schemas/spec'
status:
$ref: '#/components/schemas/status'
required:
- spec
TimeRange:
additionalProperties: false
properties:
end_time:
type: string
start_time:
type: string
required:
- start_time
- end_time
type: object
spec:
additionalProperties: false
properties:
name:
type: string
time_intervals:
items:
$ref: '#/components/schemas/Interval'
type: array
required:
- name
- time_intervals
type: object
status:
additionalProperties: false
properties:
additionalFields:
additionalProperties:
additionalProperties: {}
type: object
description: additionalFields is reserved for future use
type: object
operatorStates:
additionalProperties:
$ref: '#/components/schemas/OperatorState'
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: object
type: object
scope: Namespaced
name: v0alpha1
served: true

Some files were not shown because too many files have changed in this diff Show More