From 2f3aff0e04a22524e7d441a329a96aa49d5dfc88 Mon Sep 17 00:00:00 2001 From: Kevin Minehart <5140827+kminehart@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:02:54 -0600 Subject: [PATCH 001/312] Dashboards: Prevent title longer than 5 000 characters (#101554) Co-authored-by: AgnesToulet <35176601+AgnesToulet@users.noreply.github.com> --- pkg/services/dashboards/errors.go | 5 +++++ pkg/services/dashboards/service/dashboard_service.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/pkg/services/dashboards/errors.go b/pkg/services/dashboards/errors.go index 93f6b1152e0..8665015c940 100644 --- a/pkg/services/dashboards/errors.go +++ b/pkg/services/dashboards/errors.go @@ -42,6 +42,11 @@ var ( StatusCode: 400, Status: "empty-name", } + ErrDashboardTitleTooLong = DashboardErr{ + Reason: "Dashboard title cannot contain more than 5 000 characters", + StatusCode: 400, + Status: "title-too-long", + } ErrDashboardFolderCannotHaveParent = DashboardErr{ Reason: "A Dashboard Folder cannot be added to another folder", StatusCode: 400, diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 2d3e7c8fb07..53b705a8359 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -341,6 +341,10 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d return nil, dashboards.ErrDashboardTitleEmpty } + if len(dash.Title) > 5000 { + return nil, dashboards.ErrDashboardTitleTooLong + } + if len(dto.Message) > 500 { return nil, dashboards.ErrDashboardMessageTooLong } From 67b44ad22a8e4bfc811cd270814e731922d520ff Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Tue, 4 Mar 2025 10:05:41 -0500 Subject: [PATCH 002/312] Alerting: Fix state reason (#101530) --------- Signed-off-by: Yuri Tseretyan --- pkg/services/ngalert/state/state.go | 4 +- pkg/services/ngalert/state/state_test.go | 58 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/pkg/services/ngalert/state/state.go b/pkg/services/ngalert/state/state.go index 54694664cca..9c79b624179 100644 --- a/pkg/services/ngalert/state/state.go +++ b/pkg/services/ngalert/state/state.go @@ -832,9 +832,9 @@ func (a *State) transition(alertRule *models.AlertRule, result eval.Result, extr } func resultStateReason(result eval.Result, rule *models.AlertRule) string { - if rule.ExecErrState == models.KeepLastErrState || rule.NoDataState == models.KeepLast { + if result.State == eval.Error && rule.ExecErrState == models.KeepLastErrState || + result.State == eval.NoData && rule.NoDataState == models.KeepLast { return models.ConcatReasons(result.State.String(), models.StateReasonKeepLast) } - return result.State.String() } diff --git a/pkg/services/ngalert/state/state_test.go b/pkg/services/ngalert/state/state_test.go index 7216784a65c..8200696d385 100644 --- a/pkg/services/ngalert/state/state_test.go +++ b/pkg/services/ngalert/state/state_test.go @@ -1132,3 +1132,61 @@ func TestPatch(t *testing.T) { assert.EqualValues(t, orig.Annotations, state.Annotations) }) } + +func TestResultStateReason(t *testing.T) { + gen := ngmodels.RuleGen + tests := []struct { + name string + result eval.Result + rule *ngmodels.AlertRule + expected string + }{ + { + name: "Error state with KeepLast", + result: eval.Result{ + State: eval.Error, + }, + rule: gen.With(ngmodels.RuleMuts.WithErrorExecAs(ngmodels.KeepLastErrState)).GenerateRef(), + expected: "Error, KeepLast", + }, + { + name: "Error state without KeepLast", + result: eval.Result{ + State: eval.Error, + }, + rule: gen.With(ngmodels.RuleMuts.WithErrorExecAs(ngmodels.ErrorErrState)).GenerateRef(), + expected: "Error", + }, + { + name: "NoData state with KeepLast state", + result: eval.Result{ + State: eval.NoData, + }, + rule: gen.With(ngmodels.RuleMuts.WithNoDataExecAs(ngmodels.KeepLast)).GenerateRef(), + expected: "NoData, KeepLast", + }, + { + name: "NoData state without KeepLast", + result: eval.Result{ + State: eval.NoData, + }, + rule: gen.With(ngmodels.RuleMuts.WithNoDataExecAs(ngmodels.NoData)).GenerateRef(), + expected: "NoData", + }, + { + name: "Normal state", + result: eval.Result{ + State: eval.NoData, + }, + rule: gen.With(ngmodels.RuleMuts.WithErrorExecAs(ngmodels.ErrorErrState), ngmodels.RuleMuts.WithNoDataExecAs(ngmodels.NoData)).GenerateRef(), + expected: "NoData", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := resultStateReason(tc.result, tc.rule) + assert.Equal(t, tc.expected, result) + }) + } +} From 6d4a271a395c3bc75f1140779fd34f3205b11780 Mon Sep 17 00:00:00 2001 From: Jack Westbrook Date: Tue, 4 Mar 2025 16:22:05 +0100 Subject: [PATCH 003/312] Frontend: Expose unstable entrypoints for data and runtime (#101547) * feat(grafana-data): introduce unstable entrypoint to package * feat(grafana-runtime): introduce unstable entrypoint to package * feat(plugins): expose unstable entrypoints for data and runtime to plugins * feat(packages): dummy exports so package verification and shared deps imports work --- packages/grafana-data/package.json | 6 +++--- packages/grafana-data/rollup.config.ts | 18 ++++++++++++++++++ packages/grafana-data/src/unstable.ts | 13 +++++++++++++ packages/grafana-runtime/package.json | 6 +++--- packages/grafana-runtime/rollup.config.ts | 18 ++++++++++++++++++ packages/grafana-runtime/src/unstable.ts | 13 +++++++++++++ .../plugins/loader/sharedDependencies.ts | 2 ++ 7 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 packages/grafana-data/src/unstable.ts create mode 100644 packages/grafana-runtime/src/unstable.ts diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 6ae5450bf1f..c33053087f2 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -29,10 +29,10 @@ ], "scripts": { "build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild", - "clean": "rimraf ./dist ./compiled ./package.tgz", + "clean": "rimraf ./dist ./compiled ./unstable ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", - "postpack": "mv package.json.bak package.json" + "prepack": "cp package.json package.json.bak && ALIAS_PACKAGE_NAME=unstable node ../../scripts/prepare-npm-package.js", + "postpack": "mv package.json.bak package.json && rimraf ./unstable" }, "dependencies": { "@braintree/sanitize-url": "7.0.1", diff --git a/packages/grafana-data/rollup.config.ts b/packages/grafana-data/rollup.config.ts index 5a913449466..f67f12e07d0 100644 --- a/packages/grafana-data/rollup.config.ts +++ b/packages/grafana-data/rollup.config.ts @@ -11,5 +11,23 @@ export default [ plugins, output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-data')], }, + { + input: 'src/unstable.ts', + plugins, + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-data')], + }, tsDeclarationOutput(pkg), + tsDeclarationOutput(pkg, { + input: './compiled/unstable.d.ts', + output: [ + { + file: './dist/cjs/unstable.d.cts', + format: 'cjs', + }, + { + file: './dist/esm/unstable.d.mts', + format: 'es', + }, + ], + }), ]; diff --git a/packages/grafana-data/src/unstable.ts b/packages/grafana-data/src/unstable.ts new file mode 100644 index 00000000000..8a42447206f --- /dev/null +++ b/packages/grafana-data/src/unstable.ts @@ -0,0 +1,13 @@ +/** + * THESE APIS MUST NOT BE USED IN COMMUNITY PLUGINS. + * + * Unstable APIs are still in development and are subject to breaking changes + * at any point, like feature flags but for APIS. They must only be used in + * Grafana core and internal plugins where we can coordinate changes. + * + * Once mature, they will be moved to the main export, be available to plugins via the standard import path, + * and be subject to the standard policies + */ + +// This is a dummy export so typescript doesn't error importing an "empty module" +export const unstable = {}; diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 058e41e4d49..4211e3d2387 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -31,10 +31,10 @@ "scripts": { "build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild", "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", - "clean": "rimraf ./dist ./compiled ./package.tgz", + "clean": "rimraf ./dist ./compiled ./unstable ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", - "postpack": "mv package.json.bak package.json" + "prepack": "cp package.json package.json.bak && ALIAS_PACKAGE_NAME=unstable node ../../scripts/prepare-npm-package.js", + "postpack": "mv package.json.bak package.json && rimraf ./unstable" }, "dependencies": { "@grafana/data": "11.6.0-pre", diff --git a/packages/grafana-runtime/rollup.config.ts b/packages/grafana-runtime/rollup.config.ts index 02f523deee1..dc9c05e1421 100644 --- a/packages/grafana-runtime/rollup.config.ts +++ b/packages/grafana-runtime/rollup.config.ts @@ -11,5 +11,23 @@ export default [ plugins, output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-runtime')], }, + { + input: 'src/unstable.ts', + plugins, + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-runtime')], + }, tsDeclarationOutput(pkg), + tsDeclarationOutput(pkg, { + input: './compiled/unstable.d.ts', + output: [ + { + file: './dist/cjs/unstable.d.cts', + format: 'cjs', + }, + { + file: './dist/esm/unstable.d.mts', + format: 'es', + }, + ], + }), ]; diff --git a/packages/grafana-runtime/src/unstable.ts b/packages/grafana-runtime/src/unstable.ts new file mode 100644 index 00000000000..8a42447206f --- /dev/null +++ b/packages/grafana-runtime/src/unstable.ts @@ -0,0 +1,13 @@ +/** + * THESE APIS MUST NOT BE USED IN COMMUNITY PLUGINS. + * + * Unstable APIs are still in development and are subject to breaking changes + * at any point, like feature flags but for APIS. They must only be used in + * Grafana core and internal plugins where we can coordinate changes. + * + * Once mature, they will be moved to the main export, be available to plugins via the standard import path, + * and be subject to the standard policies + */ + +// This is a dummy export so typescript doesn't error importing an "empty module" +export const unstable = {}; diff --git a/public/app/features/plugins/loader/sharedDependencies.ts b/public/app/features/plugins/loader/sharedDependencies.ts index c294491c6ac..3d0ae285034 100644 --- a/public/app/features/plugins/loader/sharedDependencies.ts +++ b/public/app/features/plugins/loader/sharedDependencies.ts @@ -49,7 +49,9 @@ export const sharedDependenciesMap = { '@emotion/css': () => import('@emotion/css'), '@emotion/react': () => import('@emotion/react'), '@grafana/data': grafanaData, + '@grafana/data/unstable': () => import('@grafana/data/src/unstable'), '@grafana/runtime': grafanaRuntime, + '@grafana/runtime/unstable': () => import('@grafana/runtime/src/unstable'), '@grafana/slate-react': () => import('slate-react'), '@grafana/ui': grafanaUI, '@grafana/ui/unstable': () => import('@grafana/ui/src/unstable'), From 7b9970f1e77db866efa430e4ee4b251656d70d6c Mon Sep 17 00:00:00 2001 From: David Harris Date: Tue, 4 Mar 2025 15:36:56 +0000 Subject: [PATCH 004/312] chore: update text for ds connections tab (#101509) * chore: update text for ds connections tab Not sure if additional work is required to generate the translation * chore: text change for connection tab --------- Co-authored-by: Sam Kh. --- .../app/features/plugins/admin/components/ConnectionsTab.tsx | 4 ++-- public/locales/en-US/grafana.json | 2 +- public/locales/pseudo-LOCALE/grafana.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/features/plugins/admin/components/ConnectionsTab.tsx b/public/app/features/plugins/admin/components/ConnectionsTab.tsx index d4896232277..5567ac6f844 100644 --- a/public/app/features/plugins/admin/components/ConnectionsTab.tsx +++ b/public/app/features/plugins/admin/components/ConnectionsTab.tsx @@ -83,8 +83,8 @@ export function ConnectionsList({ - The data source connections below are all {'{{pluginName}}'}. You can find all of your data source connections - of all types in{' '} + You currently have the following data sources configured for {'{{pluginName}}'}, click a tile to view the + configuration details. You can find all of your data source connections in{' '} Connections -{' '} Data sources. diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index d6ecac881cc..14d83ff4b77 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -3052,7 +3052,7 @@ }, "details": { "connections-tab": { - "description": "The data source connections below are all {{pluginName}}. You can find all of your data source connections of all types in <4><0>Connections - <3>Data sources." + "description": "You currently have the following data sources configured for {{pluginName}}, click a tile to view the configuration details. You can find all of your data source connections in <4><0>Connections - <3>Data sources." }, "labels": { "contactGrafanaLabs": "Contact Grafana Labs", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index 62cb01b2939..4e3adb37edd 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -3052,7 +3052,7 @@ }, "details": { "connections-tab": { - "description": "Ŧĥę đäŧä şőūřčę čőʼnʼnęčŧįőʼnş þęľőŵ äřę äľľ {{pluginName}}. Ÿőū čäʼn ƒįʼnđ äľľ őƒ yőūř đäŧä şőūřčę čőʼnʼnęčŧįőʼnş őƒ äľľ ŧypęş įʼn <4><0>Cőʼnʼnęčŧįőʼnş - <3>Đäŧä şőūřčęş." + "description": "Ÿőū čūřřęʼnŧľy ĥävę ŧĥę ƒőľľőŵįʼnģ đäŧä şőūřčęş čőʼnƒįģūřęđ ƒőř {{pluginName}}, čľįčĸ ä ŧįľę ŧő vįęŵ ŧĥę čőʼnƒįģūřäŧįőʼn đęŧäįľş. Ÿőū čäʼn ƒįʼnđ äľľ őƒ yőūř đäŧä şőūřčę čőʼnʼnęčŧįőʼnş įʼn <4><0>Cőʼnʼnęčŧįőʼnş - <3>Đäŧä şőūřčęş." }, "labels": { "contactGrafanaLabs": "Cőʼnŧäčŧ Ğřäƒäʼnä Ŀäþş", From b8e1511c7fbe5b080a128f93cef482015c83fb10 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Tue, 4 Mar 2025 10:27:43 -0700 Subject: [PATCH 005/312] K8s: Increase max request body (#101564) --- pkg/services/apiserver/service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index 1105604aee1..18be2d05f84 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -335,6 +335,7 @@ func (s *service) start(ctx context.Context) error { transport := &roundTripperFunc{ready: make(chan struct{})} serverConfig.LoopbackClientConfig.Transport = transport serverConfig.LoopbackClientConfig.TLSClientConfig = clientrest.TLSClientConfig{} + serverConfig.MaxRequestBodyBytes = 16 * 1024 * 1024 // 16MB - determined by the size of `mediumtext` on mysql, which is used to save dashboard data var optsregister apistore.StorageOptionsRegister From 4595df9be6bc7fa38be783d5fcfc552c14807045 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Tue, 4 Mar 2025 17:30:14 +0000 Subject: [PATCH 006/312] Azure: Resource picker improvements (#101462) * Update resource group query - Updates the resource groups query to support users/apps with restricted permissions * Update resources request to be paginated - Also order by name - Add tests * Update test --- .../resourcePicker/resourcePickerData.test.ts | 48 +++++++++++++++- .../resourcePicker/resourcePickerData.ts | 56 +++++++++++++------ 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.test.ts b/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.test.ts index c8a99a18ff9..166ea418af2 100644 --- a/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.test.ts +++ b/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.test.ts @@ -66,7 +66,7 @@ describe('AzureMonitor resourcePickerData', () => { }); }); - it('makes multiple requests when arg returns a skipToken and passes the right skipToken to each subsequent call', async () => { + it('makes multiple requests for subscriptions when arg returns a skipToken and passes the right skipToken to each subsequent call', async () => { const response1 = { ...createMockARGSubscriptionResponse(), $skipToken: 'skipfirst100', @@ -82,6 +82,48 @@ describe('AzureMonitor resourcePickerData', () => { expect(postBody.options.$skipToken).toEqual('skipfirst100'); }); + it('makes multiple requests for resource groups when arg returns a skipToken and passes the right skipToken to each subsequent call', async () => { + const subscriptionResponse = createMockARGSubscriptionResponse(); + const resourceGroupResponse1 = { + ...createMockARGResourceGroupsResponse(), + $skipToken: 'skipfirst100', + }; + const resourceGroupResponse2 = createMockARGResourceGroupsResponse(); + const { resourcePickerData, postResource } = createResourcePickerData([ + subscriptionResponse, + resourceGroupResponse1, + resourceGroupResponse2, + ]); + + await resourcePickerData.getResourceGroupsBySubscriptionId('1', 'metrics'); + + expect(postResource).toHaveBeenCalledTimes(3); + const secondCall = postResource.mock.calls[2]; + const [_, postBody] = secondCall; + expect(postBody.options.$skipToken).toEqual('skipfirst100'); + }); + + it('makes multiple requests for resources when arg returns a skipToken and passes the right skipToken to each subsequent call', async () => { + const subscriptionResponse = createMockARGSubscriptionResponse(); + const resourcesResponse1 = { + ...createARGResourcesResponse(), + $skipToken: 'skipfirst100', + }; + const resourcesResponse2 = createARGResourcesResponse(); + const { resourcePickerData, postResource } = createResourcePickerData([ + subscriptionResponse, + resourcesResponse1, + resourcesResponse2, + ]); + + await resourcePickerData.getResourcesForResourceGroup('resourceGroupURI', 'metrics'); + + expect(postResource).toHaveBeenCalledTimes(3); + const secondCall = postResource.mock.calls[2]; + const [_, postBody] = secondCall; + expect(postBody.options.$skipToken).toEqual('skipfirst100'); + }); + it('returns a concatenates a formatted array of subscriptions when there are multiple pages from arg', async () => { const response1 = { ...createMockARGSubscriptionResponse(), @@ -129,7 +171,9 @@ describe('AzureMonitor resourcePickerData', () => { const firstCall = postResource.mock.calls[0]; const [path, postBody] = firstCall; expect(path).toEqual('resourcegraph/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01'); - expect(postBody.query).toContain("type == 'microsoft.resources/subscriptions/resourcegroups'"); + expect(postBody.query).toContain( + 'extend resourceGroupURI = strcat("/subscriptions/", subscriptionId, "/resourcegroups/", resourceGroup)' + ); expect(postBody.query).toContain("where subscriptionId == '123'"); }); diff --git a/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.ts b/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.ts index c0f9bd9500b..135dc332572 100644 --- a/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.ts +++ b/public/app/plugins/datasource/azuremonitor/resourcePicker/resourcePickerData.ts @@ -187,18 +187,23 @@ export default class ResourcePickerData extends DataSourceWithBackend< type: ResourcePickerQueryType ): Promise { // We can use subscription ID for the filtering here as they're unique + // The logic of this query is: + // Retrieve _all_ resources a user/app registration/identity has access to + // Filter by the namespaces that support metrics + // Filter to resources contained within the subscription + // Conduct a left-outer join on the resourcecontainers table to allow us to get the case-sensitive resource group name + // Return the count of resources in a group, the URI, and name of the group in ascending order const query = ` - resources - | join kind=inner ( - ResourceContainers - | where type == 'microsoft.resources/subscriptions/resourcegroups' - | project resourceGroupURI=id, resourceGroupName=name, resourceGroup, subscriptionId - ) on resourceGroup, subscriptionId - - ${await this.filterByType(type)} - | where subscriptionId == '${subscriptionId}' - | summarize count() by resourceGroupName, resourceGroupURI - | order by resourceGroupURI asc`; + resources + ${await this.filterByType(type)} + | where subscriptionId == '${subscriptionId}' + | extend resourceGroupURI = strcat("/subscriptions/", subscriptionId, "/resourcegroups/", resourceGroup) + | join kind=leftouter (resourcecontainers + | where type =~ 'microsoft.resources/subscriptions/resourcegroups' + | project resourceGroupName=name, resourceGroupURI=tolower(id)) on resourceGroupURI + | project resourceGroupName=iff(resourceGroupName != "", resourceGroupName, resourceGroup), resourceGroupURI + | summarize count() by resourceGroupName, resourceGroupURI + | order by tolower(resourceGroupName) asc `; let resourceGroups: RawAzureResourceGroupItem[] = []; let allFetched = false; @@ -240,13 +245,30 @@ export default class ResourcePickerData extends DataSourceWithBackend< // We use resource group URI for the filtering here because resource group names are not unique across subscriptions // We also add a slash at the end of the resource group URI to ensure we do not pull resources from a resource group // that has a similar naming prefix e.g. resourceGroup1 and resourceGroup10 - const { data: response } = await this.makeResourceGraphRequest(` - resources - | where id hasprefix "${resourceGroupUri}/" - ${await this.filterByType(type)} - `); + const query = ` + resources + | where id hasprefix "${resourceGroupUri}/" + ${await this.filterByType(type)} + | order by tolower(name) asc`; - return response.map((item) => { + let resources: RawAzureResourceItem[] = []; + let allFetched = false; + let $skipToken = undefined; + while (!allFetched) { + // The response may include several pages + let options: Partial = {}; + if ($skipToken) { + options = { + $skipToken, + }; + } + const resourceResponse = await this.makeResourceGraphRequest(query, 1, options); + resources = resources.concat(resourceResponse.data); + $skipToken = resourceResponse.$skipToken; + allFetched = !$skipToken; + } + + return resources.map((item) => { const parsedUri = parseResourceURI(item.id); if (!parsedUri || !parsedUri.resourceName) { throw new Error('unable to fetch resource details'); From e933f7cf011ce045ab999fa6cb7e40eb8dc7d962 Mon Sep 17 00:00:00 2001 From: Kevin Minehart <5140827+kminehart@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:20:04 -0600 Subject: [PATCH 007/312] CI: Add frontend lint GitHub Actions workflow (#101559) * lint frontend * set frontend as owners of the lint workflow * Fix syntax * fix warning * fix quote --- .github/CODEOWNERS | 1 + .github/workflows/frontend-lint.yml | 55 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/frontend-lint.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2fe4b77837..afe2c98e0a3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -818,6 +818,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/changelog.yml @zserge /.github/workflows/actions/changelog @zserge /.github/workflows/frontend-unit-tests.yml @grafana/grafana-frontend-platform +/.github/workflows/frontend-lint.yml @grafana/grafana-frontend-platform # Generated files not requiring owner approval /packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot diff --git a/.github/workflows/frontend-lint.yml b/.github/workflows/frontend-lint.yml new file mode 100644 index 00000000000..a9b65b247fb --- /dev/null +++ b/.github/workflows/frontend-lint.yml @@ -0,0 +1,55 @@ +name: Lint Frontend +on: + pull_request: + push: + branches: + - main + - release-*.*.* + +jobs: + verify-i18n: + name: Verify i18n + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - run: yarn install --immutable --check-cache + - run: | + extract_error_message='::error::Extraction failed. Make sure that you have no dynamic translation phrases, such as "t(`preferences.theme.{themeID}`, themeName)" and that no translation key is used twice. Search the output for '[warning]' to find the offending file.' + make i18n-extract || (echo "${extract_error_message}" && false) + - run: | + uncommited_error_message="::error::Translation extraction has not been committed. Please run 'make i18n-extract', commit the changes and push again." + file_diff=$(git diff --dirstat public/locales) + if [ -n "$file_diff" ]; then + echo $file_diff + echo "${uncommited_error_message}" + exit 1 + fi + prettier: + name: Prettier + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - 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 prettier:check + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - 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 typecheck From 7c35d741ba665235c15cda5b0de44a2525706ba1 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Tue, 4 Mar 2025 12:56:21 -0700 Subject: [PATCH 008/312] Folders: Add validation that folder is not a parent of itself (#101569) --- pkg/registry/apis/folders/register.go | 4 ++ pkg/registry/apis/folders/register_test.go | 38 ++++++++++++++++--- pkg/services/folder/folderimpl/sqlstore.go | 4 ++ .../folder/folderimpl/sqlstore_test.go | 12 ++++++ pkg/services/folder/model.go | 2 + 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/pkg/registry/apis/folders/register.go b/pkg/registry/apis/folders/register.go index 4e5e144d50e..6d4d64f1d9a 100644 --- a/pkg/registry/apis/folders/register.go +++ b/pkg/registry/apis/folders/register.go @@ -297,6 +297,10 @@ func (b *FolderAPIBuilder) validateOnCreate(ctx context.Context, id string, obj return dashboards.ErrFolderTitleEmpty } + if f.Name == getParent(obj) { + return folder.ErrFolderCannotBeParentOfItself + } + _, err := b.checkFolderMaxDepth(ctx, obj) if err != nil { return err diff --git a/pkg/registry/apis/folders/register_test.go b/pkg/registry/apis/folders/register_test.go index 5d80a2c2369..17ba1f542e1 100644 --- a/pkg/registry/apis/folders/register_test.go +++ b/pkg/registry/apis/folders/register_test.go @@ -9,6 +9,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/admission" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -26,13 +27,22 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) { name string } - circularObj := &v0alpha1.Folder{ + initialMaxDepth := folderValidationRules.maxDepth + folderValidationRules.maxDepth = 2 + defer func() { folderValidationRules.maxDepth = initialMaxDepth }() + deepFolder := &v0alpha1.Folder{ Spec: v0alpha1.Spec{ Title: "foo", }, } - circularObj.Name = "valid-name" - circularObj.Annotations = map[string]string{"grafana.app/folder": "valid-name"} + deepFolder.Name = "valid-parent" + deepFolder.Annotations = map[string]string{"grafana.app/folder": "valid-grandparent"} + parentFolder := &v0alpha1.Folder{ + Spec: v0alpha1.Spec{ + Title: "foo-grandparent", + }, + } + deepFolder.Name = "valid-grandparent" tests := []struct { name string @@ -71,12 +81,15 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) { Title: "foo", }, }, - annotations: map[string]string{"grafana.app/folder": "valid-name"}, + annotations: map[string]string{"grafana.app/folder": "valid-parent"}, name: "valid-name", }, setupFn: func(m *mock.Mock) { - m.On("Get", mock.Anything, "valid-name", mock.Anything).Return( - circularObj, + m.On("Get", mock.Anything, "valid-parent", mock.Anything).Return( + deepFolder, + nil) + m.On("Get", mock.Anything, "valid-grandparent", mock.Anything).Return( + parentFolder, nil) }, err: folder.ErrMaximumDepthReached, @@ -93,6 +106,19 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) { }, err: dashboards.ErrFolderTitleEmpty, }, + { + name: "should return error if folder is a parent of itself", + input: input{ + annotations: map[string]string{utils.AnnoKeyFolder: "myself"}, + obj: &v0alpha1.Folder{ + Spec: v0alpha1.Spec{ + Title: "title", + }, + }, + name: "myself", + }, + err: folder.ErrFolderCannotBeParentOfItself, + }, } s := (grafanarest.Storage)(nil) diff --git a/pkg/services/folder/folderimpl/sqlstore.go b/pkg/services/folder/folderimpl/sqlstore.go index e257c7d8761..13a9943b7b2 100644 --- a/pkg/services/folder/folderimpl/sqlstore.go +++ b/pkg/services/folder/folderimpl/sqlstore.go @@ -39,6 +39,10 @@ func (ss *FolderStoreImpl) Create(ctx context.Context, cmd folder.CreateFolderCo return nil, folder.ErrBadRequest.Errorf("missing UID") } + if cmd.UID == cmd.ParentUID { + return nil, folder.ErrFolderCannotBeParentOfItself + } + var foldr *folder.Folder /* version := 1 diff --git a/pkg/services/folder/folderimpl/sqlstore_test.go b/pkg/services/folder/folderimpl/sqlstore_test.go index e76607cb401..aff18e4e3d7 100644 --- a/pkg/services/folder/folderimpl/sqlstore_test.go +++ b/pkg/services/folder/folderimpl/sqlstore_test.go @@ -60,6 +60,18 @@ func TestIntegrationCreate(t *testing.T) { require.Error(t, err) }) + t.Run("creating a folder with itself as a parent should fail", func(t *testing.T) { + uid := util.GenerateShortUID() + _, err := folderStore.Create(context.Background(), folder.CreateFolderCommand{ + Title: folderTitle, + OrgID: orgID, + ParentUID: uid, + Description: folderDsc, + UID: uid, + }) + require.ErrorIs(t, err, folder.ErrFolderCannotBeParentOfItself) + }) + t.Run("creating a folder without providing a parent should default to the empty parent folder", func(t *testing.T) { uid := util.GenerateShortUID() f, err := folderStore.Create(context.Background(), folder.CreateFolderCommand{ diff --git a/pkg/services/folder/model.go b/pkg/services/folder/model.go index 769ee259f3f..19e6c43abe6 100644 --- a/pkg/services/folder/model.go +++ b/pkg/services/folder/model.go @@ -1,6 +1,7 @@ package folder import ( + "errors" "fmt" "time" @@ -20,6 +21,7 @@ var ErrInternal = errutil.Internal("folder.internal") var ErrCircularReference = errutil.BadRequest("folder.circular-reference", errutil.WithPublicMessage("Circular reference detected")) var ErrTargetRegistrySrvConflict = errutil.Internal("folder.target-registry-srv-conflict") var ErrFolderNotEmpty = errutil.BadRequest("folder.not-empty", errutil.WithPublicMessage("Folder cannot be deleted: folder is not empty")) +var ErrFolderCannotBeParentOfItself = errors.New("folder cannot be parent of itself") const ( GeneralFolderUID = "general" From 8832aa2aa21086e44ce17fb40ba457c7a5e8e3bd Mon Sep 17 00:00:00 2001 From: J Stickler Date: Tue, 4 Mar 2025 16:09:25 -0500 Subject: [PATCH 009/312] Docs: Fix typo in Drilldown note (#101579) docs: Fix typo in Drilldown note --- docs/sources/shared/plugins/rename-note.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/shared/plugins/rename-note.md b/docs/sources/shared/plugins/rename-note.md index 5eaa9a1adf4..4fec1abb499 100644 --- a/docs/sources/shared/plugins/rename-note.md +++ b/docs/sources/shared/plugins/rename-note.md @@ -15,5 +15,5 @@ labels: {{< admonition type="note" >}} The Grafana Explore apps have changed to Grafana Drilldown apps. For example, Explore Logs is now Logs Drilldown. -To learn more, read [Grafana Drilldown apps: the improved queryless experience known as the Explore apps](https://grafana.com/blog/2025/02/20/grafana-drilldown-apps-the-improved-queryless-experience-formerly-known-as-the-explore-apps/). +To learn more, read [Grafana Drilldown apps: the improved queryless experience formerly known as the Explore apps](https://grafana.com/blog/2025/02/20/grafana-drilldown-apps-the-improved-queryless-experience-formerly-known-as-the-explore-apps/). {{< /admonition >}} From 67221fb32819c60a048999bb6e6621be0b7ddfc7 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Tue, 4 Mar 2025 15:31:41 -0700 Subject: [PATCH 010/312] K8s: Folders: Fix not found errors (#101585) --- pkg/registry/apis/folders/legacy_storage.go | 10 +-- .../folder/folderimpl/unifiedstore.go | 31 +++++---- .../folder/folderimpl/unifiedstore_test.go | 63 +++++++++++++++++++ 3 files changed, 87 insertions(+), 17 deletions(-) diff --git a/pkg/registry/apis/folders/legacy_storage.go b/pkg/registry/apis/folders/legacy_storage.go index 34dcaa9a73b..550331c855e 100644 --- a/pkg/registry/apis/folders/legacy_storage.go +++ b/pkg/registry/apis/folders/legacy_storage.go @@ -2,7 +2,6 @@ package folders import ( "context" - "errors" "fmt" "strings" @@ -138,13 +137,14 @@ func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.Ge UID: &name, OrgID: info.OrgID, }) - if err != nil || dto == nil { - if errors.Is(err, dashboards.ErrFolderNotFound) || err == nil { - err = resourceInfo.NewNotFound(name) - } + if err != nil { statusErr := apierrors.ToFolderStatusError(err) return nil, &statusErr } + if dto == nil { + statusErr := apierrors.ToFolderStatusError(dashboards.ErrFolderNotFound) + return nil, &statusErr + } r, err := convertToK8sResource(dto, s.namespacer) if err != nil { diff --git a/pkg/services/folder/folderimpl/unifiedstore.go b/pkg/services/folder/folderimpl/unifiedstore.go index b1615eb4b1e..0d64cdcf2f3 100644 --- a/pkg/services/folder/folderimpl/unifiedstore.go +++ b/pkg/services/folder/folderimpl/unifiedstore.go @@ -77,6 +77,10 @@ func (ss *FolderUnifiedStoreImpl) Delete(ctx context.Context, UIDs []string, org func (ss *FolderUnifiedStoreImpl) Update(ctx context.Context, cmd folder.UpdateFolderCommand) (*folder.Folder, error) { obj, err := ss.k8sclient.Get(ctx, cmd.UID, cmd.OrgID, v1.GetOptions{}) if err != nil { + if apierrors.IsNotFound(err) { + return nil, dashboards.ErrFolderNotFound + } + return nil, err } updated := obj.DeepCopy() @@ -144,7 +148,7 @@ func (ss *FolderUnifiedStoreImpl) GetParents(ctx context.Context, q folder.GetPa parentUid := q.UID for parentUid != "" { - out, err := ss.k8sclient.Get(ctx, parentUid, q.OrgID, v1.GetOptions{}) + folder, err := ss.Get(ctx, folder.GetFolderQuery{UID: &parentUid, OrgID: q.OrgID}) if err != nil { var statusError *apierrors.StatusError if errors.As(err, &statusError) && statusError.ErrStatus.Code == http.StatusForbidden { @@ -155,11 +159,6 @@ func (ss *FolderUnifiedStoreImpl) GetParents(ctx context.Context, q folder.GetPa return nil, err } - folder, err := ss.UnstructuredToLegacyFolder(ctx, out) - if err != nil { - return nil, err - } - parentUid = folder.ParentUID hits = append(hits, folder) } @@ -183,6 +182,15 @@ func (ss *FolderUnifiedStoreImpl) GetChildren(ctx context.Context, q folder.GetC q.Page = 1 } + if q.UID != "" { + // the original get children query fails if the parent folder does not exist + // check that the parent exists first + _, err := ss.Get(ctx, folder.GetFolderQuery{UID: &q.UID, OrgID: q.OrgID}) + if err != nil { + return nil, err + } + } + req := &resource.ResourceSearchRequest{ Options: &resource.ListOptions{ Fields: []*resource.Requirement{ @@ -229,12 +237,7 @@ func (ss *FolderUnifiedStoreImpl) GetChildren(ctx context.Context, q folder.GetC } // search only returns a subset of info, get all info of the folder - item, err := ss.k8sclient.Get(ctx, item.Name, q.OrgID, v1.GetOptions{}) - if err != nil { - return nil, err - } - - f, err := ss.UnstructuredToLegacyFolder(ctx, item) + f, err := ss.Get(ctx, folder.GetFolderQuery{UID: &item.Name, OrgID: q.OrgID}) if err != nil { return nil, err } @@ -411,6 +414,10 @@ func getDescendants(nodes map[string]*folder.Folder, tree map[string]map[string] func (ss *FolderUnifiedStoreImpl) CountFolderContent(ctx context.Context, orgID int64, ancestor_uid string) (folder.DescendantCounts, error) { counts, err := ss.k8sclient.Get(ctx, ancestor_uid, orgID, v1.GetOptions{}, "counts") if err != nil { + if apierrors.IsNotFound(err) { + return nil, dashboards.ErrFolderNotFound + } + return nil, err } diff --git a/pkg/services/folder/folderimpl/unifiedstore_test.go b/pkg/services/folder/folderimpl/unifiedstore_test.go index f056ae96ba5..6c51829f671 100644 --- a/pkg/services/folder/folderimpl/unifiedstore_test.go +++ b/pkg/services/folder/folderimpl/unifiedstore_test.go @@ -9,6 +9,7 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver/client" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/storage/unified/resource" "github.com/stretchr/testify/mock" @@ -16,6 +17,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" ) @@ -170,6 +172,16 @@ func TestGetParents(t *testing.T) { require.Len(t, result, 1) require.Equal(t, "parenttwo", result[0].UID) }) + t.Run("should stop if parent folder is not found", func(t *testing.T) { + mockCli.On("Get", mock.Anything, "parentone", orgID, mock.Anything, mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "folders.folder.grafana.app", Resource: "folder"}, "parentone")).Once() + + _, err := store.GetParents(ctx, folder.GetParentsQuery{ + UID: "parentone", + OrgID: orgID, + }) + + require.ErrorIs(t, err, dashboards.ErrFolderNotFound) + }) } func TestGetChildren(t *testing.T) { @@ -213,6 +225,11 @@ func TestGetChildren(t *testing.T) { }, TotalHits: 1, }, nil).Once() + mockCli.On("Get", mock.Anything, "folder1", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{"name": "folder1"}, + }, + }, nil).Once() mockCli.On("Get", mock.Anything, "folder2", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ Object: map[string]interface{}{ "metadata": map[string]interface{}{"name": "folder2"}, @@ -235,6 +252,47 @@ func TestGetChildren(t *testing.T) { require.Equal(t, "folder3", result[1].UID) }) + t.Run("should return an error if the folder is not found", func(t *testing.T) { + mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{ + Options: &resource.ListOptions{ + Fields: []*resource.Requirement{ + { + Key: resource.SEARCH_FIELD_FOLDER, + Operator: string(selection.In), + Values: []string{"folder1"}, + }, + }, + }, + Limit: folderSearchLimit, // should default to folderSearchLimit + Offset: 0, // should be set as limit * (page - 1) + Page: 1, // should be set to 1 by default + }).Return(&resource.ResourceSearchResponse{ + Results: &resource.ResourceTable{ + Columns: []*resource.ResourceTableColumnDefinition{ + {Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING}, + }, + Rows: []*resource.ResourceTableRow{ + { + Key: &resource.ResourceKey{Name: "folder2", Resource: "folder"}, + Cells: [][]byte{[]byte("folder1")}, + }, + { + Key: &resource.ResourceKey{Name: "folder3", Resource: "folder"}, + Cells: [][]byte{[]byte("folder1")}, + }, + }, + }, + TotalHits: 1, + }, nil).Once() + mockCli.On("Get", mock.Anything, "folder1", orgID, mock.Anything, mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "folders.folder.grafana.app", Resource: "folder"}, "folder1")).Once() + + _, err := store.GetChildren(ctx, folder.GetChildrenQuery{ + UID: "folder1", + OrgID: orgID, + }) + require.ErrorIs(t, err, dashboards.ErrFolderNotFound) + }) + t.Run("pages should be able to be set, general folder should be turned to empty string, and folder uids should be passed in", func(t *testing.T) { mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{ Options: &resource.ListOptions{ @@ -301,6 +359,11 @@ func TestGetChildren(t *testing.T) { }, TotalHits: 1, }, nil) + mockCli.On("Get", mock.Anything, "folder", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{"name": "folder"}, + }, + }, nil) mockCli.On("Get", mock.Anything, accesscontrol.K6FolderUID, orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ Object: map[string]interface{}{ "metadata": map[string]interface{}{"name": accesscontrol.K6FolderUID}, From e806a00701c0ad242a9a25754f3dc8d7d565e903 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Tue, 4 Mar 2025 16:11:42 -0700 Subject: [PATCH 011/312] K8s: Dashboards: Add useful error message for too large (#101590) --- pkg/api/apierrors/dashboard.go | 6 ++++++ pkg/services/apiserver/service.go | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/api/apierrors/dashboard.go b/pkg/api/apierrors/dashboard.go index 6f740d80afd..e05c81ce329 100644 --- a/pkg/api/apierrors/dashboard.go +++ b/pkg/api/apierrors/dashboard.go @@ -7,9 +7,11 @@ import ( "net/http" "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/services/apiserver" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/util" + apierrors "k8s.io/apimachinery/pkg/api/errors" ) // ToDashboardErrorResponse returns a different response status according to the dashboard error type @@ -39,5 +41,9 @@ func ToDashboardErrorResponse(ctx context.Context, pluginStore pluginstore.Store return response.JSON(http.StatusPreconditionFailed, util.DynMap{"status": "plugin-dashboard", "message": message}) } + if apierrors.IsRequestEntityTooLargeError(err) { + return response.Error(http.StatusRequestEntityTooLarge, fmt.Sprintf("Dashboard is too large, max is %d MB", apiserver.MaxRequestBodyBytes/1024/1024), err) + } + return response.Error(http.StatusInternalServerError, "Failed to save dashboard", err) } diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index 18be2d05f84..4a4b5ac3ab0 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -80,6 +80,8 @@ var ( ready = make(chan struct{}) ) +const MaxRequestBodyBytes = 16 * 1024 * 1024 // 16MB - determined by the size of `mediumtext` on mysql, which is used to save dashboard data + func init() { // we need to add the options to empty v1 metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"}) @@ -335,7 +337,7 @@ func (s *service) start(ctx context.Context) error { transport := &roundTripperFunc{ready: make(chan struct{})} serverConfig.LoopbackClientConfig.Transport = transport serverConfig.LoopbackClientConfig.TLSClientConfig = clientrest.TLSClientConfig{} - serverConfig.MaxRequestBodyBytes = 16 * 1024 * 1024 // 16MB - determined by the size of `mediumtext` on mysql, which is used to save dashboard data + serverConfig.MaxRequestBodyBytes = MaxRequestBodyBytes var optsregister apistore.StorageOptionsRegister From 1f36a8a27c4e74200055a5a69ba4c36b23b3e02e Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 02:31:01 +0200 Subject: [PATCH 012/312] I18n: Download translations from Crowdin (#101592) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/de-DE/grafana.json | 21 +++++++++++++++------ public/locales/es-ES/grafana.json | 21 +++++++++++++++------ public/locales/fr-FR/grafana.json | 21 +++++++++++++++------ public/locales/pt-BR/grafana.json | 21 +++++++++++++++------ public/locales/zh-Hans/grafana.json | 21 +++++++++++++++------ 5 files changed, 75 insertions(+), 30 deletions(-) diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index 60ec10f7b67..a71a2d7f649 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -1087,12 +1087,6 @@ "options-header": "", "selection-number": "" } - }, - "tab": { - "multi-select": { - "options-header": "", - "selection-number": "" - } } }, "editpane": { @@ -1250,8 +1244,23 @@ }, "tabs-layout": { "description": "", + "menu": { + "move-tab": "" + }, + "multi-select": { + "title": "" + }, "name": "", "tab": { + "menu": { + "add": "", + "add-panel": "", + "add-tab-above": "", + "add-tab-after": "", + "add-tab-before": "", + "move-left": "", + "move-right": "" + }, "new": "" }, "tab-options": { diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index 7e2083358d4..bb0e9bc24ff 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -1087,12 +1087,6 @@ "options-header": "", "selection-number": "" } - }, - "tab": { - "multi-select": { - "options-header": "", - "selection-number": "" - } } }, "editpane": { @@ -1250,8 +1244,23 @@ }, "tabs-layout": { "description": "", + "menu": { + "move-tab": "" + }, + "multi-select": { + "title": "" + }, "name": "", "tab": { + "menu": { + "add": "", + "add-panel": "", + "add-tab-above": "", + "add-tab-after": "", + "add-tab-before": "", + "move-left": "", + "move-right": "" + }, "new": "" }, "tab-options": { diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 5b5045b14c7..d52cecfbc5f 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -1087,12 +1087,6 @@ "options-header": "", "selection-number": "" } - }, - "tab": { - "multi-select": { - "options-header": "", - "selection-number": "" - } } }, "editpane": { @@ -1250,8 +1244,23 @@ }, "tabs-layout": { "description": "", + "menu": { + "move-tab": "" + }, + "multi-select": { + "title": "" + }, "name": "", "tab": { + "menu": { + "add": "", + "add-panel": "", + "add-tab-above": "", + "add-tab-after": "", + "add-tab-before": "", + "move-left": "", + "move-right": "" + }, "new": "" }, "tab-options": { diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 05eaeaa5b35..6eebfbe6174 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -1087,12 +1087,6 @@ "options-header": "", "selection-number": "" } - }, - "tab": { - "multi-select": { - "options-header": "", - "selection-number": "" - } } }, "editpane": { @@ -1250,8 +1244,23 @@ }, "tabs-layout": { "description": "", + "menu": { + "move-tab": "" + }, + "multi-select": { + "title": "" + }, "name": "", "tab": { + "menu": { + "add": "", + "add-panel": "", + "add-tab-above": "", + "add-tab-after": "", + "add-tab-before": "", + "move-left": "", + "move-right": "" + }, "new": "" }, "tab-options": { diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index 62c80b1ff34..e68e88f462c 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -1078,12 +1078,6 @@ "options-header": "", "selection-number": "" } - }, - "tab": { - "multi-select": { - "options-header": "", - "selection-number": "" - } } }, "editpane": { @@ -1241,8 +1235,23 @@ }, "tabs-layout": { "description": "", + "menu": { + "move-tab": "" + }, + "multi-select": { + "title": "" + }, "name": "", "tab": { + "menu": { + "add": "", + "add-panel": "", + "add-tab-above": "", + "add-tab-after": "", + "add-tab-before": "", + "move-left": "", + "move-right": "" + }, "new": "" }, "tab-options": { From e7baf9804e17712bf715c932c00fe429505284a6 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Tue, 4 Mar 2025 20:06:24 -0600 Subject: [PATCH 013/312] Transformations: Avoid mutation during variable interpolation (#101594) --- .../src/transformations/transformDataFrame.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/grafana-data/src/transformations/transformDataFrame.ts b/packages/grafana-data/src/transformations/transformDataFrame.ts index 1a923ec0d86..35951f9f5f6 100644 --- a/packages/grafana-data/src/transformations/transformDataFrame.ts +++ b/packages/grafana-data/src/transformations/transformDataFrame.ts @@ -1,3 +1,4 @@ +import { cloneDeep } from 'lodash'; import { MonoTypeOperatorFunction, Observable, of } from 'rxjs'; import { map, mergeMap } from 'rxjs/operators'; @@ -12,9 +13,6 @@ import { import { getFrameMatchers } from './matchers'; import { standardTransformersRegistry, TransformerRegistryItem } from './standardTransformersRegistry'; -// when running within Scenes, we can skip var interpolation, since it's already handled upstream -const isScenes = window.__grafanaSceneContext != null; - const getOperator = (config: DataTransformerConfig, ctx: DataTransformContext): MonoTypeOperatorFunction => (source) => { @@ -27,9 +25,12 @@ const getOperator = const defaultOptions = info.transformation.defaultOptions ?? {}; const options = { ...defaultOptions, ...config.options }; + // when running within Scenes, we can skip var interpolation, since it's already handled upstream + const isScenes = window.__grafanaSceneContext != null; + const interpolated = isScenes ? options - : deepIterate(options, (v) => { + : deepIterate(cloneDeep(options), (v) => { if (typeof v === 'string') { return ctx.interpolate(v); } From dc2defd84f2b0a19b652b8175da8eed6d0fd89b3 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 5 Mar 2025 09:54:20 +0300 Subject: [PATCH 014/312] K8s/Annotations: Use manager/source annotations rather than repo (#101313) Co-authored-by: Stephanie Hingtgen --- pkg/api/dtos/folder.go | 5 +- pkg/api/folder.go | 12 +- pkg/apimachinery/utils/manager.go | 28 ++- pkg/apimachinery/utils/meta.go | 200 ++++++------------ pkg/apimachinery/utils/meta_test.go | 94 ++++---- pkg/apis/dashboard/utils.go | 42 +--- .../apis/dashboard/legacy/sql_dashboards.go | 24 +-- .../dashboard/legacy/sql_dashboards_test.go | 25 ++- .../dashboard/legacysearcher/search_client.go | 40 ++-- .../legacysearcher/search_client_test.go | 22 +- pkg/registry/apis/dashboard/sub_dto.go | 10 +- pkg/services/dashboards/models.go | 8 +- .../dashboards/service/dashboard_service.go | 139 ++++++------ .../service/dashboard_service_test.go | 156 ++++++++------ .../dashboards/service/search/search.go | 9 +- pkg/services/folder/folderimpl/conversions.go | 7 +- .../folder/folderimpl/conversions_test.go | 3 +- pkg/services/folder/model.go | 5 +- pkg/storage/unified/apistore/prepare.go | 15 +- pkg/storage/unified/apistore/prepare_test.go | 40 ++-- pkg/storage/unified/resource/document.go | 23 +- pkg/storage/unified/resource/document_test.go | 13 +- pkg/storage/unified/resource/server.go | 9 +- pkg/storage/unified/search/bleve.go | 30 +-- pkg/storage/unified/search/bleve_mappings.go | 28 ++- .../unified/search/bleve_mappings_test.go | 15 +- pkg/storage/unified/search/bleve_test.go | 63 +++--- pkg/storage/unified/search/document_test.go | 18 +- .../search/testdata/doc/folder-aaa-out.json | 9 +- .../search/testdata/doc/folder-aaa.json | 2 +- .../search/testdata/doc/folder-bbb-out.json | 9 +- .../search/testdata/doc/folder-bbb.json | 2 +- .../search/testdata/doc/playlist-aaa-out.json | 11 +- .../search/testdata/doc/report-aaa-out.json | 4 +- pkg/tests/apis/dashboard/dashboards_test.go | 4 +- public/api-merged.json | 19 +- public/openapi3.json | 19 +- 37 files changed, 589 insertions(+), 573 deletions(-) diff --git a/pkg/api/dtos/folder.go b/pkg/api/dtos/folder.go index 19df61f4f68..02187b73358 100644 --- a/pkg/api/dtos/folder.go +++ b/pkg/api/dtos/folder.go @@ -3,6 +3,7 @@ package dtos import ( "time" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/services/accesscontrol" ) @@ -31,7 +32,7 @@ type Folder struct { // When the folder belongs to a repository // NOTE: this is only populated when folders are managed by unified storage - Repository string `json:"repository,omitempty"` + ManagedBy utils.ManagerKind `json:"managedBy,omitempty"` } type FolderSearchHit struct { @@ -42,5 +43,5 @@ type FolderSearchHit struct { // When the folder belongs to a repository // NOTE: this is only populated when folders are managed by unified storage - Repository string `json:"repository,omitempty"` + ManagedBy utils.ManagerKind `json:"managedBy,omitempty"` } diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 185eb427d7d..b18db9c7850 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -94,11 +94,11 @@ func (hs *HTTPServer) GetFolders(c *contextmodel.ReqContext) response.Response { hits := make([]dtos.FolderSearchHit, 0) for _, f := range folders { hits = append(hits, dtos.FolderSearchHit{ - ID: f.ID, // nolint:staticcheck - UID: f.UID, - Title: f.Title, - ParentUID: f.ParentUID, - Repository: f.Repository, + ID: f.ID, // nolint:staticcheck + UID: f.UID, + Title: f.Title, + ParentUID: f.ParentUID, + ManagedBy: f.ManagedBy, }) metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetFolders).Inc() } @@ -427,7 +427,7 @@ func (hs *HTTPServer) newToFolderDto(c *contextmodel.ReqContext, f *folder.Folde Version: f.Version, AccessControl: acMetadata, ParentUID: f.ParentUID, - Repository: f.Repository, + ManagedBy: f.ManagedBy, }, nil } diff --git a/pkg/apimachinery/utils/manager.go b/pkg/apimachinery/utils/manager.go index 76add8722c7..0b76cee2928 100644 --- a/pkg/apimachinery/utils/manager.go +++ b/pkg/apimachinery/utils/manager.go @@ -1,28 +1,27 @@ package utils -import "time" - // ManagerProperties is used to identify the manager of the resource. type ManagerProperties struct { // The kind of manager, which is responsible for managing the resource. // Examples include "git", "terraform", "kubectl", etc. - Kind ManagerKind + Kind ManagerKind `json:"kind,omitempty"` // The identity of the manager, which refers to a specific instance of the manager. // The format & the value depends on the manager kind. - Identity string + Identity string `json:"id,omitempty"` // AllowsEdits indicates whether the manager allows edits to the resource. // If set to true, it means that other requesters can edit the resource. - AllowsEdits bool + AllowsEdits bool `json:"allowEdits,omitempty"` // Suspended indicates whether the manager is suspended. // If set to true, then the manager skip updates to the resource. - Suspended bool + Suspended bool `json:"suspended,omitempty"` } // ManagerKind is the type of manager, which is responsible for managing the resource. // It can be a user or a tool or a generic API client. +// +enum type ManagerKind string // Known values for ManagerKind. @@ -31,6 +30,11 @@ const ( ManagerKindRepo ManagerKind = "repo" ManagerKindTerraform ManagerKind = "terraform" ManagerKindKubectl ManagerKind = "kubectl" + ManagerKindPlugin ManagerKind = "plugin" + + // Deprecated: this is used as a shim/migration path for legacy file provisioning + // Previously this was a "file:" prefix + ManagerKindClassicFP ManagerKind = "classic-file-provisioning" ) // ParseManagerKindString parses a string into a ManagerKind. @@ -44,6 +48,10 @@ func ParseManagerKindString(v string) ManagerKind { return ManagerKindTerraform case string(ManagerKindKubectl): return ManagerKindKubectl + case string(ManagerKindPlugin): + return ManagerKindPlugin + case string(ManagerKindClassicFP): // nolint:staticcheck + return ManagerKindClassicFP // nolint:staticcheck default: return ManagerKindUnknown } @@ -55,13 +63,13 @@ func ParseManagerKindString(v string) ManagerKind { type SourceProperties struct { // The path to the source of the resource. // Can be a file path, a URL, etc. - Path string + Path string `json:"path,omitempty"` // The checksum of the source of the resource. // An example could be a git commit hash. - Checksum string + Checksum string `json:"checksum,omitempty"` - // The timestamp of the source of the resource. + // The unix millis timestamp of the source of the resource. // An example could be the file modification time. - Timestamp time.Time + TimestampMillis int64 `json:"timestampMillis,omitempty"` } diff --git a/pkg/apimachinery/utils/meta.go b/pkg/apimachinery/utils/meta.go index 116fc4f37d9..49bbcd3eb02 100644 --- a/pkg/apimachinery/utils/meta.go +++ b/pkg/apimachinery/utils/meta.go @@ -41,10 +41,10 @@ const AnnoKeyMessage = "grafana.app/message" // Identify where values came from -const AnnoKeyRepoName = "grafana.app/repoName" -const AnnoKeyRepoPath = "grafana.app/repoPath" -const AnnoKeyRepoHash = "grafana.app/repoHash" -const AnnoKeyRepoTimestamp = "grafana.app/repoTimestamp" +const oldAnnoKeyRepoName = "grafana.app/repoName" +const oldAnnoKeyRepoPath = "grafana.app/repoPath" +const oldAnnoKeyRepoHash = "grafana.app/repoHash" +const oldAnnoKeyRepoTimestamp = "grafana.app/repoTimestamp" // Annotations used to store manager properties @@ -56,41 +56,13 @@ const AnnoKeyManagerSuspended = "grafana.app/managerSuspended" // Annotations used to store source properties const AnnoKeySourcePath = "grafana.app/sourcePath" -const AnnoKeySourceHash = "grafana.app/sourceHash" +const AnnoKeySourceChecksum = "grafana.app/sourceChecksum" const AnnoKeySourceTimestamp = "grafana.app/sourceTimestamp" // LabelKeyDeprecatedInternalID gives the deprecated internal ID of a resource // Deprecated: will be removed in grafana 13 const LabelKeyDeprecatedInternalID = "grafana.app/deprecatedInternalID" -// These can be removed once we verify that non of the dual-write sources -// (for dashboards/playlists/etc) depend on the saved internal ID in SQL -const oldAnnoKeyOriginName = "grafana.app/originName" -const oldAnnoKeyOriginPath = "grafana.app/originPath" -const oldAnnoKeyOriginHash = "grafana.app/originHash" -const oldAnnoKeyOriginTimestamp = "grafana.app/originTimestamp" - -// ResourceRepositoryInfo is encoded into kubernetes metadata annotations. -// This value identifies indicates the state of the resource in its provisioning source when -// the spec was last saved. Currently this is derived from the dashboards provisioning table. -type ResourceRepositoryInfo struct { - // Name of the repository/provisioning source - Name string `json:"name,omitempty"` - - // The path within the named repository above (external_id in the existing dashboard provisioning) - Path string `json:"path,omitempty"` - - // Verification/identification hash (check_sum in existing dashboard provisioning) - Hash string `json:"hash,omitempty"` - - // Origin modification timestamp when the resource was saved - // This will be before the resource updated time - Timestamp *time.Time `json:"time,omitempty"` - - // Avoid extending - _ any `json:"-"` -} - // Accessor functions for k8s objects type GrafanaMetaAccessor interface { metav1.Object @@ -128,13 +100,6 @@ type GrafanaMetaAccessor interface { // Deprecated: This will be removed in Grafana 13 SetDeprecatedInternalID(id int64) - GetRepositoryInfo() (*ResourceRepositoryInfo, error) - SetRepositoryInfo(info *ResourceRepositoryInfo) - GetRepositoryName() string - GetRepositoryPath() string - GetRepositoryHash() string - GetRepositoryTimestamp() (*time.Time, error) - GetSpec() (any, error) SetSpec(any) error @@ -351,90 +316,6 @@ func (m *grafanaMetaAccessor) SetDeprecatedInternalID(id int64) { m.obj.SetLabels(labels) } -// This allows looking up a primary and secondary key -- if either exist the value will be returned -func (m *grafanaMetaAccessor) getAnnoValue(primary, secondary string) (string, bool) { - v, ok := m.obj.GetAnnotations()[primary] - if !ok { - v, ok = m.obj.GetAnnotations()[secondary] - } - return v, ok -} - -func (m *grafanaMetaAccessor) SetRepositoryInfo(info *ResourceRepositoryInfo) { - anno := m.obj.GetAnnotations() - if anno == nil { - if info == nil { - return - } - anno = make(map[string]string, 0) - } - - // remove legacy values - delete(anno, oldAnnoKeyOriginHash) - delete(anno, oldAnnoKeyOriginPath) - delete(anno, oldAnnoKeyOriginHash) - delete(anno, oldAnnoKeyOriginTimestamp) - - delete(anno, AnnoKeyRepoName) - delete(anno, AnnoKeyRepoPath) - delete(anno, AnnoKeyRepoHash) - delete(anno, AnnoKeyRepoTimestamp) - if info != nil && info.Name != "" { - anno[AnnoKeyRepoName] = info.Name - if info.Path != "" { - anno[AnnoKeyRepoPath] = info.Path - } - if info.Hash != "" { - anno[AnnoKeyRepoHash] = info.Hash - } - if info.Timestamp != nil { - anno[AnnoKeyRepoTimestamp] = info.Timestamp.UTC().Format(time.RFC3339) - } - } - m.obj.SetAnnotations(anno) -} - -func (m *grafanaMetaAccessor) GetRepositoryInfo() (*ResourceRepositoryInfo, error) { - v, ok := m.getAnnoValue(AnnoKeyRepoName, oldAnnoKeyOriginName) - if !ok { - return nil, nil - } - t, err := m.GetRepositoryTimestamp() - return &ResourceRepositoryInfo{ - Name: v, - Path: m.GetRepositoryPath(), - Hash: m.GetRepositoryHash(), - Timestamp: t, - }, err -} - -func (m *grafanaMetaAccessor) GetRepositoryName() string { - v, _ := m.getAnnoValue(AnnoKeyRepoName, oldAnnoKeyOriginName) - return v // will be empty string -} - -func (m *grafanaMetaAccessor) GetRepositoryPath() string { - v, _ := m.getAnnoValue(AnnoKeyRepoPath, oldAnnoKeyOriginPath) - return v // will be empty string -} - -func (m *grafanaMetaAccessor) GetRepositoryHash() string { - v, _ := m.getAnnoValue(AnnoKeyRepoHash, oldAnnoKeyOriginHash) - return v // will be empty string -} - -func (m *grafanaMetaAccessor) GetRepositoryTimestamp() (*time.Time, error) { - v, ok := m.getAnnoValue(AnnoKeyRepoTimestamp, oldAnnoKeyOriginTimestamp) - if !ok || v == "" { - return nil, nil - } - t, err := time.Parse(time.RFC3339, v) - if err != nil { - return nil, fmt.Errorf("invalid origin timestamp: %s", err.Error()) - } - return &t, nil -} - // GetAnnotations implements GrafanaMetaAccessor. func (m *grafanaMetaAccessor) GetAnnotations() map[string]string { return m.obj.GetAnnotations() @@ -751,7 +632,7 @@ func (m *grafanaMetaAccessor) GetManagerProperties() (ManagerProperties, bool) { res := ManagerProperties{ Identity: "", Kind: ManagerKindUnknown, - AllowsEdits: true, + AllowsEdits: false, Suspended: false, } @@ -759,6 +640,15 @@ func (m *grafanaMetaAccessor) GetManagerProperties() (ManagerProperties, bool) { id, ok := annot[AnnoKeyManagerIdentity] if !ok || id == "" { + // Temporarily support the repo name annotation + repo := annot[oldAnnoKeyRepoName] + if repo != "" { + return ManagerProperties{ + Kind: ManagerKindRepo, + Identity: repo, + }, true + } + // If the identity is not set, we should ignore the other annotations and return the default values. // // This is to prevent inadvertently marking resources as managed, @@ -788,10 +678,31 @@ func (m *grafanaMetaAccessor) SetManagerProperties(v ManagerProperties) { annot = make(map[string]string, 4) } - annot[AnnoKeyManagerIdentity] = v.Identity - annot[AnnoKeyManagerKind] = string(v.Kind) - annot[AnnoKeyManagerAllowsEdits] = strconv.FormatBool(v.AllowsEdits) - annot[AnnoKeyManagerSuspended] = strconv.FormatBool(v.Suspended) + if v.Identity != "" { + annot[AnnoKeyManagerIdentity] = v.Identity + } else { + delete(annot, AnnoKeyManagerIdentity) + } + + if string(v.Kind) != "" { + annot[AnnoKeyManagerKind] = string(v.Kind) + } else { + delete(annot, AnnoKeyManagerKind) + } + + if v.AllowsEdits { + annot[AnnoKeyManagerAllowsEdits] = strconv.FormatBool(v.AllowsEdits) + } else { + delete(annot, AnnoKeyManagerAllowsEdits) + } + if v.Suspended { + annot[AnnoKeyManagerSuspended] = strconv.FormatBool(v.Suspended) + } else { + delete(annot, AnnoKeyManagerSuspended) + } + + // Clean up old annotation access + delete(annot, oldAnnoKeyRepoName) m.obj.SetAnnotations(annot) } @@ -810,16 +721,27 @@ func (m *grafanaMetaAccessor) GetSourceProperties() (SourceProperties, bool) { if path, ok := annot[AnnoKeySourcePath]; ok && path != "" { res.Path = path found = true + } else if path, ok := annot[oldAnnoKeyRepoPath]; ok && path != "" { + res.Path = path + found = true } - if hash, ok := annot[AnnoKeySourceHash]; ok && hash != "" { + if hash, ok := annot[AnnoKeySourceChecksum]; ok && hash != "" { + res.Checksum = hash + found = true + } else if hash, ok := annot[oldAnnoKeyRepoHash]; ok && hash != "" { res.Checksum = hash found = true } - if timestamp, ok := annot[AnnoKeySourceTimestamp]; ok && timestamp != "" { - if t, err := time.Parse(time.RFC3339, timestamp); err == nil { - res.Timestamp = t + t, ok := annot[AnnoKeySourceTimestamp] + if !ok { + t, ok = annot[oldAnnoKeyRepoTimestamp] + } + if ok && t != "" { + var err error + res.TimestampMillis, err = strconv.ParseInt(t, 10, 64) + if err != nil { found = true } } @@ -835,14 +757,20 @@ func (m *grafanaMetaAccessor) SetSourceProperties(v SourceProperties) { if v.Path != "" { annot[AnnoKeySourcePath] = v.Path + } else { + delete(annot, AnnoKeySourcePath) } if v.Checksum != "" { - annot[AnnoKeySourceHash] = v.Checksum + annot[AnnoKeySourceChecksum] = v.Checksum + } else { + delete(annot, AnnoKeySourceChecksum) } - if !v.Timestamp.IsZero() { - annot[AnnoKeySourceTimestamp] = v.Timestamp.Format(time.RFC3339) + if v.TimestampMillis > 0 { + annot[AnnoKeySourceTimestamp] = strconv.FormatInt(v.TimestampMillis, 10) + } else { + delete(annot, AnnoKeySourceTimestamp) } m.obj.SetAnnotations(annot) diff --git a/pkg/apimachinery/utils/meta_test.go b/pkg/apimachinery/utils/meta_test.go index 1796125048f..c6908adf2dd 100644 --- a/pkg/apimachinery/utils/meta_test.go +++ b/pkg/apimachinery/utils/meta_test.go @@ -131,10 +131,13 @@ func (in *Spec2) DeepCopy() *Spec2 { } func TestMetaAccessor(t *testing.T) { - repoInfo := &utils.ResourceRepositoryInfo{ - Name: "test", - Path: "a/b/c", - Hash: "kkk", + repoInfo := utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "test", + } + sourceInfo := utils.SourceProperties{ + Path: "a/b/c", + Checksum: "kkk", } t.Run("fails for non resource objects", func(t *testing.T) { @@ -202,14 +205,13 @@ func TestMetaAccessor(t *testing.T) { }, } - meta.SetRepositoryInfo(repoInfo) + meta.SetManagerProperties(repoInfo) meta.SetFolder("folderUID") require.Equal(t, map[string]string{ - "grafana.app/repoName": "test", - "grafana.app/repoPath": "a/b/c", - "grafana.app/repoHash": "kkk", - "grafana.app/folder": "folderUID", + "grafana.app/managedBy": "repo", + "grafana.app/managerId": "test", + "grafana.app/folder": "folderUID", }, res.GetAnnotations()) meta.SetNamespace("aaa") @@ -255,14 +257,16 @@ func TestMetaAccessor(t *testing.T) { meta, err := utils.MetaAccessor(res) require.NoError(t, err) - meta.SetRepositoryInfo(repoInfo) + meta.SetManagerProperties(repoInfo) + meta.SetSourceProperties(sourceInfo) meta.SetFolder("folderUID") require.Equal(t, map[string]string{ - "grafana.app/repoName": "test", - "grafana.app/repoPath": "a/b/c", - "grafana.app/repoHash": "kkk", - "grafana.app/folder": "folderUID", + "grafana.app/managedBy": "repo", + "grafana.app/managerId": "test", + "grafana.app/sourcePath": "a/b/c", + "grafana.app/sourceChecksum": "kkk", + "grafana.app/folder": "folderUID", }, res.GetAnnotations()) meta.SetNamespace("aaa") @@ -306,14 +310,13 @@ func TestMetaAccessor(t *testing.T) { meta, err := utils.MetaAccessor(res) require.NoError(t, err) - meta.SetRepositoryInfo(repoInfo) + meta.SetManagerProperties(repoInfo) meta.SetFolder("folderUID") require.Equal(t, map[string]string{ - "grafana.app/repoName": "test", - "grafana.app/repoPath": "a/b/c", - "grafana.app/repoHash": "kkk", - "grafana.app/folder": "folderUID", + "grafana.app/managedBy": "repo", + "grafana.app/managerId": "test", + "grafana.app/folder": "folderUID", }, res.GetAnnotations()) meta.SetNamespace("aaa") @@ -347,7 +350,7 @@ func TestMetaAccessor(t *testing.T) { require.Equal(t, "ZZ", res.Status.Title) }) - t.Run("test reading old originInfo (now repository)", func(t *testing.T) { + t.Run("test reading old repo fields (now manager+source)", func(t *testing.T) { res := &TestResource2{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -362,11 +365,15 @@ func TestMetaAccessor(t *testing.T) { meta, err := utils.MetaAccessor(res) require.NoError(t, err) - info, err := meta.GetRepositoryInfo() - require.NoError(t, err) - require.Equal(t, "test", info.Name) - require.Equal(t, "a/b/c", info.Path) - require.Equal(t, "zzz", info.Hash) + manager, ok := meta.GetManagerProperties() + require.True(t, ok) + require.Equal(t, utils.ManagerKindRepo, manager.Kind) + require.Equal(t, "test", manager.Identity) + + source, ok := meta.GetSourceProperties() + require.True(t, ok) + require.Equal(t, "a/b/c", source.Path) + require.Equal(t, "zzz", source.Checksum) }) t.Run("blob info", func(t *testing.T) { @@ -391,14 +398,16 @@ func TestMetaAccessor(t *testing.T) { meta, err := utils.MetaAccessor(obj) require.NoError(t, err) - meta.SetRepositoryInfo(repoInfo) + meta.SetManagerProperties(repoInfo) + meta.SetSourceProperties(sourceInfo) meta.SetFolder("folderUID") require.Equal(t, map[string]string{ - "grafana.app/repoName": "test", - "grafana.app/repoPath": "a/b/c", - "grafana.app/repoHash": "kkk", - "grafana.app/folder": "folderUID", + "grafana.app/managedBy": "repo", + "grafana.app/managerId": "test", + "grafana.app/sourcePath": "a/b/c", + "grafana.app/sourceChecksum": "kkk", + "grafana.app/folder": "folderUID", }, obj.GetAnnotations()) require.Equal(t, "HELLO", obj.Spec.Title) @@ -414,14 +423,13 @@ func TestMetaAccessor(t *testing.T) { meta, err = utils.MetaAccessor(obj2) require.NoError(t, err) - meta.SetRepositoryInfo(repoInfo) + meta.SetManagerProperties(repoInfo) meta.SetFolder("folderUID") require.Equal(t, map[string]string{ - "grafana.app/repoName": "test", - "grafana.app/repoPath": "a/b/c", - "grafana.app/repoHash": "kkk", - "grafana.app/folder": "folderUID", + "grafana.app/managedBy": "repo", + "grafana.app/managerId": "test", + "grafana.app/folder": "folderUID", }, obj2.GetAnnotations()) require.Equal(t, "xxx", meta.FindTitle("xxx")) @@ -447,7 +455,7 @@ func TestMetaAccessor(t *testing.T) { wantProperties: utils.ManagerProperties{ Identity: "", Kind: utils.ManagerKindUnknown, - AllowsEdits: true, + AllowsEdits: false, Suspended: false, }, wantOK: false, @@ -479,7 +487,7 @@ func TestMetaAccessor(t *testing.T) { wantProperties: utils.ManagerProperties{ Identity: "", Kind: utils.ManagerKindUnknown, - AllowsEdits: true, + AllowsEdits: false, Suspended: false, }, wantOK: false, @@ -534,14 +542,14 @@ func TestMetaAccessor(t *testing.T) { { name: "set and get valid values", setProperties: &utils.SourceProperties{ - Path: "path", - Checksum: "hash", - Timestamp: time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC), + Path: "path", + Checksum: "hash", + TimestampMillis: time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC).UnixMilli(), }, wantProperties: utils.SourceProperties{ - Path: "path", - Checksum: "hash", - Timestamp: time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC), + Path: "path", + Checksum: "hash", + TimestampMillis: time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC).UnixMilli(), }, wantOK: true, }, diff --git a/pkg/apis/dashboard/utils.go b/pkg/apis/dashboard/utils.go index a7fa10af2d0..a5979151a90 100644 --- a/pkg/apis/dashboard/utils.go +++ b/pkg/apis/dashboard/utils.go @@ -1,51 +1,31 @@ package dashboard import ( - "strings" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/grafana/grafana/pkg/apimachinery/utils" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -var PluginIDRepoName = "plugin" -var fileProvisionedRepoPrefix = "file:" - -// ProvisionedFileNameWithPrefix adds the `file:` prefix to the -// provisioner name, to be used as the annotation for dashboards -// provisioned from files -func ProvisionedFileNameWithPrefix(name string) string { - if name == "" { - return "" - } - - return fileProvisionedRepoPrefix + name -} - -// GetProvisionedFileNameFromMeta returns the provisioner name -// from a given annotation string, which is in the form file: -func GetProvisionedFileNameFromMeta(annotation string) (string, bool) { - return strings.CutPrefix(annotation, fileProvisionedRepoPrefix) -} - // SetPluginIDMeta sets the repo name to "plugin" and the path to the plugin ID -func SetPluginIDMeta(obj unstructured.Unstructured, pluginID string) { +func SetPluginIDMeta(obj *unstructured.Unstructured, pluginID string) { if pluginID == "" { return } - annotations := obj.GetAnnotations() - if annotations == nil { - annotations = map[string]string{} + meta, err := utils.MetaAccessor(obj) + if err == nil { + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindPlugin, + Identity: pluginID, + }) } - annotations[utils.AnnoKeyRepoName] = PluginIDRepoName - annotations[utils.AnnoKeyRepoPath] = pluginID - obj.SetAnnotations(annotations) } // GetPluginIDFromMeta returns the plugin ID from the meta if the repo name is "plugin" func GetPluginIDFromMeta(obj utils.GrafanaMetaAccessor) string { - if obj.GetRepositoryName() == PluginIDRepoName { - return obj.GetRepositoryPath() + p, ok := obj.GetManagerProperties() + if ok && p.Kind == utils.ManagerKindPlugin { + return p.Identity } return "" } diff --git a/pkg/registry/apis/dashboard/legacy/sql_dashboards.go b/pkg/registry/apis/dashboard/legacy/sql_dashboards.go index 08e8cc2a709..53adbb60f6a 100644 --- a/pkg/registry/apis/dashboard/legacy/sql_dashboards.go +++ b/pkg/registry/apis/dashboard/legacy/sql_dashboards.go @@ -317,13 +317,6 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows, history bool) (*dashboardRo } if origin_name.String != "" { - ts := time.Unix(origin_ts.Int64, 0) - - repo := &utils.ResourceRepositoryInfo{ - Name: dashboardOG.ProvisionedFileNameWithPrefix(origin_name.String), - Hash: origin_hash.String, - Timestamp: &ts, - } // if the reader cannot be found, it may be an orphaned provisioned dashboard resolvedPath := a.provisioning.GetDashboardProvisionerResolvedPath(origin_name.String) if resolvedPath != "" { @@ -334,13 +327,20 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows, history bool) (*dashboardRo if err != nil { return nil, err } - repo.Path = originPath + meta.SetSourceProperties(utils.SourceProperties{ + Path: originPath, // relative path within source + Checksum: origin_hash.String, + TimestampMillis: origin_ts.Int64, + }) + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindClassicFP, // nolint:staticcheck + Identity: origin_name.String, + }) } - meta.SetRepositoryInfo(repo) } else if plugin_id.String != "" { - meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{ - Name: dashboardOG.PluginIDRepoName, - Path: plugin_id.String, + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindPlugin, + Identity: plugin_id.String, }) } diff --git a/pkg/registry/apis/dashboard/legacy/sql_dashboards_test.go b/pkg/registry/apis/dashboard/legacy/sql_dashboards_test.go index 2436d5a8e73..692376ebb4b 100644 --- a/pkg/registry/apis/dashboard/legacy/sql_dashboards_test.go +++ b/pkg/registry/apis/dashboard/legacy/sql_dashboards_test.go @@ -83,12 +83,18 @@ func TestScanRow(t *testing.T) { meta, err := utils.MetaAccessor(row.Dash) require.NoError(t, err) - require.Equal(t, "file:provisioner", meta.GetRepositoryName()) // should be prefixed by file: - require.Equal(t, "../"+pathToFile, meta.GetRepositoryPath()) // relative to provisioner - require.Equal(t, "hashing", meta.GetRepositoryHash()) - ts, err := meta.GetRepositoryTimestamp() + m, ok := meta.GetManagerProperties() + require.True(t, ok) + + s, ok := meta.GetSourceProperties() + require.True(t, ok) + + require.Equal(t, utils.ManagerKindClassicFP, m.Kind) // nolint:staticcheck + require.Equal(t, "provisioner", m.Identity) + require.Equal(t, "../"+pathToFile, s.Path) // relative to provisioner + require.Equal(t, "hashing", s.Checksum) require.NoError(t, err) - require.Equal(t, int64(100000), ts.Unix()) + require.Equal(t, int64(100000), s.TimestampMillis) }) t.Run("Plugin provisioned dashboard should have annotations", func(t *testing.T) { @@ -105,8 +111,11 @@ func TestScanRow(t *testing.T) { meta, err := utils.MetaAccessor(row.Dash) require.NoError(t, err) - require.Equal(t, "plugin", meta.GetRepositoryName()) - require.Equal(t, "slo", meta.GetRepositoryPath()) // the ID of the plugin - require.Equal(t, "", meta.GetRepositoryHash()) // hash is not used on plugins + manager, ok := meta.GetManagerProperties() + require.True(t, ok) + + require.Equal(t, utils.ManagerKindPlugin, manager.Kind) + require.Equal(t, "slo", manager.Identity) // the ID of the plugin + require.Equal(t, "", meta.GetAnnotations()[utils.AnnoKeySourceChecksum]) // hash is not used on plugins }) } diff --git a/pkg/registry/apis/dashboard/legacysearcher/search_client.go b/pkg/registry/apis/dashboard/legacysearcher/search_client.go index 3947cecaf1e..1f98dc3a1e1 100644 --- a/pkg/registry/apis/dashboard/legacysearcher/search_client.go +++ b/pkg/registry/apis/dashboard/legacysearcher/search_client.go @@ -13,7 +13,6 @@ import ( claims "github.com/grafana/authlib/types" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" - dashboardOG "github.com/grafana/grafana/pkg/apis/dashboard" dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" "github.com/grafana/grafana/pkg/services/dashboards" @@ -181,18 +180,22 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour } query.FolderUIDs = folders - case resource.SEARCH_FIELD_REPOSITORY_PATH: + case resource.SEARCH_FIELD_SOURCE_PATH: // only one value is supported in legacy search if len(vals) != 1 { return nil, fmt.Errorf("only one repo path query is supported") } - query.ProvisionedPath = vals[0] - case resource.SEARCH_FIELD_REPOSITORY_NAME: + query.SourcePath = vals[0] + + case resource.SEARCH_FIELD_MANAGER_KIND: + if len(vals) != 1 { + return nil, fmt.Errorf("only one manager kind supported") + } + query.ManagedBy = utils.ManagerKind(vals[0]) + + case resource.SEARCH_FIELD_MANAGER_ID: if field.Operator == string(selection.NotIn) { - for _, val := range vals { - name, _ := dashboardOG.GetProvisionedFileNameFromMeta(val) - query.ProvisionedReposNotIn = append(query.ProvisionedReposNotIn, name) - } + query.ManagerIdentityNotIn = vals continue } @@ -200,8 +203,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour if len(vals) != 1 { return nil, fmt.Errorf("only one repo name is supported") } - - query.ProvisionedRepo, _ = dashboardOG.GetProvisionedFileNameFromMeta(vals[0]) + query.ManagerIdentity = vals[0] } } searchFields := resource.StandardSearchFields() @@ -221,17 +223,21 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour // if we are querying for provisioning information, we need to use a different // legacy sql query, since legacy search does not support this - if query.ProvisionedRepo != "" || len(query.ProvisionedReposNotIn) > 0 { + if query.ManagerIdentity != "" || len(query.ManagerIdentityNotIn) > 0 { + if query.ManagedBy == utils.ManagerKindUnknown { + return nil, fmt.Errorf("query by manager identity also requires manager.kind parameter") + } + var dashes []*dashboards.Dashboard - if query.ProvisionedRepo == dashboardOG.PluginIDRepoName { + if query.ManagedBy == utils.ManagerKindPlugin { dashes, err = c.dashboardStore.GetDashboardsByPluginID(ctx, &dashboards.GetDashboardsByPluginIDQuery{ - PluginID: query.ProvisionedPath, + PluginID: query.ManagerIdentity, OrgID: user.GetOrgID(), }) - } else if query.ProvisionedRepo != "" { - dashes, err = c.dashboardStore.GetProvisionedDashboardsByName(ctx, query.ProvisionedRepo) - } else if len(query.ProvisionedReposNotIn) > 0 { - dashes, err = c.dashboardStore.GetOrphanedProvisionedDashboards(ctx, query.ProvisionedReposNotIn) + } else if query.ManagerIdentity != "" { + dashes, err = c.dashboardStore.GetProvisionedDashboardsByName(ctx, query.ManagerIdentity) + } else if len(query.ManagerIdentityNotIn) > 0 { + dashes, err = c.dashboardStore.GetOrphanedProvisionedDashboards(ctx, query.ManagerIdentityNotIn) } if err != nil { return nil, err diff --git a/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go b/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go index 5849f146184..4b56d2e9b6f 100644 --- a/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go +++ b/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go @@ -373,12 +373,12 @@ func TestDashboardSearchClient_Search(t *testing.T) { Key: dashboardKey, Fields: []*resource.Requirement{ { - Key: resource.SEARCH_FIELD_REPOSITORY_PATH, + Key: resource.SEARCH_FIELD_MANAGER_ID, Operator: "in", Values: []string{"slo"}, }, { - Key: resource.SEARCH_FIELD_REPOSITORY_NAME, + Key: resource.SEARCH_FIELD_MANAGER_KIND, Operator: "in", Values: []string{"plugin"}, }, @@ -402,9 +402,14 @@ func TestDashboardSearchClient_Search(t *testing.T) { Key: dashboardKey, Fields: []*resource.Requirement{ { - Key: resource.SEARCH_FIELD_REPOSITORY_NAME, + Key: resource.SEARCH_FIELD_MANAGER_KIND, + Operator: "=", + Values: []string{string(utils.ManagerKindClassicFP)}, // nolint:staticcheck + }, + { + Key: resource.SEARCH_FIELD_MANAGER_ID, Operator: "in", - Values: []string{"file:test"}, // file prefix should be removed before going to legacy + Values: []string{"test"}, }, }, }, @@ -426,9 +431,14 @@ func TestDashboardSearchClient_Search(t *testing.T) { Key: dashboardKey, Fields: []*resource.Requirement{ { - Key: resource.SEARCH_FIELD_REPOSITORY_NAME, + Key: resource.SEARCH_FIELD_MANAGER_KIND, + Operator: "=", + Values: []string{string(utils.ManagerKindClassicFP)}, // nolint:staticcheck + }, + { + Key: resource.SEARCH_FIELD_MANAGER_ID, Operator: string(selection.NotIn), - Values: []string{"file:test", "file:test2"}, // file prefix should be removed before going to legacy + Values: []string{"test", "test2"}, }, }, }, diff --git a/pkg/registry/apis/dashboard/sub_dto.go b/pkg/registry/apis/dashboard/sub_dto.go index abc691e3517..33ffe2c8646 100644 --- a/pkg/registry/apis/dashboard/sub_dto.go +++ b/pkg/registry/apis/dashboard/sub_dto.go @@ -134,13 +134,9 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob OrgID: info.OrgID, ID: obj.GetDeprecatedInternalID(), // nolint:staticcheck } - repo, err := obj.GetRepositoryInfo() - if err != nil { - responder.Error(err) - return - } - if repo != nil && repo.Name == dashboard.PluginIDRepoName { - dto.PluginID = repo.Path + manager, ok := obj.GetManagerProperties() + if ok && manager.Kind == utils.ManagerKindPlugin { + dto.PluginID = manager.Identity } guardian, err := guardian.NewByDashboard(ctx, dto, info.OrgID, user) diff --git a/pkg/services/dashboards/models.go b/pkg/services/dashboards/models.go index 78f9f7dc827..0128d7d2e17 100644 --- a/pkg/services/dashboards/models.go +++ b/pkg/services/dashboards/models.go @@ -5,6 +5,7 @@ import ( "time" "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/slugify" @@ -438,9 +439,10 @@ type FindPersistedDashboardsQuery struct { Sort model.SortOption IsDeleted bool - ProvisionedRepo string - ProvisionedPath string - ProvisionedReposNotIn []string + ManagedBy utils.ManagerKind + ManagerIdentity string + SourcePath string + ManagerIdentityNotIn []string Filters []any diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 53b705a8359..3b1f28764fd 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -237,7 +237,8 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardData(ctx context.Context, func(orgID int64) { g.Go(func() error { res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{ - ProvisionedRepo: name, + ManagedBy: utils.ManagerKindClassicFP, // nolint:staticcheck + ManagerIdentity: name, OrgId: orgID, }) if err != nil { @@ -568,8 +569,8 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context. ctx, _ := identity.WithServiceIdentity(ctx, org.ID) // find all dashboards in the org that have a file repo set that is not in the given readers list foundDashs, err := dr.searchProvisionedDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{ - ProvisionedReposNotIn: cmd.ReaderNames, - OrgId: org.ID, + ManagerIdentityNotIn: cmd.ReaderNames, + OrgId: org.ID, }) if err != nil { return err @@ -962,8 +963,8 @@ func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, que if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{ OrgId: query.OrgID, - ProvisionedRepo: dashboard.PluginIDRepoName, - ProvisionedPath: query.PluginID, + ManagedBy: utils.ManagerKindPlugin, + ManagerIdentity: query.PluginID, }) if err != nil { return nil, err @@ -1553,22 +1554,22 @@ func (dr *DashboardServiceImpl) saveProvisionedDashboardThroughK8s(ctx context.C return nil, err } - annotations := obj.GetAnnotations() - if annotations == nil { - annotations = map[string]string{} + meta, err := utils.MetaAccessor(obj) + if err != nil { + return nil, err } - if unprovision { - delete(annotations, utils.AnnoKeyRepoName) - delete(annotations, utils.AnnoKeyRepoPath) - delete(annotations, utils.AnnoKeyRepoHash) - delete(annotations, utils.AnnoKeyRepoTimestamp) - } else { - annotations[utils.AnnoKeyRepoName] = dashboard.ProvisionedFileNameWithPrefix(provisioning.Name) - annotations[utils.AnnoKeyRepoPath] = provisioning.ExternalID - annotations[utils.AnnoKeyRepoHash] = provisioning.CheckSum - annotations[utils.AnnoKeyRepoTimestamp] = time.Unix(provisioning.Updated, 0).UTC().Format(time.RFC3339) + + m := utils.ManagerProperties{} + s := utils.SourceProperties{} + if !unprovision { + m.Kind = utils.ManagerKindClassicFP // nolint:staticcheck + m.Identity = provisioning.Name + s.Path = provisioning.ExternalID + s.Checksum = provisioning.CheckSum + s.TimestampMillis = time.Unix(provisioning.Updated, 0).UnixMilli() } - obj.SetAnnotations(annotations) + meta.SetManagerProperties(m) + meta.SetSourceProperties(s) out, err := dr.createOrUpdateDash(ctx, obj, cmd.OrgID) if err != nil { @@ -1594,16 +1595,16 @@ func (dr *DashboardServiceImpl) saveDashboardThroughK8s(ctx context.Context, cmd return out, nil } -func (dr *DashboardServiceImpl) createOrUpdateDash(ctx context.Context, obj unstructured.Unstructured, orgID int64) (*dashboards.Dashboard, error) { +func (dr *DashboardServiceImpl) createOrUpdateDash(ctx context.Context, obj *unstructured.Unstructured, orgID int64) (*dashboards.Dashboard, error) { var out *unstructured.Unstructured current, err := dr.k8sclient.Get(ctx, obj.GetName(), orgID, v1.GetOptions{}) if current == nil || err != nil { - out, err = dr.k8sclient.Create(ctx, &obj, orgID) + out, err = dr.k8sclient.Create(ctx, obj, orgID) if err != nil { return nil, err } } else { - out, err = dr.k8sclient.Update(ctx, &obj, orgID) + out, err = dr.k8sclient.Update(ctx, obj, orgID) if err != nil { return nil, err } @@ -1723,30 +1724,35 @@ func (dr *DashboardServiceImpl) searchDashboardsThroughK8sRaw(ctx context.Contex }) } - if query.ProvisionedRepo != "" { - req := []*resource.Requirement{{ - Key: resource.SEARCH_FIELD_REPOSITORY_NAME, - Operator: string(selection.In), - Values: []string{query.ProvisionedRepo}, - }} - request.Options.Fields = append(request.Options.Fields, req...) + if query.ManagedBy != "" { + request.Options.Fields = append(request.Options.Fields, &resource.Requirement{ + Key: resource.SEARCH_FIELD_MANAGER_KIND, + Operator: string(selection.Equals), + Values: []string{string(query.ManagedBy)}, + }) } - if len(query.ProvisionedReposNotIn) > 0 { - req := []*resource.Requirement{{ - Key: resource.SEARCH_FIELD_REPOSITORY_NAME, - Operator: string(selection.NotIn), - Values: query.ProvisionedReposNotIn, - }} - request.Options.Fields = append(request.Options.Fields, req...) - } - if query.ProvisionedPath != "" { - req := []*resource.Requirement{{ - Key: resource.SEARCH_FIELD_REPOSITORY_PATH, + if query.ManagerIdentity != "" { + request.Options.Fields = append(request.Options.Fields, &resource.Requirement{ + Key: resource.SEARCH_FIELD_MANAGER_ID, Operator: string(selection.In), - Values: []string{query.ProvisionedPath}, - }} - request.Options.Fields = append(request.Options.Fields, req...) + Values: []string{query.ManagerIdentity}, + }) + } + + if len(query.ManagerIdentityNotIn) > 0 { + request.Options.Fields = append(request.Options.Fields, &resource.Requirement{ + Key: resource.SEARCH_FIELD_MANAGER_ID, + Operator: string(selection.NotIn), + Values: query.ManagerIdentityNotIn, + }) + } + if query.SourcePath != "" { + request.Options.Fields = append(request.Options.Fields, &resource.Requirement{ + Key: resource.SEARCH_FIELD_SOURCE_PATH, + Operator: string(selection.In), + Values: []string{query.SourcePath}, + }) } if query.Title != "" { @@ -1840,18 +1846,6 @@ func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx contex ctx, _ = identity.WithServiceIdentity(ctx, query.OrgId) - if query.ProvisionedRepo != "" { - query.ProvisionedRepo = dashboard.ProvisionedFileNameWithPrefix(query.ProvisionedRepo) - } - - if len(query.ProvisionedReposNotIn) > 0 { - repos := make([]string, len(query.ProvisionedReposNotIn)) - for i, v := range query.ProvisionedReposNotIn { - repos[i] = dashboard.ProvisionedFileNameWithPrefix(v) - } - query.ProvisionedReposNotIn = repos - } - query.Type = searchstore.TypeDashboard searchResults, err := dr.searchDashboardsThroughK8sRaw(ctx, query) @@ -1878,26 +1872,27 @@ func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx contex return err } - // ensure the repo is set due to file provisioning, otherwise skip it - fileRepo, found := dashboard.GetProvisionedFileNameFromMeta(meta.GetRepositoryName()) - if !found { + m, ok := meta.GetManagerProperties() + if !ok || m.Kind != utils.ManagerKindClassicFP { // nolint:staticcheck + return nil + } + + source, ok := meta.GetSourceProperties() + if !ok { return nil } provisioning := &dashboardProvisioningWithUID{ + DashboardProvisioning: dashboards.DashboardProvisioning{ + Name: m.Identity, + ExternalID: source.Path, + CheckSum: source.Checksum, + DashboardID: meta.GetDeprecatedInternalID(), // nolint:staticcheck + }, DashboardUID: hit.Name, } - provisioning.Name = fileRepo - provisioning.ExternalID = meta.GetRepositoryPath() - provisioning.CheckSum = meta.GetRepositoryHash() - provisioning.DashboardID = meta.GetDeprecatedInternalID() // nolint:staticcheck - - updated, err := meta.GetRepositoryTimestamp() - if err != nil { - return err - } - if updated != nil { - provisioning.Updated = updated.Unix() + if source.TimestampMillis > 0 { + provisioning.Updated = time.UnixMilli(source.TimestampMillis).Unix() } mu.Lock() @@ -2027,13 +2022,13 @@ func (dr *DashboardServiceImpl) UnstructuredToLegacyDashboard(ctx context.Contex return &out, nil } -func LegacySaveCommandToUnstructured(cmd *dashboards.SaveDashboardCommand, namespace string) (unstructured.Unstructured, error) { +func LegacySaveCommandToUnstructured(cmd *dashboards.SaveDashboardCommand, namespace string) (*unstructured.Unstructured, error) { uid := cmd.GetDashboardModel().UID if uid == "" { uid = uuid.NewString() } - finalObj := unstructured.Unstructured{ + finalObj := &unstructured.Unstructured{ Object: map[string]interface{}{}, } @@ -2061,7 +2056,7 @@ func LegacySaveCommandToUnstructured(cmd *dashboards.SaveDashboardCommand, names finalObj.SetNamespace(namespace) finalObj.SetGroupVersionKind(dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind()) - meta, err := utils.MetaAccessor(&finalObj) + meta, err := utils.MetaAccessor(finalObj) if err != nil { return finalObj, err } diff --git a/pkg/services/dashboards/service/dashboard_service_test.go b/pkg/services/dashboards/service/dashboard_service_test.go index 32656bf0d19..85edc00433c 100644 --- a/pkg/services/dashboards/service/dashboard_service_test.go +++ b/pkg/services/dashboards/service/dashboard_service_test.go @@ -2,6 +2,7 @@ package service import ( "context" + "fmt" "reflect" "testing" "time" @@ -9,13 +10,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" - "github.com/grafana/grafana/pkg/apis/dashboard" + dashboardv0alpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/apiserver/client" @@ -540,31 +540,38 @@ func TestGetProvisionedDashboardData(t *testing.T) { t.Run("Should use Kubernetes client if feature flags are enabled and get from relevant org", func(t *testing.T) { ctx, k8sCliMock := setupK8sDashboardTests(service) + provisioningTimestamp := int64(1234567) k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default") - k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{ - "metadata": map[string]any{ - "name": "uid", - "labels": map[string]any{ - utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck + k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(), + "kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind, + "metadata": map[string]interface{}{ + "name": "uid", + "labels": map[string]interface{}{ + utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck + }, + "annotations": map[string]interface{}{ + utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "test", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()), + }, }, - "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("test"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + "spec": map[string]interface{}{ + "test": "test", + "version": int64(1), + "title": "testing slugify", }, }, - "spec": map[string]any{ - "test": "test", - "version": int64(1), - "title": "testing slugify", - }, - }}, nil).Once() + }, nil).Once() repo := "test" k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool { - // ensure the prefix is added to the query - return req.Options.Fields[0].Values[0] == dashboard.ProvisionedFileNameWithPrefix(repo) + // make sure the kind is added to the query + return req.Options.Fields[0].Values[0] == string(utils.ManagerKindClassicFP) && // nolint:staticcheck + req.Options.Fields[1].Values[0] == repo })).Return(&resource.ResourceSearchResponse{ Results: &resource.ResourceTable{ Columns: []*resource.ResourceTableColumnDefinition{}, @@ -573,8 +580,9 @@ func TestGetProvisionedDashboardData(t *testing.T) { TotalHits: 0, }, nil).Once() k8sCliMock.On("Search", mock.Anything, int64(2), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool { - // ensure the prefix is added to the query - return req.Options.Fields[0].Values[0] == dashboard.ProvisionedFileNameWithPrefix(repo) + // make sure the kind is added to the query + return req.Options.Fields[0].Values[0] == string(utils.ManagerKindClassicFP) && // nolint:staticcheck + req.Options.Fields[1].Values[0] == repo })).Return(&resource.ResourceSearchResponse{ Results: &resource.ResourceTable{ Columns: []*resource.ResourceTableColumnDefinition{ @@ -611,7 +619,7 @@ func TestGetProvisionedDashboardData(t *testing.T) { Name: "test", ExternalID: "path/to/file", CheckSum: "hash", - Updated: 1735689600, + Updated: provisioningTimestamp, }) k8sCliMock.AssertExpectations(t) }) @@ -639,21 +647,25 @@ func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) { t.Run("Should use Kubernetes client if feature flags are enabled and get from whatever org it is in", func(t *testing.T) { ctx, k8sCliMock := setupK8sDashboardTests(service) + provisioningTimestamp := int64(1234567) k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default") - k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{ - "metadata": map[string]any{ + k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]interface{}{ + "apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(), + "kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind, + "metadata": map[string]interface{}{ "name": "uid", - "labels": map[string]any{ + "labels": map[string]interface{}{ utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck }, - "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("test"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + "annotations": map[string]interface{}{ + utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "test", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()), }, }, - "spec": map[string]any{ + "spec": map[string]interface{}{ "test": "test", "version": int64(1), "title": "testing slugify", @@ -701,7 +713,7 @@ func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) { Name: "test", ExternalID: "path/to/file", CheckSum: "hash", - Updated: 1735689600, + Updated: provisioningTimestamp, }) k8sCliMock.AssertExpectations(t) }) @@ -729,21 +741,25 @@ func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) { t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) { ctx, k8sCliMock := setupK8sDashboardTests(service) + provisioningTimestamp := int64(1234567) k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default") - k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{ - "metadata": map[string]any{ + k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]interface{}{ + "apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(), + "kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind, + "metadata": map[string]interface{}{ "name": "uid", - "labels": map[string]any{ + "labels": map[string]interface{}{ utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck }, - "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("test"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + "annotations": map[string]interface{}{ + utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "test", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()), }, }, - "spec": map[string]any{ + "spec": map[string]interface{}{ "test": "test", "version": int64(1), "title": "testing slugify", @@ -784,7 +800,7 @@ func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) { Name: "test", ExternalID: "path/to/file", CheckSum: "hash", - Updated: 1735689600, + Updated: provisioningTimestamp, }) k8sCliMock.AssertExpectations(t) }) @@ -826,10 +842,11 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) { "metadata": map[string]any{ "name": "uid", "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("orphaned"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "orphaned", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: "2025-01-01T00:00:00Z", }, }, "spec": map[string]any{}, @@ -839,8 +856,8 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) { "metadata": map[string]any{ "name": "uid2", "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.PluginIDRepoName, - utils.AnnoKeyRepoHash: "app", + utils.AnnoKeyManagerKind: string(utils.ManagerKindPlugin), + utils.AnnoKeyManagerIdentity: "app", }, }, "spec": map[string]any{}, @@ -850,16 +867,17 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) { "metadata": map[string]any{ "name": "uid3", "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("orphaned"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "orphaned", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: "2025-01-01T00:00:00Z", }, }, "spec": map[string]any{}, }}, nil).Once() k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool { - return req.Options.Fields[0].Key == "repo.name" && req.Options.Fields[0].Values[0] == dashboard.ProvisionedFileNameWithPrefix("test") && req.Options.Fields[0].Operator == "notin" + return req.Options.Fields[0].Key == "manager.id" && req.Options.Fields[0].Values[0] == "test" && req.Options.Fields[0].Operator == "notin" })).Return(&resource.ResourceSearchResponse{ Results: &resource.ResourceTable{ Columns: []*resource.ResourceTableColumnDefinition{ @@ -889,7 +907,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) { }, nil).Once() k8sCliMock.On("Search", mock.Anything, int64(2), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool { - return req.Options.Fields[0].Key == "repo.name" && req.Options.Fields[0].Values[0] == dashboard.ProvisionedFileNameWithPrefix("test") && req.Options.Fields[0].Operator == "notin" + return req.Options.Fields[0].Key == "manager.id" && req.Options.Fields[0].Values[0] == "test" && req.Options.Fields[0].Operator == "notin" })).Return(&resource.ResourceSearchResponse{ Results: &resource.ResourceTable{ Columns: []*resource.ResourceTableColumnDefinition{ @@ -960,10 +978,11 @@ func TestUnprovisionDashboard(t *testing.T) { "metadata": map[string]any{ "name": "uid", "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("test"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + utils.AnnoKeyManagerKind: utils.ManagerKindClassicFP, // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "test", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: "2025-01-01T00:00:00Z", }, }, "spec": map[string]any{}, @@ -983,7 +1002,7 @@ func TestUnprovisionDashboard(t *testing.T) { }, }} // should update it to be without annotations - k8sCliMock.On("Update", mock.Anything, dashWithoutAnnotations, mock.Anything, mock.Anything).Return(dashWithoutAnnotations, nil) + k8sCliMock.On("Update", mock.Anything, dashWithoutAnnotations, mock.Anything).Return(dashWithoutAnnotations, nil) k8sCliMock.On("GetNamespace", mock.Anything).Return("default") k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil) k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{ @@ -1054,8 +1073,9 @@ func TestGetDashboardsByPluginID(t *testing.T) { k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(uidUnstructured, nil) k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil) k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool { - return req.Options.Fields[0].Key == "repo.name" && req.Options.Fields[0].Values[0] == dashboard.PluginIDRepoName && - req.Options.Fields[1].Key == "repo.path" && req.Options.Fields[1].Values[0] == "testing" + return ( // gofmt comment helper + req.Options.Fields[0].Key == "manager.kind" && req.Options.Fields[0].Values[0] == string(utils.ManagerKindPlugin) && + req.Options.Fields[1].Key == "manager.id" && req.Options.Fields[1].Values[0] == "testing") })).Return(&resource.ResourceSearchResponse{ Results: &resource.ResourceTable{ Columns: []*resource.ResourceTableColumnDefinition{ @@ -1989,14 +2009,16 @@ func TestSearchProvisionedDashboardsThroughK8sRaw(t *testing.T) { query := &dashboards.FindPersistedDashboardsQuery{ OrgId: 1, } + provisioningTimestamp := int64(1234567) dashboardUnstructuredProvisioned := unstructured.Unstructured{Object: map[string]any{ "metadata": map[string]any{ "name": "uid", "annotations": map[string]any{ - utils.AnnoKeyRepoName: dashboard.ProvisionedFileNameWithPrefix("test"), - utils.AnnoKeyRepoHash: "hash", - utils.AnnoKeyRepoPath: "path/to/file", - utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z", + utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck + utils.AnnoKeyManagerIdentity: "test", + utils.AnnoKeySourceChecksum: "hash", + utils.AnnoKeySourcePath: "path/to/file", + utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()), }, }, "spec": map[string]any{}, @@ -2056,7 +2078,7 @@ func TestSearchProvisionedDashboardsThroughK8sRaw(t *testing.T) { Name: "test", ExternalID: "path/to/file", CheckSum: "hash", - Updated: 1735689600, + Updated: provisioningTimestamp, }, }, }, res) // only should return the one provisioned dashboard diff --git a/pkg/services/dashboards/service/search/search.go b/pkg/services/dashboards/service/search/search.go index c0f3a5598f5..1352ec52776 100644 --- a/pkg/services/dashboards/service/search/search.go +++ b/pkg/services/dashboards/service/search/search.go @@ -29,10 +29,11 @@ var ( resource.SEARCH_FIELD_CREATED_BY, resource.SEARCH_FIELD_UPDATED, resource.SEARCH_FIELD_UPDATED_BY, - resource.SEARCH_FIELD_REPOSITORY_NAME, - resource.SEARCH_FIELD_REPOSITORY_PATH, - resource.SEARCH_FIELD_REPOSITORY_HASH, - resource.SEARCH_FIELD_REPOSITORY_TIME, + resource.SEARCH_FIELD_MANAGER_KIND, + resource.SEARCH_FIELD_MANAGER_ID, + resource.SEARCH_FIELD_SOURCE_PATH, + resource.SEARCH_FIELD_SOURCE_CHECKSUM, + resource.SEARCH_FIELD_SOURCE_TIME, } ) diff --git a/pkg/services/folder/folderimpl/conversions.go b/pkg/services/folder/folderimpl/conversions.go index 29ef06d14fc..86c749556be 100644 --- a/pkg/services/folder/folderimpl/conversions.go +++ b/pkg/services/folder/folderimpl/conversions.go @@ -6,13 +6,14 @@ import ( "strconv" "strings" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/infra/slugify" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/user" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolder(ctx context.Context, item *unstructured.Unstructured) (*folder.Folder, error) { @@ -57,7 +58,7 @@ func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolder(ctx context.Context if updater.UID == "" { updater = creator } - + manager, _ := meta.GetManagerProperties() return &folder.Folder{ UID: uid, Title: title, @@ -65,7 +66,7 @@ func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolder(ctx context.Context ID: meta.GetDeprecatedInternalID(), // nolint:staticcheck ParentUID: meta.GetFolder(), Version: int(meta.GetGeneration()), - Repository: meta.GetRepositoryName(), + ManagedBy: manager.Kind, URL: url, Created: created, diff --git a/pkg/services/folder/folderimpl/conversions_test.go b/pkg/services/folder/folderimpl/conversions_test.go index a6120c7c23d..537193d5a15 100644 --- a/pkg/services/folder/folderimpl/conversions_test.go +++ b/pkg/services/folder/folderimpl/conversions_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user/usertest" @@ -63,7 +64,7 @@ func TestFolderConversions(t *testing.T) { Title: "test folder", Description: "Something set in the file", URL: "/dashboards/f/be79sztagf20wd/test-folder", - Repository: "example-repo", + ManagedBy: utils.ManagerKindRepo, Created: created, Updated: created.Add(time.Hour * 5), CreatedBy: 10, diff --git a/pkg/services/folder/model.go b/pkg/services/folder/model.go index 19e6c43abe6..ffd947c6737 100644 --- a/pkg/services/folder/model.go +++ b/pkg/services/folder/model.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/slugify" "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" @@ -56,10 +57,10 @@ type Folder struct { Fullpath string `xorm:"fullpath"` FullpathUIDs string `xorm:"fullpath_uids"` - // When the folder belongs to a repository + // The folder is managed by an external process // NOTE: this is only populated when folders are managed by unified storage // This is not ever used by xorm, but the translation functions flow through this type - Repository string `json:"repository,omitempty"` + ManagedBy utils.ManagerKind `json:"managedBy,omitempty"` } var GeneralFolder = Folder{ID: 0, Title: "General"} diff --git a/pkg/storage/unified/apistore/prepare.go b/pkg/storage/unified/apistore/prepare.go index e4bb5ed7d18..c9677fd0763 100644 --- a/pkg/storage/unified/apistore/prepare.go +++ b/pkg/storage/unified/apistore/prepare.go @@ -9,12 +9,13 @@ import ( "time" "github.com/google/uuid" - authtypes "github.com/grafana/authlib/types" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/storage" "k8s.io/klog/v2" + authtypes "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/storage/unified/resource" ) @@ -73,12 +74,6 @@ func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime obj.SetResourceVersion("") obj.SetSelfLink("") - // Read+write will verify that repository format is accurate - repo, err := obj.GetRepositoryInfo() - if err != nil { - return nil, err - } - obj.SetRepositoryInfo(repo) obj.SetUpdatedBy("") obj.SetUpdatedTimestamp(nil) obj.SetCreatedBy(info.GetUID()) @@ -136,12 +131,6 @@ func (s *Storage) prepareObjectForUpdate(ctx context.Context, updateObject runti obj.SetDeprecatedInternalID(previousInternalID) // nolint:staticcheck } - // Read+write will verify that origin format is accurate - repo, err := obj.GetRepositoryInfo() - if err != nil { - return nil, err - } - obj.SetRepositoryInfo(repo) obj.SetUpdatedBy(info.GetUID()) obj.SetUpdatedTimestampMillis(time.Now().UnixMilli()) diff --git a/pkg/storage/unified/apistore/prepare_test.go b/pkg/storage/unified/apistore/prepare_test.go index 18d87952615..c71f18572f8 100644 --- a/pkg/storage/unified/apistore/prepare_test.go +++ b/pkg/storage/unified/apistore/prepare_test.go @@ -6,16 +6,17 @@ import ( "time" "github.com/bwmarrin/snowflake" - authtypes "github.com/grafana/authlib/types" - "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/apimachinery/utils" - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" "github.com/stretchr/testify/require" "golang.org/x/exp/rand" "k8s.io/apimachinery/pkg/api/apitesting" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apiserver/pkg/storage" + + authtypes "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" ) var scheme = runtime.NewScheme() @@ -87,11 +88,14 @@ func TestPrepareObjectForStorage(t *testing.T) { meta, err := utils.MetaAccessor(obj) require.NoError(t, err) now := time.Now() - meta.SetRepositoryInfo(&utils.ResourceRepositoryInfo{ - Name: "test-repo", - Path: "test/path", - Hash: "hash", - Timestamp: &now, + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "test-repo", + }) + meta.SetSourceProperties(utils.SourceProperties{ + Path: "test/path", + Checksum: "hash", + TimestampMillis: now.UnixMilli(), }) encodedData, err := s.prepareObjectForStorage(ctx, obj) @@ -101,14 +105,16 @@ func TestPrepareObjectForStorage(t *testing.T) { require.NoError(t, err) meta, err = utils.MetaAccessor(newObject) require.NoError(t, err) - require.Equal(t, meta.GetRepositoryHash(), "hash") - require.Equal(t, meta.GetRepositoryName(), "test-repo") - require.Equal(t, meta.GetRepositoryPath(), "test/path") - ts, err := meta.GetRepositoryTimestamp() - require.NoError(t, err) - parsed, err := time.Parse(time.RFC3339, now.UTC().Format(time.RFC3339)) - require.NoError(t, err) - require.Equal(t, ts, &parsed) + + m, ok := meta.GetManagerProperties() + require.True(t, ok) + s, ok := meta.GetSourceProperties() + require.True(t, ok) + + require.Equal(t, m.Identity, "test-repo") + require.Equal(t, s.Checksum, "hash") + require.Equal(t, s.Path, "test/path") + require.Equal(t, s.TimestampMillis, now.UnixMilli()) }) s.opts.RequireDeprecatedInternalID = true diff --git a/pkg/storage/unified/resource/document.go b/pkg/storage/unified/resource/document.go index 4d0682c09d7..2229527bb56 100644 --- a/pkg/storage/unified/resource/document.go +++ b/pkg/storage/unified/resource/document.go @@ -102,7 +102,10 @@ type IndexableDocument struct { References ResourceReferences `json:"reference,omitempty"` // When the resource is managed by an upstream repository - RepoInfo *utils.ResourceRepositoryInfo `json:"repo,omitempty"` + Manager *utils.ManagerProperties `json:"manager,omitempty"` + + // When the manager knows about file paths + Source *utils.SourceProperties `json:"source,omitempty"` } func (m *IndexableDocument) Type() string { @@ -173,7 +176,14 @@ func NewIndexableDocument(key *ResourceKey, rv int64, obj utils.GrafanaMetaAcces CreatedBy: obj.GetCreatedBy(), UpdatedBy: obj.GetUpdatedBy(), } - doc.RepoInfo, _ = obj.GetRepositoryInfo() + m, ok := obj.GetManagerProperties() + if ok { + doc.Manager = &m + } + s, ok := obj.GetSourceProperties() + if ok { + doc.Source = &s + } ts := obj.GetCreationTimestamp() if !ts.Time.IsZero() { doc.Created = ts.Time.UnixMilli() @@ -265,10 +275,11 @@ const SEARCH_FIELD_CREATED_BY = "createdBy" const SEARCH_FIELD_UPDATED = "updated" const SEARCH_FIELD_UPDATED_BY = "updatedBy" -const SEARCH_FIELD_REPOSITORY_NAME = "repo.name" -const SEARCH_FIELD_REPOSITORY_PATH = "repo.path" -const SEARCH_FIELD_REPOSITORY_HASH = "repo.hash" -const SEARCH_FIELD_REPOSITORY_TIME = "repo.time" +const SEARCH_FIELD_MANAGER_KIND = "manager.kind" +const SEARCH_FIELD_MANAGER_ID = "manager.id" +const SEARCH_FIELD_SOURCE_PATH = "source.path" +const SEARCH_FIELD_SOURCE_CHECKSUM = "source.checksum" +const SEARCH_FIELD_SOURCE_TIME = "source.timestampMillis" const SEARCH_FIELD_SCORE = "_score" // the match score const SEARCH_FIELD_EXPLAIN = "_explain" // score explanation as JSON object diff --git a/pkg/storage/unified/resource/document_test.go b/pkg/storage/unified/resource/document_test.go index 593d1fbffd9..2135748533d 100644 --- a/pkg/storage/unified/resource/document_test.go +++ b/pkg/storage/unified/resource/document_test.go @@ -33,17 +33,20 @@ func TestStandardDocumentBuilder(t *testing.T) { "resource": "playlists", "name": "test1" }, + "name": "test1", "rv": 10, "title": "test playlist unified storage", "title_phrase": "test playlist unified storage", "created": 1717236672000, "createdBy": "user:ABC", "updatedBy": "user:XYZ", - "name": "test1", - "repo": { - "name": "something", + "manager": { + "kind": "repo", + "id": "something" + }, + "source": { "path": "path/in/system.json", - "hash": "xyz" + "checksum": "xyz" } - }`, string(jj)) + }`, string(jj)) } diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index 3314cdd6ffc..9cc505d40f6 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -456,12 +456,9 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *Resour } } - repo, err := obj.GetRepositoryInfo() - if err != nil { - return nil, NewBadRequestError("invalid repository info") - } - if repo != nil { - err = s.writeHooks.CanWriteValueFromRepository(ctx, user, repo.Name) + m, ok := obj.GetManagerProperties() + if ok && m.Kind == utils.ManagerKindRepo { + err = s.writeHooks.CanWriteValueFromRepository(ctx, user, m.Identity) if err != nil { return nil, AsErrorResult(err) } diff --git a/pkg/storage/unified/search/bleve.go b/pkg/storage/unified/search/bleve.go index d67b145da4d..73eee078f69 100644 --- a/pkg/storage/unified/search/bleve.go +++ b/pkg/storage/unified/search/bleve.go @@ -18,11 +18,12 @@ import ( "github.com/blevesearch/bleve/v2/search/query" bleveSearch "github.com/blevesearch/bleve/v2/search/searcher" index "github.com/blevesearch/bleve_index_api" - "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" - "github.com/grafana/grafana/pkg/services/featuremgmt" "go.opentelemetry.io/otel/trace" "k8s.io/apimachinery/pkg/selection" + "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" + "github.com/grafana/grafana/pkg/services/featuremgmt" + authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/infra/log" @@ -304,19 +305,20 @@ func (b *bleveIndex) ListRepositoryObjects(ctx context.Context, req *resource.Li found, err := b.index.SearchInContext(ctx, &bleve.SearchRequest{ Query: &query.TermQuery{ Term: req.Name, - FieldVal: resource.SEARCH_FIELD_REPOSITORY_NAME, + FieldVal: resource.SEARCH_FIELD_MANAGER_ID, }, Fields: []string{ resource.SEARCH_FIELD_TITLE, resource.SEARCH_FIELD_FOLDER, - resource.SEARCH_FIELD_REPOSITORY_NAME, - resource.SEARCH_FIELD_REPOSITORY_PATH, - resource.SEARCH_FIELD_REPOSITORY_HASH, - resource.SEARCH_FIELD_REPOSITORY_TIME, + resource.SEARCH_FIELD_MANAGER_KIND, + resource.SEARCH_FIELD_MANAGER_ID, + resource.SEARCH_FIELD_SOURCE_PATH, + resource.SEARCH_FIELD_SOURCE_CHECKSUM, + resource.SEARCH_FIELD_SOURCE_TIME, }, Sort: search.SortOrder{ &search.SortField{ - Field: resource.SEARCH_FIELD_REPOSITORY_PATH, + Field: resource.SEARCH_FIELD_SOURCE_PATH, Type: search.SortFieldAsString, Desc: false, }, @@ -347,6 +349,10 @@ func (b *bleveIndex) ListRepositoryObjects(ctx context.Context, req *resource.Li if ok { return intV } + floatV, ok := v.(float64) + if ok { + return int64(floatV) + } str, ok := v.(string) if ok { t, _ := time.Parse(time.RFC3339, str) @@ -359,9 +365,9 @@ func (b *bleveIndex) ListRepositoryObjects(ctx context.Context, req *resource.Li for _, hit := range found.Hits { item := &resource.ListRepositoryObjectsResponse_Item{ Object: &resource.ResourceKey{}, - Hash: asString(hit.Fields[resource.SEARCH_FIELD_REPOSITORY_HASH]), - Path: asString(hit.Fields[resource.SEARCH_FIELD_REPOSITORY_PATH]), - Time: asTime(hit.Fields[resource.SEARCH_FIELD_REPOSITORY_TIME]), + Hash: asString(hit.Fields[resource.SEARCH_FIELD_SOURCE_CHECKSUM]), + Path: asString(hit.Fields[resource.SEARCH_FIELD_SOURCE_PATH]), + Time: asTime(hit.Fields[resource.SEARCH_FIELD_SOURCE_TIME]), Title: asString(hit.Fields[resource.SEARCH_FIELD_TITLE]), Folder: asString(hit.Fields[resource.SEARCH_FIELD_FOLDER]), } @@ -379,7 +385,7 @@ func (b *bleveIndex) CountRepositoryObjects(ctx context.Context) ([]*resource.Co Query: bleve.NewMatchAllQuery(), Size: 0, Facets: bleve.FacetsRequest{ - "count": bleve.NewFacetRequest(resource.SEARCH_FIELD_REPOSITORY_NAME, 1000), // typically less then 5 + "count": bleve.NewFacetRequest(resource.SEARCH_FIELD_MANAGER_ID, 1000), // typically less then 5 }, }) if err != nil { diff --git a/pkg/storage/unified/search/bleve_mappings.go b/pkg/storage/unified/search/bleve_mappings.go index 027fd87b947..7c59bbae559 100644 --- a/pkg/storage/unified/search/bleve_mappings.go +++ b/pkg/storage/unified/search/bleve_mappings.go @@ -69,9 +69,9 @@ func getBleveDocMappings(_ resource.SearchableDocumentFields) *mapping.DocumentM mapper.AddFieldMappingsAt(resource.SEARCH_FIELD_FOLDER, folderMapping) // Repositories - repo := bleve.NewDocumentStaticMapping() - repo.AddFieldMappingsAt("name", &mapping.FieldMapping{ - Name: "name", + manager := bleve.NewDocumentStaticMapping() + manager.AddFieldMappingsAt("kind", &mapping.FieldMapping{ + Name: "kind", Type: "text", Analyzer: keyword.Name, Store: true, @@ -79,7 +79,18 @@ func getBleveDocMappings(_ resource.SearchableDocumentFields) *mapping.DocumentM IncludeTermVectors: false, IncludeInAll: true, }) - repo.AddFieldMappingsAt("path", &mapping.FieldMapping{ + manager.AddFieldMappingsAt("id", &mapping.FieldMapping{ + Name: "id", + Type: "text", + Analyzer: keyword.Name, + Store: true, + Index: true, + IncludeTermVectors: false, + IncludeInAll: true, + }) + + source := bleve.NewDocumentStaticMapping() + source.AddFieldMappingsAt("path", &mapping.FieldMapping{ Name: "path", Type: "text", Analyzer: keyword.Name, @@ -88,8 +99,8 @@ func getBleveDocMappings(_ resource.SearchableDocumentFields) *mapping.DocumentM IncludeTermVectors: false, IncludeInAll: true, }) - repo.AddFieldMappingsAt("hash", &mapping.FieldMapping{ - Name: "hash", + source.AddFieldMappingsAt("checksum", &mapping.FieldMapping{ + Name: "checksum", Type: "text", Analyzer: keyword.Name, Store: true, @@ -97,9 +108,10 @@ func getBleveDocMappings(_ resource.SearchableDocumentFields) *mapping.DocumentM IncludeTermVectors: false, IncludeInAll: true, }) - repo.AddFieldMappingsAt("time", mapping.NewDateTimeFieldMapping()) + source.AddFieldMappingsAt("timestampMillis", mapping.NewNumericFieldMapping()) - mapper.AddSubDocumentMapping("repo", repo) + mapper.AddSubDocumentMapping("manager", manager) + mapper.AddSubDocumentMapping("source", source) labelMapper := bleve.NewDocumentMapping() mapper.AddSubDocumentMapping(resource.SEARCH_FIELD_LABELS, labelMapper) diff --git a/pkg/storage/unified/search/bleve_mappings_test.go b/pkg/storage/unified/search/bleve_mappings_test.go index d1a07fdf116..7a563fff827 100644 --- a/pkg/storage/unified/search/bleve_mappings_test.go +++ b/pkg/storage/unified/search/bleve_mappings_test.go @@ -25,11 +25,14 @@ func TestDocumentMapping(t *testing.T) { "x": "y", }, RV: 1234, - RepoInfo: &utils.ResourceRepositoryInfo{ - Name: "nnn", - Path: "ppp", - Hash: "hhh", - Timestamp: asTimePointer(1234), + Manager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "rrr", + }, + Source: &utils.SourceProperties{ + Path: "ppp", + Checksum: "ooo", + TimestampMillis: 1234, }, } @@ -43,5 +46,5 @@ func TestDocumentMapping(t *testing.T) { fmt.Printf("DOC: fields %d\n", len(doc.Fields)) fmt.Printf("DOC: size %d\n", doc.Size()) - require.Equal(t, 13, len(doc.Fields)) + require.Equal(t, 14, len(doc.Fields)) } diff --git a/pkg/storage/unified/search/bleve_test.go b/pkg/storage/unified/search/bleve_test.go index b2d90f25c0f..fa572536d05 100644 --- a/pkg/storage/unified/search/bleve_test.go +++ b/pkg/storage/unified/search/bleve_test.go @@ -7,20 +7,18 @@ import ( "os" "path/filepath" "testing" - "time" - "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana/pkg/apimachinery/identity" - authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" - "github.com/grafana/grafana/pkg/services/user" - "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/infra/tracing" + authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" + "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/store/kind/dashboard" + "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/storage/unified/resource" ) @@ -90,11 +88,14 @@ func TestBleveBackend(t *testing.T) { utils.LabelKeyDeprecatedInternalID: "10", // nolint:staticcheck }, Tags: []string{"aa", "bb"}, - RepoInfo: &utils.ResourceRepositoryInfo{ - Name: "repo-1", - Path: "path/to/aaa.json", - Hash: "xyz", - Timestamp: asTimePointer(1609462800000), // 2021 + Manager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + Source: &utils.SourceProperties{ + Path: "path/to/aaa.json", + Checksum: "xyz", + TimestampMillis: 1609462800000, // 2021 }, }) _ = index.Write(&resource.IndexableDocument{ @@ -119,11 +120,14 @@ func TestBleveBackend(t *testing.T) { "region": "east", utils.LabelKeyDeprecatedInternalID: "11", // nolint:staticcheck }, - RepoInfo: &utils.ResourceRepositoryInfo{ - Name: "repo-1", - Path: "path/to/bbb.json", - Hash: "hijk", - Timestamp: asTimePointer(1640998800000), // 2022 + Manager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + Source: &utils.SourceProperties{ + Path: "path/to/bbb.json", + Checksum: "hijk", + TimestampMillis: 1640998800000, // 2022 }, }) _ = index.Write(&resource.IndexableDocument{ @@ -138,8 +142,11 @@ func TestBleveBackend(t *testing.T) { Title: "ccc (dash)", TitlePhrase: "ccc (dash)", Folder: "zzz", - RepoInfo: &utils.ResourceRepositoryInfo{ - Name: "repo2", + Manager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo2", + }, + Source: &utils.SourceProperties{ Path: "path/in/repo2.yaml", }, Fields: map[string]any{}, @@ -263,6 +270,7 @@ func TestBleveBackend(t *testing.T) { jj, err := json.MarshalIndent(found, "", " ") require.NoError(t, err) fmt.Printf("%s\n", string(jj)) + // NOTE "hash" -> "checksum" requires changing the protobuf require.JSONEq(t, `{ "items": [ { @@ -334,11 +342,14 @@ func TestBleveBackend(t *testing.T) { }, Title: "zzz (folder)", TitlePhrase: "zzz (folder)", - RepoInfo: &utils.ResourceRepositoryInfo{ - Name: "repo-1", - Path: "path/to/folder.json", - Hash: "xxxx", - Timestamp: asTimePointer(300), + Manager: &utils.ManagerProperties{ + Kind: utils.ManagerKindRepo, + Identity: "repo-1", + }, + Source: &utils.SourceProperties{ + Path: "path/to/folder.json", + Checksum: "xxxx", + TimestampMillis: 300, }, }) _ = index.Write(&resource.IndexableDocument{ @@ -559,14 +570,6 @@ func TestGetSortFields(t *testing.T) { }) } -func asTimePointer(milli int64) *time.Time { - if milli > 0 { - t := time.UnixMilli(milli) - return &t - } - return nil -} - var _ authlib.AccessClient = (*StubAccessClient)(nil) func NewStubAccessClient(permissions map[string]bool) *StubAccessClient { diff --git a/pkg/storage/unified/search/document_test.go b/pkg/storage/unified/search/document_test.go index 04f9637cb77..c11ada997a6 100644 --- a/pkg/storage/unified/search/document_test.go +++ b/pkg/storage/unified/search/document_test.go @@ -81,14 +81,26 @@ func TestDashboardDocumentBuilder(t *testing.T) { // Standard builder = resource.StandardDocumentBuilder() - doSnapshotTests(t, builder, "folder", key, []string{ + doSnapshotTests(t, builder, "folder", &resource.ResourceKey{ + Namespace: "default", + Group: "folder.grafana.app", + Resource: "folders", + }, []string{ "aaa", "bbb", }) - doSnapshotTests(t, builder, "playlist", key, []string{ + doSnapshotTests(t, builder, "playlist", &resource.ResourceKey{ + Namespace: "default", + Group: "playlist.grafana.app", + Resource: "playlists", + }, []string{ "aaa", }) - doSnapshotTests(t, builder, "report", key, []string{ + doSnapshotTests(t, builder, "report", &resource.ResourceKey{ + Namespace: "default", + Group: "reporting.grafana.app", + Resource: "reports", + }, []string{ "aaa", }) } diff --git a/pkg/storage/unified/search/testdata/doc/folder-aaa-out.json b/pkg/storage/unified/search/testdata/doc/folder-aaa-out.json index 86cb4f5df16..c2807e7f48a 100644 --- a/pkg/storage/unified/search/testdata/doc/folder-aaa-out.json +++ b/pkg/storage/unified/search/testdata/doc/folder-aaa-out.json @@ -1,8 +1,8 @@ { "key": { "namespace": "default", - "group": "dashboard.grafana.app", - "resource": "dashboards", + "group": "folder.grafana.app", + "resource": "folders", "name": "aaa" }, "name": "aaa", @@ -11,7 +11,8 @@ "title_phrase": "test-aaa", "created": 1730490142000, "createdBy": "user:1", - "repo": { - "name": "SQL" + "manager": { + "kind": "repo", + "id": "MyGIT" } } \ No newline at end of file diff --git a/pkg/storage/unified/search/testdata/doc/folder-aaa.json b/pkg/storage/unified/search/testdata/doc/folder-aaa.json index 97262d5926e..27ef4d28ffb 100644 --- a/pkg/storage/unified/search/testdata/doc/folder-aaa.json +++ b/pkg/storage/unified/search/testdata/doc/folder-aaa.json @@ -8,7 +8,7 @@ "creationTimestamp": "2024-11-01T19:42:22Z", "annotations": { "grafana.app/createdBy": "user:1", - "grafana.app/originName": "SQL" + "grafana.app/repoName": "MyGIT" } }, "spec": { diff --git a/pkg/storage/unified/search/testdata/doc/folder-bbb-out.json b/pkg/storage/unified/search/testdata/doc/folder-bbb-out.json index 5d268564f5b..cabacb8b404 100644 --- a/pkg/storage/unified/search/testdata/doc/folder-bbb-out.json +++ b/pkg/storage/unified/search/testdata/doc/folder-bbb-out.json @@ -1,8 +1,8 @@ { "key": { "namespace": "default", - "group": "dashboard.grafana.app", - "resource": "dashboards", + "group": "folder.grafana.app", + "resource": "folders", "name": "bbb" }, "name": "bbb", @@ -11,7 +11,8 @@ "title_phrase": "test-bbb", "created": 1730490142000, "createdBy": "user:1", - "repo": { - "name": "SQL" + "manager": { + "kind": "repo", + "id": "MyGIT" } } \ No newline at end of file diff --git a/pkg/storage/unified/search/testdata/doc/folder-bbb.json b/pkg/storage/unified/search/testdata/doc/folder-bbb.json index 6d5f66d648d..81f97cc332a 100644 --- a/pkg/storage/unified/search/testdata/doc/folder-bbb.json +++ b/pkg/storage/unified/search/testdata/doc/folder-bbb.json @@ -8,7 +8,7 @@ "creationTimestamp": "2024-11-01T19:42:22Z", "annotations": { "grafana.app/createdBy": "user:1", - "grafana.app/originName": "SQL" + "grafana.app/repoName": "MyGIT" } }, "spec": { diff --git a/pkg/storage/unified/search/testdata/doc/playlist-aaa-out.json b/pkg/storage/unified/search/testdata/doc/playlist-aaa-out.json index c0f12814338..0bc5b824883 100644 --- a/pkg/storage/unified/search/testdata/doc/playlist-aaa-out.json +++ b/pkg/storage/unified/search/testdata/doc/playlist-aaa-out.json @@ -1,8 +1,8 @@ { "key": { "namespace": "default", - "group": "dashboard.grafana.app", - "resource": "dashboards", + "group": "playlist.grafana.app", + "resource": "playlists", "name": "aaa" }, "name": "aaa", @@ -10,10 +10,5 @@ "title": "Test AAA", "title_phrase": "test aaa", "created": 1731336353000, - "createdBy": "user:t000000001", - "repo": { - "name": "UI", - "path": "/playlists/new", - "hash": "Grafana v11.4.0-pre (c0de407fee)" - } + "createdBy": "user:t000000001" } \ No newline at end of file diff --git a/pkg/storage/unified/search/testdata/doc/report-aaa-out.json b/pkg/storage/unified/search/testdata/doc/report-aaa-out.json index 6b193267072..656f57c46f1 100644 --- a/pkg/storage/unified/search/testdata/doc/report-aaa-out.json +++ b/pkg/storage/unified/search/testdata/doc/report-aaa-out.json @@ -1,8 +1,8 @@ { "key": { "namespace": "default", - "group": "dashboard.grafana.app", - "resource": "dashboards", + "group": "reporting.grafana.app", + "resource": "reports", "name": "aaa" }, "name": "aaa", diff --git a/pkg/tests/apis/dashboard/dashboards_test.go b/pkg/tests/apis/dashboard/dashboards_test.go index e13e26a1b3d..317945da7f3 100644 --- a/pkg/tests/apis/dashboard/dashboards_test.go +++ b/pkg/tests/apis/dashboard/dashboards_test.go @@ -68,7 +68,9 @@ func runDashboardTest(t *testing.T, helper *apis.K8sTestHelper, gvr schema.Group wrap, err := utils.MetaAccessor(obj) require.NoError(t, err) - require.Empty(t, wrap.GetRepositoryName()) // no SQL repo stub + + m, _ := wrap.GetManagerProperties() + require.Empty(t, m.Identity) // no SQL repo stub require.Equal(t, helper.Org1.Admin.Identity.GetUID(), wrap.GetCreatedBy()) // Commented out because the dynamic client does not like lists as sub-resource diff --git a/public/api-merged.json b/public/api-merged.json index c14b5edc8c6..7d1e7214293 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -15514,6 +15514,9 @@ "type": "integer", "format": "int64" }, + "managedBy": { + "$ref": "#/definitions/ManagerKind" + }, "orgId": { "type": "integer", "format": "int64" @@ -15529,10 +15532,6 @@ "$ref": "#/definitions/Folder" } }, - "repository": { - "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", - "type": "string" - }, "title": { "type": "string" }, @@ -15562,11 +15561,10 @@ "type": "integer", "format": "int64" }, - "parentUid": { - "type": "string" + "managedBy": { + "$ref": "#/definitions/ManagerKind" }, - "repository": { - "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", + "parentUid": { "type": "string" }, "title": { @@ -17047,6 +17045,11 @@ } } }, + "ManagerKind": { + "description": "It can be a user or a tool or a generic API client.\n+enum", + "type": "string", + "title": "ManagerKind is the type of manager, which is responsible for managing the resource." + }, "MassDeleteAnnotationsCmd": { "type": "object", "properties": { diff --git a/public/openapi3.json b/public/openapi3.json index 2f9323187d3..a8dffd5d069 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -5569,6 +5569,9 @@ "format": "int64", "type": "integer" }, + "managedBy": { + "$ref": "#/components/schemas/ManagerKind" + }, "orgId": { "format": "int64", "type": "integer" @@ -5584,10 +5587,6 @@ }, "type": "array" }, - "repository": { - "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", - "type": "string" - }, "title": { "type": "string" }, @@ -5617,11 +5616,10 @@ "format": "int64", "type": "integer" }, - "parentUid": { - "type": "string" + "managedBy": { + "$ref": "#/components/schemas/ManagerKind" }, - "repository": { - "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", + "parentUid": { "type": "string" }, "title": { @@ -7103,6 +7101,11 @@ }, "type": "object" }, + "ManagerKind": { + "description": "It can be a user or a tool or a generic API client.\n+enum", + "title": "ManagerKind is the type of manager, which is responsible for managing the resource.", + "type": "string" + }, "MassDeleteAnnotationsCmd": { "properties": { "annotationId": { From 353976400825d80346516ca1ee3513ea1ae0f2d2 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Wed, 5 Mar 2025 08:00:12 +0100 Subject: [PATCH 015/312] Hackaton: Add more unit tests, take 3 (#101525) * serviceaccounts/secretscan: test Service more thoroughly * middleware/cookies: add tests for CookieOptions * anonymous/anonimpl: cover a couple more methods * components/imguploader: Implement WebDAV integration tests * components/apikeygen: also check IsValid method * bus: cover invalid callback signature cases * cloudmigration/objectstorage: add basic unit tests * login/social/connectors: add test case for GitHub OAuth fetch emails+orgs * expr/classic: cover more evaluator types in tests --- pkg/bus/bus_test.go | 59 +++++++++ pkg/components/apikeygen/apikeygen_test.go | 4 + .../imguploader/webdavuploader_test.go | 70 ++++++++--- pkg/expr/classic/evaluator_test.go | 48 ++++++++ .../social/connectors/github_oauth_test.go | 51 +++++++- pkg/middleware/cookies/cookies_test.go | 37 ++++++ pkg/services/anonymous/anonimpl/impl_test.go | 60 ++++++++- .../cloudmigration/objectstorage/s3_test.go | 103 ++++++++++++++++ .../secretscan/service_test.go | 115 ++++++++++++++++++ 9 files changed, 522 insertions(+), 25 deletions(-) create mode 100644 pkg/middleware/cookies/cookies_test.go create mode 100644 pkg/services/cloudmigration/objectstorage/s3_test.go diff --git a/pkg/bus/bus_test.go b/pkg/bus/bus_test.go index 170297797bd..f7af56e6fa2 100644 --- a/pkg/bus/bus_test.go +++ b/pkg/bus/bus_test.go @@ -2,6 +2,7 @@ package bus import ( "context" + "errors" "testing" "github.com/stretchr/testify/require" @@ -91,3 +92,61 @@ func TestEventCtxPublish(t *testing.T) { require.True(t, invoked) } + +func TestEventListenerError(t *testing.T) { + bus := ProvideBus(tracing.InitializeTracerForTest()) + + mockErr := errors.New("error") + + invocations := 0 + + // Will be called in order of declaration. + bus.AddEventListener(func(ctx context.Context, query *testQuery) error { + invocations++ + return nil + }) + + bus.AddEventListener(func(ctx context.Context, query *testQuery) error { + invocations++ + return mockErr + }) + + bus.AddEventListener(func(ctx context.Context, query *testQuery) { + invocations++ + }) + + err := bus.Publish(context.Background(), &testQuery{}) + require.ErrorIs(t, err, mockErr) + require.Equal(t, 2, invocations) +} + +func TestEventListenerInvalidCallbackType(t *testing.T) { + bus := ProvideBus(tracing.InitializeTracerForTest()) + + invoked := false + + bus.AddEventListener(func(ctx context.Context, query *testQuery) bool { + invoked = true + return invoked + }) + + err := bus.Publish(context.Background(), &testQuery{}) + require.Error(t, err) + require.True(t, invoked) +} + +func TestEventListenerInvalidCallback(t *testing.T) { + bus := ProvideBus(tracing.InitializeTracerForTest()) + + invoked := false + + bus.AddEventListener(func(ctx context.Context, query *testQuery) { + invoked = true + }) + + require.Panics(t, func() { + err := bus.Publish(context.Background(), &testQuery{}) + require.NoError(t, err) // unreachable + }) + require.True(t, invoked) +} diff --git a/pkg/components/apikeygen/apikeygen_test.go b/pkg/components/apikeygen/apikeygen_test.go index 80418c04a80..82ac531c959 100644 --- a/pkg/components/apikeygen/apikeygen_test.go +++ b/pkg/components/apikeygen/apikeygen_test.go @@ -22,4 +22,8 @@ func TestApiKeyGen(t *testing.T) { keyHashed, err := util.EncodePassword(keyInfo.Key, keyInfo.Name) require.NoError(t, err) assert.Equal(t, result.HashedKey, keyHashed) + + valid, err := IsValid(keyInfo, keyHashed) + require.NoError(t, err) + require.True(t, valid) } diff --git a/pkg/components/imguploader/webdavuploader_test.go b/pkg/components/imguploader/webdavuploader_test.go index acea35a67a5..a3d1d7b4a18 100644 --- a/pkg/components/imguploader/webdavuploader_test.go +++ b/pkg/components/imguploader/webdavuploader_test.go @@ -2,43 +2,83 @@ package imguploader import ( "context" + "net/http" + "net/http/httptest" "strings" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/net/webdav" ) func TestUploadToWebdav(t *testing.T) { - // Can be tested with this docker container: https://hub.docker.com/r/morrisjobke/webdav/ - t.Run("[Integration test] for external_image_store.webdav", func(t *testing.T) { - t.Skip("Skip test [Integration test] for external_image_store.webdav") - webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "") - path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png") + t.Parallel() + t.Run("[Integration test] for external_image_store.webdav", func(t *testing.T) { + t.Parallel() + + handler := &webdav.Handler{ + FileSystem: webdav.Dir(t.TempDir()), + LockSystem: webdav.NewMemLS(), + Logger: func(r *http.Request, err error) { + require.Equal(t, http.MethodPut, r.Method) + require.NoError(t, err) + }, + } + + server := httptest.NewServer(handler) + t.Cleanup(server.Close) + + webdavUploader, err := NewWebdavImageUploader(server.URL, "test", "test", "") require.NoError(t, err) - require.True(t, strings.HasPrefix(path, "http://localhost:8888/webdav/")) + require.NotNil(t, webdavUploader) + + path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png") + require.NoError(t, err) + require.True(t, strings.HasPrefix(path, server.URL)) }) t.Run("[Integration test] for external_image_store.webdav with public url", func(t *testing.T) { - t.Skip("Skip test [Integration test] for external_image_store.webdav with public url") - webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://publicurl:8888/webdav") - path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png") + t.Parallel() + handler := &webdav.Handler{ + FileSystem: webdav.Dir(t.TempDir()), + LockSystem: webdav.NewMemLS(), + Logger: func(r *http.Request, err error) { + require.Equal(t, http.MethodPut, r.Method) + require.NoError(t, err) + }, + } + + server := httptest.NewServer(handler) + t.Cleanup(server.Close) + + webdavUploader, err := NewWebdavImageUploader(server.URL, "test", "test", "http://publicurl:8888/webdav") require.NoError(t, err) - require.True(t, strings.HasPrefix(path, "http://publicurl:8888/webdav/")) + require.NotNil(t, webdavUploader) + path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png") + require.NoError(t, err) require.True(t, strings.HasPrefix(path, "http://publicurl:8888/webdav/")) }) } func TestPublicURL(t *testing.T) { + t.Parallel() + t.Run("Given a public URL with parameters, and no template", func(t *testing.T) { - webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files=") - assert.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download/fileyfile.png?files=", webdavUploader.PublicURL("fileyfile.png")) + t.Parallel() + + webdavUploader, err := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files=") + require.NoError(t, err) + require.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download/fileyfile.png?files=", webdavUploader.PublicURL("fileyfile.png")) }) + t.Run("Given a public URL with parameters, and a template", func(t *testing.T) { - webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files={{file}}") - assert.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download?files=fileyfile.png", webdavUploader.PublicURL("fileyfile.png")) + t.Parallel() + + webdavUploader, err := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files={{file}}") + require.NoError(t, err) + require.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download?files=fileyfile.png", webdavUploader.PublicURL("fileyfile.png")) }) } diff --git a/pkg/expr/classic/evaluator_test.go b/pkg/expr/classic/evaluator_test.go index 4a82b1c920c..ba38be57480 100644 --- a/pkg/expr/classic/evaluator_test.go +++ b/pkg/expr/classic/evaluator_test.go @@ -148,6 +148,54 @@ func TestRangedEvaluator(t *testing.T) { inputNumber: newNumber(util.Pointer(50.0)), expected: false, }, + { + name: "value 100 is outside range 1, 100: false", + evaluator: &rangedEvaluator{"outside_range", 1, 100}, + inputNumber: newNumber(util.Pointer(100.)), + expected: false, + }, + { + name: "value 1 is outside range 1, 100: false", + evaluator: &rangedEvaluator{"outside_range", 1, 100}, + inputNumber: newNumber(util.Pointer(1.)), + expected: false, + }, + { + name: "value 100 is within range included 1, 100: true", + evaluator: &rangedEvaluator{"within_range_included", 1, 100}, + inputNumber: newNumber(util.Pointer(100.)), + expected: true, + }, + { + name: "value 1 is within range included 1, 100: true", + evaluator: &rangedEvaluator{"within_range_included", 1, 100}, + inputNumber: newNumber(util.Pointer(1.)), + expected: true, + }, + { + name: "value 100 is outside range included 1, 100: true", + evaluator: &rangedEvaluator{"outside_range_included", 1, 100}, + inputNumber: newNumber(util.Pointer(100.)), + expected: true, + }, + { + name: "value 1 is outside range included 1, 100: true", + evaluator: &rangedEvaluator{"outside_range_included", 1, 100}, + inputNumber: newNumber(util.Pointer(1.)), + expected: true, + }, + { + name: "unknown evaluator type returns false", + evaluator: &rangedEvaluator{"", 1, 100}, + inputNumber: newNumber(util.Pointer(1.)), + expected: false, + }, + { + name: "nil number conversion returns false", + evaluator: &rangedEvaluator{"", 1, 100}, + inputNumber: newNumber(nil), + expected: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/login/social/connectors/github_oauth_test.go b/pkg/login/social/connectors/github_oauth_test.go index e54d617c217..90b648f5f84 100644 --- a/pkg/login/social/connectors/github_oauth_test.go +++ b/pkg/login/social/connectors/github_oauth_test.go @@ -2,6 +2,7 @@ package connectors import ( "context" + "fmt" "net/http" "net/http/httptest" "strings" @@ -86,7 +87,12 @@ const testGHUserTeamsJSON = `[ } ]` -const testGHUserJSON = `{ +var ( + testGHUserJSON = fmt.Sprintf(testGHUserJSONTemplate, "octocat@github.com") + testGHUserEmptyEmailJSON = fmt.Sprintf(testGHUserJSONTemplate, "") +) + +const testGHUserJSONTemplate = `{ "login": "octocat", "id": 1, "node_id": "MDQ6VXNlcjE=", @@ -109,7 +115,7 @@ const testGHUserJSON = `{ "company": "GitHub", "blog": "https://github.com/blog", "location": "San Francisco", - "email": "octocat@github.com", + "email": "%s", "hireable": false, "bio": "There once was...", "twitter_username": "monatheoctocat", @@ -133,6 +139,16 @@ const testGHUserJSON = `{ } }` +const testGHUserEmailJSON = `[{ + "email": "octocat@github.com", + "primary": true, + "verified": true +}]` + +const testGHOrgsJSON = `[{ + "login": "github" +}]` + func TestSocialGitHub_UserInfo(t *testing.T) { var boolPointer *bool tests := []struct { @@ -310,20 +326,45 @@ func TestSocialGitHub_UserInfo(t *testing.T) { userTeamsRawJSON: testGHUserTeamsJSON, wantErr: true, }, + { + name: "should fetch email and allowed orgs", + userRawJSON: testGHUserEmptyEmailJSON, + userTeamsRawJSON: testGHUserTeamsJSON, + oAuthExtraInfo: map[string]string{ + "allowed_organizations": "github", + }, + want: &social.BasicUserInfo{ + Id: "1", + Name: "monalisa octocat", + Email: "octocat@github.com", + Login: "octocat", + OrgRoles: map[int64]org.RoleType{1: org.RoleViewer}, + Groups: []string{"https://github.com/orgs/github/teams/justice-league", "@github/justice-league"}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - writer.WriteHeader(http.StatusOK) + reqURL := request.URL.String() + // return JSON if matches user endpoint - if strings.HasSuffix(request.URL.String(), "/user") { + if strings.HasSuffix(reqURL, "/user") { writer.Header().Set("Content-Type", "application/json") _, err := writer.Write([]byte(tt.userRawJSON)) require.NoError(t, err) - } else if strings.HasSuffix(request.URL.String(), "/user/teams?per_page=100") { + } else if strings.HasSuffix(reqURL, "/user/teams?per_page=100") { writer.Header().Set("Content-Type", "application/json") _, err := writer.Write([]byte(tt.userTeamsRawJSON)) require.NoError(t, err) + } else if strings.HasSuffix(reqURL, "/emails") { // only called if email is empty + writer.Header().Set("Content-Type", "application/json") + _, err := writer.Write([]byte(testGHUserEmailJSON)) + require.NoError(t, err) + } else if strings.HasSuffix(reqURL, "/orgs?per_page=100") { + writer.Header().Set("Content-Type", "application/json") + _, err := writer.Write([]byte(testGHOrgsJSON)) + require.NoError(t, err) } else { writer.WriteHeader(http.StatusNotFound) } diff --git a/pkg/middleware/cookies/cookies_test.go b/pkg/middleware/cookies/cookies_test.go new file mode 100644 index 00000000000..6e2c822ce3d --- /dev/null +++ b/pkg/middleware/cookies/cookies_test.go @@ -0,0 +1,37 @@ +package cookies + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCookieOptions(t *testing.T) { + rr := httptest.NewRecorder() + + expectedName := "cookie-name" + expectedValue := "cookie-value" + + WriteCookie(rr, expectedName, expectedValue, 100, nil) + + cookie, err := http.ParseSetCookie(rr.Header().Get("Set-Cookie")) + require.NoError(t, err) + require.NotNil(t, cookie) + + require.Equal(t, expectedName, cookie.Name) + require.Equal(t, expectedValue, cookie.Value) + require.GreaterOrEqual(t, cookie.MaxAge, 0) + + // Does not override but appends to the `Set-Cookie` header. + DeleteCookie(rr, expectedName, nil) + + cookieHeader := rr.Header().Values("Set-Cookie") + require.Len(t, cookieHeader, 2) + + cookie, err = http.ParseSetCookie(cookieHeader[1]) + require.NoError(t, err) + require.NotNil(t, cookie) + require.NoError(t, cookie.Valid()) +} diff --git a/pkg/services/anonymous/anonimpl/impl_test.go b/pkg/services/anonymous/anonimpl/impl_test.go index 0ca7cb2cf78..8a81c2035c9 100644 --- a/pkg/services/anonymous/anonimpl/impl_test.go +++ b/pkg/services/anonymous/anonimpl/impl_test.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/grafana/pkg/services/anonymous" "github.com/grafana/grafana/pkg/services/anonymous/anonimpl/anonstore" "github.com/grafana/grafana/pkg/services/anonymous/validator" + "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn/authntest" "github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/setting" @@ -41,6 +42,7 @@ func TestIntegrationDeviceService_tag(t *testing.T) { expectedAnonUICount int64 expectedKey string expectedDevice *anonstore.Device + disableService bool }{ { name: "no requests", @@ -118,20 +120,49 @@ func TestIntegrationDeviceService_tag(t *testing.T) { }, expectedAnonUICount: 2, }, + { + name: "when the service is disabled, read operations return empty", + req: []tagReq{ + { + httpReq: &http.Request{ + Header: http.Header{ + "User-Agent": []string{"test"}, + "X-Forwarded-For": []string{"10.30.30.1"}, + http.CanonicalHeaderKey(deviceIDHeader): []string{"32mdo31deeqwes"}, + }, + }, + kind: anonymous.AnonDeviceUI, + }, + }, + disableService: true, + expectedAnonUICount: 0, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + store := db.InitTestDB(t) - anonService := ProvideAnonymousDeviceService(&usagestats.UsageStatsMock{}, - &authntest.FakeService{}, store, setting.NewCfg(), orgtest.NewOrgServiceFake(), nil, actest.FakeAccessControl{}, &routing.RouteRegisterImpl{}, validator.FakeAnonUserLimitValidator{}) + + cfg := setting.NewCfg() + cfg.Anonymous.Enabled = !tc.disableService + + anonService := ProvideAnonymousDeviceService( + &usagestats.UsageStatsMock{}, &authntest.FakeService{}, store, cfg, orgtest.NewOrgServiceFake(), + nil, actest.FakeAccessControl{}, &routing.RouteRegisterImpl{}, validator.FakeAnonUserLimitValidator{}, + ) for _, req := range tc.req { - err := anonService.TagDevice(context.Background(), req.httpReq, req.kind) + err := anonService.TagDevice(ctx, req.httpReq, req.kind) require.NoError(t, err) + + t.Cleanup(func() { + anonService.untagDevice(ctx, nil, &authn.Request{HTTPRequest: req.httpReq}, nil) + }) } - devices, err := anonService.anonStore.ListDevices(context.Background(), nil, nil) + devices, err := anonService.ListDevices(ctx, nil, nil) require.NoError(t, err) require.Len(t, devices, int(tc.expectedAnonUICount)) if tc.expectedDevice != nil { @@ -147,10 +178,29 @@ func TestIntegrationDeviceService_tag(t *testing.T) { assert.Equal(t, tc.expectedDevice, devices[0]) } + to := time.Now() + from := to.AddDate(0, 0, -1) + + devicesCount, err := anonService.CountDevices(ctx, from, to) + require.NoError(t, err) + require.Equal(t, tc.expectedAnonUICount, devicesCount) + + devicesFound, err := anonService.SearchDevices(ctx, &anonstore.SearchDeviceQuery{ + From: from, + To: to, + }) + require.NoError(t, err) + if tc.expectedAnonUICount > 0 { + require.NotNil(t, devicesFound) + require.Equal(t, tc.expectedAnonUICount, devicesFound.TotalCount) + } + stats, err := anonService.usageStatFn(context.Background()) require.NoError(t, err) - assert.Equal(t, tc.expectedAnonUICount, stats["stats.anonymous.device.ui.count"].(int64), stats) + if !tc.disableService { + assert.Equal(t, tc.expectedAnonUICount, stats["stats.anonymous.device.ui.count"].(int64), stats) + } }) } } diff --git a/pkg/services/cloudmigration/objectstorage/s3_test.go b/pkg/services/cloudmigration/objectstorage/s3_test.go new file mode 100644 index 00000000000..f4fe4614ebd --- /dev/null +++ b/pkg/services/cloudmigration/objectstorage/s3_test.go @@ -0,0 +1,103 @@ +package objectstorage + +import ( + "bytes" + "context" + "io" + "math" + "mime/multipart" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/grafana/grafana/pkg/infra/tracing" + "github.com/stretchr/testify/require" +) + +func TestPresignedURLUpload(t *testing.T) { + t.Parallel() + + t.Run("successfully send data to the server", func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + key := "snapshot/uuid/key" + data := "sending-some-data" + + reader := bytes.NewBufferString(data) + + qs, err := url.ParseQuery("one=a&two=b") + require.NoError(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + _, boundary, found := strings.Cut(contentType, "boundary=") + require.True(t, found) + + mpr := multipart.NewReader(r.Body, boundary) + + form, err := mpr.ReadForm(math.MaxInt64) + require.NoError(t, err) + require.NotNil(t, form) + require.NotNil(t, form.Value) + + require.Equal(t, key, form.Value["key"][0]) + require.Equal(t, qs.Get("one"), form.Value["one"][0]) + require.Equal(t, qs.Get("two"), form.Value["two"][0]) + + require.Len(t, form.File, 1) + require.Len(t, form.File["file"], 1) + + fileHeader := form.File["file"][0] + require.Equal(t, "file", fileHeader.Filename) + + file, err := fileHeader.Open() + require.NoError(t, err) + + contents, err := io.ReadAll(file) + require.NoError(t, err) + require.EqualValues(t, data, string(contents)) + + require.NoError(t, file.Close()) + })) + t.Cleanup(server.Close) + + s3 := NewS3(http.DefaultClient, tracing.NewNoopTracerService()) + + presignedURL, err := url.Parse(server.URL + "?" + qs.Encode()) + require.NoError(t, err) + + err = s3.PresignedURLUpload(ctx, presignedURL.String(), key, reader) + require.NoError(t, err) + }) + + t.Run("when the request to the server returns an error, it is propagated", func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + key := "snapshot/uuid/key" + data := "sending-some-data" + + reader := bytes.NewBufferString(data) + + body := "test error" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(`{"message": "` + body + `}`)) + })) + t.Cleanup(server.Close) + + s3 := NewS3(http.DefaultClient, tracing.NewNoopTracerService()) + + presignedURL, err := url.Parse(server.URL) + require.NoError(t, err) + + err = s3.PresignedURLUpload(ctx, presignedURL.String(), key, reader) + require.Error(t, err) + require.Contains(t, err.Error(), body) + }) +} diff --git a/pkg/services/serviceaccounts/secretscan/service_test.go b/pkg/services/serviceaccounts/secretscan/service_test.go index f1260acb575..eee781f4ea4 100644 --- a/pkg/services/serviceaccounts/secretscan/service_test.go +++ b/pkg/services/serviceaccounts/secretscan/service_test.go @@ -2,13 +2,20 @@ package secretscan import ( "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/apikey" + "github.com/grafana/grafana/pkg/setting" ) func TestService_CheckTokens(t *testing.T) { @@ -170,3 +177,111 @@ func TestService_CheckTokens(t *testing.T) { }) } } + +func TestService(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + // Fake Secret Scanner + Webhook. + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.RequestURI, "/tokens") { + _, err := io.Copy(io.Discard, r.Body) + require.NoError(t, err) + + defer func() { + _ = r.Body.Close() + }() + + _, _ = w.Write([]byte(`[ + {"type": "token_type", "hash": "test-hash-1", "url": "http://example.com", "reported_at": "2006-01-20T01:02:03Z" } + ]`)) + } + + if strings.Contains(r.RequestURI, "/oncall") { + var webhookReq struct { + State string `json:"state"` + Message string `json:"message"` + } + + err := json.NewDecoder(r.Body).Decode(&webhookReq) + require.NoError(t, err) + + defer func() { + _ = r.Body.Close() + }() + + require.Equal(t, "alerting", webhookReq.State) + require.Contains(t, webhookReq.Message, "test-1") + } + })) + t.Cleanup(server.Close) + + unixZero := time.Unix(0, 0).Unix() + revoked := true + + tokenRetriever := &MockTokenRetriever{keys: []apikey.APIKey{ + // Valid + { + ID: 1, + OrgID: 1, + Name: "test-1", + Key: "test-hash-1", + Role: "Viewer", + Expires: nil, + ServiceAccountId: new(int64), + IsRevoked: new(bool), + }, + // Expired + { + ID: 2, + OrgID: 1, + Name: "test-2", + Key: "test-hash-2", + Role: "Viewer", + Expires: &unixZero, + ServiceAccountId: new(int64), + IsRevoked: new(bool), + }, + // Revoked + { + ID: 3, + OrgID: 1, + Name: "test-3", + Key: "test-hash-3", + Role: "Viewer", + Expires: nil, + ServiceAccountId: new(int64), + IsRevoked: &revoked, + }, + // Revoked + Expired + { + ID: 4, + OrgID: 1, + Name: "test-4", + Key: "test-hash-4", + Role: "Viewer", + Expires: &unixZero, + ServiceAccountId: new(int64), + IsRevoked: &revoked, + }, + }} + + cfg := setting.NewCfg() + section := cfg.Raw.Section("secretscan") + + baseURL := section.Key("base_url") + baseURL.SetValue(server.URL) + + oncallURL := section.Key("oncall_url") + oncallURL.SetValue(server.URL + "/oncall") + + revoke := section.Key("revoke") + revoke.SetValue("true") + + service, err := NewService(tokenRetriever, cfg) + require.NoError(t, err) + require.NotNil(t, service) + + err = service.CheckTokens(ctx) + require.NoError(t, err) +} From 01899d761e452ef132ec3201f80a84f7924b1f5d Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 5 Mar 2025 11:58:31 +0100 Subject: [PATCH 016/312] Zanzana: Upgrade to OpenFGA v1.8.6 (#101553) * Zanzana: Upgrade to OpenFGA v1.8.6 * upgrade openfga --- apps/alerting/notifications/go.mod | 10 +-- apps/alerting/notifications/go.sum | 30 ++++---- apps/investigations/go.mod | 10 ++- apps/investigations/go.sum | 25 +++--- apps/playlist/go.mod | 10 ++- apps/playlist/go.sum | 25 +++--- go.mod | 46 +++++------ go.sum | 104 ++++++++++++------------- go.work.sum | 66 ++++++++++++++++ pkg/aggregator/go.mod | 20 ++--- pkg/aggregator/go.sum | 47 ++++++------ pkg/apimachinery/go.mod | 1 + pkg/apimachinery/go.sum | 5 +- pkg/apiserver/go.mod | 12 +-- pkg/apiserver/go.sum | 30 ++++---- pkg/build/go.mod | 12 +-- pkg/build/go.sum | 19 ++--- pkg/promlib/go.mod | 11 +-- pkg/promlib/go.sum | 30 +++----- pkg/storage/unified/apistore/go.mod | 41 +++++----- pkg/storage/unified/apistore/go.sum | 113 ++++++++++++++-------------- pkg/storage/unified/resource/go.mod | 20 ++--- pkg/storage/unified/resource/go.sum | 40 +++++----- pkg/util/xorm/go.mod | 8 +- pkg/util/xorm/go.sum | 28 ++----- 25 files changed, 407 insertions(+), 356 deletions(-) diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index dfcdc5a722a..ff1ac050736 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -38,7 +38,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath-community/go-jmespath v1.1.1 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -50,12 +50,12 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/etcd/api/v3 v3.5.16 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect @@ -80,7 +80,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index d871841faf1..a863aa17822 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -19,7 +19,7 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -81,8 +81,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a534 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath-community/go-jmespath v1.1.1 h1:bFikPhsi/FdmlZhVgSCd2jj1e7G/rw+zyQfyg5UF+L4= @@ -113,18 +113,18 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -142,10 +142,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -262,8 +262,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index a57bc190e02..7c8d9255258 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -32,7 +32,7 @@ require ( github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -45,13 +45,15 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect @@ -70,7 +72,7 @@ require ( golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index f21718f2a17..73abebc47d9 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -53,8 +53,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -89,10 +89,10 @@ github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9Top github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -100,8 +100,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= @@ -112,8 +112,8 @@ github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9p github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -189,9 +189,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 9b03e7fcc38..f42605d0aff 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -33,7 +33,7 @@ require ( github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -46,13 +46,15 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect @@ -71,7 +73,7 @@ require ( golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index f21718f2a17..73abebc47d9 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -53,8 +53,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -89,10 +89,10 @@ github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9Top github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8= github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc= github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -100,8 +100,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= @@ -112,8 +112,8 @@ github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9p github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -189,9 +189,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= diff --git a/go.mod b/go.mod index 7c34d6cdad5..55202622f20 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/go-openapi/strfmt v0.23.0 // @grafana/alerting-backend github.com/go-redis/redis/v8 v8.11.5 // @grafana/grafana-backend-group github.com/go-sourcemap/sourcemap v2.1.4+incompatible // @grafana/grafana-backend-group - github.com/go-sql-driver/mysql v1.8.1 // @grafana/grafana-search-and-storage + github.com/go-sql-driver/mysql v1.9.0 // @grafana/grafana-search-and-storage github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group github.com/gogo/protobuf v1.3.2 // @grafana/alerting-backend @@ -95,8 +95,8 @@ require ( github.com/grafana/pyroscope/api v1.0.0 // @grafana/observability-traces-and-profiling github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 // @grafana/observability-traces-and-profiling github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // @grafana/plugins-platform-backend - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 // @grafana/grafana-backend-group - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // @grafana/identity-access-team + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // @grafana/grafana-backend-group + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // @grafana/identity-access-team github.com/hashicorp/go-hclog v1.6.3 // @grafana/plugins-platform-backend github.com/hashicorp/go-multierror v1.1.1 // @grafana/alerting-squad github.com/hashicorp/go-plugin v1.6.3 // @grafana/plugins-platform-backend @@ -125,14 +125,14 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // @grafana/alerting-backend github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // @grafana/grafana-operator-experience-squad github.com/olekukonko/tablewriter v0.0.5 // @grafana/grafana-backend-group - github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5 // @grafana/identity-access-team - github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c // @grafana/identity-access-team - github.com/openfga/openfga v1.8.4 // @grafana/identity-access-team + github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369 // @grafana/identity-access-team + github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20250121233318-0eae96a39570 // @grafana/identity-access-team + github.com/openfga/openfga v1.8.6 // @grafana/identity-access-team github.com/openzipkin/zipkin-go v0.4.3 // @grafana/oss-big-tent github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend github.com/phpdave11/gofpdi v1.0.13 // @grafana/sharing-squad github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend - github.com/prometheus/client_golang v1.20.5 // @grafana/alerting-backend + github.com/prometheus/client_golang v1.21.0 // @grafana/alerting-backend github.com/prometheus/client_model v0.6.1 // @grafana/grafana-backend-group github.com/prometheus/common v0.62.0 // @grafana/alerting-backend github.com/prometheus/prometheus v0.301.0 // @grafana/alerting-backend @@ -140,8 +140,8 @@ require ( github.com/robfig/cron/v3 v3.0.1 // @grafana/grafana-backend-group github.com/rs/cors v1.11.1 // @grafana/identity-access-team github.com/russellhaering/goxmldsig v1.4.0 // @grafana/grafana-backend-group - github.com/spf13/cobra v1.8.1 // @grafana/grafana-app-platform-squad - github.com/spf13/pflag v1.0.5 // @grafana-app-platform-squad + github.com/spf13/cobra v1.9.1 // @grafana/grafana-app-platform-squad + github.com/spf13/pflag v1.0.6 // @grafana-app-platform-squad github.com/spyzhov/ajson v0.9.0 // @grafana/grafana-app-platform-squad github.com/stretchr/testify v1.10.0 // @grafana/grafana-backend-group github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // @grafana/grafana-backend-group @@ -220,7 +220,7 @@ require ( require github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend require ( - cel.dev/expr v0.19.0 // indirect + cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect cloud.google.com/go/auth v0.14.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect @@ -318,7 +318,7 @@ require ( github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect @@ -334,7 +334,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/proto v1.13.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect @@ -361,7 +361,7 @@ require ( github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.22.1 // indirect + github.com/google/cel-go v0.23.2 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -462,7 +462,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/pressly/goose/v3 v3.24.0 // indirect + github.com/pressly/goose/v3 v3.24.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.13.2 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -531,7 +531,7 @@ require ( golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect @@ -542,13 +542,10 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.32.1 // indirect k8s.io/kms v0.32.1 // indirect - modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.55.3 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect - modernc.org/sqlite v1.34.4 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect + modernc.org/libc v1.61.13 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.2 // indirect + modernc.org/sqlite v1.35.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/yaml v1.4.0 // indirect @@ -558,6 +555,8 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect ) // Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream @@ -583,3 +582,6 @@ exclude github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3 // This was retracted, but seems to be known by the Go module proxy, and is // otherwise pulled in as a transitive dependency. exclude k8s.io/client-go v12.0.0+incompatible + +// k8s.io/apiserver fails due to incompatibility with cel-go 0.23 +replace github.com/google/cel-go => github.com/google/cel-go v0.22.1 diff --git a/go.sum b/go.sum index e46281414a0..35afa9d4c03 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd7 buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2/go.mod h1:w3CrNzdvwGJ4FwUlhshojc2FDXDN+3ou5nlcLTu7dHs= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -1030,9 +1030,9 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= @@ -1067,8 +1067,8 @@ github.com/dlmiddlecote/sqlstats v1.0.2/go.mod h1:0CWaIh/Th+z2aI6Q9Jpfg/o21zmGxW github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4= -github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= +github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= @@ -1135,8 +1135,8 @@ github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -1624,8 +1624,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDa github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= @@ -1635,8 +1635,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -2046,25 +2046,25 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5 h1:z9jaRoo+NIN1AB0ogjtrjx1316TTuq6IbqpEg3UJycA= -github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5/go.mod h1:m74TNgnAAIJ03gfHcx+xaRWnr+IbQy3y/AVNwwCFrC0= -github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c h1:1y84C0V4NRfPtRi4MqQ7+gnFtYgeBKPIeIAPLdVJ7j4= -github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c/go.mod h1:12RMe/HuRNyOzS33RQa53jwdcxE2znr8ycXMlVbgQN4= -github.com/openfga/openfga v1.8.4 h1:OqyRpuxMCxcS7irTFYFkhAIYzmAnczNwxUqjnuZOQyo= -github.com/openfga/openfga v1.8.4/go.mod h1:9Ax9VMMySV2JMsCT8MTePeYt4OrTnPAy1XUV1y9RyuU= +github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369 h1:wEsCZ4oBuu8LfEJ3VXbveXO8uEhCthrxA40WSvxO044= +github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369/go.mod h1:m74TNgnAAIJ03gfHcx+xaRWnr+IbQy3y/AVNwwCFrC0= +github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20250121233318-0eae96a39570 h1:fvc/m49myT+YTVsktQ7nUFep0N6836nFBqBI2/k+8W8= +github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20250121233318-0eae96a39570/go.mod h1:xW/ZQnpRIbs9AdeCPhMXt1veWV/VOuQHz1Qubn5YYxU= +github.com/openfga/openfga v1.8.6 h1:QGYAk4GSZZYoNTwKbC9bjd/7zPWW5/KpmgQfDLP/M1E= +github.com/openfga/openfga v1.8.6/go.mod h1:VSqaE/XwWRUvgC4t/NFlqfL5noxmDURjuQex3d+1hLU= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= @@ -2120,8 +2120,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pressly/goose/v3 v3.24.0 h1:sFbNms7Bd++2VMq6HSgDHDLWa7kHz1qXzPb3ZIU72VU= -github.com/pressly/goose/v3 v3.24.0/go.mod h1:rEWreU9uVtt0DHCyLzF9gRcWiiTF/V+528DV+4DORug= +github.com/pressly/goose/v3 v3.24.1 h1:bZmxRco2uy5uu5Ng1MMVEfYsFlrMJI+e/VMXHQ3C4LY= +github.com/pressly/goose/v3 v3.24.1/go.mod h1:rEWreU9uVtt0DHCyLzF9gRcWiiTF/V+528DV+4DORug= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -2138,8 +2138,8 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -2306,14 +2306,15 @@ github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155 github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= @@ -3293,8 +3294,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go. google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= @@ -3467,23 +3468,21 @@ lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= -modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= +modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= -modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= +modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= +modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= @@ -3492,30 +3491,31 @@ modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= -modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= +modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8= -modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk= +modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw= +modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/go.work.sum b/go.work.sum index 5ef60cf78bc..cfee1ab5daa 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,9 +1,12 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.4-20250130201111-63bb56e20495.1 h1:4erM3WLgEG/HIBrpBDmRbs1puhd7p0z7kNXDuhHthwM= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.4-20250130201111-63bb56e20495.1/go.mod h1:novQBstnxcGpfKf8qGRATqn1anQKwMJIbH5Q581jibU= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= @@ -125,6 +128,7 @@ cloud.google.com/go/compute v1.28.1/go.mod h1:b72iXMY4FucVry3NR3Li4kVyyTvbMDE7x5 cloud.google.com/go/compute v1.31.1 h1:SObuy8Fs6woazArpXp1fsHCw+ZH4iJ/8dGGTxUhHZQA= cloud.google.com/go/compute v1.31.1/go.mod h1:hyOponWhXviDptJCJSoEh89XO1cfv616wbwbkde1/+8= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/contactcenterinsights v1.15.0 h1:jHwyL2TQTaLauRRz5Uv7/sL7PNAK1VAMy/UIT9vsFzk= cloud.google.com/go/contactcenterinsights v1.15.0/go.mod h1:6bJGBQrJsnATv2s6Dh/c6HCRanq2kCZ0kIIjRV1G0mI= @@ -539,6 +543,7 @@ github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26 github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY= github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= @@ -627,6 +632,8 @@ github.com/brianvoe/gofakeit/v6 v6.25.0 h1:ZpFjktOpLZUeF8q223o0rUuXtA+m5qW5srjvV github.com/brianvoe/gofakeit/v6 v6.25.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bufbuild/protovalidate-go v0.2.1 h1:pJr07sYhliyfj/STAM7hU4J3FKpVeLVKvOBmOTN8j+s= github.com/bufbuild/protovalidate-go v0.2.1/go.mod h1:e7XXDtlxj5vlEyAgsrxpzayp4cEMKCSSb8ZCkin+MVA= +github.com/bufbuild/protovalidate-go v0.9.1 h1:cdrIA33994yCcJyEIZRL36ZGTe9UDM/WHs5MBHEimiE= +github.com/bufbuild/protovalidate-go v0.9.1/go.mod h1:5jptBxfvlY51RhX32zR6875JfPBRXUsQjyZjm/NqkLQ= github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= @@ -681,6 +688,7 @@ github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiG github.com/couchbase/moss v0.2.0 h1:VCYrMzFwEryyhRSeI+/b3tRBSeTpi/8gn5Kf6dxqn+o= github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= @@ -722,6 +730,7 @@ github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b h1:Yqiad0+sloMPdd github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= @@ -750,13 +759,16 @@ github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1 github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b h1:ZHiD4/yE4idlbqvAO6iYCOYRzOMRpxkW+FKasRA3tsQ= github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M= +github.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM= github.com/elastic/go-sysinfo v1.11.2 h1:mcm4OSYVMyws6+n2HIVMGkln5HOpo5Ie1ZmbbNn0jg4= github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJa7AfT8HpBFQ= +github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= github.com/elazarl/goproxy v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -844,6 +856,8 @@ github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDz github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td8= github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= @@ -898,7 +912,9 @@ github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hamba/avro/v2 v2.27.0 h1:IAM4lQ0VzUIKBuo4qlAiLKfqALSrFC+zi1iseTtbBKU= @@ -934,6 +950,7 @@ github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvP github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 h1:9Xm8CKtMZIXgcopfdWk/qZ1rt0HjMgfMR9nxxSeK6vk= github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1/go.mod h1:zuHl3Hh+e9P6gmBPvcqR1HjkaWHC/csgyskg6IaFKFo= github.com/jaegertracing/jaeger v1.57.0 h1:3wDtUUPs6NRYH7+d+y8MilDkLHdpPrVlQ2wbcsA62bs= @@ -942,6 +959,7 @@ github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq github.com/jedib0t/go-pretty/v6 v6.2.4/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0= github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs= github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg= github.com/jhump/goprotoc v0.5.0 h1:Y1UgUX+txUznfqcGdDef8ZOVlyQvnV0pKWZH08RmZuo= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= @@ -978,6 +996,7 @@ github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3ro github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= @@ -1054,6 +1073,8 @@ github.com/ncw/swift/v2 v2.0.2/go.mod h1:z0A9RVdYPjNjXVo2pDOPxZ4eu3oarO1P91fTItc github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= +github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.102.0 h1:R70PpK14trQfL/Vj5oAiGRqX09s2gOWuf6t1Ae5fevQ= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.102.0/go.mod h1:xmy/yFFmB1Epy+czrYMbA+4xeOKvhFqNqYWU6qINeis= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/zipkinexporter v0.102.0 h1:N3vWsp3xealy4AX8TovfHG5EKi/k7z+F/8LFP4SVAgo= @@ -1094,6 +1115,7 @@ github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceive github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver v0.102.0/go.mod h1:fvjAM+jOQdiXCmAENKH/eWxBBqTaImbq3lpoBI4X5Ek= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/openfga/api/proto v0.0.0-20240905181937-3583905f61a6/go.mod h1:gil5LBD8tSdFQbUkCQdnXsoeU9kDJdJgbGdHkgJfcd0= github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w= github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= @@ -1102,11 +1124,13 @@ github.com/parquet-go/parquet-go v0.23.0 h1:dyEU5oiHCtbASyItMCD2tXtT2nPmoPbKpqf0 github.com/parquet-go/parquet-go v0.23.0/go.mod h1:MnwbUcFHU6uBYMymKAlPPAw9yh3kE1wWl6Gl1uLdkNk= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30 h1:BHT1/DKsYDGkUgQ2jmMaozVcdk+sVfz0+1ZJq4zkWgw= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= @@ -1123,6 +1147,7 @@ github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJL github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= +github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/statsd_exporter v0.26.0 h1:SQl3M6suC6NWQYEzOvIv+EF6dAMYEqIuZy+o4H9F5Ig= github.com/prometheus/statsd_exporter v0.26.0/go.mod h1:GXFLADOmBTVDrHc7b04nX8ooq3azG61pnECNqT7O5DM= github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo= @@ -1158,6 +1183,8 @@ github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -1210,7 +1237,9 @@ github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= github.com/xanzy/go-gitlab v0.15.0 h1:rWtwKTgEnXyNUGrOArN7yyc3THRkpYcKXIXia9abywQ= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= @@ -1241,6 +1270,7 @@ go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw= go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg= go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/collector v0.102.1 h1:M/ciCcReQsSDYG9bJ2Qwqk7pQILDJ2bM/l0MdeCAvJE= go.opentelemetry.io/collector v0.102.1/go.mod h1:yF1lDRgL/Eksb4/LUnkMjvLvHHpi6wqBVlzp+dACnPM= @@ -1328,6 +1358,7 @@ go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4d go.opentelemetry.io/contrib/propagators/jaeger v1.33.0/go.mod h1:ku/EpGk44S5lyVMbtJRK2KFOnXEehxf6SDnhu1eZmjA= go.opentelemetry.io/contrib/samplers/jaegerremote v0.27.0/go.mod h1:IohbtCIY5Erb6wKnDddXOMNlG7GwyZnkrgcqjPmhpaA= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= @@ -1342,6 +1373,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLm go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0 h1:0MH3f8lZrflbUWXVxyBg/zviDFdGE062uKh5+fu8Vv0= @@ -1363,6 +1395,7 @@ go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJC go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= @@ -1383,17 +1416,22 @@ golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1410,11 +1448,13 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1436,8 +1476,12 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= @@ -1463,9 +1507,11 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go. google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6/go.mod h1:10yRODfgim2/T8csjQsMPgZOMvtytXKTDRzH6HRGzRw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88= @@ -1473,6 +1519,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go. google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb/go.mod h1:E5//3O5ZIG2l71Xnt+P/CYUY8Bxs8E7WMoZ9tlcMbAY= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250102185135-69823020774d h1:NZBSeFsuFS5YrgHMW/8xfTbzNXMshQPNgq2Yb7xipEs= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250102185135-69823020774d/go.mod h1:s4mHJ3FfG8P6A3O+gZ8TVqB3ufjOl9UG3ANCMMwCHmo= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250127172529-29210b9bc287 h1:c/HGC2hBfwgjeBtQMLjfmuS2KG28ngtUpn5XiX8o3rY= @@ -1481,9 +1528,11 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= @@ -1493,15 +1542,20 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= @@ -1517,8 +1571,11 @@ gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= k8s.io/code-generator v0.32.1 h1:4lw1kFNDuFYXquTkB7Sl5EwPMUP2yyW9hh6BnFfRZFY= @@ -1532,14 +1589,23 @@ k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= +modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= +modernc.org/ccgo/v3 v3.17.0 h1:o3OmOqx4/OFnl4Vm3G8Bgmqxnvxnh0nbxeT5p/dWChA= +modernc.org/ccgo/v3 v3.17.0/go.mod h1:Sg3fwVpmLvCUTaqEUjiBDAvshIaKDB0RXaf+zgqFu8I= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= +sigs.k8s.io/controller-runtime v0.20.2 h1:/439OZVxoEc02psi1h4QO3bHzTgu49bb347Xp4gW1pc= +sigs.k8s.io/controller-runtime v0.20.2/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index fe5a29c5024..4d707fc4210 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -23,7 +23,7 @@ require ( ) require ( - cel.dev/expr v0.19.0 // indirect + cel.dev/expr v0.19.1 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect @@ -37,7 +37,7 @@ require ( github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/elazarl/goproxy v1.7.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect @@ -56,7 +56,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.22.1 // indirect + github.com/google/cel-go v0.23.2 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.0 // indirect @@ -69,9 +69,9 @@ require ( github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -96,19 +96,21 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect github.com/oklog/run v1.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect @@ -147,7 +149,7 @@ require ( golang.org/x/tools v0.29.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index acf0f589b72..c3f73e18187 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= @@ -40,9 +40,9 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -110,8 +110,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= -github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -149,14 +149,14 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDa github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= @@ -240,10 +240,10 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= @@ -256,8 +256,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -289,10 +289,10 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -484,13 +484,10 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index cf9304efb51..a01ad0639a7 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -33,6 +33,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/sdk v1.34.0 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index da0e0ee3648..66a916ee5f9 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -60,8 +60,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -149,7 +149,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 0902897c1a9..de4e560a09e 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -9,7 +9,7 @@ require ( github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 github.com/grafana/grafana-app-sdk/logging v0.30.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 - github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/client_golang v1.21.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 go.opentelemetry.io/otel v1.34.0 @@ -49,7 +49,7 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -59,13 +59,15 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/onsi/gomega v1.36.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/etcd/api/v3 v3.5.16 // indirect @@ -90,7 +92,7 @@ require ( golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.29.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 336976f8472..79ec4b5887c 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -17,7 +17,7 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -91,8 +91,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a534 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= @@ -125,10 +125,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -137,8 +137,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -158,10 +158,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -300,8 +300,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index 6eeec2f5377..325e87190ea 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -11,7 +11,7 @@ require ( cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources - github.com/docker/docker v27.4.1+incompatible // @grafana/grafana-developer-enablement-squad + github.com/docker/docker v27.5.1+incompatible // @grafana/grafana-developer-enablement-squad github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend github.com/google/go-cmp v0.7.0 // @grafana/grafana-backend-group @@ -48,7 +48,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect github.com/buildkite/yaml v2.1.0+incompatible // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -74,7 +74,7 @@ require ( go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/sys v0.30.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect; @grafana/grafana-backend-group - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) @@ -82,7 +82,7 @@ require ( require dagger.io/dagger v0.11.8-rc.2 require ( - cel.dev/expr v0.19.0 // indirect + cel.dev/expr v0.19.1 // indirect cloud.google.com/go/monitoring v1.23.0 // indirect github.com/99designs/gqlgen v0.17.44 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -97,8 +97,8 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/term v0.5.0 // indirect diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 8d07672f075..b97f2a7189b 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY= cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= @@ -76,8 +76,9 @@ github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -114,8 +115,8 @@ github.com/envoyproxy/go-control-plane/envoy v1.32.3/go.mod h1:F6hWupPfh75TBXGKA github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -166,8 +167,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKG github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -365,8 +366,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 29499e52ad2..9195b58d961 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -8,7 +8,7 @@ require ( github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 github.com/grafana/grafana-plugin-sdk-go v0.267.0 github.com/json-iterator/go v1.1.12 - github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/client_golang v1.21.0 github.com/prometheus/common v0.62.0 github.com/prometheus/prometheus v0.301.0 github.com/stretchr/testify v1.10.0 @@ -30,7 +30,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/elazarl/goproxy v1.7.1 // indirect @@ -56,8 +56,8 @@ require ( github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -88,6 +88,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect github.com/unknwon/com v1.0.1 // indirect @@ -118,7 +119,7 @@ require ( golang.org/x/tools v0.29.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/api v0.220.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 7d0e120b3f8..5df6d3bfefb 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -2,9 +2,6 @@ cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0= cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= -cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/compute v1.23.4 h1:EBT9Nw4q3zyE7G45Wvv3MzolIrCJEuHys5muLY0wvAw= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= @@ -49,8 +46,9 @@ github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wX github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU= github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -110,7 +108,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= -github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= @@ -134,10 +131,10 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= @@ -231,8 +228,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= @@ -258,8 +255,8 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -392,13 +389,10 @@ gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= google.golang.org/api v0.220.0 h1:3oMI4gdBgB72WFVwE1nerDD8W3HUOS4kypK6rRLbGns= google.golang.org/api v0.220.0/go.mod h1:26ZAlY6aN/8WgpCzjPNy18QpYaz7Zgg1h0qe1GkZEmY= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= -google.golang.org/api v0.220.0 h1:3oMI4gdBgB72WFVwE1nerDD8W3HUOS4kypK6rRLbGns= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index cccba3e9b51..e695a670216 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -30,7 +30,7 @@ require ( ) require ( - cel.dev/expr v0.19.0 // indirect + cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect cloud.google.com/go/auth v0.14.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect @@ -128,7 +128,7 @@ require ( github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -145,7 +145,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect @@ -171,7 +171,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/go-sql-driver/mysql v1.9.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.4 // indirect @@ -188,7 +188,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.22.1 // indirect + github.com/google/cel-go v0.23.2 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.0 // indirect @@ -217,9 +217,9 @@ require ( github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -297,9 +297,9 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/oklog/ulid/v2 v2.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5 // indirect - github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c // indirect - github.com/openfga/openfga v1.8.4 // indirect + github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369 // indirect + github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20250121233318-0eae96a39570 // indirect + github.com/openfga/openfga v1.8.6 // indirect github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect @@ -310,9 +310,9 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/pressly/goose/v3 v3.24.0 // indirect + github.com/pressly/goose/v3 v3.24.1 // indirect github.com/prometheus/alertmanager v0.27.0 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect @@ -334,8 +334,8 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -391,7 +391,7 @@ require ( gonum.org/v1/gonum v0.15.1 // indirect google.golang.org/api v0.220.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect @@ -410,13 +410,10 @@ require ( k8s.io/kube-aggregator v0.32.0 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.55.3 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect - modernc.org/sqlite v1.34.4 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect + modernc.org/libc v1.61.13 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.2 // indirect + modernc.org/sqlite v1.35.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 3d896faaa26..927fde6c697 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -889,9 +889,9 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= @@ -922,8 +922,8 @@ github.com/dlmiddlecote/sqlstats v1.0.2/go.mod h1:0CWaIh/Th+z2aI6Q9Jpfg/o21zmGxW github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4= -github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= +github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= @@ -975,8 +975,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -1061,8 +1061,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= +github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= @@ -1149,8 +1149,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= -github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -1289,16 +1289,16 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDa github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= @@ -1557,23 +1557,23 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5 h1:z9jaRoo+NIN1AB0ogjtrjx1316TTuq6IbqpEg3UJycA= -github.com/openfga/api/proto v0.0.0-20250107154247-c22e6db5c4f5/go.mod h1:m74TNgnAAIJ03gfHcx+xaRWnr+IbQy3y/AVNwwCFrC0= -github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c h1:1y84C0V4NRfPtRi4MqQ7+gnFtYgeBKPIeIAPLdVJ7j4= -github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20241115164311-10e575c8e47c/go.mod h1:12RMe/HuRNyOzS33RQa53jwdcxE2znr8ycXMlVbgQN4= -github.com/openfga/openfga v1.8.4 h1:OqyRpuxMCxcS7irTFYFkhAIYzmAnczNwxUqjnuZOQyo= -github.com/openfga/openfga v1.8.4/go.mod h1:9Ax9VMMySV2JMsCT8MTePeYt4OrTnPAy1XUV1y9RyuU= +github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369 h1:wEsCZ4oBuu8LfEJ3VXbveXO8uEhCthrxA40WSvxO044= +github.com/openfga/api/proto v0.0.0-20250127102726-f9709139a369/go.mod h1:m74TNgnAAIJ03gfHcx+xaRWnr+IbQy3y/AVNwwCFrC0= +github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20250121233318-0eae96a39570 h1:fvc/m49myT+YTVsktQ7nUFep0N6836nFBqBI2/k+8W8= +github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20250121233318-0eae96a39570/go.mod h1:xW/ZQnpRIbs9AdeCPhMXt1veWV/VOuQHz1Qubn5YYxU= +github.com/openfga/openfga v1.8.6 h1:QGYAk4GSZZYoNTwKbC9bjd/7zPWW5/KpmgQfDLP/M1E= +github.com/openfga/openfga v1.8.6/go.mod h1:VSqaE/XwWRUvgC4t/NFlqfL5noxmDURjuQex3d+1hLU= github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1611,8 +1611,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1 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/pressly/goose/v3 v3.24.0 h1:sFbNms7Bd++2VMq6HSgDHDLWa7kHz1qXzPb3ZIU72VU= -github.com/pressly/goose/v3 v3.24.0/go.mod h1:rEWreU9uVtt0DHCyLzF9gRcWiiTF/V+528DV+4DORug= +github.com/pressly/goose/v3 v3.24.1 h1:bZmxRco2uy5uu5Ng1MMVEfYsFlrMJI+e/VMXHQ3C4LY= +github.com/pressly/goose/v3 v3.24.1/go.mod h1:rEWreU9uVtt0DHCyLzF9gRcWiiTF/V+528DV+4DORug= github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I= github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -1623,8 +1623,8 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1723,10 +1723,10 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= @@ -2548,8 +2548,8 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -2692,23 +2692,21 @@ lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= -modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= +modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= -modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= +modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= +modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= @@ -2717,30 +2715,31 @@ modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= -modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= +modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8= -modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk= +modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw= +modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index a115ecf8c80..3cc30478e1f 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -18,10 +18,10 @@ require ( github.com/grafana/grafana-plugin-sdk-go v0.267.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250220154326-6e5de80ef295 github.com/grafana/grafana/pkg/apiserver v0.0.0-20250220154326-6e5de80ef295 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/client_golang v1.21.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/otel v1.34.0 go.opentelemetry.io/otel/trace v1.34.0 @@ -33,7 +33,7 @@ require ( ) require ( - cel.dev/expr v0.19.0 // indirect + cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect cloud.google.com/go/auth v0.14.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect @@ -89,12 +89,12 @@ require ( github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/elazarl/goproxy v1.7.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/envoyproxy/go-control-plane v0.13.1 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect @@ -107,7 +107,7 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/go-sql-driver/mysql v1.9.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.4 // indirect @@ -202,8 +202,8 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tjhop/slog-gokit v0.1.3 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect @@ -244,7 +244,7 @@ require ( golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/api v0.220.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 3dd216db29c..5967f1f16d7 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -820,9 +820,9 @@ github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= @@ -890,8 +890,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= @@ -968,8 +968,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= +github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= @@ -1172,13 +1172,13 @@ github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7f github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= @@ -1422,8 +1422,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1493,10 +1493,10 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -2261,8 +2261,8 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= diff --git a/pkg/util/xorm/go.mod b/pkg/util/xorm/go.mod index 1d642bc0a4a..dbe7c972121 100644 --- a/pkg/util/xorm/go.mod +++ b/pkg/util/xorm/go.mod @@ -13,7 +13,7 @@ require ( ) require ( - cel.dev/expr v0.19.0 // indirect + cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect cloud.google.com/go/auth v0.14.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect @@ -28,11 +28,11 @@ require ( github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect - github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/go-sql-driver/mysql v1.9.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect @@ -60,7 +60,7 @@ require ( golang.org/x/time v0.9.0 // indirect google.golang.org/api v0.220.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect diff --git a/pkg/util/xorm/go.sum b/pkg/util/xorm/go.sum index 9836c572b87..33c37dbdf63 100644 --- a/pkg/util/xorm/go.sum +++ b/pkg/util/xorm/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= -cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -685,15 +685,12 @@ github.com/envoyproxy/go-control-plane/envoy v1.32.3 h1:hVEaommgvzTjTd4xCaFd+kEQ github.com/envoyproxy/go-control-plane/envoy v1.32.3/go.mod h1:F6hWupPfh75TBXGKA++MCT/CZHFq5r9/uwt/kQYkZfE= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= -github.com/envoyproxy/go-control-plane/envoy v1.32.3 h1:hVEaommgvzTjTd4xCaFd+kEQ2iYBtGxP6luyLrx6uOk= -github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -717,8 +714,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= +github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -784,7 +781,6 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -945,8 +941,6 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.5 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= @@ -970,7 +964,6 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1088,7 +1081,6 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1120,7 +1112,6 @@ golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1138,7 +1129,6 @@ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1219,7 +1209,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -1245,7 +1234,6 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1533,8 +1521,8 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= -google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o= +google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= From b5be6cd4d659b3640038e5ae09121bcdbb9415a7 Mon Sep 17 00:00:00 2001 From: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:18:41 +0000 Subject: [PATCH 017/312] Dashboard: Redesign row edit panes (#101510) * add row item changes * redesign row edit panes and header actions * clean up after merges * adjust to sentence casing * bring back layout selection extra options --- .../edit-pane/DashboardEditableElement.tsx | 10 +- .../MultiSelectedVizPanelsEditableElement.tsx | 2 +- .../scene/layout-rows/RowItem.tsx | 44 +++++++-- .../scene/layout-rows/RowItemEditor.tsx | 86 ++++++++--------- .../scene/layout-rows/RowItemMenu.tsx | 93 +++++++++++++++++++ .../scene/layout-rows/RowItemRenderer.tsx | 30 +++--- .../scene/layout-rows/RowItems.tsx | 9 +- .../scene/layout-rows/RowItemsEditor.tsx | 36 +++---- .../scene/layout-rows/RowsLayoutManager.tsx | 76 ++++++++++++++- public/locales/en-US/grafana.json | 24 +++-- public/locales/pseudo-LOCALE/grafana.json | 24 +++-- 11 files changed, 308 insertions(+), 126 deletions(-) create mode 100644 public/app/features/dashboard-scene/scene/layout-rows/RowItemMenu.tsx diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx index ae3085bc055..e324b09075c 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx @@ -25,7 +25,7 @@ export class DashboardEditableElement implements EditableDashboardElement { const { body } = dashboard.useState(); const dashboardOptions = useMemo(() => { - return new OptionsPaneCategoryDescriptor({ + const editPaneHeaderOptions = new OptionsPaneCategoryDescriptor({ title: t('dashboard.options.title', 'Dashboard options'), id: 'dashboard-options', isOpenable: false, @@ -48,6 +48,14 @@ export class DashboardEditableElement implements EditableDashboardElement { render: () => , }) ); + + if (body.getOptions) { + for (const option of body.getOptions()) { + editPaneHeaderOptions.addItem(option); + } + } + + return editPaneHeaderOptions; }, [body, dashboard]); return [dashboardOptions]; diff --git a/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx index 3e6a7fa98ae..a14b10f485b 100644 --- a/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/MultiSelectedVizPanelsEditableElement.tsx @@ -29,7 +29,7 @@ export class MultiSelectedVizPanelsEditableElement implements MultiSelectedEdita isOpenable: false, renderTitle: () => ( this.onDelete()} diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx index e92ed0f5466..71099c6fa02 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItem.tsx @@ -1,16 +1,15 @@ -import { ReactNode } from 'react'; - import { SceneObjectState, SceneObjectBase, sceneGraph, VariableDependencyConfig, SceneObject } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; +import { getDefaultVizPanel } from '../../utils/utils'; import { ResponsiveGridLayoutManager } from '../layout-responsive-grid/ResponsiveGridLayoutManager'; import { BulkActionElement } from '../types/BulkActionElement'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { EditableDashboardElement, EditableDashboardElementInfo } from '../types/EditableDashboardElement'; import { LayoutParent } from '../types/LayoutParent'; -import { getEditOptions, renderActions } from './RowItemEditor'; +import { getEditOptions } from './RowItemEditor'; import { RowItemRenderer } from './RowItemRenderer'; import { RowItemRepeaterBehavior } from './RowItemRepeaterBehavior'; import { RowItems } from './RowItems'; @@ -60,19 +59,42 @@ export class RowItem return getEditOptions(this); } - public renderActions(): ReactNode { - return renderActions(this); - } - public onDelete() { - const layout = sceneGraph.getAncestor(this, RowsLayoutManager); - layout.removeRow(this); + this._getParentLayout().removeRow(this); } public createMultiSelectedElement(items: SceneObject[]): RowItems { return new RowItems(items.filter((item) => item instanceof RowItem)); } + public onAddPanel(panel = getDefaultVizPanel()) { + this.getLayout().addPanel(panel); + } + + public onAddRowAbove() { + this._getParentLayout().addRowAbove(this); + } + + public onAddRowBelow() { + this._getParentLayout().addRowBelow(this); + } + + public onMoveUp() { + this._getParentLayout().moveRowUp(this); + } + + public onMoveDown() { + this._getParentLayout().moveRowDown(this); + } + + public isFirstRow(): boolean { + return this._getParentLayout().isFirstRow(this); + } + + public isLastRow(): boolean { + return this._getParentLayout().isLastRow(this); + } + public getRepeatVariable(): string | undefined { return this._getRepeatBehavior()?.state.variableName; } @@ -110,6 +132,10 @@ export class RowItem this.setState({ isCollapsed: !this.state.isCollapsed }); } + private _getParentLayout(): RowsLayoutManager { + return sceneGraph.getAncestor(this, RowsLayoutManager); + } + private _getRepeatBehavior(): RowItemRepeaterBehavior | undefined { return this.state.$behaviors?.find((b) => b instanceof RowItemRepeaterBehavior); } diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx index e45f9fa99ba..3ff3181129d 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx @@ -1,8 +1,7 @@ import { useMemo } from 'react'; -import { SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import { Alert, Button, Input, RadioButtonGroup, Switch, TextLink } from '@grafana/ui'; +import { Alert, Input, Switch, TextLink } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; @@ -10,18 +9,25 @@ import { RepeatRowSelect2 } from 'app/features/dashboard/components/RepeatRowSel import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/constants'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; +import { EditPaneHeader } from '../../edit-pane/EditPaneHeader'; import { getDashboardSceneFor, getQueryRunnerFor } from '../../utils/utils'; import { DashboardScene } from '../DashboardScene'; -import { useLayoutCategory } from '../layouts-shared/DashboardLayoutSelector'; +import { DashboardLayoutSelector } from '../layouts-shared/DashboardLayoutSelector'; import { RowItem } from './RowItem'; export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] { + const { layout } = model.useState(); const rowOptions = useMemo(() => { - return new OptionsPaneCategoryDescriptor({ - title: t('dashboard.rows-layout.row-options.title', 'Row options'), + const dashboard = getDashboardSceneFor(model); + + const editPaneHeaderOptions = new OptionsPaneCategoryDescriptor({ + title: t('dashboard.rows-layout.row-options.title', 'Row'), id: 'row-options', - isOpenDefault: true, + isOpenable: false, + renderTitle: () => ( + model.onDelete()} /> + ), }) .addItem( new OptionsPaneItemDescriptor({ @@ -31,8 +37,22 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] ) .addItem( new OptionsPaneItemDescriptor({ - title: t('dashboard.rows-layout.row-options.height.title', 'Height'), - render: () => , + title: t('dashboard.layout.common.layout', 'Layout'), + render: () => , + }) + ); + + if (layout.getOptions) { + for (const option of layout.getOptions()) { + editPaneHeaderOptions.addItem(option); + } + } + + editPaneHeaderOptions + .addItem( + new OptionsPaneItemDescriptor({ + title: t('dashboard.rows-layout.row-options.repeat.variable.title', 'Repeat for'), + render: () => , }) ) .addItem( @@ -41,42 +61,23 @@ export function getEditOptions(model: RowItem): OptionsPaneCategoryDescriptor[] render: () => , }) ); - }, [model]); - const rowRepeatOptions = useMemo(() => { - const dashboard = getDashboardSceneFor(model); + return editPaneHeaderOptions; + }, [layout, model]); - return new OptionsPaneCategoryDescriptor({ - title: t('dashboard.rows-layout.row-options.repeat.title', 'Repeat options'), - id: 'row-repeat-options', - isOpenDefault: true, - }).addItem( - new OptionsPaneItemDescriptor({ - title: t('dashboard.rows-layout.row-options.repeat.variable.title', 'Variable'), - render: () => , - }) - ); - }, [model]); - - const { layout } = model.useState(); - const layoutOptions = useLayoutCategory(layout); - - return [rowOptions, rowRepeatOptions, layoutOptions]; -} - -export function renderActions(model: RowItem) { - return ( - <> - - {!isClone && isEditing && ( - + ); + + profileLinkButtons = ( + <> + {profileLinkButton} + {profileDrilldownLinkButton} + + ); + } + } + } + + return { profileLinkButtons, logLinkButton, sessionLinkButton }; +}; + +export const getProfileLinkButtonsContext = ( + span: TraceSpan, + traceToProfilesOptions: TraceToProfilesOptions | undefined, + timeRange: TimeRange +) => { + const spanSelector = span.tags.filter((tag) => tag.key === pyroscopeProfileIdTagKey); + const context: ProfilesButtonContext = { + serviceName: span.process.serviceName ?? '', + profileTypeId: traceToProfilesOptions?.profileTypeId ?? '', + spanSelector: spanSelector.length === 1 && spanSelector[0].value ? spanSelector[0].value : '', + explorationType: 'flame-graph', + timeRange: { + from: timeRange.from.toISOString(), + to: timeRange.to.toISOString(), + }, + datasource: { uid: traceToProfilesOptions?.datasourceUid }, + }; + return context; +}; + +const createLinkButton = ( + link: SpanLinkDef, + type: SpanLinkType, + title: string, + icon: IconName, + datasourceType: string, + className?: string +) => { + return ( + { + // DataLinkButton assumes if you provide an onClick event you would want to prevent default behavior like navigation + // In this case, if an onClick is not defined, restore navigation to the provided href while keeping the tracking + // this interaction will not be tracked with link right clicks + reportInteraction('grafana_traces_trace_view_span_link_clicked', { + datasourceType, + grafana_version: config.buildInfo.version, + type, + location: 'spanDetails', + }); + + if (link.onClick) { + link.onClick?.(event); + } else { + locationService.push(link.href); + } + }, + }} + buttonProps={{ icon, className }} + /> + ); +}; diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx index d9b3c14f52c..1ad71c04cbd 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx @@ -19,7 +19,7 @@ import userEvent from '@testing-library/user-event'; import { createDataFrame, DataSourceInstanceSettings } from '@grafana/data'; import { data } from '@grafana/flamegraph'; -import { DataSourceSrv, setDataSourceSrv } from '@grafana/runtime'; +import { DataSourceSrv, setDataSourceSrv, setPluginLinksHook } from '@grafana/runtime'; import { pyroscopeProfileIdTagKey } from '../../../createSpanLink'; import traceGenerator from '../../demo/trace-generators'; @@ -70,6 +70,12 @@ describe('', () => { createFocusSpanLink: jest.fn().mockReturnValue({}), traceFlameGraphs: { [span.spanID]: createDataFrame(data) }, setRedrawListView: jest.fn(), + timeRange: { + raw: { + from: 0, + to: 1000000000000, + }, + }, }; span.tags = [ @@ -156,6 +162,11 @@ describe('', () => { props.logsToggle.mockReset(); props.logItemToggle.mockReset(); + setPluginLinksHook(() => ({ + isLoading: false, + links: [], + })); + setDataSourceSrv({ getList() { return [pyroSettings]; diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx index 9d6c42fe92c..7a6ec6dbc17 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx @@ -15,29 +15,26 @@ import { css } from '@emotion/css'; import { SpanStatusCode } from '@opentelemetry/api'; import cx from 'classnames'; -import * as React from 'react'; import { + CoreApp, DataFrame, dateTimeFormat, GrafanaTheme2, - IconName, LinkModel, + TimeRange, TraceKeyValuePair, TraceLog, } from '@grafana/data'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; -import { config, locationService, reportInteraction } from '@grafana/runtime'; import { TimeZone } from '@grafana/schema'; -import { DataLinkButton, Divider, Icon, TextArea, useStyles2 } from '@grafana/ui'; -import { RelatedProfilesTitle } from '@grafana-plugins/tempo/resultTransformer'; +import { Divider, Icon, TextArea, useStyles2 } from '@grafana/ui'; import { pyroscopeProfileIdTagKey } from '../../../createSpanLink'; import { autoColor } from '../../Theme'; import LabeledList from '../../common/LabeledList'; import { KIND, LIBRARY_NAME, LIBRARY_VERSION, STATUS, STATUS_MESSAGE, TRACE_STATE } from '../../constants/span'; import { SpanLinkFunc, TNil } from '../../types'; -import { SpanLinkDef, SpanLinkType } from '../../types/links'; import { TraceLink, TraceSpan, TraceSpanReference } from '../../types/trace'; import { formatDuration } from '../utils'; @@ -46,6 +43,7 @@ import AccordianLogs from './AccordianLogs'; import AccordianReferences from './AccordianReferences'; import AccordianText from './AccordianText'; import DetailState from './DetailState'; +import { getSpanDetailLinkButtons } from './SpanDetailLinkButtons'; import SpanFlameGraph from './SpanFlameGraph'; const getStyles = (theme: GrafanaTheme2) => { @@ -168,6 +166,8 @@ export type SpanDetailProps = { traceFlameGraphs: TraceFlameGraphs; setTraceFlameGraphs: (flameGraphs: TraceFlameGraphs) => void; setRedrawListView: (redraw: {}) => void; + timeRange: TimeRange; + app: CoreApp; }; export default function SpanDetail(props: SpanDetailProps) { @@ -193,6 +193,8 @@ export default function SpanDetail(props: SpanDetailProps) { setTraceFlameGraphs, traceToProfilesOptions, setRedrawListView, + timeRange, + app, } = props; const { isTagsOpen, @@ -289,62 +291,14 @@ export default function SpanDetail(props: SpanDetailProps) { }); } - const createLinkButton = (link: SpanLinkDef, type: SpanLinkType, title: string, icon: IconName) => { - return ( - { - // DataLinkButton assumes if you provide an onClick event you would want to prevent default behavior like navigation - // In this case, if an onClick is not defined, restore navigation to the provided href while keeping the tracking - // this interaction will not be tracked with link right clicks - reportInteraction('grafana_traces_trace_view_span_link_clicked', { - datasourceType: datasourceType, - grafana_version: config.buildInfo.version, - type, - location: 'spanDetails', - }); - - if (link.onClick) { - link.onClick?.(event); - } else { - locationService.push(link.href); - } - }, - }} - buttonProps={{ icon }} - /> - ); - }; - - let logLinkButton: JSX.Element | null = null; - let profileLinkButton: JSX.Element | null = null; - let sessionLinkButton: JSX.Element | null = null; - if (createSpanLink) { - const links = createSpanLink(span); - const logsLink = links?.filter((link) => link.type === SpanLinkType.Logs); - if (links && logsLink && logsLink.length > 0) { - logLinkButton = createLinkButton(logsLink[0], SpanLinkType.Logs, 'Logs for this span', 'gf-logs'); - } - const profilesLink = links?.filter( - (link) => link.type === SpanLinkType.Profiles && link.title === RelatedProfilesTitle - ); - if (links && profilesLink && profilesLink.length > 0) { - profileLinkButton = createLinkButton(profilesLink[0], SpanLinkType.Profiles, 'Profiles for this span', 'link'); - } - const sessionLink = links?.filter((link) => link.type === SpanLinkType.Session); - if (links && sessionLink && sessionLink.length > 0) { - sessionLinkButton = createLinkButton( - sessionLink[0], - SpanLinkType.Session, - 'Session for this span', - 'frontend-observability' - ); - } - } + const { profileLinkButtons, logLinkButton, sessionLinkButton } = getSpanDetailLinkButtons({ + span, + createSpanLink, + datasourceType, + traceToProfilesOptions, + timeRange, + app, + }); const focusSpanLink = createFocusSpanLink(traceID, spanID); return ( @@ -359,7 +313,7 @@ export default function SpanDetail(props: SpanDetailProps) {
{logLinkButton} - {profileLinkButton} + {profileLinkButtons} {sessionLinkButton}
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.test.tsx index 90223e8ed2b..6ac58bf1b1f 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.test.tsx @@ -16,6 +16,7 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createTheme } from '@grafana/data'; +import { setPluginLinksHook } from '@grafana/runtime'; import DetailState from './SpanDetail/DetailState'; import { UnthemedSpanDetailRow, SpanDetailRowProps } from './SpanDetailRow'; @@ -47,12 +48,25 @@ const setup = (propOverrides?: SpanDetailRowProps) => { traceStartTime: 1000, theme: createTheme(), traceFlameGraphs: {}, + timeRange: { + raw: { + from: 0, + to: 1000000000000, + }, + }, ...propOverrides, }; return render(); }; describe('SpanDetailRow tests', () => { + beforeEach(() => { + setPluginLinksHook(() => ({ + isLoading: false, + links: [], + })); + }); + it('renders without exploding', () => { expect(() => setup()).not.toThrow(); }); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx index 799e8cc739b..e8b8c3b4000 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetailRow.tsx @@ -16,7 +16,7 @@ import { css } from '@emotion/css'; import classNames from 'classnames'; import { PureComponent } from 'react'; -import { GrafanaTheme2, LinkModel, TraceKeyValuePair, TraceLog } from '@grafana/data'; +import { CoreApp, GrafanaTheme2, LinkModel, TimeRange, TraceKeyValuePair, TraceLog } from '@grafana/data'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { TimeZone } from '@grafana/schema'; import { Button, clearButtonStyles, stylesFactory, withTheme2 } from '@grafana/ui'; @@ -103,6 +103,8 @@ export type SpanDetailRowProps = { traceFlameGraphs: TraceFlameGraphs; setTraceFlameGraphs: (flameGraphs: TraceFlameGraphs) => void; setRedrawListView: (redraw: {}) => void; + timeRange: TimeRange; + app: CoreApp; }; export class UnthemedSpanDetailRow extends PureComponent { @@ -146,6 +148,8 @@ export class UnthemedSpanDetailRow extends PureComponent { traceFlameGraphs, setTraceFlameGraphs, setRedrawListView, + timeRange, + app, } = this.props; const styles = getStyles(theme); return ( @@ -193,6 +197,8 @@ export class UnthemedSpanDetailRow extends PureComponent { traceFlameGraphs={traceFlameGraphs} setTraceFlameGraphs={setTraceFlameGraphs} setRedrawListView={setRedrawListView} + timeRange={timeRange} + app={app} /> diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx index c769f585b77..d84d9e984c5 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -18,7 +18,7 @@ import memoizeOne from 'memoize-one'; import * as React from 'react'; import { RefObject } from 'react'; -import { GrafanaTheme2, LinkModel, TraceKeyValuePair, TraceLog } from '@grafana/data'; +import { CoreApp, GrafanaTheme2, LinkModel, TimeRange, TraceKeyValuePair, TraceLog } from '@grafana/data'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { config, reportInteraction } from '@grafana/runtime'; import { TimeZone } from '@grafana/schema'; @@ -109,6 +109,8 @@ type TVirtualizedTraceViewOwnProps = { setTraceFlameGraphs: (flameGraphs: TraceFlameGraphs) => void; redrawListView: {}; setRedrawListView: (redraw: {}) => void; + timeRange: TimeRange; + app: CoreApp; }; export type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TTraceTimeline; @@ -557,6 +559,8 @@ export class UnthemedVirtualizedTraceView extends React.Component ); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.tsx index 0857ad6f588..25885c1b0f7 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/index.tsx @@ -15,7 +15,7 @@ import { css } from '@emotion/css'; import { PureComponent, RefObject } from 'react'; -import { GrafanaTheme2, LinkModel, TraceKeyValuePair, TraceLog } from '@grafana/data'; +import { CoreApp, GrafanaTheme2, LinkModel, TimeRange, TraceKeyValuePair, TraceLog } from '@grafana/data'; import { SpanBarOptions, TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { config, reportInteraction } from '@grafana/runtime'; import { TimeZone } from '@grafana/schema'; @@ -112,6 +112,8 @@ export type TProps = { setTraceFlameGraphs: (flameGraphs: TraceFlameGraphs) => void; redrawListView: {}; setRedrawListView: (redraw: {}) => void; + timeRange: TimeRange; + app: CoreApp; }; type State = { diff --git a/public/app/features/explore/TraceView/components/types/links.ts b/public/app/features/explore/TraceView/components/types/links.ts index 00f8f74b6ba..2610e9511cf 100644 --- a/public/app/features/explore/TraceView/components/types/links.ts +++ b/public/app/features/explore/TraceView/components/types/links.ts @@ -9,6 +9,7 @@ export enum SpanLinkType { Traces = 'trace', Metrics = 'metric', Profiles = 'profile', + ProfilesDrilldown = 'profile-drilldown', Session = 'session', Unknown = 'unknown', } diff --git a/public/app/plugins/panel/traces/TracesPanel.tsx b/public/app/plugins/panel/traces/TracesPanel.tsx index b83e64af263..cf6fe0fae97 100644 --- a/public/app/plugins/panel/traces/TracesPanel.tsx +++ b/public/app/plugins/panel/traces/TracesPanel.tsx @@ -52,6 +52,7 @@ export const TracesPanel = ({ data, options, replaceVariables }: PanelProps ); From 3bdc9d1e19da047daf054da912f795cc5f5a272d Mon Sep 17 00:00:00 2001 From: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:32:58 -0600 Subject: [PATCH 026/312] Graphite: Compare query builder query to raw query (#101104) * compare queries to insure query isnt changed * comment * removed calls to getTemplateSrv because it makes things impossible to test. added a check for empty raw queries * prettier * Update public/app/plugins/datasource/graphite/graphite_query.ts Co-authored-by: Adam Yeats <16296989+adamyeats@users.noreply.github.com> --------- Co-authored-by: Adam Yeats <16296989+adamyeats@users.noreply.github.com> --- .../components/GraphiteQueryEditor.tsx | 1 + .../datasource/graphite/datasource.test.ts | 3 +++ .../plugins/datasource/graphite/datasource.ts | 2 +- .../datasource/graphite/graphite_query.ts | 24 ++++++++++++++++--- .../graphite/specs/graphite_query.test.ts | 14 +++++++++++ .../datasource/graphite/state/store.ts | 3 +-- 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/graphite/components/GraphiteQueryEditor.tsx b/public/app/plugins/datasource/graphite/components/GraphiteQueryEditor.tsx index 3d5413717a3..cfa65a29086 100644 --- a/public/app/plugins/datasource/graphite/components/GraphiteQueryEditor.tsx +++ b/public/app/plugins/datasource/graphite/components/GraphiteQueryEditor.tsx @@ -53,6 +53,7 @@ function GraphiteQueryEditorContent() { icon="pen" variant="secondary" aria-label="Toggle editor mode" + tooltip={state?.queryModel?.error} onClick={() => { dispatch(actions.toggleEditorMode()); }} diff --git a/public/app/plugins/datasource/graphite/datasource.test.ts b/public/app/plugins/datasource/graphite/datasource.test.ts index de993ff6102..548797d7342 100644 --- a/public/app/plugins/datasource/graphite/datasource.test.ts +++ b/public/app/plugins/datasource/graphite/datasource.test.ts @@ -744,6 +744,9 @@ describe('graphiteDatasource', () => { params: [{ multiple: true }], }, updateText: () => {}, + render: () => { + return ''; + }, })); }); diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 9998fe8980e..e8a6487a275 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -143,7 +143,7 @@ export class GraphiteDatasource target: query.target || '', textEditor: false, }, - getTemplateSrv() + this.templateSrv ); graphiteQuery.parseTarget(); diff --git a/public/app/plugins/datasource/graphite/graphite_query.ts b/public/app/plugins/datasource/graphite/graphite_query.ts index d3009ecb71f..5c23e83f695 100644 --- a/public/app/plugins/datasource/graphite/graphite_query.ts +++ b/public/app/plugins/datasource/graphite/graphite_query.ts @@ -77,6 +77,21 @@ export default class GraphiteQuery { try { this.parseTargetRecursive(astNode, null); + if (this.target.target) { + const oldQuery = this.target.target; + const newQuery = this.generateQueryString(); + + // Spaces, quotes, and commas are used when rendering the AST back into a string. + // We are removing these for less false positives of query changes. + const sanitizeQuery = (o: string): string => o.replace(/\s|'|"|,/g, ''); + const oldSanitized = sanitizeQuery(oldQuery); + const newSanitized = sanitizeQuery(newQuery); + if (oldSanitized && newSanitized && oldSanitized !== newSanitized) { + throw new Error( + `Failed to make a visual query builder query that is equivalent to the query.\nOriginal query: ${oldQuery}\nQuery builder query: ${newQuery}` + ); + } + } } catch (err) { if (err instanceof Error) { console.error('error parsing target:', err.message); @@ -181,16 +196,19 @@ export default class GraphiteQuery { arrayMove(this.functions, index, index + offset); } - updateModelTarget(targets: any) { + generateQueryString(): string { const wrapFunction = (target: string, func: FuncInstance) => { return func.render(target, (value: string) => { return this.templateSrv ? this.templateSrv.replace(value, this.scopedVars) : value; }); }; + const metricPath = this.getSegmentPathUpTo(this.segments.length).replace(/\.?select metric$/, ''); + return reduce(this.functions, wrapFunction, metricPath); + } + updateModelTarget(targets: any) { if (!this.target.textEditor) { - const metricPath = this.getSegmentPathUpTo(this.segments.length).replace(/\.?select metric$/, ''); - this.target.target = reduce(this.functions, wrapFunction, metricPath); + this.target.target = this.generateQueryString(); } this.updateRenderedTarget(this.target, targets); diff --git a/public/app/plugins/datasource/graphite/specs/graphite_query.test.ts b/public/app/plugins/datasource/graphite/specs/graphite_query.test.ts index 5d5035c5dd6..e6e048e45de 100644 --- a/public/app/plugins/datasource/graphite/specs/graphite_query.test.ts +++ b/public/app/plugins/datasource/graphite/specs/graphite_query.test.ts @@ -258,6 +258,20 @@ describe('Graphite query model', () => { ctx.queryModel.updateModelTarget(targets); expect(ctx.queryModel.target.target).toContain(nestedFunctionAsParam); }); + + //This is not preferred behavior. The query builder cannot parse `maxSeries(sum(testSeries1), sum(testSeries2))` and when it can, remove this test + it('should return an error when visual query builder query does not match raw query', () => { + jest.spyOn(console, 'error').mockImplementation(); + ctx.target = { + refId: 'A', + target: 'maxSeries(sum(testSeries1), sum(testSeries2))', + }; + ctx.targets = [ctx.target]; + ctx.queryModel = new GraphiteQuery(ctx.datasource, ctx.target, ctx.templateSrv); + expect(ctx.queryModel.error).toBe( + 'Failed to make a visual query builder query that is equivalent to the query.\nOriginal query: maxSeries(sum(testSeries1), sum(testSeries2))\nQuery builder query: maxSeries(sumSeries(sumSeries(testSeries1), testSeries2))' + ); + }); }); }); }); diff --git a/public/app/plugins/datasource/graphite/state/store.ts b/public/app/plugins/datasource/graphite/state/store.ts index 0081c2bd0ea..6df365d4eee 100644 --- a/public/app/plugins/datasource/graphite/state/store.ts +++ b/public/app/plugins/datasource/graphite/state/store.ts @@ -2,7 +2,6 @@ import { AnyAction } from '@reduxjs/toolkit'; import { Action, Dispatch } from 'redux'; import { DataQuery, TimeRange } from '@grafana/data'; -import { getTemplateSrv } from '@grafana/runtime'; import { TemplateSrv } from '../../../../features/templating/template_srv'; import { GraphiteDatasource } from '../datasource'; @@ -57,7 +56,7 @@ const reducer = async (action: Action, state: GraphiteQueryEditorState): Promise state = { ...state, ...deps, - queryModel: new GraphiteQuery(deps.datasource, deps.target, getTemplateSrv()), + queryModel: new GraphiteQuery(deps.datasource, deps.target, state.templateSrv), supportsTags: deps.datasource.supportsTags, paused: false, removeTagValue: '-- remove tag --', From 47f82a0c16e06b182693754bd85edcef3178f5f2 Mon Sep 17 00:00:00 2001 From: Sam Jewell <2903904+samjewell@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:52:07 +0000 Subject: [PATCH 027/312] SQL Expressions: Resizable code-editor (#101407) * Resizeable SQL expressions text-area Generated by an LLM for me - we'll see. It expands quite large on first page-load * Switch to useLayoutEffect to avoid visual flashing * Get the LLM to rewrite the approach, inspired by InfluxDB The Influx DB text-area is also resizable vertically, but that one isn't a Monaco editor (we need to tell Monaco to update its own size when the outer div is resized), so this is necessarily a little more complex than Influx. But still this approach looks simpler: The Javascript here is shorter * Start at 240px, to match the current default size Question: Is there a better approach to achieve this? * Don't clip the bottom border of the Monaco editor * Fix linting errors --- .../expressions/components/SqlExpr.tsx | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/public/app/features/expressions/components/SqlExpr.tsx b/public/app/features/expressions/components/SqlExpr.tsx index 5b8d6c0a424..93fad1699b7 100644 --- a/public/app/features/expressions/components/SqlExpr.tsx +++ b/public/app/features/expressions/components/SqlExpr.tsx @@ -1,10 +1,15 @@ -import { useMemo } from 'react'; +import { css } from '@emotion/css'; +import { useMemo, useRef, useEffect, useState } from 'react'; import { SelectableValue } from '@grafana/data'; import { SQLEditor } from '@grafana/plugin-ui'; +import { useStyles2 } from '@grafana/ui'; import { ExpressionQuery } from '../types'; +// Account for Monaco editor's border to prevent clipping +const EDITOR_BORDER_ADJUSTMENT = 2; // 1px border on top and bottom + interface Props { refIds: Array>; query: ExpressionQuery; @@ -13,8 +18,10 @@ interface Props { export const SqlExpr = ({ onChange, refIds, query }: Props) => { const vars = useMemo(() => refIds.map((v) => v.value!), [refIds]); - const initialQuery = `select * from ${vars[0]} limit 1`; + const styles = useStyles2(getStyles); + const containerRef = useRef(null); + const [dimensions, setDimensions] = useState({ height: 0 }); const onEditorChange = (expression: string) => { onChange({ @@ -23,5 +30,37 @@ export const SqlExpr = ({ onChange, refIds, query }: Props) => { }); }; - return ; + // Set up resize observer to handle container resizing + useEffect(() => { + if (!containerRef.current) { + return; + } + + const resizeObserver = new ResizeObserver((entries) => { + const { height } = entries[0].contentRect; + setDimensions({ height }); + }); + + resizeObserver.observe(containerRef.current); + return () => resizeObserver.disconnect(); + }, []); + + return ( +
+ +
+ ); }; + +const getStyles = () => ({ + editorContainer: css({ + height: '240px', + resize: 'vertical', + overflow: 'auto', + minHeight: '100px', + }), +}); From 962496c50d188e8d35467643cb504203d1e21bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 5 Mar 2025 17:01:53 +0100 Subject: [PATCH 028/312] DashboardLayouts: Remove unused functions (#101602) --- .../DefaultGridLayoutManager.tsx | 28 ------------- .../ResponsiveGridLayoutManager.tsx | 41 +------------------ .../scene/layout-rows/RowsLayoutManager.tsx | 10 ----- .../scene/layout-tabs/TabsLayoutManager.tsx | 10 ----- .../scene/types/DashboardLayoutManager.ts | 5 --- 5 files changed, 1 insertion(+), 93 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx index bad747f55de..22452f73c61 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx @@ -26,7 +26,6 @@ import { getGridItemKeyForPanelId, getDashboardSceneFor, } from '../../utils/utils'; -import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -180,22 +179,6 @@ export class DefaultGridLayoutManager return panels; } - public hasVizPanels(): boolean { - for (const child of this.state.grid.state.children) { - if (child instanceof DashboardGridItem) { - return true; - } else if (child instanceof SceneGridRow) { - for (const rowChild of child.state.children) { - if (rowChild instanceof DashboardGridItem) { - return true; - } - } - } - } - - return false; - } - public addNewRow(): SceneGridRow { const id = dashboardSceneGraph.getNextPanelId(this); @@ -224,17 +207,6 @@ export class DefaultGridLayoutManager return row; } - public addNewTab() { - const shouldAddTab = this.hasVizPanels(); - const tabsLayout = TabsLayoutManager.createFromLayout(this); - - if (shouldAddTab) { - tabsLayout.addNewTab(); - } - - getDashboardSceneFor(this).switchLayout(tabsLayout); - } - public editModeChanged(isEditing: boolean) { const updateResizeAndDragging = () => { this.state.grid.setState({ isDraggable: isEditing, isResizable: isEditing }); diff --git a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx index 8c3505038c2..0823498bccf 100644 --- a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx @@ -4,14 +4,7 @@ import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/Pan import { joinCloneKeys } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; -import { - getDashboardSceneFor, - getGridItemKeyForPanelId, - getPanelIdForVizPanel, - getVizPanelKeyForPanelId, -} from '../../utils/utils'; -import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager'; -import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; +import { getGridItemKeyForPanelId, getPanelIdForVizPanel, getVizPanelKeyForPanelId } from '../../utils/utils'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -111,16 +104,6 @@ export class ResponsiveGridLayoutManager return panels; } - public hasVizPanels(): boolean { - for (const child of this.state.layout.state.children) { - if (child instanceof ResponsiveGridItem) { - return true; - } - } - - return false; - } - public cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager { return this.clone({ layout: this.state.layout.clone({ @@ -143,28 +126,6 @@ export class ResponsiveGridLayoutManager }); } - public addNewRow() { - const shouldAddRow = this.hasVizPanels(); - const rowsLayout = RowsLayoutManager.createFromLayout(this); - - if (shouldAddRow) { - rowsLayout.addNewRow(); - } - - getDashboardSceneFor(this).switchLayout(rowsLayout); - } - - public addNewTab() { - const shouldAddTab = this.hasVizPanels(); - const tabsLayout = TabsLayoutManager.createFromLayout(this); - - if (shouldAddTab) { - tabsLayout.addNewTab(); - } - - getDashboardSceneFor(this).switchLayout(tabsLayout); - } - public getOptions(): OptionsPaneItemDescriptor[] { return getEditOptions(this); } diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx index a4959f810ec..5acf9063531 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx @@ -65,16 +65,6 @@ export class RowsLayoutManager extends SceneObjectBase i return panels; } - public hasVizPanels(): boolean { - for (const row of this.state.rows) { - if (row.getLayout().hasVizPanels()) { - return true; - } - } - - return false; - } - public cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager { throw new Error('Method not implemented.'); } diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx index c30491cc6fc..410fe06d781 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx @@ -85,16 +85,6 @@ export class TabsLayoutManager extends SceneObjectBase i throw new Error('Method not implemented.'); } - public hasVizPanels(): boolean { - for (const tab of this.state.tabs) { - if (tab.getLayout().hasVizPanels()) { - return true; - } - } - - return false; - } - public addNewTab() { const currentTab = new TabItem(); this.setState({ tabs: [...this.state.tabs, currentTab], currentTabIndex: this.state.tabs.length }); diff --git a/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts b/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts index 53862b2578d..190a6806467 100644 --- a/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts +++ b/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts @@ -39,11 +39,6 @@ export interface DashboardLayoutManager extends SceneObject { */ getVizPanels(): VizPanel[]; - /** - * Check if the layout has viz panels - */ - hasVizPanels(): boolean; - /** * Notify the layout manager that the edit mode has changed * @param isEditing From c1c9ea6964897d74ca62b1a5bd2e4fd7f6b9bb75 Mon Sep 17 00:00:00 2001 From: linoman <2051016+linoman@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:11:44 +0100 Subject: [PATCH 029/312] SCIM: Assign requester org to new provisioned users (#101548) Assign requester org to new provisioned users Co-authored-by: Mihai Doarna --- pkg/services/user/userimpl/user.go | 33 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/pkg/services/user/userimpl/user.go b/pkg/services/user/userimpl/user.go index 68e229d2140..55b3b809d23 100644 --- a/pkg/services/user/userimpl/user.go +++ b/pkg/services/user/userimpl/user.go @@ -10,6 +10,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/tracing" @@ -100,16 +101,28 @@ func (s *Service) Create(ctx context.Context, cmd *user.CreateUserCommand) (*use return nil, user.ErrEmptyUsernameAndEmail.Errorf("user cannot be created with empty username and email") } - cmdOrg := org.GetOrgIDForNewUserCommand{ - Email: cmd.Email, - Login: cmd.Login, - OrgID: cmd.OrgID, - OrgName: cmd.OrgName, - SkipOrgSetup: cmd.SkipOrgSetup, - } - orgID, err := s.orgService.GetIDForNewUser(ctx, cmdOrg) - if err != nil { - return nil, err + // if the user is provisioned, use the org ID from the requester + var orgID int64 + var err error + if cmd.IsProvisioned { + requester, err := identity.GetRequester(ctx) + if err != nil { + return nil, err + } + + orgID = requester.GetOrgID() + } else { + cmdOrg := org.GetOrgIDForNewUserCommand{ + Email: cmd.Email, + Login: cmd.Login, + OrgID: cmd.OrgID, + OrgName: cmd.OrgName, + SkipOrgSetup: cmd.SkipOrgSetup, + } + orgID, err = s.orgService.GetIDForNewUser(ctx, cmdOrg) + if err != nil { + return nil, err + } } if cmd.Email == "" { cmd.Email = cmd.Login From 627e8995c0bd5c6aa302c7fb37821ba80707b655 Mon Sep 17 00:00:00 2001 From: Eric Leijonmarck Date: Wed, 5 Mar 2025 17:04:06 +0000 Subject: [PATCH 030/312] LBAC for datasources: Adds feature availability (#101604) * adds feature availability * spellling * table format enough --- .../data-source-management/teamlbac/_index.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/sources/administration/data-source-management/teamlbac/_index.md b/docs/sources/administration/data-source-management/teamlbac/_index.md index 74470c792bf..e254a81cfca 100644 --- a/docs/sources/administration/data-source-management/teamlbac/_index.md +++ b/docs/sources/administration/data-source-management/teamlbac/_index.md @@ -20,8 +20,19 @@ Label-Based Access Control (LBAC) allows fine-grained access control to data sou ## Supported Data Sources +### Feature availability + LBAC for data sources is currently generally available for `Loki` and in **experimental** for `Prometheus`. Support for additional data sources may be added in future updates. +| Data source | [Grafana Cloud](/docs/grafana-cloud) | [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) | +| ----------- | ------------------------------------ | ----------------------------------------------------------------------------- | +| Loki | GA | GA | +| Prometheus | PrivatePreview | PrivatePreview | + +{{% admonition type="note" %}} +For enterprise this feature requires Grafana Enterprise Metrics (GEM) or Grafana Enterprise Logs (GEL) to function. +{{% /admonition %}} + **LBAC for data sources offers:** - Team-based access control using `LogQL` rules. From 5980c8efbb7d4c1af1c497741d5e48f2b4e67091 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 5 Mar 2025 20:08:20 +0300 Subject: [PATCH 031/312] Dashboards: Show provisioned badge from annotations (#101625) --- public/app/features/apiserver/types.ts | 26 ++++++++++------ .../dashboard-scene/scene/DashboardScene.tsx | 30 +++++++++++++++++++ .../scene/ManagedDashboardNavBarBadge.tsx | 23 ++++++++++++++ .../scene/NavToolbarActions.tsx | 16 ++++++++-- 4 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index 89f15d33a80..6df2e903ca0 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -43,11 +43,18 @@ export const AnnoKeyFolderUrl = 'grafana.app/folderUrl'; export const AnnoKeyMessage = 'grafana.app/message'; export const AnnoKeySlug = 'grafana.app/slug'; -// Identify where values came from -export const AnnoKeyRepoName = 'grafana.app/repoName'; -export const AnnoKeyRepoPath = 'grafana.app/repoPath'; -export const AnnoKeyRepoHash = 'grafana.app/repoHash'; -export const AnnoKeyRepoTimestamp = 'grafana.app/repoTimestamp'; +export enum ManagerKind { + Repo = 'repo', + Terraform = 'terraform', + Kubectl = 'kubectl', + Plugin = 'plugin', +} + +export const AnnoKeyManagerKind = 'grafana.app/managedBy'; +export const AnnoKeyManagerIdentity = 'grafana.app/managerId'; +export const AnnoKeySourcePath = 'grafana.app/sourcePath'; +export const AnnoKeySourceChecksum = 'grafana.app/sourceChecksum'; +export const AnnoKeySourceTimestamp = 'grafana.app/sourceTimestamp'; export const AnnoKeySavedFromUI = 'grafana.app/saved-from-ui'; export const AnnoKeyDashboardNotFound = 'grafana.app/dashboard-not-found'; @@ -66,10 +73,11 @@ type GrafanaAnnotations = { [AnnoKeyFolder]?: string; [AnnoKeySlug]?: string; - [AnnoKeyRepoName]?: string; - [AnnoKeyRepoPath]?: string; - [AnnoKeyRepoHash]?: string; - [AnnoKeyRepoTimestamp]?: string; + [AnnoKeyManagerKind]?: ManagerKind; + [AnnoKeyManagerIdentity]?: string; + [AnnoKeySourcePath]?: string; + [AnnoKeySourceChecksum]?: string; + [AnnoKeySourceTimestamp]?: string; }; // Annotations provided by the front-end client diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 9978a002fe7..81c0acacd8f 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -42,6 +42,7 @@ import { VariablesChanged } from 'app/features/variables/types'; import { DashboardDTO, DashboardMeta, KioskMode, SaveDashboardResponseDTO } from 'app/types'; import { ShowConfirmModalEvent } from 'app/types/events'; +import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, AnnoKeySourcePath, ManagerKind } from '../../apiserver/types'; import { DashboardEditPane } from '../edit-pane/DashboardEditPane'; import { PanelEditor } from '../panel-edit/PanelEditor'; import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker'; @@ -741,6 +742,35 @@ export class DashboardScene extends SceneObjectBase impleme getDashboardChanges(saveTimeRange?: boolean, saveVariables?: boolean, saveRefresh?: boolean): DashboardChangeInfo { return this._serializer.getDashboardChangesFromScene(this, { saveTimeRange, saveVariables, saveRefresh }); } + + getManagerKind(): ManagerKind | undefined { + return this.state.meta.k8s?.annotations?.[AnnoKeyManagerKind]; + } + + isManaged() { + return Boolean(this.getManagerKind()); + } + + isManagedRepository() { + return Boolean(this.getManagerKind() === ManagerKind.Repo); + } + + getPath() { + return this.state.meta.k8s?.annotations?.[AnnoKeySourcePath]; + } + + setManager(kind: ManagerKind, id: string) { + this.setState({ + meta: { + k8s: { + annotations: { + [AnnoKeyManagerKind]: kind, + [AnnoKeyManagerIdentity]: id, + }, + }, + }, + }); + } } export class DashboardVariableDependency implements SceneVariableDependencyConfigLike { diff --git a/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx b/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx new file mode 100644 index 00000000000..9594e1c1eb3 --- /dev/null +++ b/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx @@ -0,0 +1,23 @@ +import { Badge } from '@grafana/ui'; +import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, ManagerKind } from 'app/features/apiserver/types'; +import { DashboardMeta } from 'app/types'; + +export default function ManagedDashboardNavBarBadge({ meta }: { meta: DashboardMeta }) { + const obj = meta.k8s; + if (!obj?.annotations) { + return; + } + + let text = 'Provisioned'; + const kind = obj.annotations?.[AnnoKeyManagerKind]; + const id = obj.annotations?.[AnnoKeyManagerIdentity]; + switch (kind) { + case ManagerKind.Terraform: + text = 'Terraform'; + case ManagerKind.Kubectl: + text = 'Kubectl'; + case ManagerKind.Plugin: + text = `Plugin: ${id}`; + } + return ; +} diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index d5564aa2814..65eed8a797d 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -37,6 +37,7 @@ import { isLibraryPanel } from '../utils/utils'; import { DashboardScene } from './DashboardScene'; import { GoToSnapshotOriginButton } from './GoToSnapshotOriginButton'; +import ManagedDashboardNavBarBadge from './ManagedDashboardNavBarBadge'; interface Props { dashboard: DashboardScene; @@ -69,7 +70,7 @@ export function ToolbarActions({ dashboard }: Props) { const isViewingPanel = Boolean(viewPanelScene); const isEditedPanelDirty = usePanelEditDirty(editPanel); const isEditingLibraryPanel = editPanel && isLibraryPanel(editPanel.state.panelRef.resolve()); - const isNew = !Boolean(uid); + const isNew = !Boolean(uid || dashboard.isManaged()); const hasCopiedPanel = store.exists(LS_PANEL_COPY_KEY); // Means we are not in settings view, fullscreen panel or edit panel @@ -77,6 +78,7 @@ export function ToolbarActions({ dashboard }: Props) { const isEditingAndShowingDashboard = isEditing && isShowingDashboard; const showScopesSelector = config.featureToggles.scopeFilters && !isEditing; const dashboardNewLayouts = config.featureToggles.dashboardNewLayouts; + const isManaged = Boolean(dashboard.isManaged()); if (!isEditingPanel) { // This adds the presence indicators in enterprise @@ -125,6 +127,16 @@ export function ToolbarActions({ dashboard }: Props) { }); } + if (isManaged && meta.canEdit) { + toolbarActions.push({ + group: 'icon-actions', + condition: true, + render: () => { + return ; + }, + }); + } + const isDevEnv = config.buildInfo.env === 'development'; toolbarActions.push({ @@ -548,7 +560,7 @@ export function ToolbarActions({ dashboard }: Props) { } // If we only can save as copy - if (canSaveAs && !meta.canSave && !meta.canMakeEditable) { + if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isManaged) { return (
{expandable && isExpanded && ( diff --git a/public/app/features/dashboard-scene/edit-pane/EditPaneHeader.tsx b/public/app/features/dashboard-scene/edit-pane/EditPaneHeader.tsx index 653daf384f2..5f9828c834b 100644 --- a/public/app/features/dashboard-scene/edit-pane/EditPaneHeader.tsx +++ b/public/app/features/dashboard-scene/edit-pane/EditPaneHeader.tsx @@ -1,57 +1,86 @@ -import { Dropdown, Button, IconButton, Menu, Stack, Icon } from '@grafana/ui'; +import { css } from '@emotion/css'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { Button, Menu, Stack, Text, useStyles2, ConfirmButton, Dropdown, Icon } from '@grafana/ui'; import { t } from 'app/core/internationalization'; +import { EditableDashboardElement } from '../scene/types/EditableDashboardElement'; + interface EditPaneHeaderProps { - title: string; - onDelete?: () => void; - onCopy?: () => void; - onDuplicate?: () => void; + element: EditableDashboardElement; } -export const EditPaneHeader = ({ title, onDelete, onCopy, onDuplicate }: EditPaneHeaderProps) => { - const addCopyOrDuplicate = onCopy || onDuplicate; +export function EditPaneHeader({ element }: EditPaneHeaderProps) { + const elementInfo = element.getEditableElementInfo(); + const styles = useStyles2(getStyles); + + const onCopy = element.onCopy?.bind(element); + const onDuplicate = element.onDuplicate?.bind(element); + const onDelete = element.onDelete?.bind(element); + return ( - - {title} - - {addCopyOrDuplicate ? ( - }> +
+ {elementInfo.typeName} + + {(onCopy || onDelete) && ( + + {onCopy ? ( + + ) : null} + {onDuplicate ? ( + + ) : null} + + } + > - ) : null} + )} - + {onDelete && ( + +
); -}; +} -type MenuItemsProps = { - onCopy?: () => void; - onDuplicate?: () => void; -}; - -const MenuItems = ({ onCopy, onDuplicate }: MenuItemsProps) => { - return ( - - {onCopy ? : null} - {onDuplicate ? ( - - ) : null} - - ); -}; +function getStyles(theme: GrafanaTheme2) { + return { + wrapper: css({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: theme.spacing(2), + borderBottom: `1px solid ${theme.colors.border.weak}`, + }), + }; +} diff --git a/public/app/features/dashboard-scene/edit-pane/ElementEditPane.tsx b/public/app/features/dashboard-scene/edit-pane/ElementEditPane.tsx index 01b9a3f2398..f60b4bd7e26 100644 --- a/public/app/features/dashboard-scene/edit-pane/ElementEditPane.tsx +++ b/public/app/features/dashboard-scene/edit-pane/ElementEditPane.tsx @@ -1,50 +1,20 @@ -import { css } from '@emotion/css'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Stack, useStyles2 } from '@grafana/ui'; -import { OptionsPaneCategory } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategory'; +import { Stack } from '@grafana/ui'; import { EditableDashboardElement } from '../scene/types/EditableDashboardElement'; -import { MultiSelectedEditableDashboardElement } from '../scene/types/MultiSelectedEditableDashboardElement'; + +import { EditPaneHeader } from './EditPaneHeader'; export interface Props { - element: EditableDashboardElement | MultiSelectedEditableDashboardElement; + element: EditableDashboardElement; } export function ElementEditPane({ element }: Props) { const categories = element.useEditPaneOptions ? element.useEditPaneOptions() : []; - const styles = useStyles2(getStyles); - const elementInfo = element.getEditableElementInfo(); return ( - {element.renderActions && ( - -
{element.renderActions()}
-
- )} + {categories.map((cat) => cat.render())}
); } - -function getStyles(theme: GrafanaTheme2) { - return { - noBorderTop: css({ - borderTop: 'none', - }), - actionsBox: css({ - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1), - paddingBottom: theme.spacing(1), - }), - }; -} diff --git a/public/app/features/dashboard-scene/edit-pane/ElementSelection.ts b/public/app/features/dashboard-scene/edit-pane/ElementSelection.ts index 2141781408d..0bae42a69f4 100644 --- a/public/app/features/dashboard-scene/edit-pane/ElementSelection.ts +++ b/public/app/features/dashboard-scene/edit-pane/ElementSelection.ts @@ -1,17 +1,14 @@ -import { SceneObject, SceneObjectRef, VizPanel } from '@grafana/scenes'; +import { SceneObject, SceneObjectRef } from '@grafana/scenes'; import { ElementSelectionContextItem } from '@grafana/ui'; import { isBulkActionElement } from '../scene/types/BulkActionElement'; -import { EditableDashboardElement, isEditableDashboardElement } from '../scene/types/EditableDashboardElement'; -import { MultiSelectedEditableDashboardElement } from '../scene/types/MultiSelectedEditableDashboardElement'; +import { EditableDashboardElement } from '../scene/types/EditableDashboardElement'; import { MultiSelectedObjectsEditableElement } from './MultiSelectedObjectsEditableElement'; -import { MultiSelectedVizPanelsEditableElement } from './MultiSelectedVizPanelsEditableElement'; -import { VizPanelEditableElement } from './VizPanelEditableElement'; import { getEditableElementFor } from './shared'; export class ElementSelection { - private selectedObjects?: Map>; + private selectedObjects: Map>; private sameType?: boolean; private _isMultiSelection: boolean; @@ -26,15 +23,15 @@ export class ElementSelection { } private checkSameType() { - const values = this.selectedObjects?.values(); - const firstType = values?.next().value?.resolve()?.constructor.name; + const values = this.selectedObjects.values(); + const firstType = values.next().value?.resolve().constructor.name; if (!firstType) { return false; } for (let obj of values ?? []) { - if (obj.resolve()?.constructor.name !== firstType) { + if (obj.resolve().constructor.name !== firstType) { return false; } } @@ -43,13 +40,13 @@ export class ElementSelection { } public hasValue(id: string) { - return this.selectedObjects?.has(id); + return this.selectedObjects.has(id); } public removeValue(id: string) { - this.selectedObjects?.delete(id); + this.selectedObjects.delete(id); - if (this.selectedObjects && this.selectedObjects.size < 2) { + if (this.selectedObjects.size < 2) { this.sameType = undefined; this._isMultiSelection = false; } @@ -95,11 +92,11 @@ export class ElementSelection { } public getSelectionEntries(): Array<[string, SceneObjectRef]> { - return Array.from(this.selectedObjects?.entries() ?? []); + return Array.from(this.selectedObjects.entries()); } public getFirstObject(): SceneObject | undefined { - return this.selectedObjects?.values().next().value?.resolve(); + return this.selectedObjects.values().next().value?.resolve(); } public get isMultiSelection(): boolean { @@ -107,51 +104,38 @@ export class ElementSelection { } private getSceneObjects(): SceneObject[] { - return Array.from(this.selectedObjects?.values() ?? []).map((obj) => obj.resolve()); + return Array.from(this.selectedObjects.values() ?? []).map((obj) => obj.resolve()); } - public createSelectionElement() { - if (this.isMultiSelection) { - return this.createMultiSelectedElement(); - } - - return this.createSingleSelectedElement(); - } - - private createSingleSelectedElement(): EditableDashboardElement | undefined { - const sceneObj = this.selectedObjects?.values().next().value?.resolve(); - return getEditableElementFor(sceneObj); - } - - private createMultiSelectedElement(): MultiSelectedEditableDashboardElement | undefined { - if (!this.isMultiSelection) { - return; - } - + public createSelectionElement(): EditableDashboardElement | undefined { const sceneObjects = this.getSceneObjects(); - if (this.sameType) { - const firstObj = this.selectedObjects?.values().next().value?.resolve(); + if (sceneObjects.length === 0) { + return undefined; + } - if (firstObj instanceof VizPanel) { - return new MultiSelectedVizPanelsEditableElement(sceneObjects.filter((obj) => obj instanceof VizPanel)); - } + const firstElement = getEditableElementFor(sceneObjects[0]); - if (isEditableDashboardElement(firstObj!)) { - return firstObj.createMultiSelectedElement?.(sceneObjects); - } + if (!firstElement) { + return undefined; + } + + if (sceneObjects.length === 1) { + return firstElement; + } + + if (this.sameType && firstElement.createMultiSelectedElement) { + const elements = sceneObjects.map((obj) => getEditableElementFor(obj)!); + return firstElement.createMultiSelectedElement(elements); } const bulkActionElements = []; for (const sceneObject of sceneObjects) { - if (sceneObject instanceof VizPanel) { - const editableElement = new VizPanelEditableElement(sceneObject); - bulkActionElements.push(editableElement); - } + const element = getEditableElementFor(sceneObject); - if (isBulkActionElement(sceneObject)) { - bulkActionElements.push(sceneObject); + if (element && isBulkActionElement(element)) { + bulkActionElements.push(element); } } diff --git a/public/app/features/dashboard-scene/edit-pane/MultiSelectedObjectsEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/MultiSelectedObjectsEditableElement.tsx index a55b5f0d8c9..25bcdd807c9 100644 --- a/public/app/features/dashboard-scene/edit-pane/MultiSelectedObjectsEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/MultiSelectedObjectsEditableElement.tsx @@ -1,42 +1,20 @@ -import { ReactNode } from 'react'; -import { v4 as uuidv4 } from 'uuid'; - -import { Stack, Text, Button } from '@grafana/ui'; -import { t, Trans } from 'app/core/internationalization'; +import { t } from 'app/core/internationalization'; +import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { BulkActionElement } from '../scene/types/BulkActionElement'; -import { EditableDashboardElementInfo } from '../scene/types/EditableDashboardElement'; -import { MultiSelectedEditableDashboardElement } from '../scene/types/MultiSelectedEditableDashboardElement'; +import { EditableDashboardElement, EditableDashboardElementInfo } from '../scene/types/EditableDashboardElement'; -export class MultiSelectedObjectsEditableElement implements MultiSelectedEditableDashboardElement { - public readonly isMultiSelectedEditableDashboardElement = true; - public readonly key: string; +export class MultiSelectedObjectsEditableElement implements EditableDashboardElement { + public readonly isEditableDashboardElement = true; - constructor(private _elements: BulkActionElement[]) { - this.key = uuidv4(); + constructor(private _elements: BulkActionElement[]) {} + + public useEditPaneOptions(): OptionsPaneCategoryDescriptor[] { + return []; } public getEditableElementInfo(): EditableDashboardElementInfo { - return { name: t('dashboard.edit-pane.elements.objects', 'Objects'), typeId: 'objects', icon: 'folder' }; - } - - public renderActions(): ReactNode { - return ( - - - - No. of objects selected: {{ length }} - - - - + + + + + )} + + ); +}; + +const getStyles = (theme: GrafanaTheme2, menuDockedAndOpen: boolean) => { + return { + container: css({ + display: 'flex', + flexDirection: 'row', + paddingLeft: menuDockedAndOpen ? theme.spacing(2) : 'unset', + }), + dashboards: css({ + color: theme.colors.text.secondary, + marginRight: theme.spacing(2), + + '&:hover': css({ + color: theme.colors.text.primary, + }), + }), + drawerContainer: css({ + display: 'flex', + flexDirection: 'column', + height: '100%', + }), + treeContainer: css({ + display: 'flex', + flexDirection: 'column', + maxHeight: '100%', + overflowY: 'hidden', + // Fix for top level search outline overflow due to scrollbars + paddingLeft: theme.spacing(0.5), + }), + buttonsContainer: css({ + display: 'flex', + gap: theme.spacing(1), + marginTop: theme.spacing(8), + }), + }; +}; diff --git a/public/app/features/scopes/selector/ScopesSelectorService.ts b/public/app/features/scopes/selector/ScopesSelectorService.ts new file mode 100644 index 00000000000..705145829d3 --- /dev/null +++ b/public/app/features/scopes/selector/ScopesSelectorService.ts @@ -0,0 +1,392 @@ +import { isEqual } from 'lodash'; +import { finalize, from } from 'rxjs'; + +import { Scope, ScopeNode } from '@grafana/data'; +import { config, getBackendSrv } from '@grafana/runtime'; + +import { ScopesService } from '../ScopesService'; +import { ScopesServiceBase } from '../ScopesServiceBase'; +import { ScopesDashboardsService } from '../dashboards/ScopesDashboardsService'; + +import { NodeReason, NodesMap, SelectedScope, TreeScope } from './types'; + +interface ScopesSelectorServiceState { + opened: boolean; + loadingNodeName: string | undefined; + nodes: NodesMap; + selectedScopes: SelectedScope[]; + treeScopes: TreeScope[]; +} + +export class ScopesSelectorService extends ScopesServiceBase { + static #instance: ScopesSelectorService | undefined = undefined; + + private _scopesCache = new Map>(); + + private constructor() { + super({ + opened: false, + loadingNodeName: undefined, + nodes: { + '': { + name: '', + reason: NodeReason.Result, + nodeType: 'container', + title: '', + expandable: true, + selectable: false, + expanded: true, + query: '', + nodes: {}, + }, + }, + selectedScopes: [], + treeScopes: [], + }); + } + + public static get instance(): ScopesSelectorService | undefined { + if (!ScopesSelectorService.#instance && config.featureToggles.scopeFilters) { + ScopesSelectorService.#instance = new ScopesSelectorService(); + } + + return ScopesSelectorService.#instance; + } + + public updateNode = async (path: string[], expanded: boolean, query: string) => { + this._fetchSub?.unsubscribe(); + + let nodes = { ...this.state.nodes }; + let currentLevel: NodesMap = nodes; + + for (let idx = 0; idx < path.length - 1; idx++) { + currentLevel = currentLevel[path[idx]].nodes; + } + + const loadingNodeName = path[path.length - 1]; + const currentNode = currentLevel[loadingNodeName]; + + const differentQuery = currentNode.query !== query; + + currentNode.expanded = expanded; + currentNode.query = query; + + if (expanded || differentQuery) { + this.updateState({ nodes, loadingNodeName }); + + this._fetchSub = from(this.fetchNodeApi(loadingNodeName, query)) + .pipe( + finalize(() => { + this.updateState({ loadingNodeName: undefined }); + }) + ) + .subscribe((childNodes) => { + const [selectedScopes, treeScopes] = this.getScopesAndTreeScopesWithPaths( + this.state.selectedScopes, + this.state.treeScopes, + path, + childNodes + ); + + const persistedNodes = treeScopes + .map(({ path }) => path[path.length - 1]) + .filter((nodeName) => nodeName in currentNode.nodes && !(nodeName in childNodes)) + .reduce((acc, nodeName) => { + acc[nodeName] = { + ...currentNode.nodes[nodeName], + reason: NodeReason.Persisted, + }; + + return acc; + }, {}); + + currentNode.nodes = { ...persistedNodes, ...childNodes }; + + this.updateState({ nodes, selectedScopes, treeScopes }); + + this._fetchSub?.unsubscribe(); + }); + } else { + this.updateState({ nodes, loadingNodeName: undefined }); + } + }; + + public toggleNodeSelect = (path: string[]) => { + let treeScopes = [...this.state.treeScopes]; + + let parentNode = this.state.nodes['']; + + for (let idx = 1; idx < path.length - 1; idx++) { + parentNode = parentNode.nodes[path[idx]]; + } + + const nodeName = path[path.length - 1]; + const { linkId } = parentNode.nodes[nodeName]; + + const selectedIdx = treeScopes.findIndex(({ scopeName }) => scopeName === linkId); + + if (selectedIdx === -1) { + this.fetchScopeApi(linkId!); + + const selectedFromSameNode = + treeScopes.length === 0 || + Object.values(parentNode.nodes).some(({ linkId }) => linkId === treeScopes[0].scopeName); + + const treeScope = { + scopeName: linkId!, + path, + }; + + this.updateState({ + treeScopes: parentNode?.disableMultiSelect || !selectedFromSameNode ? [treeScope] : [...treeScopes, treeScope], + }); + } else { + treeScopes.splice(selectedIdx, 1); + + this.updateState({ treeScopes }); + } + }; + + public changeScopes = (scopeNames: string[]) => + this.setNewScopes(scopeNames.map((scopeName) => ({ scopeName, path: [] }))); + + public setNewScopes = async (treeScopes = this.state.treeScopes) => { + if (isEqual(treeScopes, this.getTreeScopesFromSelectedScopes(this.state.selectedScopes))) { + return; + } + + let selectedScopes = treeScopes.map(({ scopeName, path }) => ({ + scope: this.getBasicScope(scopeName), + path, + })); + this.updateState({ selectedScopes, treeScopes }); + ScopesService.instance?.setLoading(true); + ScopesDashboardsService.instance?.fetchDashboards(selectedScopes.map(({ scope }) => scope.metadata.name)); + + selectedScopes = await this.fetchScopesApi(treeScopes); + this.updateState({ selectedScopes }); + ScopesService.instance?.setScopes(selectedScopes.map(({ scope }) => scope)); + ScopesService.instance?.setLoading(false); + }; + + public removeAllScopes = () => this.setNewScopes([]); + + public open = async () => { + if (!ScopesService.instance?.state.readOnly) { + if (Object.keys(this.state.nodes[''].nodes).length === 0) { + await this.updateNode([''], true, ''); + } + + let nodes = { ...this.state.nodes }; + + // First close all nodes + nodes = this.closeNodes(nodes); + + // Extract the path of a scope + let path = [...(this.state.selectedScopes[0]?.path ?? ['', ''])]; + path.splice(path.length - 1, 1); + + // Expand the nodes to the selected scope + nodes = this.expandNodes(nodes, path); + + this.updateState({ nodes, opened: true }); + } + }; + + public closeAndReset = () => { + this.updateState({ opened: false, treeScopes: this.getTreeScopesFromSelectedScopes(this.state.selectedScopes) }); + }; + + public closeAndApply = () => { + this.updateState({ opened: false }); + this.setNewScopes(); + }; + + public toggleDrawer = () => ScopesService.instance?.setDrawerOpened(!ScopesService.instance?.state.drawerOpened); + + private closeNodes = (nodes: NodesMap): NodesMap => { + return Object.entries(nodes).reduce((acc, [id, node]) => { + acc[id] = { + ...node, + expanded: false, + nodes: this.closeNodes(node.nodes), + }; + + return acc; + }, {}); + }; + + private expandNodes = (nodes: NodesMap, path: string[]): NodesMap => { + nodes = { ...nodes }; + let currentNodes = nodes; + + for (let i = 0; i < path.length; i++) { + const nodeId = path[i]; + + currentNodes[nodeId] = { + ...currentNodes[nodeId], + expanded: true, + }; + currentNodes = currentNodes[nodeId].nodes; + } + + return nodes; + }; + + private getBasicScope = (name: string): Scope => { + return { + metadata: { name }, + spec: { + filters: [], + title: name, + type: '', + category: '', + description: '', + }, + }; + }; + + private getTreeScopesFromSelectedScopes = (scopes: SelectedScope[]): TreeScope[] => { + return scopes.map(({ scope, path }) => ({ + scopeName: scope.metadata.name, + path, + })); + }; + + // helper func to get the selected/tree scopes together with their paths + // needed to maintain selected scopes in tree for example when navigating + // between categories or when loading scopes from URL to find the scope's path + private getScopesAndTreeScopesWithPaths = ( + selectedScopes: SelectedScope[], + treeScopes: TreeScope[], + path: string[], + childNodes: NodesMap + ): [SelectedScope[], TreeScope[]] => { + const childNodesArr = Object.values(childNodes); + + // Get all scopes without paths + // We use tree scopes as the list is always up to date as opposed to selected scopes which can be outdated + const scopeNamesWithoutPaths = treeScopes.filter(({ path }) => path.length === 0).map(({ scopeName }) => scopeName); + + // We search for the path of each scope name without a path + const scopeNamesWithPaths = scopeNamesWithoutPaths.reduce>((acc, scopeName) => { + const possibleParent = childNodesArr.find((childNode) => childNode.selectable && childNode.linkId === scopeName); + + if (possibleParent) { + acc[scopeName] = [...path, possibleParent.name]; + } + + return acc; + }, {}); + + // Update the paths of the selected scopes based on what we found + const newSelectedScopes = selectedScopes.map((selectedScope) => { + if (selectedScope.path.length > 0) { + return selectedScope; + } + + return { + ...selectedScope, + path: scopeNamesWithPaths[selectedScope.scope.metadata.name] ?? [], + }; + }); + + // Update the paths of the tree scopes based on what we found + const newTreeScopes = treeScopes.map((treeScope) => { + if (treeScope.path.length > 0) { + return treeScope; + } + + return { + ...treeScope, + path: scopeNamesWithPaths[treeScope.scopeName] ?? [], + }; + }); + + return [newSelectedScopes, newTreeScopes]; + }; + + public fetchNodeApi = async (parent: string, query: string): Promise => { + try { + const nodes = + ( + await getBackendSrv().get<{ items: ScopeNode[] }>( + `/apis/${this._apiGroup}/${this._apiVersion}/namespaces/${this._apiNamespace}/find/scope_node_children`, + { parent, query } + ) + )?.items ?? []; + + return nodes.reduce((acc, { metadata: { name }, spec }) => { + acc[name] = { + name, + ...spec, + expandable: spec.nodeType === 'container', + selectable: spec.linkType === 'scope', + expanded: false, + query: '', + reason: NodeReason.Result, + nodes: {}, + }; + return acc; + }, {}); + } catch (err) { + return {}; + } + }; + + public fetchScopeApi = async (name: string): Promise => { + if (this._scopesCache.has(name)) { + return this._scopesCache.get(name)!; + } + + const response = new Promise(async (resolve) => { + const basicScope = this.getBasicScope(name); + + try { + const serverScope = await getBackendSrv().get( + `/apis/${this._apiGroup}/${this._apiVersion}/namespaces/${this._apiNamespace}/scopes/${name}` + ); + + const scope = { + ...basicScope, + ...serverScope, + metadata: { + ...basicScope.metadata, + ...serverScope.metadata, + }, + spec: { + ...basicScope.spec, + ...serverScope.spec, + }, + }; + + resolve(scope); + } catch (err) { + this._scopesCache.delete(name); + + resolve(basicScope); + } + }); + + this._scopesCache.set(name, response); + + return response; + }; + + public fetchScopesApi = async (treeScopes: TreeScope[]): Promise => { + const scopes = await Promise.all(treeScopes.map(({ scopeName }) => this.fetchScopeApi(scopeName))); + + return scopes.reduce((acc, scope, idx) => { + acc.push({ + scope, + path: treeScopes[idx].path, + }); + + return acc; + }, []); + }; + + public reset = () => { + ScopesSelectorService.#instance = undefined; + }; +} diff --git a/public/app/features/scopes/internal/ScopesTree.tsx b/public/app/features/scopes/selector/ScopesTree.tsx similarity index 86% rename from public/app/features/scopes/internal/ScopesTree.tsx rename to public/app/features/scopes/selector/ScopesTree.tsx index d4116f6ba1e..ed37df63729 100644 --- a/public/app/features/scopes/internal/ScopesTree.tsx +++ b/public/app/features/scopes/selector/ScopesTree.tsx @@ -27,11 +27,11 @@ export function ScopesTree({ const nodeId = nodePath[nodePath.length - 1]; const node = nodes[nodeId]; const childNodes = Object.values(node.nodes); - const isNodeLoading = loadingNodeName === nodeId; + const nodeLoading = loadingNodeName === nodeId; const scopeNames = scopes.map(({ scopeName }) => scopeName); - const anyChildExpanded = childNodes.some(({ isExpanded }) => isExpanded); + const anyChildExpanded = childNodes.some(({ expanded }) => expanded); const groupedNodes: Dictionary = useMemo(() => groupBy(childNodes, 'reason'), [childNodes]); - const isLastExpandedNode = !anyChildExpanded && node.isExpanded; + const lastExpandedNode = !anyChildExpanded && node.expanded; return ( <> @@ -42,11 +42,11 @@ export function ScopesTree({ onNodeUpdate={onNodeUpdate} /> - + ; - isLastExpandedNode: boolean; + lastExpandedNode: boolean; loadingNodeName: string | undefined; node: Node; nodePath: string[]; @@ -26,7 +26,7 @@ export interface ScopesTreeItemProps { export function ScopesTreeItem({ anyChildExpanded, groupedNodes, - isLastExpandedNode, + lastExpandedNode, loadingNodeName, node, nodePath, @@ -48,9 +48,9 @@ export function ScopesTreeItem({ const children = (
{nodes.map((childNode) => { - const isSelected = childNode.isSelectable && scopeNames.includes(childNode.linkId!); + const selected = childNode.selectable && scopeNames.includes(childNode.linkId!); - if (anyChildExpanded && !childNode.isExpanded) { + if (anyChildExpanded && !childNode.expanded) { return null; } @@ -62,16 +62,16 @@ export function ScopesTreeItem({
-
- {childNode.isSelectable && !childNode.isExpanded ? ( +
+ {childNode.selectable && !childNode.expanded ? ( node.disableMultiSelect ? ( { @@ -80,7 +80,7 @@ export function ScopesTreeItem({ /> ) : ( { onNodeSelectToggle(childNodePath); @@ -89,18 +89,18 @@ export function ScopesTreeItem({ ) ) : null} - {childNode.isExpandable ? ( + {childNode.expandable ? ( @@ -110,7 +110,7 @@ export function ScopesTreeItem({
- {childNode.isExpanded && ( + {childNode.expanded && ( ); - if (isLastExpandedNode) { + if (lastExpandedNode) { return ( ; } diff --git a/public/app/features/scopes/internal/ScopesTreeSearch.tsx b/public/app/features/scopes/selector/ScopesTreeSearch.tsx similarity index 82% rename from public/app/features/scopes/internal/ScopesTreeSearch.tsx rename to public/app/features/scopes/selector/ScopesTreeSearch.tsx index 273d2fa24b0..f8bc5c071ec 100644 --- a/public/app/features/scopes/internal/ScopesTreeSearch.tsx +++ b/public/app/features/scopes/selector/ScopesTreeSearch.tsx @@ -18,22 +18,22 @@ export interface ScopesTreeSearchProps { export function ScopesTreeSearch({ anyChildExpanded, nodePath, query, onNodeUpdate }: ScopesTreeSearchProps) { const styles = useStyles2(getStyles); - const [inputState, setInputState] = useState<{ value: string; isDirty: boolean }>({ value: query, isDirty: false }); + const [inputState, setInputState] = useState<{ value: string; dirty: boolean }>({ value: query, dirty: false }); useEffect(() => { - if (!inputState.isDirty && inputState.value !== query) { - setInputState({ value: query, isDirty: false }); + if (!inputState.dirty && inputState.value !== query) { + setInputState({ value: query, dirty: false }); } }, [inputState, query]); useDebounce( () => { - if (inputState.isDirty) { + if (inputState.dirty) { onNodeUpdate(nodePath, true, inputState.value); } }, 500, - [inputState.isDirty, inputState.value] + [inputState.dirty, inputState.value] ); if (anyChildExpanded) { @@ -48,7 +48,7 @@ export function ScopesTreeSearch({ anyChildExpanded, nodePath, query, onNodeUpda data-testid="scopes-tree-search" escapeRegex={false} onChange={(value) => { - setInputState({ value, isDirty: true }); + setInputState({ value, dirty: true }); }} /> ); diff --git a/public/app/features/scopes/selector/types.ts b/public/app/features/scopes/selector/types.ts new file mode 100644 index 00000000000..672a6f158b4 --- /dev/null +++ b/public/app/features/scopes/selector/types.ts @@ -0,0 +1,31 @@ +import { Scope, ScopeNodeSpec } from '@grafana/data'; + +export enum NodeReason { + Persisted, + Result, +} + +export interface Node extends ScopeNodeSpec { + name: string; + reason: NodeReason; + expandable: boolean; + selectable: boolean; + expanded: boolean; + query: string; + nodes: NodesMap; +} + +export type NodesMap = Record; + +export interface SelectedScope { + scope: Scope; + path: string[]; +} + +export interface TreeScope { + scopeName: string; + path: string[]; +} + +export type OnNodeUpdate = (path: string[], expanded: boolean, query: string) => void; +export type OnNodeSelectToggle = (path: string[]) => void; diff --git a/public/app/features/scopes/tests/dashboardReload.test.ts b/public/app/features/scopes/tests/dashboardReload.test.ts index 24b07bca51a..7973842c33e 100644 --- a/public/app/features/scopes/tests/dashboardReload.test.ts +++ b/public/app/features/scopes/tests/dashboardReload.test.ts @@ -2,8 +2,7 @@ import { config } from '@grafana/runtime'; import { setDashboardAPI } from 'app/features/dashboard/api/dashboard_api'; import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager'; -import { clearMocks, enterEditMode, updateMyVar, updateScopes, updateTimeRange } from './utils/actions'; -import { expectDashboardReload, expectNotDashboardReload } from './utils/assertions'; +import { enterEditMode, updateMyVar, updateScopes, updateTimeRange } from './utils/actions'; import { getDatasource, getInstanceSettings, getMock } from './utils/mocks'; import { renderDashboard, resetScenes } from './utils/render'; @@ -17,6 +16,8 @@ jest.mock('@grafana/runtime', () => ({ })); describe('Dashboard reload', () => { + let dashboardReloadSpy: jest.SpyInstance; + beforeAll(() => { config.featureToggles.scopeFilters = true; config.featureToggles.groupByVariable = true; @@ -39,43 +40,45 @@ describe('Dashboard reload', () => { config.featureToggles.reloadDashboardsOnParamsChange = reloadDashboardsOnParamsChange; setDashboardAPI(undefined); - const dashboardScene = renderDashboard({ uid: withUid ? 'dash-1' : undefined }, { reloadOnParamsChange }); + const dashboardScene = await renderDashboard({ uid: withUid ? 'dash-1' : undefined }, { reloadOnParamsChange }); + + dashboardReloadSpy = jest.spyOn(getDashboardScenePageStateManager(), 'reloadDashboard'); if (editMode) { await enterEditMode(dashboardScene); } const shouldReload = reloadDashboardsOnParamsChange && reloadOnParamsChange && withUid && !editMode; + dashboardReloadSpy.mockClear(); await updateTimeRange(dashboardScene); await jest.advanceTimersToNextTimerAsync(); if (!shouldReload) { - expectNotDashboardReload(); + expect(dashboardReloadSpy).not.toHaveBeenCalled(); } else { - expectDashboardReload(); + expect(dashboardReloadSpy).toHaveBeenCalled(); } await updateMyVar(dashboardScene, '2'); await jest.advanceTimersToNextTimerAsync(); if (!shouldReload) { - expectNotDashboardReload(); + expect(dashboardReloadSpy).not.toHaveBeenCalled(); } else { - expectDashboardReload(); + expect(dashboardReloadSpy).toHaveBeenCalled(); } await updateScopes(['grafana']); await jest.advanceTimersToNextTimerAsync(); if (!shouldReload) { - expectNotDashboardReload(); + expect(dashboardReloadSpy).not.toHaveBeenCalled(); } else { - expectDashboardReload(); + expect(dashboardReloadSpy).toHaveBeenCalled(); } getDashboardScenePageStateManager().clearDashboardCache(); getDashboardScenePageStateManager().clearSceneCache(); setDashboardAPI(undefined); - await resetScenes(); - clearMocks(); + await resetScenes([dashboardReloadSpy]); } ); }); diff --git a/public/app/features/scopes/tests/dashboardsList.test.ts b/public/app/features/scopes/tests/dashboardsList.test.ts index 3b5f2fe1885..895337f69d2 100644 --- a/public/app/features/scopes/tests/dashboardsList.test.ts +++ b/public/app/features/scopes/tests/dashboardsList.test.ts @@ -1,5 +1,7 @@ import { config } from '@grafana/runtime'; +import { ScopesDashboardsService } from '../dashboards/ScopesDashboardsService'; + import { clearNotFound, expandDashboardFolder, @@ -21,7 +23,18 @@ import { expectNoDashboardsNoScopes, expectNoDashboardsSearch, } from './utils/assertions'; -import { fetchDashboardsSpy, getDatasource, getInstanceSettings, getMock } from './utils/mocks'; +import { + alternativeDashboardWithRootFolder, + alternativeDashboardWithTwoFolders, + dashboardWithOneFolder, + dashboardWithoutFolder, + dashboardWithRootFolder, + dashboardWithRootFolderAndOtherFolder, + dashboardWithTwoFolders, + getDatasource, + getInstanceSettings, + getMock, +} from './utils/mocks'; import { renderDashboard, resetScenes } from './utils/render'; jest.mock('@grafana/runtime', () => ({ @@ -34,17 +47,20 @@ jest.mock('@grafana/runtime', () => ({ })); describe('Dashboards list', () => { + let fetchDashboardsSpy: jest.SpyInstance; + beforeAll(() => { config.featureToggles.scopeFilters = true; config.featureToggles.groupByVariable = true; }); - beforeEach(() => { - renderDashboard(); + beforeEach(async () => { + await renderDashboard(); + fetchDashboardsSpy = jest.spyOn(ScopesDashboardsService.instance!, 'fetchDashboardsApi'); }); afterEach(async () => { - await resetScenes(); + await resetScenes([fetchDashboardsSpy]); }); it('Opens container and fetches dashboards list when a scope is selected', async () => { @@ -244,14 +260,13 @@ describe('Dashboards list', () => { }); it('Does not show the input when there are no dashboards found for scope', async () => { - await toggleDashboards(); await updateScopes(['cloud']); + await toggleDashboards(); expectNoDashboardsForScope(); expectNoDashboardsSearch(); }); it('Shows the input and a message when there are no dashboards found for filter', async () => { - await toggleDashboards(); await updateScopes(['mimir']); await searchDashboards('unknown'); expectDashboardsSearch(); @@ -260,4 +275,359 @@ describe('Dashboards list', () => { await clearNotFound(); expectDashboardSearchValue(''); }); + + describe('groupDashboards', () => { + it('Assigns dashboards without groups to root folder', () => { + expect(ScopesDashboardsService.instance?.groupDashboards([dashboardWithoutFolder])).toEqual({ + '': { + title: '', + expanded: true, + folders: {}, + dashboards: { + [dashboardWithoutFolder.spec.dashboard]: { + dashboard: dashboardWithoutFolder.spec.dashboard, + dashboardTitle: dashboardWithoutFolder.status.dashboardTitle, + items: [dashboardWithoutFolder], + }, + }, + }, + }); + }); + + it('Assigns dashboards with root group to root folder', () => { + expect(ScopesDashboardsService.instance?.groupDashboards([dashboardWithRootFolder])).toEqual({ + '': { + title: '', + expanded: true, + folders: {}, + dashboards: { + [dashboardWithRootFolder.spec.dashboard]: { + dashboard: dashboardWithRootFolder.spec.dashboard, + dashboardTitle: dashboardWithRootFolder.status.dashboardTitle, + items: [dashboardWithRootFolder], + }, + }, + }, + }); + }); + + it('Merges folders from multiple dashboards', () => { + expect( + ScopesDashboardsService.instance?.groupDashboards([dashboardWithOneFolder, dashboardWithTwoFolders]) + ).toEqual({ + '': { + title: '', + expanded: true, + folders: { + 'Folder 1': { + title: 'Folder 1', + expanded: false, + folders: {}, + dashboards: { + [dashboardWithOneFolder.spec.dashboard]: { + dashboard: dashboardWithOneFolder.spec.dashboard, + dashboardTitle: dashboardWithOneFolder.status.dashboardTitle, + items: [dashboardWithOneFolder], + }, + [dashboardWithTwoFolders.spec.dashboard]: { + dashboard: dashboardWithTwoFolders.spec.dashboard, + dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, + items: [dashboardWithTwoFolders], + }, + }, + }, + 'Folder 2': { + title: 'Folder 2', + expanded: false, + folders: {}, + dashboards: { + [dashboardWithTwoFolders.spec.dashboard]: { + dashboard: dashboardWithTwoFolders.spec.dashboard, + dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, + items: [dashboardWithTwoFolders], + }, + }, + }, + }, + dashboards: {}, + }, + }); + }); + + it('Merges scopes from multiple dashboards', () => { + expect( + ScopesDashboardsService.instance?.groupDashboards([dashboardWithTwoFolders, alternativeDashboardWithTwoFolders]) + ).toEqual({ + '': { + title: '', + expanded: true, + folders: { + 'Folder 1': { + title: 'Folder 1', + expanded: false, + folders: {}, + dashboards: { + [dashboardWithTwoFolders.spec.dashboard]: { + dashboard: dashboardWithTwoFolders.spec.dashboard, + dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, + items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], + }, + }, + }, + 'Folder 2': { + title: 'Folder 2', + expanded: false, + folders: {}, + dashboards: { + [dashboardWithTwoFolders.spec.dashboard]: { + dashboard: dashboardWithTwoFolders.spec.dashboard, + dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, + items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], + }, + }, + }, + }, + dashboards: {}, + }, + }); + }); + + it('Matches snapshot', () => { + expect( + ScopesDashboardsService.instance?.groupDashboards([ + dashboardWithoutFolder, + dashboardWithOneFolder, + dashboardWithTwoFolders, + alternativeDashboardWithTwoFolders, + dashboardWithRootFolder, + alternativeDashboardWithRootFolder, + dashboardWithRootFolderAndOtherFolder, + ]) + ).toEqual({ + '': { + dashboards: { + [dashboardWithRootFolderAndOtherFolder.spec.dashboard]: { + dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard, + dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle, + items: [dashboardWithRootFolderAndOtherFolder], + }, + [dashboardWithRootFolder.spec.dashboard]: { + dashboard: dashboardWithRootFolder.spec.dashboard, + dashboardTitle: dashboardWithRootFolder.status.dashboardTitle, + items: [dashboardWithRootFolder, alternativeDashboardWithRootFolder], + }, + [dashboardWithoutFolder.spec.dashboard]: { + dashboard: dashboardWithoutFolder.spec.dashboard, + dashboardTitle: dashboardWithoutFolder.status.dashboardTitle, + items: [dashboardWithoutFolder], + }, + }, + folders: { + 'Folder 1': { + dashboards: { + [dashboardWithOneFolder.spec.dashboard]: { + dashboard: dashboardWithOneFolder.spec.dashboard, + dashboardTitle: dashboardWithOneFolder.status.dashboardTitle, + items: [dashboardWithOneFolder], + }, + [dashboardWithTwoFolders.spec.dashboard]: { + dashboard: dashboardWithTwoFolders.spec.dashboard, + dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, + items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], + }, + }, + folders: {}, + expanded: false, + title: 'Folder 1', + }, + 'Folder 2': { + dashboards: { + [dashboardWithTwoFolders.spec.dashboard]: { + dashboard: dashboardWithTwoFolders.spec.dashboard, + dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, + items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], + }, + }, + folders: {}, + expanded: false, + title: 'Folder 2', + }, + 'Folder 3': { + dashboards: { + [dashboardWithRootFolderAndOtherFolder.spec.dashboard]: { + dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard, + dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle, + items: [dashboardWithRootFolderAndOtherFolder], + }, + }, + folders: {}, + expanded: false, + title: 'Folder 3', + }, + }, + expanded: true, + title: '', + }, + }); + }); + }); + + describe('filterFolders', () => { + it('Shows folders matching criteria', () => { + expect( + ScopesDashboardsService.instance?.filterFolders( + { + '': { + title: '', + expanded: true, + folders: { + 'Folder 1': { + title: 'Folder 1', + expanded: false, + folders: {}, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + 'Folder 2': { + title: 'Folder 2', + expanded: true, + folders: {}, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + }, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + }, + 'Folder' + ) + ).toEqual({ + '': { + title: '', + expanded: true, + folders: { + 'Folder 1': { + title: 'Folder 1', + expanded: true, + folders: {}, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + 'Folder 2': { + title: 'Folder 2', + expanded: true, + folders: {}, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + }, + dashboards: {}, + }, + }); + }); + + it('Shows dashboards matching criteria', () => { + expect( + ScopesDashboardsService.instance?.filterFolders( + { + '': { + title: '', + expanded: true, + folders: { + 'Folder 1': { + title: 'Folder 1', + expanded: false, + folders: {}, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + 'Folder 2': { + title: 'Folder 2', + expanded: true, + folders: {}, + dashboards: { + 'Random ID': { + dashboard: 'Random ID', + dashboardTitle: 'Random Title', + items: [], + }, + }, + }, + }, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + 'Random ID': { + dashboard: 'Random ID', + dashboardTitle: 'Random Title', + items: [], + }, + }, + }, + }, + 'dash' + ) + ).toEqual({ + '': { + title: '', + expanded: true, + folders: { + 'Folder 1': { + title: 'Folder 1', + expanded: true, + folders: {}, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + }, + dashboards: { + 'Dashboard ID': { + dashboard: 'Dashboard ID', + dashboardTitle: 'Dashboard Title', + items: [], + }, + }, + }, + }); + }); + }); }); diff --git a/public/app/features/scopes/tests/featureFlag.test.ts b/public/app/features/scopes/tests/featureFlag.test.ts deleted file mode 100644 index efe209a6f5b..00000000000 --- a/public/app/features/scopes/tests/featureFlag.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { config } from '@grafana/runtime'; - -import { scopesSelectorScene } from '../instance'; - -import { getDatasource, getInstanceSettings, getMock } from './utils/mocks'; -import { renderDashboard } from './utils/render'; - -jest.mock('@grafana/runtime', () => ({ - __esModule: true, - ...jest.requireActual('@grafana/runtime'), - useChromeHeaderHeight: jest.fn(), - getBackendSrv: () => ({ get: getMock }), - getDataSourceSrv: () => ({ get: getDatasource, getInstanceSettings }), - usePluginLinks: jest.fn().mockReturnValue({ links: [] }), -})); - -describe('Feature flag off', () => { - beforeAll(() => { - config.featureToggles.scopeFilters = false; - config.featureToggles.groupByVariable = true; - }); - - it('Does not initialize', () => { - renderDashboard(); - expect(scopesSelectorScene).toBeNull(); - }); -}); diff --git a/public/app/features/scopes/tests/selector.test.ts b/public/app/features/scopes/tests/selector.test.ts index 6435de62803..951c6f54b2e 100644 --- a/public/app/features/scopes/tests/selector.test.ts +++ b/public/app/features/scopes/tests/selector.test.ts @@ -1,13 +1,13 @@ import { config } from '@grafana/runtime'; -import { sceneGraph } from '@grafana/scenes'; -import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; -import { getClosestScopesFacade } from '../utils'; +import { getDashboardScenePageStateManager } from '../../dashboard-scene/pages/DashboardScenePageStateManager'; +import { ScopesSelectorService } from '../selector/ScopesSelectorService'; import { applyScopes, cancelScopes, openSelector, selectResultCloud, updateScopes } from './utils/actions'; -import { expectNotDashboardReload, expectScopesSelectorValue } from './utils/assertions'; -import { fetchSelectedScopesSpy, getDatasource, getInstanceSettings, getMock, mocksScopes } from './utils/mocks'; +import { expectScopesSelectorValue } from './utils/assertions'; +import { getDatasource, getInstanceSettings, getMock, mocksScopes } from './utils/mocks'; import { renderDashboard, resetScenes } from './utils/render'; +import { getListOfScopes } from './utils/selectors'; jest.mock('@grafana/runtime', () => ({ __esModule: true, @@ -19,19 +19,22 @@ jest.mock('@grafana/runtime', () => ({ })); describe('Selector', () => { - let dashboardScene: DashboardScene; + let fetchSelectedScopesSpy: jest.SpyInstance; + let dashboardReloadSpy: jest.SpyInstance; beforeAll(() => { config.featureToggles.scopeFilters = true; config.featureToggles.groupByVariable = true; }); - beforeEach(() => { - dashboardScene = renderDashboard(); + beforeEach(async () => { + await renderDashboard(); + fetchSelectedScopesSpy = jest.spyOn(ScopesSelectorService.instance!, 'fetchScopesApi'); + dashboardReloadSpy = jest.spyOn(getDashboardScenePageStateManager(), 'reloadDashboard'); }); afterEach(async () => { - await resetScenes(); + await resetScenes([fetchSelectedScopesSpy, dashboardReloadSpy]); }); it('Fetches scope details on save', async () => { @@ -39,9 +42,7 @@ describe('Selector', () => { await selectResultCloud(); await applyScopes(); expect(fetchSelectedScopesSpy).toHaveBeenCalled(); - expect(getClosestScopesFacade(dashboardScene)?.value).toEqual( - mocksScopes.filter(({ metadata: { name } }) => name === 'cloud') - ); + expect(getListOfScopes()).toEqual(mocksScopes.filter(({ metadata: { name } }) => name === 'cloud')); }); it('Does not save the scopes on close', async () => { @@ -49,7 +50,7 @@ describe('Selector', () => { await selectResultCloud(); await cancelScopes(); expect(fetchSelectedScopesSpy).not.toHaveBeenCalled(); - expect(getClosestScopesFacade(dashboardScene)?.value).toEqual([]); + expect(getListOfScopes()).toEqual([]); }); it('Shows selected scopes', async () => { @@ -59,25 +60,6 @@ describe('Selector', () => { it('Does not reload the dashboard on scope change', async () => { await updateScopes(['grafana']); - expectNotDashboardReload(); - }); - - it('Adds scopes to enrichers', async () => { - const queryRunner = sceneGraph.getQueryController(dashboardScene)!; - - await updateScopes(['grafana']); - let scopes = mocksScopes.filter(({ metadata: { name } }) => name === 'grafana'); - expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(scopes); - expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(scopes); - - await updateScopes(['grafana', 'mimir']); - scopes = mocksScopes.filter(({ metadata: { name } }) => name === 'grafana' || name === 'mimir'); - expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(scopes); - expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(scopes); - - await updateScopes(['mimir']); - scopes = mocksScopes.filter(({ metadata: { name } }) => name === 'mimir'); - expect(dashboardScene.enrichDataRequest(queryRunner).scopes).toEqual(scopes); - expect(dashboardScene.enrichFiltersRequest().scopes).toEqual(scopes); + expect(dashboardReloadSpy).not.toHaveBeenCalled(); }); }); diff --git a/public/app/features/scopes/tests/tree.test.ts b/public/app/features/scopes/tests/tree.test.ts index 983393ca204..186e6fe06f0 100644 --- a/public/app/features/scopes/tests/tree.test.ts +++ b/public/app/features/scopes/tests/tree.test.ts @@ -1,5 +1,7 @@ import { config } from '@grafana/runtime'; +import { ScopesSelectorService } from '../selector/ScopesSelectorService'; + import { applyScopes, clearScopesSearch, @@ -39,7 +41,7 @@ import { expectSelectedScopePath, expectTreeScopePath, } from './utils/assertions'; -import { fetchNodesSpy, fetchScopeSpy, getDatasource, getInstanceSettings, getMock } from './utils/mocks'; +import { getDatasource, getInstanceSettings, getMock } from './utils/mocks'; import { renderDashboard, resetScenes } from './utils/render'; jest.mock('@grafana/runtime', () => ({ @@ -52,17 +54,22 @@ jest.mock('@grafana/runtime', () => ({ })); describe('Tree', () => { + let fetchNodesSpy: jest.SpyInstance; + let fetchScopeSpy: jest.SpyInstance; + beforeAll(() => { config.featureToggles.scopeFilters = true; config.featureToggles.groupByVariable = true; }); - beforeEach(() => { - renderDashboard(); + beforeEach(async () => { + await renderDashboard(); + fetchNodesSpy = jest.spyOn(ScopesSelectorService.instance!, 'fetchNodeApi'); + fetchScopeSpy = jest.spyOn(ScopesSelectorService.instance!, 'fetchScopeApi'); }); afterEach(async () => { - await resetScenes(); + await resetScenes([fetchNodesSpy, fetchScopeSpy]); }); it('Fetches scope details on select', async () => { @@ -126,16 +133,16 @@ describe('Tree', () => { await openSelector(); await expandResultApplications(); await searchScopes('Cloud'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); expectResultApplicationsGrafanaNotPresent(); expectResultApplicationsMimirNotPresent(); expectResultApplicationsCloudPresent(); await clearScopesSearch(); - expect(fetchNodesSpy).toHaveBeenCalledTimes(3); + expect(fetchNodesSpy).toHaveBeenCalledTimes(4); await searchScopes('Grafana'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(4); + expect(fetchNodesSpy).toHaveBeenCalledTimes(5); expectResultApplicationsGrafanaPresent(); expectResultApplicationsCloudNotPresent(); }); @@ -156,7 +163,7 @@ describe('Tree', () => { await expandResultApplications(); await selectResultApplicationsMimir(); await searchScopes('grafana'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); expectPersistedApplicationsMimirPresent(); expectPersistedApplicationsGrafanaNotPresent(); expectResultApplicationsMimirNotPresent(); @@ -168,7 +175,7 @@ describe('Tree', () => { await expandResultApplications(); await selectResultApplicationsMimir(); await searchScopes('mimir'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); expectPersistedApplicationsMimirNotPresent(); expectResultApplicationsMimirPresent(); }); @@ -178,10 +185,10 @@ describe('Tree', () => { await expandResultApplications(); await selectResultApplicationsMimir(); await searchScopes('grafana'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); await clearScopesSearch(); - expect(fetchNodesSpy).toHaveBeenCalledTimes(3); + expect(fetchNodesSpy).toHaveBeenCalledTimes(4); expectPersistedApplicationsMimirNotPresent(); expectPersistedApplicationsGrafanaNotPresent(); expectResultApplicationsMimirPresent(); @@ -192,15 +199,15 @@ describe('Tree', () => { await openSelector(); await expandResultApplications(); await searchScopes('mimir'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); await selectResultApplicationsMimir(); await searchScopes('unknown'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(3); + expect(fetchNodesSpy).toHaveBeenCalledTimes(4); expectPersistedApplicationsMimirPresent(); await clearScopesSearch(); - expect(fetchNodesSpy).toHaveBeenCalledTimes(4); + expect(fetchNodesSpy).toHaveBeenCalledTimes(5); expectResultApplicationsMimirPresent(); expectResultApplicationsGrafanaPresent(); }); @@ -210,7 +217,7 @@ describe('Tree', () => { await expandResultApplications(); await selectResultApplicationsMimir(); await searchScopes('grafana'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); await selectResultApplicationsGrafana(); await applyScopes(); @@ -222,7 +229,7 @@ describe('Tree', () => { await expandResultApplications(); await selectResultApplicationsMimir(); await searchScopes('grafana'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); await selectResultApplicationsGrafana(); await applyScopes(); @@ -239,11 +246,11 @@ describe('Tree', () => { expectScopesHeadline('Recommended'); await searchScopes('Applications'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(1); + expect(fetchNodesSpy).toHaveBeenCalledTimes(2); expectScopesHeadline('Results'); await searchScopes('unknown'); - expect(fetchNodesSpy).toHaveBeenCalledTimes(2); + expect(fetchNodesSpy).toHaveBeenCalledTimes(3); expectScopesHeadline('No results found for your query'); }); diff --git a/public/app/features/scopes/tests/utils.test.ts b/public/app/features/scopes/tests/utils.test.ts deleted file mode 100644 index 7b37541fb05..00000000000 --- a/public/app/features/scopes/tests/utils.test.ts +++ /dev/null @@ -1,364 +0,0 @@ -import { filterFolders, groupDashboards } from '../internal/utils'; - -import { - alternativeDashboardWithRootFolder, - alternativeDashboardWithTwoFolders, - dashboardWithOneFolder, - dashboardWithoutFolder, - dashboardWithRootFolder, - dashboardWithRootFolderAndOtherFolder, - dashboardWithTwoFolders, -} from './utils/mocks'; - -describe('Utils', () => { - describe('groupDashboards', () => { - it('Assigns dashboards without groups to root folder', () => { - expect(groupDashboards([dashboardWithoutFolder])).toEqual({ - '': { - title: '', - isExpanded: true, - folders: {}, - dashboards: { - [dashboardWithoutFolder.spec.dashboard]: { - dashboard: dashboardWithoutFolder.spec.dashboard, - dashboardTitle: dashboardWithoutFolder.status.dashboardTitle, - items: [dashboardWithoutFolder], - }, - }, - }, - }); - }); - - it('Assigns dashboards with root group to root folder', () => { - expect(groupDashboards([dashboardWithRootFolder])).toEqual({ - '': { - title: '', - isExpanded: true, - folders: {}, - dashboards: { - [dashboardWithRootFolder.spec.dashboard]: { - dashboard: dashboardWithRootFolder.spec.dashboard, - dashboardTitle: dashboardWithRootFolder.status.dashboardTitle, - items: [dashboardWithRootFolder], - }, - }, - }, - }); - }); - - it('Merges folders from multiple dashboards', () => { - expect(groupDashboards([dashboardWithOneFolder, dashboardWithTwoFolders])).toEqual({ - '': { - title: '', - isExpanded: true, - folders: { - 'Folder 1': { - title: 'Folder 1', - isExpanded: false, - folders: {}, - dashboards: { - [dashboardWithOneFolder.spec.dashboard]: { - dashboard: dashboardWithOneFolder.spec.dashboard, - dashboardTitle: dashboardWithOneFolder.status.dashboardTitle, - items: [dashboardWithOneFolder], - }, - [dashboardWithTwoFolders.spec.dashboard]: { - dashboard: dashboardWithTwoFolders.spec.dashboard, - dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, - items: [dashboardWithTwoFolders], - }, - }, - }, - 'Folder 2': { - title: 'Folder 2', - isExpanded: false, - folders: {}, - dashboards: { - [dashboardWithTwoFolders.spec.dashboard]: { - dashboard: dashboardWithTwoFolders.spec.dashboard, - dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, - items: [dashboardWithTwoFolders], - }, - }, - }, - }, - dashboards: {}, - }, - }); - }); - - it('Merges scopes from multiple dashboards', () => { - expect(groupDashboards([dashboardWithTwoFolders, alternativeDashboardWithTwoFolders])).toEqual({ - '': { - title: '', - isExpanded: true, - folders: { - 'Folder 1': { - title: 'Folder 1', - isExpanded: false, - folders: {}, - dashboards: { - [dashboardWithTwoFolders.spec.dashboard]: { - dashboard: dashboardWithTwoFolders.spec.dashboard, - dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, - items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], - }, - }, - }, - 'Folder 2': { - title: 'Folder 2', - isExpanded: false, - folders: {}, - dashboards: { - [dashboardWithTwoFolders.spec.dashboard]: { - dashboard: dashboardWithTwoFolders.spec.dashboard, - dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, - items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], - }, - }, - }, - }, - dashboards: {}, - }, - }); - }); - - it('Matches snapshot', () => { - expect( - groupDashboards([ - dashboardWithoutFolder, - dashboardWithOneFolder, - dashboardWithTwoFolders, - alternativeDashboardWithTwoFolders, - dashboardWithRootFolder, - alternativeDashboardWithRootFolder, - dashboardWithRootFolderAndOtherFolder, - ]) - ).toEqual({ - '': { - dashboards: { - [dashboardWithRootFolderAndOtherFolder.spec.dashboard]: { - dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard, - dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle, - items: [dashboardWithRootFolderAndOtherFolder], - }, - [dashboardWithRootFolder.spec.dashboard]: { - dashboard: dashboardWithRootFolder.spec.dashboard, - dashboardTitle: dashboardWithRootFolder.status.dashboardTitle, - items: [dashboardWithRootFolder, alternativeDashboardWithRootFolder], - }, - [dashboardWithoutFolder.spec.dashboard]: { - dashboard: dashboardWithoutFolder.spec.dashboard, - dashboardTitle: dashboardWithoutFolder.status.dashboardTitle, - items: [dashboardWithoutFolder], - }, - }, - folders: { - 'Folder 1': { - dashboards: { - [dashboardWithOneFolder.spec.dashboard]: { - dashboard: dashboardWithOneFolder.spec.dashboard, - dashboardTitle: dashboardWithOneFolder.status.dashboardTitle, - items: [dashboardWithOneFolder], - }, - [dashboardWithTwoFolders.spec.dashboard]: { - dashboard: dashboardWithTwoFolders.spec.dashboard, - dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, - items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], - }, - }, - folders: {}, - isExpanded: false, - title: 'Folder 1', - }, - 'Folder 2': { - dashboards: { - [dashboardWithTwoFolders.spec.dashboard]: { - dashboard: dashboardWithTwoFolders.spec.dashboard, - dashboardTitle: dashboardWithTwoFolders.status.dashboardTitle, - items: [dashboardWithTwoFolders, alternativeDashboardWithTwoFolders], - }, - }, - folders: {}, - isExpanded: false, - title: 'Folder 2', - }, - 'Folder 3': { - dashboards: { - [dashboardWithRootFolderAndOtherFolder.spec.dashboard]: { - dashboard: dashboardWithRootFolderAndOtherFolder.spec.dashboard, - dashboardTitle: dashboardWithRootFolderAndOtherFolder.status.dashboardTitle, - items: [dashboardWithRootFolderAndOtherFolder], - }, - }, - folders: {}, - isExpanded: false, - title: 'Folder 3', - }, - }, - isExpanded: true, - title: '', - }, - }); - }); - }); - - describe('filterFolders', () => { - it('Shows folders matching criteria', () => { - expect( - filterFolders( - { - '': { - title: '', - isExpanded: true, - folders: { - 'Folder 1': { - title: 'Folder 1', - isExpanded: false, - folders: {}, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - 'Folder 2': { - title: 'Folder 2', - isExpanded: true, - folders: {}, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - }, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - }, - 'Folder' - ) - ).toEqual({ - '': { - title: '', - isExpanded: true, - folders: { - 'Folder 1': { - title: 'Folder 1', - isExpanded: true, - folders: {}, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - 'Folder 2': { - title: 'Folder 2', - isExpanded: true, - folders: {}, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - }, - dashboards: {}, - }, - }); - }); - - it('Shows dashboards matching criteria', () => { - expect( - filterFolders( - { - '': { - title: '', - isExpanded: true, - folders: { - 'Folder 1': { - title: 'Folder 1', - isExpanded: false, - folders: {}, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - 'Folder 2': { - title: 'Folder 2', - isExpanded: true, - folders: {}, - dashboards: { - 'Random ID': { - dashboard: 'Random ID', - dashboardTitle: 'Random Title', - items: [], - }, - }, - }, - }, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - 'Random ID': { - dashboard: 'Random ID', - dashboardTitle: 'Random Title', - items: [], - }, - }, - }, - }, - 'dash' - ) - ).toEqual({ - '': { - title: '', - isExpanded: true, - folders: { - 'Folder 1': { - title: 'Folder 1', - isExpanded: true, - folders: {}, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - }, - dashboards: { - 'Dashboard ID': { - dashboard: 'Dashboard ID', - dashboardTitle: 'Dashboard Title', - items: [], - }, - }, - }, - }); - }); - }); -}); diff --git a/public/app/features/scopes/tests/utils/actions.ts b/public/app/features/scopes/tests/utils/actions.ts index 9781fee2b6d..5972a430fc5 100644 --- a/public/app/features/scopes/tests/utils/actions.ts +++ b/public/app/features/scopes/tests/utils/actions.ts @@ -5,16 +5,8 @@ import { MultiValueVariable, sceneGraph, VariableValue } from '@grafana/scenes'; import { defaultTimeZone, TimeZone } from '@grafana/schema'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; -import { scopesSelectorScene } from '../../instance'; +import { ScopesService } from '../../ScopesService'; -import { - dashboardReloadSpy, - fetchDashboardsSpy, - fetchNodesSpy, - fetchScopeSpy, - fetchSelectedScopesSpy, - getMock, -} from './mocks'; import { getDashboardFolderExpand, getDashboardsExpand, @@ -37,30 +29,13 @@ import { getTreeSearch, } from './selectors'; -export const clearMocks = () => { - fetchNodesSpy.mockClear(); - fetchScopeSpy.mockClear(); - fetchSelectedScopesSpy.mockClear(); - fetchDashboardsSpy.mockClear(); - dashboardReloadSpy.mockClear(); - getMock.mockClear(); -}; - const click = async (selector: () => HTMLElement) => act(() => fireEvent.click(selector())); const type = async (selector: () => HTMLInputElement, value: string) => { await act(() => fireEvent.input(selector(), { target: { value } })); await jest.runOnlyPendingTimersAsync(); }; -export const updateScopes = async (scopes: string[]) => - act(async () => - scopesSelectorScene?.updateScopes( - scopes.map((scopeName) => ({ - scopeName, - path: [], - })) - ) - ); +export const updateScopes = async (scopes: string[]) => act(async () => ScopesService.instance?.changeScopes(scopes)); export const openSelector = async () => click(getSelectorInput); export const applyScopes = async () => { await click(getSelectorApply); diff --git a/public/app/features/scopes/tests/utils/assertions.ts b/public/app/features/scopes/tests/utils/assertions.ts index f58d73de50f..c842e81b3fa 100644 --- a/public/app/features/scopes/tests/utils/assertions.ts +++ b/public/app/features/scopes/tests/utils/assertions.ts @@ -1,4 +1,3 @@ -import { dashboardReloadSpy } from './mocks'; import { getDashboard, getDashboardsContainer, @@ -21,7 +20,6 @@ import { queryDashboard, queryDashboardFolderExpand, queryDashboardsContainer, - queryDashboardsExpand, queryDashboardsSearch, queryPersistedApplicationsGrafanaSelect, queryPersistedApplicationsMimirSelect, @@ -29,7 +27,6 @@ import { queryResultApplicationsGrafanaSelect, queryResultApplicationsMimirSelect, querySelectorApply, - querySelectorInput, } from './selectors'; const expectInDocument = (selector: () => HTMLElement) => expect(selector()).toBeInTheDocument(); @@ -42,7 +39,7 @@ const expectTextContent = (selector: () => HTMLElement, text: string) => expect( const expectDisabled = (selector: () => HTMLElement) => expect(selector()).toBeDisabled(); export const expectScopesSelectorClosed = () => expectNotInDocument(querySelectorApply); -export const expectScopesSelectorNotInDocument = () => expectNotInDocument(querySelectorInput); +export const expectScopesSelectorDisabled = () => expectDisabled(getSelectorInput); export const expectScopesSelectorValue = (value: string) => expectValue(getSelectorInput, value); export const expectScopesHeadline = (value: string) => expectTextContent(getTreeHeadline, value); export const expectPersistedApplicationsGrafanaNotPresent = () => @@ -65,7 +62,6 @@ export const expectResultCloudOpsSelected = () => expectRadioChecked(getResultCl export const expectResultCloudOpsNotSelected = () => expectRadioNotChecked(getResultCloudOpsRadio); export const expectDashboardsDisabled = () => expectDisabled(getDashboardsExpand); -export const expectDashboardsNotInDocument = () => expectNotInDocument(queryDashboardsExpand); export const expectDashboardsClosed = () => expectNotInDocument(queryDashboardsContainer); export const expectDashboardsOpen = () => expectInDocument(getDashboardsContainer); export const expectNoDashboardsSearch = () => expectNotInDocument(queryDashboardsSearch); @@ -81,9 +77,6 @@ export const expectDashboardNotInDocument = (uid: string) => expectNotInDocument export const expectDashboardLength = (uid: string, length: number) => expect(queryAllDashboard(uid)).toHaveLength(length); -export const expectNotDashboardReload = () => expect(dashboardReloadSpy).not.toHaveBeenCalled(); -export const expectDashboardReload = () => expect(dashboardReloadSpy).toHaveBeenCalled(); - export const expectSelectedScopePath = (name: string, path: string[] | undefined) => expect(getSelectedScope(name)?.path).toEqual(path); export const expectTreeScopePath = (name: string, path: string[] | undefined) => diff --git a/public/app/features/scopes/tests/utils/mocks.ts b/public/app/features/scopes/tests/utils/mocks.ts index 0ac0a785237..f61e251a8bb 100644 --- a/public/app/features/scopes/tests/utils/mocks.ts +++ b/public/app/features/scopes/tests/utils/mocks.ts @@ -2,8 +2,6 @@ import { Scope, ScopeDashboardBinding, ScopeNode } from '@grafana/data'; import { DataSourceRef } from '@grafana/schema/dist/esm/common/common.gen'; import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager'; -import * as api from '../../internal/api'; - export const mocksScopes: Scope[] = [ { metadata: { name: 'cloud' }, @@ -369,10 +367,6 @@ export const mocksNodes: Array = [ }, ] as const; -export const fetchNodesSpy = jest.spyOn(api, 'fetchNodes'); -export const fetchScopeSpy = jest.spyOn(api, 'fetchScope'); -export const fetchSelectedScopesSpy = jest.spyOn(api, 'fetchSelectedScopes'); -export const fetchDashboardsSpy = jest.spyOn(api, 'fetchDashboards'); export const dashboardReloadSpy = jest.spyOn(getDashboardScenePageStateManager(), 'reloadDashboard'); export const getMock = jest diff --git a/public/app/features/scopes/tests/utils/render.tsx b/public/app/features/scopes/tests/utils/render.tsx index 32ff0010d48..66072bd43b2 100644 --- a/public/app/features/scopes/tests/utils/render.tsx +++ b/public/app/features/scopes/tests/utils/render.tsx @@ -1,19 +1,21 @@ -import { cleanup } from '@testing-library/react'; +import { cleanup, waitFor } from '@testing-library/react'; import { KBarProvider } from 'kbar'; import { render } from 'test/test-utils'; import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks'; import { config, setPluginImportUtils } from '@grafana/runtime'; +import { sceneGraph } from '@grafana/scenes'; import { defaultDashboard } from '@grafana/schema'; import { AppChrome } from 'app/core/components/AppChrome/AppChrome'; import { transformSaveModelToScene } from 'app/features/dashboard-scene/serialization/transformSaveModelToScene'; import { DashboardDataDTO, DashboardDTO, DashboardMeta } from 'app/types'; -import { initializeScopes, scopesDashboardsScene, scopesSelectorScene } from '../../instance'; -import { getInitialDashboardsState } from '../../internal/ScopesDashboardsScene'; -import { initialSelectorState } from '../../internal/ScopesSelectorScene'; +import { ScopesContextProvider } from '../../ScopesContextProvider'; +import { ScopesService } from '../../ScopesService'; +import { ScopesDashboardsService } from '../../dashboards/ScopesDashboardsService'; +import { ScopesSelectorService } from '../../selector/ScopesSelectorService'; -import { clearMocks } from './actions'; +import { getMock } from './mocks'; const getDashboardDTO: ( overrideDashboard: Partial, @@ -176,33 +178,38 @@ setPluginImportUtils({ getPanelPluginFromCache: () => undefined, }); -export function renderDashboard( +export async function renderDashboard( overrideDashboard: Partial = {}, overrideMeta: Partial = {} ) { jest.useFakeTimers({ advanceTimers: true }); jest.spyOn(console, 'error').mockImplementation(jest.fn()); - clearMocks(); - initializeScopes(); const dto: DashboardDTO = getDashboardDTO(overrideDashboard, overrideMeta); const scene = transformSaveModelToScene(dto); render( - - - + + + + + ); + await waitFor(() => expect(sceneGraph.getScopesBridge(scene)).toBeDefined()); + return scene; } -export async function resetScenes() { +export async function resetScenes(spies: jest.SpyInstance[] = []) { await jest.runOnlyPendingTimersAsync(); jest.useRealTimers(); - scopesSelectorScene?.setState(initialSelectorState); - scopesDashboardsScene?.setState(getInitialDashboardsState()); + getMock.mockClear(); + spies.forEach((spy) => spy.mockClear()); + ScopesService.instance?.reset(); + ScopesSelectorService.instance?.reset(); + ScopesDashboardsService.instance?.reset(); cleanup(); } diff --git a/public/app/features/scopes/tests/utils/selectors.ts b/public/app/features/scopes/tests/utils/selectors.ts index 689e82ca87f..e5ef25e26bb 100644 --- a/public/app/features/scopes/tests/utils/selectors.ts +++ b/public/app/features/scopes/tests/utils/selectors.ts @@ -1,6 +1,7 @@ import { screen } from '@testing-library/react'; -import { scopesSelectorScene } from '../../instance'; +import { ScopesService } from '../../ScopesService'; +import { ScopesSelectorService } from '../../selector/ScopesSelectorService'; const selectors = { tree: { @@ -33,14 +34,12 @@ const selectors = { }; export const getSelectorInput = () => screen.getByTestId(selectors.selector.input); -export const querySelectorInput = () => screen.queryByTestId(selectors.selector.input); export const querySelectorApply = () => screen.queryByTestId(selectors.selector.apply); export const getSelectorApply = () => screen.getByTestId(selectors.selector.apply); export const getSelectorCancel = () => screen.getByTestId(selectors.selector.cancel); export const getDashboardsExpand = () => screen.getByTestId(selectors.dashboards.expand); export const getDashboardsContainer = () => screen.getByTestId(selectors.dashboards.container); -export const queryDashboardsExpand = () => screen.queryByTestId(selectors.dashboards.expand); export const queryDashboardsContainer = () => screen.queryByTestId(selectors.dashboards.container); export const queryDashboardsSearch = () => screen.queryByTestId(selectors.dashboards.search); export const getDashboardsSearch = () => screen.getByTestId(selectors.dashboards.search); @@ -88,8 +87,9 @@ export const getResultCloudDevRadio = () => export const getResultCloudOpsRadio = () => screen.getByTestId(selectors.tree.radio('cloud-ops', 'result')); -export const getListOfSelectedScopes = () => scopesSelectorScene?.state.scopes; -export const getListOfTreeScopes = () => scopesSelectorScene?.state.treeScopes; +export const getListOfScopes = () => ScopesService.instance?.state.value; +export const getListOfSelectedScopes = () => ScopesSelectorService.instance?.state.selectedScopes; +export const getListOfTreeScopes = () => ScopesSelectorService.instance?.state.treeScopes; export const getSelectedScope = (name: string) => getListOfSelectedScopes()?.find((selectedScope) => selectedScope.scope.metadata.name === name); export const getTreeScope = (name: string) => getListOfTreeScopes()?.find((treeScope) => treeScope.scopeName === name); diff --git a/public/app/features/scopes/tests/viewMode.test.ts b/public/app/features/scopes/tests/viewMode.test.ts index 89d698b9b0f..ba115534f98 100644 --- a/public/app/features/scopes/tests/viewMode.test.ts +++ b/public/app/features/scopes/tests/viewMode.test.ts @@ -1,14 +1,14 @@ import { config } from '@grafana/runtime'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; -import { scopesDashboardsScene, scopesSelectorScene } from '../instance'; +import { ScopesService } from '../ScopesService'; import { enterEditMode, openSelector, toggleDashboards } from './utils/actions'; import { expectDashboardsClosed, - expectDashboardsNotInDocument, + expectDashboardsDisabled, expectScopesSelectorClosed, - expectScopesSelectorNotInDocument, + expectScopesSelectorDisabled, } from './utils/assertions'; import { getDatasource, getInstanceSettings, getMock } from './utils/mocks'; import { renderDashboard, resetScenes } from './utils/render'; @@ -30,8 +30,8 @@ describe('View mode', () => { config.featureToggles.groupByVariable = true; }); - beforeEach(() => { - dashboardScene = renderDashboard(); + beforeEach(async () => { + dashboardScene = await renderDashboard(); }); afterEach(async () => { @@ -40,8 +40,8 @@ describe('View mode', () => { it('Enters view mode', async () => { await enterEditMode(dashboardScene); - expect(scopesSelectorScene?.state?.isReadOnly).toEqual(true); - expect(scopesDashboardsScene?.state?.isPanelOpened).toEqual(false); + expect(ScopesService.instance?.state.readOnly).toEqual(true); + expect(ScopesService.instance?.state.drawerOpened).toEqual(false); }); it('Closes selector on enter', async () => { @@ -58,11 +58,11 @@ describe('View mode', () => { it('Does not show selector when view mode is active', async () => { await enterEditMode(dashboardScene); - expectScopesSelectorNotInDocument(); + expectScopesSelectorDisabled(); }); it('Does not show the expand button when view mode is active', async () => { await enterEditMode(dashboardScene); - expectDashboardsNotInDocument(); + expectDashboardsDisabled(); }); }); diff --git a/public/app/features/scopes/useScopesDashboardsState.ts b/public/app/features/scopes/useScopesDashboardsState.ts deleted file mode 100644 index 7fc2e7d385a..00000000000 --- a/public/app/features/scopes/useScopesDashboardsState.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { scopesDashboardsScene } from './instance'; - -export const useScopesDashboardsState = () => { - return scopesDashboardsScene?.useState(); -}; diff --git a/public/app/features/scopes/utils.ts b/public/app/features/scopes/utils.ts deleted file mode 100644 index 29ddd616382..00000000000 --- a/public/app/features/scopes/utils.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Scope } from '@grafana/data'; -import { sceneGraph, SceneObject } from '@grafana/scenes'; - -import { ScopesFacade } from './ScopesFacadeScene'; -import { scopesDashboardsScene, scopesSelectorScene } from './instance'; -import { getScopesFromSelectedScopes } from './internal/utils'; - -export function getSelectedScopes(): Scope[] { - return getScopesFromSelectedScopes(scopesSelectorScene?.state.scopes ?? []); -} - -export function getSelectedScopesNames(): string[] { - return getSelectedScopes().map((scope) => scope.metadata.name); -} - -export function enableScopes() { - scopesSelectorScene?.enable(); - scopesDashboardsScene?.enable(); -} - -export function disableScopes() { - scopesSelectorScene?.disable(); - scopesDashboardsScene?.disable(); -} - -export function exitScopesReadOnly() { - scopesSelectorScene?.exitReadOnly(); - scopesDashboardsScene?.exitReadOnly(); -} - -export function enterScopesReadOnly() { - scopesSelectorScene?.enterReadOnly(); - scopesDashboardsScene?.enterReadOnly(); -} - -export function getClosestScopesFacade(scene: SceneObject): ScopesFacade | null { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return sceneGraph.findObject(scene, (obj) => obj instanceof ScopesFacade) as ScopesFacade | null; -} diff --git a/public/app/features/trails/DataTrail.tsx b/public/app/features/trails/DataTrail.tsx index aef53bad053..3a6502f894f 100644 --- a/public/app/features/trails/DataTrail.tsx +++ b/public/app/features/trails/DataTrail.tsx @@ -31,7 +31,6 @@ import { VariableValueSelectors, } from '@grafana/scenes'; import { useStyles2 } from '@grafana/ui'; -import { getSelectedScopes } from 'app/features/scopes'; import { DataTrailSettings } from './DataTrailSettings'; import { DataTrailHistory } from './DataTrailsHistory'; @@ -426,7 +425,11 @@ export class DataTrail extends SceneObjectBase implements SceneO if (timeRange) { const datasourceUid = sceneGraph.interpolate(trail, VAR_DATASOURCE_EXPR); const otelTargets = await totalOtelResources(datasourceUid, timeRange); - const deploymentEnvironments = await getDeploymentEnvironments(datasourceUid, timeRange, getSelectedScopes()); + const deploymentEnvironments = await getDeploymentEnvironments( + datasourceUid, + timeRange, + sceneGraph.getScopesBridge(trail)?.getValue() ?? [] + ); const hasOtelResources = otelTargets.jobs.length > 0 && otelTargets.instances.length > 0; // loading from the url with otel resources selected will result in turning on OTel experience const otelResourcesVariable = sceneGraph.lookupVariable(VAR_OTEL_AND_METRIC_FILTERS, this); diff --git a/public/app/features/trails/DataTrailsApp.tsx b/public/app/features/trails/DataTrailsApp.tsx index b1fdf17a467..156a77e3e47 100644 --- a/public/app/features/trails/DataTrailsApp.tsx +++ b/public/app/features/trails/DataTrailsApp.tsx @@ -1,20 +1,16 @@ -import { css } from '@emotion/css'; import { useEffect, useState } from 'react'; import { Routes, Route } from 'react-router-dom-v5-compat'; -import { - DataQueryRequest, - DataSourceGetTagKeysOptions, - DataSourceGetTagValuesOptions, - PageLayoutType, -} from '@grafana/data'; +import { PageLayoutType } from '@grafana/data'; import { config, locationService } from '@grafana/runtime'; -import { SceneComponentProps, SceneObjectBase, SceneObjectState, UrlSyncContextProvider } from '@grafana/scenes'; -import { useStyles2 } from '@grafana/ui/'; +import { + SceneComponentProps, + SceneObjectBase, + SceneObjectState, + SceneScopesBridge, + UrlSyncContextProvider, +} from '@grafana/scenes'; import { Page } from 'app/core/components/Page/Page'; -import { getClosestScopesFacade, ScopesFacade, ScopesSelector } from 'app/features/scopes'; - -import { AppChromeUpdate } from '../../core/components/AppChrome/AppChromeUpdate'; import { DataTrail } from './DataTrail'; import { DataTrailsHome } from './DataTrailsHome'; @@ -25,35 +21,14 @@ import { getMetricName, getUrlForTrail, newMetricsTrail } from './utils'; export interface DataTrailsAppState extends SceneObjectState { trail: DataTrail; home: DataTrailsHome; + scopesBridge?: SceneScopesBridge | undefined; } export class DataTrailsApp extends SceneObjectBase { - private _scopesFacade: ScopesFacade | null; + protected _renderBeforeActivation = true; public constructor(state: DataTrailsAppState) { super(state); - - this._scopesFacade = getClosestScopesFacade(this); - } - - public enrichDataRequest(): Partial { - if (!config.featureToggles.promQLScope) { - return {}; - } - - return { - scopes: this._scopesFacade?.value, - }; - } - - public enrichFiltersRequest(): Partial { - if (!config.featureToggles.promQLScope) { - return {}; - } - - return { - scopes: this._scopesFacade?.value, - }; } goToUrlForTrail(trail: DataTrail) { @@ -62,33 +37,35 @@ export class DataTrailsApp extends SceneObjectBase { } static Component = ({ model }: SceneComponentProps) => { - const { trail, home } = model.useState(); + const { trail, home, scopesBridge } = model.useState(); return ( - - {/* The routes are relative to the HOME_ROUTE */} - null} - subTitle="" - > - - - } - /> - } /> - + <> + {scopesBridge && } + + {/* The routes are relative to the HOME_ROUTE */} + null} + subTitle="" + > + + + } + /> + } /> + + ); }; } function DataTrailView({ trail }: { trail: DataTrail }) { - const styles = useStyles2(getStyles); const [isInitialized, setIsInitialized] = useState(false); const { metric } = trail.useState(); @@ -108,15 +85,6 @@ function DataTrailView({ trail }: { trail: DataTrail }) { return ( - {config.featureToggles.enableScopesInMetricsExplore && ( - - -
- } - /> - )} @@ -127,37 +95,32 @@ let dataTrailsApp: DataTrailsApp; export function getDataTrailsApp() { if (!dataTrailsApp) { - const $behaviors = config.featureToggles.enableScopesInMetricsExplore - ? [ - new ScopesFacade({ - handler: (facade) => { - const trail = facade.parent && 'trail' in facade.parent.state ? facade.parent.state.trail : undefined; - - if (trail instanceof DataTrail) { - trail.publishEvent(new RefreshMetricsEvent()); - trail.checkDataSourceForOTelResources(); - } - }, - }), - ] - : undefined; + const scopesBridge = + config.featureToggles.scopeFilters && config.featureToggles.enableScopesInMetricsExplore + ? new SceneScopesBridge({}) + : undefined; dataTrailsApp = new DataTrailsApp({ trail: newMetricsTrail(), home: new DataTrailsHome({}), - $behaviors, + scopesBridge, + $behaviors: [ + () => { + scopesBridge?.setEnabled(true); + + const sub = scopesBridge?.subscribeToValue(() => { + dataTrailsApp.state.trail.publishEvent(new RefreshMetricsEvent()); + dataTrailsApp.state.trail.checkDataSourceForOTelResources(); + }); + + return () => { + scopesBridge?.setEnabled(false); + sub?.unsubscribe(); + }; + }, + ], }); } return dataTrailsApp; } - -const getStyles = () => ({ - topNavContainer: css({ - width: '100%', - height: '100%', - display: 'flex', - flexDirection: 'row', - justifyItems: 'flex-start', - }), -}); diff --git a/public/app/features/trails/MetricSelect/MetricSelectScene.tsx b/public/app/features/trails/MetricSelect/MetricSelectScene.tsx index 8a3fcd462e3..9cea458adaa 100644 --- a/public/app/features/trails/MetricSelect/MetricSelectScene.tsx +++ b/public/app/features/trails/MetricSelect/MetricSelectScene.tsx @@ -27,7 +27,6 @@ import { } from '@grafana/scenes'; import { Alert, Badge, Field, Icon, IconButton, InlineSwitch, Input, Select, Tooltip, useStyles2 } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; -import { getSelectedScopes } from 'app/features/scopes'; import { MetricScene } from '../MetricScene'; import { StatusWrapper } from '../StatusWrapper'; @@ -257,7 +256,7 @@ export class MetricSelectScene extends SceneObjectBase i const response = await getMetricNames( datasourceUid, timeRange, - getSelectedScopes(), + sceneGraph.getScopesBridge(this)?.getValue() ?? [], filters, jobsList, instancesList, diff --git a/public/app/features/trails/utils.test.ts b/public/app/features/trails/utils.test.ts index 04b15995982..9552b691641 100644 --- a/public/app/features/trails/utils.test.ts +++ b/public/app/features/trails/utils.test.ts @@ -50,6 +50,7 @@ describe('limitAdhocProviders', () => { } as unknown as MetricDatasourceHelper; dataTrail = { + forEachChild: jest.fn(), getQueries: jest.fn().mockReturnValue([]), } as unknown as DataTrail; }); diff --git a/public/app/features/trails/utils.ts b/public/app/features/trails/utils.ts index 860dc630a30..2b721161203 100644 --- a/public/app/features/trails/utils.ts +++ b/public/app/features/trails/utils.ts @@ -20,12 +20,12 @@ import { SceneObject, SceneObjectState, SceneObjectUrlValues, + SceneScopesBridge, SceneTimeRange, sceneUtils, SceneVariable, SceneVariableState, } from '@grafana/scenes'; -import { getClosestScopesFacade } from 'app/features/scopes'; import { getDatasourceSrv } from '../plugins/datasource_srv'; @@ -53,6 +53,10 @@ export function getTrailFor(model: SceneObject): DataTrail { return sceneGraph.getAncestor(model, DataTrail); } +export function getScopesBridgeFor(model: SceneObject): SceneScopesBridge | undefined { + return sceneGraph.getScopesBridge(getTrailFor(model)); +} + export function getTrailSettings(model: SceneObject): DataTrailSettings { return sceneGraph.getAncestor(model, DataTrail).state.settings; } @@ -193,7 +197,7 @@ export function limitAdhocProviders( const opts = { filters, - scopes: getClosestScopesFacade(variable)?.value, + scopes: sceneGraph.getScopesBridge(dataTrail)?.getValue(), queries: dataTrail.getQueries(), }; @@ -237,7 +241,7 @@ export function limitAdhocProviders( const opts = { key: filter.key, filters, - scopes: getClosestScopesFacade(variable)?.value, + scopes: sceneGraph.getScopesBridge(dataTrail)?.getValue(), queries: dataTrail.getQueries(), }; diff --git a/yarn.lock b/yarn.lock index 3171da05a1d..a65a3d0f5dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,7 +81,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.3, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.26.2": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.3, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.2": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -362,6 +362,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.25.9": + version: 7.26.7 + resolution: "@babel/parser@npm:7.26.7" + dependencies: + "@babel/types": "npm:^7.26.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10/3ccc384366ca9a9b49c54f5b24c9d8cff9a505f2fbdd1cfc04941c8e1897084cc32f100e77900c12bc14a176cf88daa3c155faad680d9a23491b997fd2a59ffc + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.9" @@ -1425,7 +1436,18 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.22.5, @babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9, @babel/template@npm:^7.26.9, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.22.5, @babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9, @babel/template@npm:^7.3.3": + version: 7.25.9 + resolution: "@babel/template@npm:7.25.9" + dependencies: + "@babel/code-frame": "npm:^7.25.9" + "@babel/parser": "npm:^7.25.9" + "@babel/types": "npm:^7.25.9" + checksum: 10/e861180881507210150c1335ad94aff80fd9e9be6202e1efa752059c93224e2d5310186ddcdd4c0f0b0fc658ce48cb47823f15142b5c00c8456dde54f5de80b2 + languageName: node + linkType: hard + +"@babel/template@npm:^7.26.9": version: 7.26.9 resolution: "@babel/template@npm:7.26.9" dependencies: @@ -1461,6 +1483,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.26.7": + version: 7.26.7 + resolution: "@babel/types@npm:7.26.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10/2264efd02cc261ca5d1c5bc94497c8995238f28afd2b7483b24ea64dd694cf46b00d51815bf0c87f0d0061ea221569c77893aeecb0d4b4bb254e9c2f938d7669 + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -3597,11 +3629,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:6.2.1": - version: 6.2.1 - resolution: "@grafana/scenes-react@npm:6.2.1" +"@grafana/scenes-react@npm:6.3.1": + version: 6.3.1 + resolution: "@grafana/scenes-react@npm:6.3.1" dependencies: - "@grafana/scenes": "npm:6.2.1" + "@grafana/scenes": "npm:6.3.1" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3613,13 +3645,13 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/b8f44f087999fd6074233090a005de03e9d0ed1ba965a05659773cf48afb3c11ed508371fcf0c4cdc3c9a360b3f33e5126ed9082e2f4ee306459d768bd0a5a34 + checksum: 10/77dd6f7bbe3699ece25435623a78f2ef5d831ab83b59612baa362581f7c3f6c02cb420b5acfff4e3d266872a694e08b98340370468bb246d102b20e9fcc79438 languageName: node linkType: hard -"@grafana/scenes@npm:6.2.1": - version: 6.2.1 - resolution: "@grafana/scenes@npm:6.2.1" +"@grafana/scenes@npm:6.3.1": + version: 6.3.1 + resolution: "@grafana/scenes@npm:6.3.1" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" @@ -3637,7 +3669,7 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/44e9a0386dd09a1a7a45bcdcbf285a00996e885cf56640179cbc35deb1f26af37ce34744321193f98aa078a7e02fc8a06b8f07c03b392229cc42ed56733bab05 + checksum: 10/98e3e96b9ce12ae67aa458819dc9eed1eabd1b7421e768074bd97a0fa2a0b1e080d4121b1ddd357ce41ccaf9252439163aae7e612f12cba878ee6dca34f73831 languageName: node linkType: hard @@ -18071,8 +18103,8 @@ __metadata: "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/saga-icons": "workspace:*" - "@grafana/scenes": "npm:6.2.1" - "@grafana/scenes-react": "npm:6.2.1" + "@grafana/scenes": "npm:6.3.1" + "@grafana/scenes-react": "npm:6.3.1" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/tsconfig": "npm:^2.0.0" From 3e7626cc96c91af4b168514a778f4fbab5b3281d Mon Sep 17 00:00:00 2001 From: Victor Marin <36818606+mdvictor@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:40:37 +0200 Subject: [PATCH 162/312] Dashboards: Fix missing `v/e/i` keybindings to return back to dashboard (#101876) readd keybindings to return from edit/view/inspect modes --- .../scene/keyboardShortcuts.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts b/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts index 3a4bd4687f0..cde032a413e 100644 --- a/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts +++ b/public/app/features/dashboard-scene/scene/keyboardShortcuts.ts @@ -43,7 +43,13 @@ export function setupKeyboardShortcuts(scene: DashboardScene) { keybindings.addBinding({ key: 'v', onTrigger: withFocusedPanel(scene, (vizPanel: VizPanel) => { - if (!scene.state.viewPanelScene) { + if (scene.state.viewPanelScene) { + locationService.push( + locationUtil.getUrlForPartial(locationService.getLocation(), { + viewPanel: undefined, + }) + ); + } else { const url = locationUtil.stripBaseFromUrl(getViewPanelUrl(vizPanel)); locationService.push(url); } @@ -105,7 +111,15 @@ export function setupKeyboardShortcuts(scene: DashboardScene) { keybindings.addBinding({ key: 'i', onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => { - locationService.push(locationUtil.stripBaseFromUrl(getInspectUrl(vizPanel))); + if (scene.state.inspectPanelKey) { + locationService.push( + locationUtil.getUrlForPartial(locationService.getLocation(), { + inspect: undefined, + }) + ); + } else { + locationService.push(locationUtil.stripBaseFromUrl(getInspectUrl(vizPanel))); + } }), }); @@ -178,7 +192,13 @@ export function setupKeyboardShortcuts(scene: DashboardScene) { const sceneRoot = vizPanel.getRoot(); if (sceneRoot instanceof DashboardScene) { const panelId = getPanelIdForVizPanel(vizPanel); - if (!scene.state.editPanel) { + if (scene.state.editPanel) { + locationService.push( + locationUtil.getUrlForPartial(locationService.getLocation(), { + editPanel: undefined, + }) + ); + } else { const url = locationUtil.stripBaseFromUrl(getEditPanelUrl(panelId)); locationService.push(url); } From 3fffb2872e20c895b0acdf48800ba81b9d5fa2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Fern=C3=A1ndez?= Date: Tue, 11 Mar 2025 11:47:59 +0100 Subject: [PATCH 163/312] InlineField: Use `Combobox` instead of `Select` (#101923) Use Combobox instead of Select --- .../components/Forms/InlineField.story.tsx | 24 +++++++++---------- .../src/components/Forms/InlineField.test.tsx | 8 +++++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/grafana-ui/src/components/Forms/InlineField.story.tsx b/packages/grafana-ui/src/components/Forms/InlineField.story.tsx index 384e5aa98de..da42f012f3a 100644 --- a/packages/grafana-ui/src/components/Forms/InlineField.story.tsx +++ b/packages/grafana-ui/src/components/Forms/InlineField.story.tsx @@ -1,8 +1,8 @@ -import { action } from '@storybook/addon-actions'; import { Meta, StoryFn } from '@storybook/react'; +import { useState } from 'react'; +import { Combobox } from '../Combobox/Combobox'; import { Input } from '../Input/Input'; -import { Select } from '../Select/Select'; import { InlineField } from './InlineField'; import mdx from './InlineField.mdx'; @@ -75,24 +75,22 @@ grow.args = { grow: true, }; -export const withSelect: StoryFn = (args) => { +export const withCombobox: StoryFn = (args) => { + const comboboxOptions = [ + { value: 1, label: 'One' }, + { value: 2, label: 'Two' }, + ]; + const [selected, setSelected] = useState(1); return ( - {}} /> + {}} /> ); From ea89a68028805fa1ec7d0d810a22b1d6d20bf8f2 Mon Sep 17 00:00:00 2001 From: Igor Suleymanov Date: Tue, 11 Mar 2025 13:00:37 +0200 Subject: [PATCH 164/312] K8s/Dashboards: Generate Dashboards k8s APIs using Grafana App SDK (#99966) * Generate Dashboard kinds with `grafana-app-sdk` Signed-off-by: Igor Suleymanov * Hack together a fix for invalid TS codegen for v0 & v1 Signed-off-by: Igor Suleymanov * Address Go linter issues Signed-off-by: Igor Suleymanov * Address TS linter issues Signed-off-by: Igor Suleymanov * Add new app to CODEOWNERS Signed-off-by: Igor Suleymanov * Fix a couple of issues detected by tests Signed-off-by: Igor Suleymanov * Update OpenAPI definitions and test files Signed-off-by: Igor Suleymanov * Remove title from Dashboard v1alpha1 spec Signed-off-by: Igor Suleymanov * Remove unused CUE schemas Signed-off-by: Igor Suleymanov * remove unrelated files * allow any in the generated betterer * Add a comment explaining why we don't use deepcopy-gen Signed-off-by: Igor Suleymanov * Default to v2alpha1 if dashboards v2 FF is enabled Signed-off-by: Igor Suleymanov --------- Signed-off-by: Igor Suleymanov Co-authored-by: Ryan McKinley --- .betterer.results | 6 + .github/CODEOWNERS | 1 + .prettierignore | 3 + apps/dashboard/Makefile | 18 + apps/dashboard/kinds/cue.mod/module.cue | 4 + apps/dashboard/kinds/dashboard.cue | 68 + apps/dashboard/kinds/manifest.cue | 9 + .../kinds/v0alpha1/dashboard_spec.cue | 6 + .../kinds/v1alpha1/dashboard_spec.cue | 6 + .../kinds/v2alpha1/dashboard_spec.cue | 923 ++++ apps/dashboard/tshack/v0alpha1_spec_gen.ts | 7 + apps/dashboard/tshack/v1alpha1_spec_gen.ts | 7 + .../v0alpha1/dashboard_object_gen.ts | 49 + .../dashboard/v0alpha1/types.metadata.gen.ts | 30 + .../dashboard/v0alpha1/types.spec.gen.ts | 7 + .../dashboard/v0alpha1/types.status.gen.ts | 30 + .../v1alpha1/dashboard_object_gen.ts | 49 + .../dashboard/v1alpha1/types.metadata.gen.ts | 30 + .../dashboard/v1alpha1/types.spec.gen.ts | 7 + .../dashboard/v1alpha1/types.status.gen.ts | 30 + .../v2alpha1/dashboard_object_gen.ts | 49 + .../dashboard/v2alpha1/types.metadata.gen.ts | 30 + .../dashboard/v2alpha1/types.spec.gen.ts | 1316 +++++ .../dashboard/v2alpha1/types.status.gen.ts | 30 + .../migration/conversion/conversion.go | 92 +- pkg/apis/dashboard/v0alpha1/constants.go | 18 + .../dashboard/v0alpha1/dashboard_codec_gen.go | 28 + .../v0alpha1/dashboard_metadata_gen.go | 28 + .../v0alpha1/dashboard_object_gen.go | 269 ++ .../v0alpha1/dashboard_schema_gen.go | 34 + pkg/apis/dashboard/v0alpha1/dashboard_spec.go | 13 + .../dashboard/v0alpha1/dashboard_spec_gen.go | 3 + .../v0alpha1/dashboard_status_gen.go | 34 + pkg/apis/dashboard/v0alpha1/deepcopy.go | 41 + pkg/apis/dashboard/v0alpha1/doc.go | 5 +- pkg/apis/dashboard/v0alpha1/search.go | 7 + pkg/apis/dashboard/v0alpha1/types.go | 46 +- .../v0alpha1/zz_generated.deepcopy.go | 102 - .../v0alpha1/zz_generated.openapi.go | 258 +- ...enerated.openapi_violation_exceptions.list | 3 +- pkg/apis/dashboard/v1alpha1/constants.go | 18 + .../dashboard/v1alpha1/dashboard_codec_gen.go | 28 + .../v1alpha1/dashboard_metadata_gen.go | 28 + .../v1alpha1/dashboard_object_gen.go | 269 ++ .../v1alpha1/dashboard_schema_gen.go | 34 + pkg/apis/dashboard/v1alpha1/dashboard_spec.go | 11 + .../dashboard/v1alpha1/dashboard_spec_gen.go | 3 + .../v1alpha1/dashboard_status_gen.go | 34 + pkg/apis/dashboard/v1alpha1/deepcopy.go | 41 + pkg/apis/dashboard/v1alpha1/doc.go | 5 +- pkg/apis/dashboard/v1alpha1/types.go | 45 +- .../v1alpha1/zz_generated.deepcopy.go | 102 - .../v1alpha1/zz_generated.openapi.go | 241 +- ...enerated.openapi_violation_exceptions.list | 3 +- pkg/apis/dashboard/v2alpha1/constants.go | 18 + .../dashboard/v2alpha1/dashboard_codec_gen.go | 28 + .../v2alpha1/dashboard_metadata_gen.go | 28 + .../v2alpha1/dashboard_object_gen.go | 269 ++ .../v2alpha1/dashboard_schema_gen.go | 34 + .../dashboard/v2alpha1/dashboard_spec_gen.go | 2373 +++++++++ .../v2alpha1/dashboard_status_gen.go | 34 + pkg/apis/dashboard/v2alpha1/deepcopy.go | 63 + pkg/apis/dashboard/v2alpha1/doc.go | 5 +- pkg/apis/dashboard/v2alpha1/register.go | 14 +- pkg/apis/dashboard/v2alpha1/types.go | 45 +- .../v2alpha1/zz_generated.deepcopy.go | 102 - .../v2alpha1/zz_generated.openapi.go | 4230 ++++++++++++++++- ...enerated.openapi_violation_exceptions.list | 65 +- pkg/apis/dashboard_manifest.go | 53 + pkg/registry/apis/dashboard/large.go | 20 +- pkg/registry/apis/dashboard/mutate.go | 5 +- pkg/registry/apis/dashboard/register.go | 10 + pkg/registry/apis/dashboard/register_test.go | 94 + pkg/storage/unified/apistore/go.mod | 1 + pkg/storage/unified/apistore/go.sum | 2 + pkg/storage/unified/resource/go.mod | 2 +- pkg/storage/unified/resource/go.sum | 2 + .../dashboard/testdata/dashboard-test-v2.yaml | 4 + .../dashboard.grafana.app-v0alpha1.json | 68 +- public/api-merged.json | 2 + public/openapi3.json | 2 + 81 files changed, 11452 insertions(+), 679 deletions(-) create mode 100644 apps/dashboard/Makefile create mode 100644 apps/dashboard/kinds/cue.mod/module.cue create mode 100644 apps/dashboard/kinds/dashboard.cue create mode 100644 apps/dashboard/kinds/manifest.cue create mode 100644 apps/dashboard/kinds/v0alpha1/dashboard_spec.cue create mode 100644 apps/dashboard/kinds/v1alpha1/dashboard_spec.cue create mode 100644 apps/dashboard/kinds/v2alpha1/dashboard_spec.cue create mode 100644 apps/dashboard/tshack/v0alpha1_spec_gen.ts create mode 100644 apps/dashboard/tshack/v1alpha1_spec_gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v0alpha1/dashboard_object_gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v0alpha1/types.metadata.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v1alpha1/dashboard_object_gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v1alpha1/types.metadata.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v1alpha1/types.status.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v2alpha1/dashboard_object_gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v2alpha1/types.metadata.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts create mode 100644 packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts create mode 100644 pkg/apis/dashboard/v0alpha1/constants.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_codec_gen.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_spec.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_spec_gen.go create mode 100644 pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go create mode 100644 pkg/apis/dashboard/v0alpha1/deepcopy.go create mode 100644 pkg/apis/dashboard/v1alpha1/constants.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_codec_gen.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_metadata_gen.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_object_gen.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_schema_gen.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_spec.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_spec_gen.go create mode 100644 pkg/apis/dashboard/v1alpha1/dashboard_status_gen.go create mode 100644 pkg/apis/dashboard/v1alpha1/deepcopy.go create mode 100644 pkg/apis/dashboard/v2alpha1/constants.go create mode 100644 pkg/apis/dashboard/v2alpha1/dashboard_codec_gen.go create mode 100644 pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go create mode 100644 pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go create mode 100644 pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go create mode 100644 pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go create mode 100644 pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go create mode 100644 pkg/apis/dashboard/v2alpha1/deepcopy.go create mode 100644 pkg/apis/dashboard_manifest.go diff --git a/.betterer.results b/.betterer.results index c0486d07719..b03dac144aa 100644 --- a/.betterer.results +++ b/.betterer.results @@ -5,6 +5,12 @@ // exports[`better eslint`] = { value: `{ + "apps/dashboard/tshack/v0alpha1_spec_gen.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], + "apps/dashboard/tshack/v1alpha1_spec_gen.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "e2e/old-arch/utils/support/types.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 65f706f256e..7ff10207c35 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -71,6 +71,7 @@ /pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team /apps/alerting/ @grafana/alerting-backend +/apps/dashboard/ @grafana/grafana-app-platform-squad @grafana/dashboards-squad /apps/playlist/ @grafana/grafana-app-platform-squad /apps/investigations/ @fcjack @matryer @svennergr /apps/advisor/ @grafana/plugins-platform-backend diff --git a/.prettierignore b/.prettierignore index 8bf9dc13276..23d9231bc49 100644 --- a/.prettierignore +++ b/.prettierignore @@ -19,6 +19,9 @@ vendor # TS generate from cue by cuetsy **/*.gen.ts +# TS generated by grafana-app-sdk +**/*_gen.ts + # Auto-generated theme files theme.light.generated.json theme.dark.generated.json diff --git a/apps/dashboard/Makefile b/apps/dashboard/Makefile new file mode 100644 index 00000000000..d95e25e1abd --- /dev/null +++ b/apps/dashboard/Makefile @@ -0,0 +1,18 @@ +.PHONY: generate +generate: + @grafana-app-sdk generate \ + --source=./kinds/ \ + --gogenpath=../../pkg/apis \ + --tsgenpath=../../packages/grafana-schema/src/schema \ + --grouping=group \ + --defencoding=none \ + --genoperatorstate=false \ + --noschemasinmanifest + + # This is a workaround for SDK codegen not producing correct output for v0alpha1 + @rm ../../packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts + @cp ./tshack/v0alpha1_spec_gen.ts ../../packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts + + # Same for v1alpha1 + @rm ../../packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts + @cp ./tshack/v1alpha1_spec_gen.ts ../../packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts diff --git a/apps/dashboard/kinds/cue.mod/module.cue b/apps/dashboard/kinds/cue.mod/module.cue new file mode 100644 index 00000000000..a3b88f8aa81 --- /dev/null +++ b/apps/dashboard/kinds/cue.mod/module.cue @@ -0,0 +1,4 @@ +module: "github.com/grafana/grafana/sdkkinds/dashboard" +language: { + version: "v0.9.0" +} diff --git a/apps/dashboard/kinds/dashboard.cue b/apps/dashboard/kinds/dashboard.cue new file mode 100644 index 00000000000..da09322e0e8 --- /dev/null +++ b/apps/dashboard/kinds/dashboard.cue @@ -0,0 +1,68 @@ +package kinds + +import ( + "github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1" + "github.com/grafana/grafana/sdkkinds/dashboard/v1alpha1" + "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1" +) + +// Status is the shared status of all dashboard versions. +DashboardStatus: { + // Optional conversion status. + conversion?: ConversionStatus +} + +// ConversionStatus is the status of the conversion of the dashboard. +ConversionStatus: { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + failed: bool + + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + storedVersion: string + + // The error message from the conversion. + // Empty if the conversion has not failed. + error: string +} + +dashboard: { + kind: "Dashboard" + pluralName: "Dashboards" + current: "v0alpha1" + + codegen: { + ts: { + enabled: true + config: { + enumsAsUnionTypes: true + } + } + go: { + enabled: true + } + } + + versions: { + "v0alpha1": { + schema: { + spec: v0alpha1.DashboardSpec + status: DashboardStatus + } + } + "v1alpha1": { + schema: { + spec: v1alpha1.DashboardSpec + status: DashboardStatus + } + } + "v2alpha1": { + schema: { + spec: v2alpha1.DashboardSpec + status: DashboardStatus + } + } + } +} diff --git a/apps/dashboard/kinds/manifest.cue b/apps/dashboard/kinds/manifest.cue new file mode 100644 index 00000000000..1ec0fb4f21c --- /dev/null +++ b/apps/dashboard/kinds/manifest.cue @@ -0,0 +1,9 @@ +package kinds + +manifest: { + appName: "dashboard" + groupOverride: "dashboard.grafana.app" + kinds: [ + dashboard, + ] +} diff --git a/apps/dashboard/kinds/v0alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v0alpha1/dashboard_spec.cue new file mode 100644 index 00000000000..a59f79e97e9 --- /dev/null +++ b/apps/dashboard/kinds/v0alpha1/dashboard_spec.cue @@ -0,0 +1,6 @@ +package v0alpha1 + +// TODO: this outputs nothing. +// For now, we use unstructured for the spec, +// but it cannot be produced by the SDK codegen. +DashboardSpec: [string]: _ diff --git a/apps/dashboard/kinds/v1alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v1alpha1/dashboard_spec.cue new file mode 100644 index 00000000000..709fb40fd02 --- /dev/null +++ b/apps/dashboard/kinds/v1alpha1/dashboard_spec.cue @@ -0,0 +1,6 @@ +package v1alpha1 + +// TODO: this outputs nothing. +// For now, we use unstructured for the spec, +// but it cannot be produced by the SDK codegen. +DashboardSpec: [string]: _ diff --git a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue new file mode 100644 index 00000000000..a1216596268 --- /dev/null +++ b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue @@ -0,0 +1,923 @@ +package v2alpha1 + +DashboardSpec: { + // Title of dashboard. + annotations: [...AnnotationQueryKind] + + // Configuration of dashboard cursor sync behavior. + // "Off" for no shared crosshair or tooltip (default). + // "Crosshair" for shared crosshair. + // "Tooltip" for shared crosshair AND shared tooltip. + cursorSync: DashboardCursorSync + + // Description of dashboard. + description?: string + + // Whether a dashboard is editable or not. + editable?: bool | *true + + elements: [ElementReference.name]: Element + + layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind + + // Links with references to other dashboards or external websites. + links: [...DashboardLink] + + // When set to true, the dashboard will redraw panels at an interval matching the pixel width. + // This will keep data "moving left" regardless of the query refresh rate. This setting helps + // avoid dashboards presenting stale live data. + liveNow?: bool + + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + preload: bool + + // Plugins only. The version of the dashboard installed together with the plugin. + // This is used to determine if the dashboard should be updated when the plugin is updated. + revision?: uint16 + + // Tags associated with dashboard. + tags: [...string] + + timeSettings: TimeSettingsSpec + + // Title of dashboard. + title: string + + // Configured template variables. + variables: [...VariableKind] +} + +// Supported dashboard elements +Element: PanelKind | LibraryPanelKind // |* more element types in the future + +LibraryPanelKind: { + kind: "LibraryPanel" + spec: LibraryPanelKindSpec +} + +LibraryPanelKindSpec: { + // Panel ID for the library panel in the dashboard + id: number + // Title for the library panel in the dashboard + title: string + + libraryPanel: LibraryPanelRef +} + +// A library panel is a reusable panel that you can use in any dashboard. +// When you make a change to a library panel, that change propagates to all instances of where the panel is used. +// Library panels streamline reuse of panels across multiple dashboards. +LibraryPanelRef: { + // Library panel name + name: string + // Library panel uid + uid: string +} + +AnnotationPanelFilter: { + // Should the specified panels be included or excluded + exclude?: bool | *false + + // Panel IDs that should be included or excluded + ids: [...uint8] +} + +// "Off" for no shared crosshair or tooltip (default). +// "Crosshair" for shared crosshair. +// "Tooltip" for shared crosshair AND shared tooltip. +DashboardCursorSync: "Off" | "Crosshair" | "Tooltip" + +// Links with references to other dashboards or external resources +DashboardLink: { + // Title to display with the link + title: string + // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) + // FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType` + type: DashboardLinkType + // Icon name to be displayed with the link + icon: string + // Tooltip to display when the user hovers their mouse over it + tooltip: string + // Link URL. Only required/valid if the type is link + url?: string + // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards + tags: [...string] + // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards + asDropdown: bool | *false + // If true, the link will be opened in a new tab + targetBlank: bool | *false + // If true, includes current template variables values in the link as query params + includeVars: bool | *false + // If true, includes current time range in the link as query params + keepTime: bool | *false +} + +DataSourceRef: { + // The plugin type-id + type?: string + + // Specific datasource instance + uid?: string +} + +// A topic is attached to DataFrame metadata in query results. +// This specifies where the data should be used. +DataTopic: "series" | "annotations" | "alertStates" @cog(kind="enum",memberNames="Series|Annotations|AlertStates") + +// Transformations allow to manipulate data returned by a query before the system applies a visualization. +// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, +// use the output of one transformation as the input to another transformation, etc. +DataTransformerConfig: { + // Unique identifier of transformer + id: string + // Disabled transformations are skipped + disabled?: bool + // Optional frame matcher. When missing it will be applied to all results + filter?: MatcherConfig + // Where to pull DataFrames from as input to transformation + topic?: DataTopic + // Options to be passed to the transformer + // Valid options depend on the transformer id + options: _ +} + +DataLink: { + title: string + url: string + targetBlank?: bool +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +FieldConfigSource: { + // Defaults are the options applied to all fields. + defaults: FieldConfig + // Overrides are the options applied to specific fields overriding the defaults. + overrides: [...{ + matcher: MatcherConfig + properties: [...DynamicConfigValue] + }] +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +FieldConfig: { + // The display value for this field. This supports template variables blank is auto + displayName?: string + + // This can be used by data sources that return and explicit naming structure for values and labels + // When this property is configured, this value is used rather than the default naming strategy. + displayNameFromDS?: string + + // Human readable field metadata + description?: string + + // An explicit path to the field in the datasource. When the frame meta includes a path, + // This will default to `${frame.meta.path}/${field.name} + // + // When defined, this value can be used as an identifier within the datasource scope, and + // may be used to update the results + path?: string + + // True if data source can write a value to the path. Auth/authz are supported separately + writeable?: bool + + // True if data source field supports ad-hoc filters + filterable?: bool + + // Unit a field should use. The unit you select is applied to all fields except time. + // You can use the units ID availables in Grafana or a custom unit. + // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts + // As custom unit, you can use the following formats: + // `suffix:` for custom unit that should go after value. + // `prefix:` for custom unit that should go before value. + // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. + // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. + // `count:` for a custom count unit. + // `currency:` for custom a currency unit. + unit?: string + + // Specify the number of decimals Grafana includes in the rendered value. + // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. + // For example 1.1234 will display as 1.12 and 100.456 will display as 100. + // To display all decimals, set the unit to `String`. + decimals?: number + + // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + min?: number + // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + max?: number + + // Convert input values into a display string + mappings?: [...ValueMapping] + + // Map numeric values to states + thresholds?: ThresholdsConfig + + // Panel color configuration + color?: FieldColor + + // The behavior when clicking on a result + links?: [...] + + // Alternative to empty string + noValue?: string + + // custom is specified by the FieldConfig field + // in panel plugin schemas. + custom?: {...} +} + +DynamicConfigValue: { + id: string | *"" + value?: _ +} + +// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. +// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. +MatcherConfig: { + // The matcher id. This is used to find the matcher implementation from registry. + id: string | *"" + // The matcher options. This is specific to the matcher implementation. + options?: _ +} + +Threshold: { + value: number + color: string +} + +ThresholdsMode: "absolute" | "percentage" + +ThresholdsConfig: { + mode: ThresholdsMode + steps: [...Threshold] +} + +ValueMapping: ValueMap | RangeMap | RegexMap | SpecialValueMap + +// Supported value mapping types +// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. +MappingType: "value" | "range" | "regex" | "special" @cog(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue") + +// Maps text values to a color or different display text and color. +// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +ValueMap: { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "value" + type: "value" + // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } + options: [string]: ValueMappingResult +} + +// Maps numerical ranges to a display text and color. +// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +RangeMap: { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "range" + type: "range" + // Range to match against and the result to apply when the value is within the range + options: { + // Min value of the range. It can be null which means -Infinity + from: float64 | null + // Max value of the range. It can be null which means +Infinity + to: float64 | null + // Config to apply when the value is within the range + result: ValueMappingResult + } +} + +// Maps regular expressions to replacement text and a color. +// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +RegexMap: { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "regex" + type: "regex" + // Regular expression to match against and the result to apply when the value matches the regex + options: { + // Regular expression to match against + pattern: string + // Config to apply when the value matches the regex + result: ValueMappingResult + } +} + +// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. +// See SpecialValueMatch to see the list of special values. +// For example, you can configure a special value mapping so that null values appear as N/A. +SpecialValueMap: { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "special" + type: "special" + options: { + // Special value to match against + match: SpecialValueMatch + // Config to apply when the value matches the special value + result: ValueMappingResult + } +} + +// Special value types supported by the `SpecialValueMap` +SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cog(kind="enum",memberNames="True|False|Null|NaN|NullAndNaN|Empty") + +// Result used as replacement with text and color when the value matches +ValueMappingResult: { + // Text to display when the value matches + text?: string + // Text to use when the value matches + color?: string + // Icon to display when the value matches. Only specific visualizations. + icon?: string + // Position in the mapping array. Only used internally. + index?: int32 +} + +// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. +// Continuous color interpolates a color using the percentage of a value relative to min and max. +// Accepted values are: +// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold +// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations +// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations +// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode +// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode +// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode +// `continuous-YlRd`: Continuous Yellow-Red palette mode +// `continuous-BlPu`: Continuous Blue-Purple palette mode +// `continuous-YlBl`: Continuous Yellow-Blue palette mode +// `continuous-blues`: Continuous Blue palette mode +// `continuous-reds`: Continuous Red palette mode +// `continuous-greens`: Continuous Green palette mode +// `continuous-purples`: Continuous Purple palette mode +// `shades`: Shades of a single color. Specify a single color, useful in an override rule. +// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. +FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" + +// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. +FieldColorSeriesByMode: "min" | "max" | "last" + +// Map a field to a color. +FieldColor: { + // The main color scheme mode. + mode: FieldColorModeId + // The fixed color value for fixed or shades color modes. + fixedColor?: string + // Some visualizations need to know how to assign a series color from by value color schemes. + seriesBy?: FieldColorSeriesByMode +} + +// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) +DashboardLinkType: "link" | "dashboards" + +// --- Common types --- +Kind: { + kind: string + spec: _ + metadata?: _ +} + +// --- Kinds --- +VizConfigSpec: { + pluginVersion: string + options: [string]: _ + fieldConfig: FieldConfigSource +} + +VizConfigKind: { + // The kind of a VizConfigKind is the plugin ID + kind: string + spec: VizConfigSpec +} + +AnnotationQuerySpec: { + datasource?: DataSourceRef + query?: DataQueryKind + enable: bool + hide: bool + iconColor: string + name: string + builtIn?: bool | *false + filter?: AnnotationPanelFilter +} + +AnnotationQueryKind: { + kind: "AnnotationQuery" + spec: AnnotationQuerySpec +} + +QueryOptionsSpec: { + timeFrom?: string + maxDataPoints?: int + timeShift?: string + queryCachingTTL?: int + interval?: string + cacheTimeout?: string + hideTimeOverride?: bool +} + +DataQueryKind: { + // The kind of a DataQueryKind is the datasource type + kind: string + spec: [string]: _ +} + +PanelQuerySpec: { + query: DataQueryKind + datasource?: DataSourceRef + + refId: string + hidden: bool +} + +PanelQueryKind: { + kind: "PanelQuery" + spec: PanelQuerySpec +} + +TransformationKind: { + // The kind of a TransformationKind is the transformation ID + kind: string + spec: DataTransformerConfig +} + +QueryGroupSpec: { + queries: [...PanelQueryKind] + transformations: [...TransformationKind] + queryOptions: QueryOptionsSpec +} + +QueryGroupKind: { + kind: "QueryGroup" + spec: QueryGroupSpec +} + +TimeRangeOption: { + display: string | *"Last 6 hours" + from: string | *"now-6h" + to: string | *"now" +} + +// Time configuration +// It defines the default time config for the time picker, the refresh picker for the specific dashboard. +TimeSettingsSpec: { + // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". + timezone?: string | *"browser" + // Start time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + from: string | *"now-6h" + // End time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + to: string | *"now" + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + autoRefresh: string // v1: refresh + // Interval options available in the refresh picker dropdown. + autoRefreshIntervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] // v1: timepicker.refresh_intervals + // Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. + quickRanges?: [...TimeRangeOption] // v1: timepicker.quick_ranges , not exposed in the UI + // Whether timepicker is visible or not. + hideTimepicker: bool // v1: timepicker.hidden + // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". + weekStart?: "saturday" | "monday" | "sunday" + // The month that the fiscal year starts on. 0 = January, 11 = December + fiscalYearStartMonth: int + // Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. + nowDelay?: string // v1: timepicker.nowDelay +} + +RepeatMode: "variable" // other repeat modes will be added in the future: label, frame + +RepeatOptions: { + mode: RepeatMode + value: string + direction?: "h" | "v" + maxPerRow?: int +} + +RowRepeatOptions: { + mode: RepeatMode + value: string +} + +ResponsiveGridRepeatOptions: { + mode: RepeatMode + value: string +} + +GridLayoutItemSpec: { + x: int + y: int + width: int + height: int + element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference + repeat?: RepeatOptions +} + +GridLayoutItemKind: { + kind: "GridLayoutItem" + spec: GridLayoutItemSpec +} + +GridLayoutRowKind: { + kind: "GridLayoutRow" + spec: GridLayoutRowSpec +} + +GridLayoutRowSpec: { + y: int + collapsed: bool + title: string + elements: [...GridLayoutItemKind] // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. + repeat?: RowRepeatOptions +} + +GridLayoutSpec: { + items: [...GridLayoutItemKind | GridLayoutRowKind] +} + +GridLayoutKind: { + kind: "GridLayout" + spec: GridLayoutSpec +} + +RowsLayoutKind: { + kind: "RowsLayout" + spec: RowsLayoutSpec +} + +RowsLayoutSpec: { + rows: [...RowsLayoutRowKind] +} + +RowsLayoutRowKind: { + kind: "RowsLayoutRow" + spec: RowsLayoutRowSpec +} + +RowsLayoutRowSpec: { + title?: string + collapsed: bool + repeat?: RowRepeatOptions + layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind +} + +ResponsiveGridLayoutKind: { + kind: "ResponsiveGridLayout" + spec: ResponsiveGridLayoutSpec +} + +ResponsiveGridLayoutSpec: { + row: string + col: string + items: [...ResponsiveGridLayoutItemKind] +} + +ResponsiveGridLayoutItemKind: { + kind: "ResponsiveGridLayoutItem" + spec: ResponsiveGridLayoutItemSpec +} + +ResponsiveGridLayoutItemSpec: { + element: ElementReference + repeat?: ResponsiveGridRepeatOptions +} + +TabsLayoutKind: { + kind: "TabsLayout" + spec: TabsLayoutSpec +} + +TabsLayoutSpec: { + tabs: [...TabsLayoutTabKind] +} + +TabsLayoutTabKind: { + kind: "TabsLayoutTab" + spec: TabsLayoutTabSpec +} + +TabsLayoutTabSpec: { + title?: string + layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind +} + +PanelSpec: { + id: number + title: string + description: string + links: [...DataLink] + data: QueryGroupKind + vizConfig: VizConfigKind + transparent?: bool +} + +PanelKind: { + kind: "Panel" + spec: PanelSpec +} + +ElementReference: { + kind: "ElementReference" + name: string +} + +// Start FIXME: variables - in CUE PR - this are things that should be added into the cue schema +// TODO: properties such as `hide`, `skipUrlSync`, `multi` are type boolean, and in the old schema they are conditional, +// should we make them conditional in the new schema as well? or should we make them required but default to false? + +// Variable types +VariableValue: VariableValueSingle | [...VariableValueSingle] + +VariableValueSingle: string | bool | number | CustomVariableValue + +// Custom formatter variable +CustomFormatterVariable: { + name: string + type: VariableType + multi: bool + includeAll: bool +} + +// Custom variable value +CustomVariableValue: { + // The format name or function used in the expression + formatter: *null | string | VariableCustomFormatterFn +} + +// Custom formatter function +VariableCustomFormatterFn: { + value: _ + legacyVariableModel: { + name: string + type: VariableType + multi: bool + includeAll: bool + } + legacyDefaultFormatter?: VariableCustomFormatterFn +} + +// Dashboard variable type +// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. +// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). +// `constant`: Define a hidden constant. +// `datasource`: Quickly change the data source for an entire dashboard. +// `interval`: Interval variables represent time spans. +// `textbox`: Display a free text input field with an optional default value. +// `custom`: Define the variable options manually using a comma-separated list. +// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables +VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | + "system" | "snapshot" + +VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind + +// Sort variable options +// Accepted values are: +// `disabled`: No sorting +// `alphabeticalAsc`: Alphabetical ASC +// `alphabeticalDesc`: Alphabetical DESC +// `numericalAsc`: Numerical ASC +// `numericalDesc`: Numerical DESC +// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC +// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC +// `naturalAsc`: Natural ASC +// `naturalDesc`: Natural DESC +// VariableSort enum with default value +VariableSort: "disabled" | "alphabeticalAsc" | "alphabeticalDesc" | "numericalAsc" | "numericalDesc" | "alphabeticalCaseInsensitiveAsc" | "alphabeticalCaseInsensitiveDesc" | "naturalAsc" | "naturalDesc" + +// Options to config when to refresh a variable +// `never`: Never refresh the variable +// `onDashboardLoad`: Queries the data source every time the dashboard loads. +// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes. +VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged" + +// Determine if the variable shows on dashboard +// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing). +VariableHide: *"dontHide" | "hideLabel" | "hideVariable" + +// FIXME: should we introduce this? --- Variable value option +VariableValueOption: { + label: string + value: VariableValueSingle + group?: string +} + +// Variable option specification +VariableOption: { + // Whether the option is selected or not + selected?: bool + // Text to be displayed for the option + text: string | [...string] + // Value of the option + value: string | [...string] +} + +// Query variable specification +QueryVariableSpec: { + name: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + label?: string + hide: VariableHide + refresh: VariableRefresh + skipUrlSync: bool | *false + description?: string + datasource?: DataSourceRef + query: DataQueryKind + regex: string | *"" + sort: VariableSort + definition?: string + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + placeholder?: string +} + +// Query variable kind +QueryVariableKind: { + kind: "QueryVariable" + spec: QueryVariableSpec +} + +// Text variable specification +TextVariableSpec: { + name: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + query: string | *"" + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Text variable kind +TextVariableKind: { + kind: "TextVariable" + spec: TextVariableSpec +} + +// Constant variable specification +ConstantVariableSpec: { + name: string | *"" + query: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Constant variable kind +ConstantVariableKind: { + kind: "ConstantVariable" + spec: ConstantVariableSpec +} + +// Datasource variable specification +DatasourceVariableSpec: { + name: string | *"" + pluginId: string | *"" + refresh: VariableRefresh + regex: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Datasource variable kind +DatasourceVariableKind: { + kind: "DatasourceVariable" + spec: DatasourceVariableSpec +} + +// Interval variable specification +IntervalVariableSpec: { + name: string | *"" + query: string | *"" + current: VariableOption | *{ + text: "" + value: "" + } + options: [...VariableOption] | *[] + auto: bool | *false + auto_min: string | *"" + auto_count: int | *0 + refresh: VariableRefresh + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Interval variable kind +IntervalVariableKind: { + kind: "IntervalVariable" + spec: IntervalVariableSpec +} + +// Custom variable specification +CustomVariableSpec: { + name: string | *"" + query: string | *"" + current: VariableOption + options: [...VariableOption] | *[] + multi: bool | *false + includeAll: bool | *false + allValue?: string + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Custom variable kind +CustomVariableKind: { + kind: "CustomVariable" + spec: CustomVariableSpec +} + +// GroupBy variable specification +GroupByVariableSpec: { + name: string | *"" + datasource?: DataSourceRef + current: VariableOption | *{ + text: "" + value: "" + } + options: [...VariableOption] | *[] + multi: bool | *false + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Group variable kind +GroupByVariableKind: { + kind: "GroupByVariable" + spec: GroupByVariableSpec +} + +// Adhoc variable specification +AdhocVariableSpec: { + name: string | *"" + datasource?: DataSourceRef + baseFilters: [...AdHocFilterWithLabels] | *[] + filters: [...AdHocFilterWithLabels] | *[] + defaultKeys: [...MetricFindValue] | *[] + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +// Define the MetricFindValue type +MetricFindValue: { + text: string + value?: string | number + group?: string + expandable?: bool +} + +// Define the AdHocFilterWithLabels type +AdHocFilterWithLabels: { + key: string + operator: string + value: string + values?: [...string] + keyLabel?: string + valueLabels?: [...string] + forceEdit?: bool + // @deprecated + condition?: string +} + +// Adhoc variable kind +AdhocVariableKind: { + kind: "AdhocVariable" + spec: AdhocVariableSpec +} diff --git a/apps/dashboard/tshack/v0alpha1_spec_gen.ts b/apps/dashboard/tshack/v0alpha1_spec_gen.ts new file mode 100644 index 00000000000..3de7e52c72e --- /dev/null +++ b/apps/dashboard/tshack/v0alpha1_spec_gen.ts @@ -0,0 +1,7 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface Spec { + [key: string]: any; +} + +export const defaultSpec = (): Spec => ({}); diff --git a/apps/dashboard/tshack/v1alpha1_spec_gen.ts b/apps/dashboard/tshack/v1alpha1_spec_gen.ts new file mode 100644 index 00000000000..3de7e52c72e --- /dev/null +++ b/apps/dashboard/tshack/v1alpha1_spec_gen.ts @@ -0,0 +1,7 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface Spec { + [key: string]: any; +} + +export const defaultSpec = (): Spec => ({}); diff --git a/packages/grafana-schema/src/schema/dashboard/v0alpha1/dashboard_object_gen.ts b/packages/grafana-schema/src/schema/dashboard/v0alpha1/dashboard_object_gen.ts new file mode 100644 index 00000000000..a89d50ebd2d --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v0alpha1/dashboard_object_gen.ts @@ -0,0 +1,49 @@ +/* + * This file was generated by grafana-app-sdk. DO NOT EDIT. + */ +import { Spec } from './types.spec.gen'; +import { Status } from './types.status.gen'; + +export interface Metadata { + name: string; + namespace: string; + generateName?: string; + selfLink?: string; + uid?: string; + resourceVersion?: string; + generation?: number; + creationTimestamp?: string; + deletionTimestamp?: string; + deletionGracePeriodSeconds?: number; + labels?: Record; + annotations?: Record; + ownerReferences?: OwnerReference[]; + finalizers?: string[]; + managedFields?: ManagedFieldsEntry[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller?: boolean; + blockOwnerDeletion?: boolean; +} + +export interface ManagedFieldsEntry { + manager?: string; + operation?: string; + apiVersion?: string; + time?: string; + fieldsType?: string; + subresource?: string; +} + +export interface Dashboard { + kind: string; + apiVersion: string; + metadata: Metadata; + spec: Spec; + status: Status; +} diff --git a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.metadata.gen.ts b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.metadata.gen.ts new file mode 100644 index 00000000000..4377f3c1d08 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.metadata.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +export interface Metadata { + updateTimestamp: string; + createdBy: string; + uid: string; + creationTimestamp: string; + deletionTimestamp?: string; + finalizers: string[]; + resourceVersion: string; + generation: number; + updatedBy: string; + labels: Record; +} + +export const defaultMetadata = (): Metadata => ({ + updateTimestamp: "", + createdBy: "", + uid: "", + creationTimestamp: "", + finalizers: [], + resourceVersion: "", + generation: 0, + updatedBy: "", + labels: {}, +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts new file mode 100644 index 00000000000..3de7e52c72e --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts @@ -0,0 +1,7 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface Spec { + [key: string]: any; +} + +export const defaultSpec = (): Spec => ({}); diff --git a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts new file mode 100644 index 00000000000..29494dfaf24 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// ConversionStatus is the status of the conversion of the dashboard. +export interface ConversionStatus { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + failed: boolean; + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + storedVersion: string; + // The error message from the conversion. + // Empty if the conversion has not failed. + error: string; +} + +export const defaultConversionStatus = (): ConversionStatus => ({ + failed: false, + storedVersion: "", + error: "", +}); + +export interface Status { + // Optional conversion status. + conversion?: ConversionStatus; +} + +export const defaultStatus = (): Status => ({ +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v1alpha1/dashboard_object_gen.ts b/packages/grafana-schema/src/schema/dashboard/v1alpha1/dashboard_object_gen.ts new file mode 100644 index 00000000000..a89d50ebd2d --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v1alpha1/dashboard_object_gen.ts @@ -0,0 +1,49 @@ +/* + * This file was generated by grafana-app-sdk. DO NOT EDIT. + */ +import { Spec } from './types.spec.gen'; +import { Status } from './types.status.gen'; + +export interface Metadata { + name: string; + namespace: string; + generateName?: string; + selfLink?: string; + uid?: string; + resourceVersion?: string; + generation?: number; + creationTimestamp?: string; + deletionTimestamp?: string; + deletionGracePeriodSeconds?: number; + labels?: Record; + annotations?: Record; + ownerReferences?: OwnerReference[]; + finalizers?: string[]; + managedFields?: ManagedFieldsEntry[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller?: boolean; + blockOwnerDeletion?: boolean; +} + +export interface ManagedFieldsEntry { + manager?: string; + operation?: string; + apiVersion?: string; + time?: string; + fieldsType?: string; + subresource?: string; +} + +export interface Dashboard { + kind: string; + apiVersion: string; + metadata: Metadata; + spec: Spec; + status: Status; +} diff --git a/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.metadata.gen.ts b/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.metadata.gen.ts new file mode 100644 index 00000000000..4377f3c1d08 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.metadata.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +export interface Metadata { + updateTimestamp: string; + createdBy: string; + uid: string; + creationTimestamp: string; + deletionTimestamp?: string; + finalizers: string[]; + resourceVersion: string; + generation: number; + updatedBy: string; + labels: Record; +} + +export const defaultMetadata = (): Metadata => ({ + updateTimestamp: "", + createdBy: "", + uid: "", + creationTimestamp: "", + finalizers: [], + resourceVersion: "", + generation: 0, + updatedBy: "", + labels: {}, +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts new file mode 100644 index 00000000000..3de7e52c72e --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts @@ -0,0 +1,7 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface Spec { + [key: string]: any; +} + +export const defaultSpec = (): Spec => ({}); diff --git a/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.status.gen.ts new file mode 100644 index 00000000000..29494dfaf24 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v1alpha1/types.status.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// ConversionStatus is the status of the conversion of the dashboard. +export interface ConversionStatus { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + failed: boolean; + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + storedVersion: string; + // The error message from the conversion. + // Empty if the conversion has not failed. + error: string; +} + +export const defaultConversionStatus = (): ConversionStatus => ({ + failed: false, + storedVersion: "", + error: "", +}); + +export interface Status { + // Optional conversion status. + conversion?: ConversionStatus; +} + +export const defaultStatus = (): Status => ({ +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/dashboard_object_gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/dashboard_object_gen.ts new file mode 100644 index 00000000000..a89d50ebd2d --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/dashboard_object_gen.ts @@ -0,0 +1,49 @@ +/* + * This file was generated by grafana-app-sdk. DO NOT EDIT. + */ +import { Spec } from './types.spec.gen'; +import { Status } from './types.status.gen'; + +export interface Metadata { + name: string; + namespace: string; + generateName?: string; + selfLink?: string; + uid?: string; + resourceVersion?: string; + generation?: number; + creationTimestamp?: string; + deletionTimestamp?: string; + deletionGracePeriodSeconds?: number; + labels?: Record; + annotations?: Record; + ownerReferences?: OwnerReference[]; + finalizers?: string[]; + managedFields?: ManagedFieldsEntry[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller?: boolean; + blockOwnerDeletion?: boolean; +} + +export interface ManagedFieldsEntry { + manager?: string; + operation?: string; + apiVersion?: string; + time?: string; + fieldsType?: string; + subresource?: string; +} + +export interface Dashboard { + kind: string; + apiVersion: string; + metadata: Metadata; + spec: Spec; + status: Status; +} diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.metadata.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.metadata.gen.ts new file mode 100644 index 00000000000..4377f3c1d08 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.metadata.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +export interface Metadata { + updateTimestamp: string; + createdBy: string; + uid: string; + creationTimestamp: string; + deletionTimestamp?: string; + finalizers: string[]; + resourceVersion: string; + generation: number; + updatedBy: string; + labels: Record; +} + +export const defaultMetadata = (): Metadata => ({ + updateTimestamp: "", + createdBy: "", + uid: "", + creationTimestamp: "", + finalizers: [], + resourceVersion: "", + generation: 0, + updatedBy: "", + labels: {}, +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts new file mode 100644 index 00000000000..477ec4e63ad --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts @@ -0,0 +1,1316 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface AnnotationQueryKind { + kind: "AnnotationQuery"; + spec: AnnotationQuerySpec; +} + +export const defaultAnnotationQueryKind = (): AnnotationQueryKind => ({ + kind: "AnnotationQuery", + spec: defaultAnnotationQuerySpec(), +}); + +export interface AnnotationQuerySpec { + datasource?: DataSourceRef; + query?: DataQueryKind; + enable: boolean; + hide: boolean; + iconColor: string; + name: string; + builtIn?: boolean; + filter?: AnnotationPanelFilter; +} + +export const defaultAnnotationQuerySpec = (): AnnotationQuerySpec => ({ + enable: false, + hide: false, + iconColor: "", + name: "", + builtIn: false, +}); + +export interface DataSourceRef { + // The plugin type-id + type?: string; + // Specific datasource instance + uid?: string; +} + +export const defaultDataSourceRef = (): DataSourceRef => ({ +}); + +export interface DataQueryKind { + // The kind of a DataQueryKind is the datasource type + kind: string; + spec: Record; +} + +export const defaultDataQueryKind = (): DataQueryKind => ({ + kind: "", + spec: {}, +}); + +export interface AnnotationPanelFilter { + // Should the specified panels be included or excluded + exclude?: boolean; + // Panel IDs that should be included or excluded + ids: number[]; +} + +export const defaultAnnotationPanelFilter = (): AnnotationPanelFilter => ({ + exclude: false, + ids: [], +}); + +// "Off" for no shared crosshair or tooltip (default). +// "Crosshair" for shared crosshair. +// "Tooltip" for shared crosshair AND shared tooltip. +export type DashboardCursorSync = "Off" | "Crosshair" | "Tooltip"; + +export const defaultDashboardCursorSync = (): DashboardCursorSync => ("Off"); + +// Supported dashboard elements +// |* more element types in the future +export type Element = PanelKind | LibraryPanelKind; + +export const defaultElement = (): Element => (defaultPanelKind()); + +export interface PanelKind { + kind: "Panel"; + spec: PanelSpec; +} + +export const defaultPanelKind = (): PanelKind => ({ + kind: "Panel", + spec: defaultPanelSpec(), +}); + +export interface PanelSpec { + id: number; + title: string; + description: string; + links: DataLink[]; + data: QueryGroupKind; + vizConfig: VizConfigKind; + transparent?: boolean; +} + +export const defaultPanelSpec = (): PanelSpec => ({ + id: 0, + title: "", + description: "", + links: [], + data: defaultQueryGroupKind(), + vizConfig: defaultVizConfigKind(), +}); + +export interface DataLink { + title: string; + url: string; + targetBlank?: boolean; +} + +export const defaultDataLink = (): DataLink => ({ + title: "", + url: "", +}); + +export interface QueryGroupKind { + kind: "QueryGroup"; + spec: QueryGroupSpec; +} + +export const defaultQueryGroupKind = (): QueryGroupKind => ({ + kind: "QueryGroup", + spec: defaultQueryGroupSpec(), +}); + +export interface QueryGroupSpec { + queries: PanelQueryKind[]; + transformations: TransformationKind[]; + queryOptions: QueryOptionsSpec; +} + +export const defaultQueryGroupSpec = (): QueryGroupSpec => ({ + queries: [], + transformations: [], + queryOptions: defaultQueryOptionsSpec(), +}); + +export interface PanelQueryKind { + kind: "PanelQuery"; + spec: PanelQuerySpec; +} + +export const defaultPanelQueryKind = (): PanelQueryKind => ({ + kind: "PanelQuery", + spec: defaultPanelQuerySpec(), +}); + +export interface PanelQuerySpec { + query: DataQueryKind; + datasource?: DataSourceRef; + refId: string; + hidden: boolean; +} + +export const defaultPanelQuerySpec = (): PanelQuerySpec => ({ + query: defaultDataQueryKind(), + refId: "", + hidden: false, +}); + +export interface TransformationKind { + // The kind of a TransformationKind is the transformation ID + kind: string; + spec: DataTransformerConfig; +} + +export const defaultTransformationKind = (): TransformationKind => ({ + kind: "", + spec: defaultDataTransformerConfig(), +}); + +// Transformations allow to manipulate data returned by a query before the system applies a visualization. +// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, +// use the output of one transformation as the input to another transformation, etc. +export interface DataTransformerConfig { + // Unique identifier of transformer + id: string; + // Disabled transformations are skipped + disabled?: boolean; + // Optional frame matcher. When missing it will be applied to all results + filter?: MatcherConfig; + // Where to pull DataFrames from as input to transformation + topic?: DataTopic; + // Options to be passed to the transformer + // Valid options depend on the transformer id + options: any; +} + +export const defaultDataTransformerConfig = (): DataTransformerConfig => ({ + id: "", + options: {}, +}); + +// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. +// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. +export interface MatcherConfig { + // The matcher id. This is used to find the matcher implementation from registry. + id: string; + // The matcher options. This is specific to the matcher implementation. + options?: any; +} + +export const defaultMatcherConfig = (): MatcherConfig => ({ + id: "", +}); + +// A topic is attached to DataFrame metadata in query results. +// This specifies where the data should be used. +export type DataTopic = "series" | "annotations" | "alertStates"; + +export const defaultDataTopic = (): DataTopic => ("series"); + +export interface QueryOptionsSpec { + timeFrom?: string; + maxDataPoints?: number; + timeShift?: string; + queryCachingTTL?: number; + interval?: string; + cacheTimeout?: string; + hideTimeOverride?: boolean; +} + +export const defaultQueryOptionsSpec = (): QueryOptionsSpec => ({ +}); + +export interface VizConfigKind { + // The kind of a VizConfigKind is the plugin ID + kind: string; + spec: VizConfigSpec; +} + +export const defaultVizConfigKind = (): VizConfigKind => ({ + kind: "", + spec: defaultVizConfigSpec(), +}); + +// --- Kinds --- +export interface VizConfigSpec { + pluginVersion: string; + options: Record; + fieldConfig: FieldConfigSource; +} + +export const defaultVizConfigSpec = (): VizConfigSpec => ({ + pluginVersion: "", + options: {}, + fieldConfig: defaultFieldConfigSource(), +}); + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +export interface FieldConfigSource { + // Defaults are the options applied to all fields. + defaults: FieldConfig; + // Overrides are the options applied to specific fields overriding the defaults. + overrides: { + matcher: MatcherConfig; + properties: DynamicConfigValue[]; + }[]; +} + +export const defaultFieldConfigSource = (): FieldConfigSource => ({ + defaults: defaultFieldConfig(), + overrides: [], +}); + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +export interface FieldConfig { + // The display value for this field. This supports template variables blank is auto + displayName?: string; + // This can be used by data sources that return and explicit naming structure for values and labels + // When this property is configured, this value is used rather than the default naming strategy. + displayNameFromDS?: string; + // Human readable field metadata + description?: string; + // An explicit path to the field in the datasource. When the frame meta includes a path, + // This will default to `${frame.meta.path}/${field.name} + // + // When defined, this value can be used as an identifier within the datasource scope, and + // may be used to update the results + path?: string; + // True if data source can write a value to the path. Auth/authz are supported separately + writeable?: boolean; + // True if data source field supports ad-hoc filters + filterable?: boolean; + // Unit a field should use. The unit you select is applied to all fields except time. + // You can use the units ID availables in Grafana or a custom unit. + // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts + // As custom unit, you can use the following formats: + // `suffix:` for custom unit that should go after value. + // `prefix:` for custom unit that should go before value. + // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. + // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. + // `count:` for a custom count unit. + // `currency:` for custom a currency unit. + unit?: string; + // Specify the number of decimals Grafana includes in the rendered value. + // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. + // For example 1.1234 will display as 1.12 and 100.456 will display as 100. + // To display all decimals, set the unit to `String`. + decimals?: number; + // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + min?: number; + // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + max?: number; + // Convert input values into a display string + mappings?: ValueMapping[]; + // Map numeric values to states + thresholds?: ThresholdsConfig; + // Panel color configuration + color?: FieldColor; + // The behavior when clicking on a result + links?: any[]; + // Alternative to empty string + noValue?: string; + // custom is specified by the FieldConfig field + // in panel plugin schemas. + custom?: Record; +} + +export const defaultFieldConfig = (): FieldConfig => ({ +}); + +export type ValueMapping = ValueMap | RangeMap | RegexMap | SpecialValueMap; + +export const defaultValueMapping = (): ValueMapping => (defaultValueMap()); + +// Maps text values to a color or different display text and color. +// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +export interface ValueMap { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "value" + type: "value"; + // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } + options: Record; +} + +export const defaultValueMap = (): ValueMap => ({ + type: "value", + options: {}, +}); + +// Result used as replacement with text and color when the value matches +export interface ValueMappingResult { + // Text to display when the value matches + text?: string; + // Text to use when the value matches + color?: string; + // Icon to display when the value matches. Only specific visualizations. + icon?: string; + // Position in the mapping array. Only used internally. + index?: number; +} + +export const defaultValueMappingResult = (): ValueMappingResult => ({ +}); + +// Maps numerical ranges to a display text and color. +// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +export interface RangeMap { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "range" + type: "range"; + // Range to match against and the result to apply when the value is within the range + options: { + // Min value of the range. It can be null which means -Infinity + from: number | null; + // Max value of the range. It can be null which means +Infinity + to: number | null; + // Config to apply when the value is within the range + result: ValueMappingResult; + }; +} + +export const defaultRangeMap = (): RangeMap => ({ + type: "range", + options: { + from: 0, + to: 0, + result: defaultValueMappingResult(), +}, +}); + +// Maps regular expressions to replacement text and a color. +// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +export interface RegexMap { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "regex" + type: "regex"; + // Regular expression to match against and the result to apply when the value matches the regex + options: { + // Regular expression to match against + pattern: string; + // Config to apply when the value matches the regex + result: ValueMappingResult; + }; +} + +export const defaultRegexMap = (): RegexMap => ({ + type: "regex", + options: { + pattern: "", + result: defaultValueMappingResult(), +}, +}); + +// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. +// See SpecialValueMatch to see the list of special values. +// For example, you can configure a special value mapping so that null values appear as N/A. +export interface SpecialValueMap { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "special" + type: "special"; + options: { + // Special value to match against + match: SpecialValueMatch; + // Config to apply when the value matches the special value + result: ValueMappingResult; + }; +} + +export const defaultSpecialValueMap = (): SpecialValueMap => ({ + type: "special", + options: { + match: "true", + result: defaultValueMappingResult(), +}, +}); + +// Special value types supported by the `SpecialValueMap` +export type SpecialValueMatch = "true" | "false" | "null" | "nan" | "null+nan" | "empty"; + +export const defaultSpecialValueMatch = (): SpecialValueMatch => ("true"); + +export interface ThresholdsConfig { + mode: ThresholdsMode; + steps: Threshold[]; +} + +export const defaultThresholdsConfig = (): ThresholdsConfig => ({ + mode: "absolute", + steps: [], +}); + +export type ThresholdsMode = "absolute" | "percentage"; + +export const defaultThresholdsMode = (): ThresholdsMode => ("absolute"); + +export interface Threshold { + value: number; + color: string; +} + +export const defaultThreshold = (): Threshold => ({ + value: 0, + color: "", +}); + +// Map a field to a color. +export interface FieldColor { + // The main color scheme mode. + mode: FieldColorModeId; + // The fixed color value for fixed or shades color modes. + fixedColor?: string; + // Some visualizations need to know how to assign a series color from by value color schemes. + seriesBy?: FieldColorSeriesByMode; +} + +export const defaultFieldColor = (): FieldColor => ({ + mode: "thresholds", +}); + +// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. +// Continuous color interpolates a color using the percentage of a value relative to min and max. +// Accepted values are: +// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold +// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations +// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations +// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode +// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode +// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode +// `continuous-YlRd`: Continuous Yellow-Red palette mode +// `continuous-BlPu`: Continuous Blue-Purple palette mode +// `continuous-YlBl`: Continuous Yellow-Blue palette mode +// `continuous-blues`: Continuous Blue palette mode +// `continuous-reds`: Continuous Red palette mode +// `continuous-greens`: Continuous Green palette mode +// `continuous-purples`: Continuous Purple palette mode +// `shades`: Shades of a single color. Specify a single color, useful in an override rule. +// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. +export type FieldColorModeId = "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades"; + +export const defaultFieldColorModeId = (): FieldColorModeId => ("thresholds"); + +// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. +export type FieldColorSeriesByMode = "min" | "max" | "last"; + +export const defaultFieldColorSeriesByMode = (): FieldColorSeriesByMode => ("min"); + +export interface DynamicConfigValue { + id: string; + value?: any; +} + +export const defaultDynamicConfigValue = (): DynamicConfigValue => ({ + id: "", +}); + +export interface LibraryPanelKind { + kind: "LibraryPanel"; + spec: LibraryPanelKindSpec; +} + +export const defaultLibraryPanelKind = (): LibraryPanelKind => ({ + kind: "LibraryPanel", + spec: defaultLibraryPanelKindSpec(), +}); + +export interface LibraryPanelKindSpec { + // Panel ID for the library panel in the dashboard + id: number; + // Title for the library panel in the dashboard + title: string; + libraryPanel: LibraryPanelRef; +} + +export const defaultLibraryPanelKindSpec = (): LibraryPanelKindSpec => ({ + id: 0, + title: "", + libraryPanel: defaultLibraryPanelRef(), +}); + +// A library panel is a reusable panel that you can use in any dashboard. +// When you make a change to a library panel, that change propagates to all instances of where the panel is used. +// Library panels streamline reuse of panels across multiple dashboards. +export interface LibraryPanelRef { + // Library panel name + name: string; + // Library panel uid + uid: string; +} + +export const defaultLibraryPanelRef = (): LibraryPanelRef => ({ + name: "", + uid: "", +}); + +export interface GridLayoutKind { + kind: "GridLayout"; + spec: GridLayoutSpec; +} + +export const defaultGridLayoutKind = (): GridLayoutKind => ({ + kind: "GridLayout", + spec: defaultGridLayoutSpec(), +}); + +export interface GridLayoutSpec { + items: (GridLayoutItemKind | GridLayoutRowKind)[]; +} + +export const defaultGridLayoutSpec = (): GridLayoutSpec => ({ + items: [], +}); + +export interface GridLayoutItemKind { + kind: "GridLayoutItem"; + spec: GridLayoutItemSpec; +} + +export const defaultGridLayoutItemKind = (): GridLayoutItemKind => ({ + kind: "GridLayoutItem", + spec: defaultGridLayoutItemSpec(), +}); + +export interface GridLayoutItemSpec { + x: number; + y: number; + width: number; + height: number; + // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference + element: ElementReference; + repeat?: RepeatOptions; +} + +export const defaultGridLayoutItemSpec = (): GridLayoutItemSpec => ({ + x: 0, + y: 0, + width: 0, + height: 0, + element: defaultElementReference(), +}); + +export interface ElementReference { + kind: "ElementReference"; + name: string; +} + +export const defaultElementReference = (): ElementReference => ({ + kind: "ElementReference", + name: "", +}); + +export interface RepeatOptions { + mode: "variable"; + value: string; + direction?: "h" | "v"; + maxPerRow?: number; +} + +export const defaultRepeatOptions = (): RepeatOptions => ({ + mode: RepeatMode, + value: "", +}); + +// other repeat modes will be added in the future: label, frame +export const RepeatMode = "variable"; + +export interface GridLayoutRowKind { + kind: "GridLayoutRow"; + spec: GridLayoutRowSpec; +} + +export const defaultGridLayoutRowKind = (): GridLayoutRowKind => ({ + kind: "GridLayoutRow", + spec: defaultGridLayoutRowSpec(), +}); + +export interface GridLayoutRowSpec { + y: number; + collapsed: boolean; + title: string; + // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. + elements: GridLayoutItemKind[]; + repeat?: RowRepeatOptions; +} + +export const defaultGridLayoutRowSpec = (): GridLayoutRowSpec => ({ + y: 0, + collapsed: false, + title: "", + elements: [], +}); + +export interface RowRepeatOptions { + mode: "variable"; + value: string; +} + +export const defaultRowRepeatOptions = (): RowRepeatOptions => ({ + mode: RepeatMode, + value: "", +}); + +export interface RowsLayoutKind { + kind: "RowsLayout"; + spec: RowsLayoutSpec; +} + +export const defaultRowsLayoutKind = (): RowsLayoutKind => ({ + kind: "RowsLayout", + spec: defaultRowsLayoutSpec(), +}); + +export interface RowsLayoutSpec { + rows: RowsLayoutRowKind[]; +} + +export const defaultRowsLayoutSpec = (): RowsLayoutSpec => ({ + rows: [], +}); + +export interface RowsLayoutRowKind { + kind: "RowsLayoutRow"; + spec: RowsLayoutRowSpec; +} + +export const defaultRowsLayoutRowKind = (): RowsLayoutRowKind => ({ + kind: "RowsLayoutRow", + spec: defaultRowsLayoutRowSpec(), +}); + +export interface RowsLayoutRowSpec { + title?: string; + collapsed: boolean; + repeat?: RowRepeatOptions; + layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind; +} + +export const defaultRowsLayoutRowSpec = (): RowsLayoutRowSpec => ({ + collapsed: false, + layout: defaultGridLayoutKind(), +}); + +export interface ResponsiveGridLayoutKind { + kind: "ResponsiveGridLayout"; + spec: ResponsiveGridLayoutSpec; +} + +export const defaultResponsiveGridLayoutKind = (): ResponsiveGridLayoutKind => ({ + kind: "ResponsiveGridLayout", + spec: defaultResponsiveGridLayoutSpec(), +}); + +export interface ResponsiveGridLayoutSpec { + row: string; + col: string; + items: ResponsiveGridLayoutItemKind[]; +} + +export const defaultResponsiveGridLayoutSpec = (): ResponsiveGridLayoutSpec => ({ + row: "", + col: "", + items: [], +}); + +export interface ResponsiveGridLayoutItemKind { + kind: "ResponsiveGridLayoutItem"; + spec: ResponsiveGridLayoutItemSpec; +} + +export const defaultResponsiveGridLayoutItemKind = (): ResponsiveGridLayoutItemKind => ({ + kind: "ResponsiveGridLayoutItem", + spec: defaultResponsiveGridLayoutItemSpec(), +}); + +export interface ResponsiveGridLayoutItemSpec { + element: ElementReference; + repeat?: ResponsiveGridRepeatOptions; +} + +export const defaultResponsiveGridLayoutItemSpec = (): ResponsiveGridLayoutItemSpec => ({ + element: defaultElementReference(), +}); + +export interface ResponsiveGridRepeatOptions { + mode: "variable"; + value: string; +} + +export const defaultResponsiveGridRepeatOptions = (): ResponsiveGridRepeatOptions => ({ + mode: RepeatMode, + value: "", +}); + +export interface TabsLayoutKind { + kind: "TabsLayout"; + spec: TabsLayoutSpec; +} + +export const defaultTabsLayoutKind = (): TabsLayoutKind => ({ + kind: "TabsLayout", + spec: defaultTabsLayoutSpec(), +}); + +export interface TabsLayoutSpec { + tabs: TabsLayoutTabKind[]; +} + +export const defaultTabsLayoutSpec = (): TabsLayoutSpec => ({ + tabs: [], +}); + +export interface TabsLayoutTabKind { + kind: "TabsLayoutTab"; + spec: TabsLayoutTabSpec; +} + +export const defaultTabsLayoutTabKind = (): TabsLayoutTabKind => ({ + kind: "TabsLayoutTab", + spec: defaultTabsLayoutTabSpec(), +}); + +export interface TabsLayoutTabSpec { + title?: string; + layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind; +} + +export const defaultTabsLayoutTabSpec = (): TabsLayoutTabSpec => ({ + layout: defaultGridLayoutKind(), +}); + +// Links with references to other dashboards or external resources +export interface DashboardLink { + // Title to display with the link + title: string; + // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) + // FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType` + type: DashboardLinkType; + // Icon name to be displayed with the link + icon: string; + // Tooltip to display when the user hovers their mouse over it + tooltip: string; + // Link URL. Only required/valid if the type is link + url?: string; + // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards + tags: string[]; + // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards + asDropdown: boolean; + // If true, the link will be opened in a new tab + targetBlank: boolean; + // If true, includes current template variables values in the link as query params + includeVars: boolean; + // If true, includes current time range in the link as query params + keepTime: boolean; +} + +export const defaultDashboardLink = (): DashboardLink => ({ + title: "", + type: "link", + icon: "", + tooltip: "", + tags: [], + asDropdown: false, + targetBlank: false, + includeVars: false, + keepTime: false, +}); + +// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) +export type DashboardLinkType = "link" | "dashboards"; + +export const defaultDashboardLinkType = (): DashboardLinkType => ("link"); + +// Time configuration +// It defines the default time config for the time picker, the refresh picker for the specific dashboard. +export interface TimeSettingsSpec { + // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". + timezone?: string; + // Start time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + from: string; + // End time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + to: string; + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + // v1: refresh + autoRefresh: string; + // Interval options available in the refresh picker dropdown. + // v1: timepicker.refresh_intervals + autoRefreshIntervals: string[]; + // Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. + // v1: timepicker.quick_ranges , not exposed in the UI + quickRanges?: TimeRangeOption[]; + // Whether timepicker is visible or not. + // v1: timepicker.hidden + hideTimepicker: boolean; + // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". + weekStart?: "saturday" | "monday" | "sunday"; + // The month that the fiscal year starts on. 0 = January, 11 = December + fiscalYearStartMonth: number; + // Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. + // v1: timepicker.nowDelay + nowDelay?: string; +} + +export const defaultTimeSettingsSpec = (): TimeSettingsSpec => ({ + timezone: "browser", + from: "now-6h", + to: "now", + autoRefresh: "", + autoRefreshIntervals: [ +"5s", +"10s", +"30s", +"1m", +"5m", +"15m", +"30m", +"1h", +"2h", +"1d", +], + hideTimepicker: false, + fiscalYearStartMonth: 0, +}); + +export interface TimeRangeOption { + display: string; + from: string; + to: string; +} + +export const defaultTimeRangeOption = (): TimeRangeOption => ({ + display: "Last 6 hours", + from: "now-6h", + to: "now", +}); + +export type VariableKind = QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind; + +export const defaultVariableKind = (): VariableKind => (defaultQueryVariableKind()); + +// Query variable kind +export interface QueryVariableKind { + kind: "QueryVariable"; + spec: QueryVariableSpec; +} + +export const defaultQueryVariableKind = (): QueryVariableKind => ({ + kind: "QueryVariable", + spec: defaultQueryVariableSpec(), +}); + +// Query variable specification +export interface QueryVariableSpec { + name: string; + current: VariableOption; + label?: string; + hide: VariableHide; + refresh: VariableRefresh; + skipUrlSync: boolean; + description?: string; + datasource?: DataSourceRef; + query: DataQueryKind; + regex: string; + sort: VariableSort; + definition?: string; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + placeholder?: string; +} + +export const defaultQueryVariableSpec = (): QueryVariableSpec => ({ + name: "", + current: { text: "", value: "", }, + hide: "dontHide", + refresh: "never", + skipUrlSync: false, + query: defaultDataQueryKind(), + regex: "", + sort: "disabled", + options: [], + multi: false, + includeAll: false, +}); + +// Variable option specification +export interface VariableOption { + // Whether the option is selected or not + selected?: boolean; + // Text to be displayed for the option + text: string | string[]; + // Value of the option + value: string | string[]; +} + +export const defaultVariableOption = (): VariableOption => ({ + text: "", + value: "", +}); + +// Determine if the variable shows on dashboard +// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing). +export type VariableHide = "dontHide" | "hideLabel" | "hideVariable"; + +export const defaultVariableHide = (): VariableHide => ("dontHide"); + +// Options to config when to refresh a variable +// `never`: Never refresh the variable +// `onDashboardLoad`: Queries the data source every time the dashboard loads. +// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes. +export type VariableRefresh = "never" | "onDashboardLoad" | "onTimeRangeChanged"; + +export const defaultVariableRefresh = (): VariableRefresh => ("never"); + +// Sort variable options +// Accepted values are: +// `disabled`: No sorting +// `alphabeticalAsc`: Alphabetical ASC +// `alphabeticalDesc`: Alphabetical DESC +// `numericalAsc`: Numerical ASC +// `numericalDesc`: Numerical DESC +// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC +// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC +// `naturalAsc`: Natural ASC +// `naturalDesc`: Natural DESC +// VariableSort enum with default value +export type VariableSort = "disabled" | "alphabeticalAsc" | "alphabeticalDesc" | "numericalAsc" | "numericalDesc" | "alphabeticalCaseInsensitiveAsc" | "alphabeticalCaseInsensitiveDesc" | "naturalAsc" | "naturalDesc"; + +export const defaultVariableSort = (): VariableSort => ("disabled"); + +// Text variable kind +export interface TextVariableKind { + kind: "TextVariable"; + spec: TextVariableSpec; +} + +export const defaultTextVariableKind = (): TextVariableKind => ({ + kind: "TextVariable", + spec: defaultTextVariableSpec(), +}); + +// Text variable specification +export interface TextVariableSpec { + name: string; + current: VariableOption; + query: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultTextVariableSpec = (): TextVariableSpec => ({ + name: "", + current: { text: "", value: "", }, + query: "", + hide: "dontHide", + skipUrlSync: false, +}); + +// Constant variable kind +export interface ConstantVariableKind { + kind: "ConstantVariable"; + spec: ConstantVariableSpec; +} + +export const defaultConstantVariableKind = (): ConstantVariableKind => ({ + kind: "ConstantVariable", + spec: defaultConstantVariableSpec(), +}); + +// Constant variable specification +export interface ConstantVariableSpec { + name: string; + query: string; + current: VariableOption; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultConstantVariableSpec = (): ConstantVariableSpec => ({ + name: "", + query: "", + current: { text: "", value: "", }, + hide: "dontHide", + skipUrlSync: false, +}); + +// Datasource variable kind +export interface DatasourceVariableKind { + kind: "DatasourceVariable"; + spec: DatasourceVariableSpec; +} + +export const defaultDatasourceVariableKind = (): DatasourceVariableKind => ({ + kind: "DatasourceVariable", + spec: defaultDatasourceVariableSpec(), +}); + +// Datasource variable specification +export interface DatasourceVariableSpec { + name: string; + pluginId: string; + refresh: VariableRefresh; + regex: string; + current: VariableOption; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultDatasourceVariableSpec = (): DatasourceVariableSpec => ({ + name: "", + pluginId: "", + refresh: "never", + regex: "", + current: { text: "", value: "", }, + options: [], + multi: false, + includeAll: false, + hide: "dontHide", + skipUrlSync: false, +}); + +// Interval variable kind +export interface IntervalVariableKind { + kind: "IntervalVariable"; + spec: IntervalVariableSpec; +} + +export const defaultIntervalVariableKind = (): IntervalVariableKind => ({ + kind: "IntervalVariable", + spec: defaultIntervalVariableSpec(), +}); + +// Interval variable specification +export interface IntervalVariableSpec { + name: string; + query: string; + current: VariableOption; + options: VariableOption[]; + auto: boolean; + auto_min: string; + auto_count: number; + refresh: VariableRefresh; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultIntervalVariableSpec = (): IntervalVariableSpec => ({ + name: "", + query: "", + current: { text: "", value: "", }, + options: [], + auto: false, + auto_min: "", + auto_count: 0, + refresh: "never", + hide: "dontHide", + skipUrlSync: false, +}); + +// Custom variable kind +export interface CustomVariableKind { + kind: "CustomVariable"; + spec: CustomVariableSpec; +} + +export const defaultCustomVariableKind = (): CustomVariableKind => ({ + kind: "CustomVariable", + spec: defaultCustomVariableSpec(), +}); + +// Custom variable specification +export interface CustomVariableSpec { + name: string; + query: string; + current: VariableOption; + options: VariableOption[]; + multi: boolean; + includeAll: boolean; + allValue?: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultCustomVariableSpec = (): CustomVariableSpec => ({ + name: "", + query: "", + current: defaultVariableOption(), + options: [], + multi: false, + includeAll: false, + hide: "dontHide", + skipUrlSync: false, +}); + +// Group variable kind +export interface GroupByVariableKind { + kind: "GroupByVariable"; + spec: GroupByVariableSpec; +} + +export const defaultGroupByVariableKind = (): GroupByVariableKind => ({ + kind: "GroupByVariable", + spec: defaultGroupByVariableSpec(), +}); + +// GroupBy variable specification +export interface GroupByVariableSpec { + name: string; + datasource?: DataSourceRef; + current: VariableOption; + options: VariableOption[]; + multi: boolean; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultGroupByVariableSpec = (): GroupByVariableSpec => ({ + name: "", + current: { text: "", value: "", }, + options: [], + multi: false, + hide: "dontHide", + skipUrlSync: false, +}); + +// Adhoc variable kind +export interface AdhocVariableKind { + kind: "AdhocVariable"; + spec: AdhocVariableSpec; +} + +export const defaultAdhocVariableKind = (): AdhocVariableKind => ({ + kind: "AdhocVariable", + spec: defaultAdhocVariableSpec(), +}); + +// Adhoc variable specification +export interface AdhocVariableSpec { + name: string; + datasource?: DataSourceRef; + baseFilters: AdHocFilterWithLabels[]; + filters: AdHocFilterWithLabels[]; + defaultKeys: MetricFindValue[]; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultAdhocVariableSpec = (): AdhocVariableSpec => ({ + name: "", + baseFilters: [], + filters: [], + defaultKeys: [], + hide: "dontHide", + skipUrlSync: false, +}); + +// Define the AdHocFilterWithLabels type +export interface AdHocFilterWithLabels { + key: string; + operator: string; + value: string; + values?: string[]; + keyLabel?: string; + valueLabels?: string[]; + forceEdit?: boolean; + // @deprecated + condition?: string; +} + +export const defaultAdHocFilterWithLabels = (): AdHocFilterWithLabels => ({ + key: "", + operator: "", + value: "", +}); + +// Define the MetricFindValue type +export interface MetricFindValue { + text: string; + value?: string | number; + group?: string; + expandable?: boolean; +} + +export const defaultMetricFindValue = (): MetricFindValue => ({ + text: "", +}); + +export interface Spec { + // Title of dashboard. + annotations: AnnotationQueryKind[]; + // Configuration of dashboard cursor sync behavior. + // "Off" for no shared crosshair or tooltip (default). + // "Crosshair" for shared crosshair. + // "Tooltip" for shared crosshair AND shared tooltip. + cursorSync: DashboardCursorSync; + // Description of dashboard. + description?: string; + // Whether a dashboard is editable or not. + editable?: boolean; + elements: Record; + layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind; + // Links with references to other dashboards or external websites. + links: DashboardLink[]; + // When set to true, the dashboard will redraw panels at an interval matching the pixel width. + // This will keep data "moving left" regardless of the query refresh rate. This setting helps + // avoid dashboards presenting stale live data. + liveNow?: boolean; + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + preload: boolean; + // Plugins only. The version of the dashboard installed together with the plugin. + // This is used to determine if the dashboard should be updated when the plugin is updated. + revision?: number; + // Tags associated with dashboard. + tags: string[]; + timeSettings: TimeSettingsSpec; + // Title of dashboard. + title: string; + // Configured template variables. + variables: VariableKind[]; +} + +export const defaultSpec = (): Spec => ({ + annotations: [], + cursorSync: "Off", + editable: true, + elements: {}, + layout: defaultGridLayoutKind(), + links: [], + preload: false, + tags: [], + timeSettings: defaultTimeSettingsSpec(), + title: "", + variables: [], +}); + diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts new file mode 100644 index 00000000000..29494dfaf24 --- /dev/null +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// ConversionStatus is the status of the conversion of the dashboard. +export interface ConversionStatus { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + failed: boolean; + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + storedVersion: string; + // The error message from the conversion. + // Empty if the conversion has not failed. + error: string; +} + +export const defaultConversionStatus = (): ConversionStatus => ({ + failed: false, + storedVersion: "", + error: "", +}); + +export interface Status { + // Optional conversion status. + conversion?: ConversionStatus; +} + +export const defaultStatus = (): Status => ({ +}); + diff --git a/pkg/apis/dashboard/migration/conversion/conversion.go b/pkg/apis/dashboard/migration/conversion/conversion.go index f14162ff817..830353ce1f2 100644 --- a/pkg/apis/dashboard/migration/conversion/conversion.go +++ b/pkg/apis/dashboard/migration/conversion/conversion.go @@ -47,79 +47,127 @@ func RegisterConversions(s *runtime.Scheme) error { func Convert_V0_to_V1(in *dashboardV0.Dashboard, out *dashboardV1.Dashboard, scope conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - out.Spec = in.Spec - out.Status = &dashboardV1.DashboardStatus{ - ConversionStatus: &dashboardV1.ConversionStatus{ + + out.Spec.Object = in.Spec.Object + + out.Status = dashboardV1.DashboardStatus{ + Conversion: &dashboardV1.DashboardConversionStatus{ StoredVersion: dashboardV0.VERSION, }, } - err := migration.Migrate(out.Spec.Object, schemaversion.LATEST_VERSION) - if err != nil { - out.Status.ConversionStatus.Failed = true - out.Status.ConversionStatus.Error = err.Error() + + if err := migration.Migrate(out.Spec.Object, schemaversion.LATEST_VERSION); err != nil { + out.Status.Conversion.Failed = true + out.Status.Conversion.Error = err.Error() } + return nil } func Convert_V0_to_V2(in *dashboardV0.Dashboard, out *dashboardV2.Dashboard, scope conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - out.Spec = in.Spec - out.Status = &dashboardV2.DashboardStatus{ - ConversionStatus: &dashboardV2.ConversionStatus{ + + // TODO (@radiohead): implement V0 to V2 conversion + // This is the bare minimum conversion that is needed to make the dashboard servable. + + if v, ok := in.Spec.Object["title"]; ok { + if title, ok := v.(string); ok { + out.Spec.Title = title + } + } + + // We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail. + out.Spec.Layout = dashboardV2.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashboardV2.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashboardV2.DashboardGridLayoutSpec{}, + }, + } + + out.Status = dashboardV2.DashboardStatus{ + Conversion: &dashboardV2.DashboardConversionStatus{ StoredVersion: dashboardV0.VERSION, Failed: true, Error: "backend conversion not yet implemented", }, } + return nil } func Convert_V1_to_V0(in *dashboardV1.Dashboard, out *dashboardV0.Dashboard, scope conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - out.Spec = in.Spec - out.Status = &dashboardV0.DashboardStatus{ - ConversionStatus: &dashboardV0.ConversionStatus{ + + out.Spec.Object = in.Spec.Object + + out.Status = dashboardV0.DashboardStatus{ + Conversion: &dashboardV0.DashboardConversionStatus{ StoredVersion: dashboardV1.VERSION, }, } + return nil } func Convert_V1_to_V2(in *dashboardV1.Dashboard, out *dashboardV2.Dashboard, scope conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - out.Spec = in.Spec - out.Status = &dashboardV2.DashboardStatus{ - ConversionStatus: &dashboardV2.ConversionStatus{ + + // TODO (@radiohead): implement V1 to V2 conversion + // This is the bare minimum conversion that is needed to make the dashboard servable. + + if v, ok := in.Spec.Object["title"]; ok { + if title, ok := v.(string); ok { + out.Spec.Title = title + } + } + + // We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail. + out.Spec.Layout = dashboardV2.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashboardV2.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashboardV2.DashboardGridLayoutSpec{}, + }, + } + + out.Status = dashboardV2.DashboardStatus{ + Conversion: &dashboardV2.DashboardConversionStatus{ StoredVersion: dashboardV1.VERSION, Failed: true, Error: "backend conversion not yet implemented", }, } + return nil } func Convert_V2_to_V0(in *dashboardV2.Dashboard, out *dashboardV0.Dashboard, scope conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - out.Spec = in.Spec - out.Status = &dashboardV0.DashboardStatus{ - ConversionStatus: &dashboardV0.ConversionStatus{ + + // TODO: implement V2 to V0 conversion + + out.Status = dashboardV0.DashboardStatus{ + Conversion: &dashboardV0.DashboardConversionStatus{ StoredVersion: dashboardV2.VERSION, Failed: true, Error: "backend conversion not yet implemented", }, } + return nil } func Convert_V2_to_V1(in *dashboardV2.Dashboard, out *dashboardV1.Dashboard, scope conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - out.Spec = in.Spec - out.Status = &dashboardV1.DashboardStatus{ - ConversionStatus: &dashboardV1.ConversionStatus{ + + // TODO: implement V2 to V1 conversion + + out.Status = dashboardV1.DashboardStatus{ + Conversion: &dashboardV1.DashboardConversionStatus{ StoredVersion: dashboardV2.VERSION, Failed: true, Error: "backend conversion not yet implemented", }, } + return nil } diff --git a/pkg/apis/dashboard/v0alpha1/constants.go b/pkg/apis/dashboard/v0alpha1/constants.go new file mode 100644 index 00000000000..90cb8b29db7 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/constants.go @@ -0,0 +1,18 @@ +package v0alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // Group is the API group used by all kinds in this package + Group = "dashboard.grafana.app" + // Version is the API version used by all kinds in this package + Version = "v0alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: Group, + Version: Version, + } +) diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_codec_gen.go b/pkg/apis/dashboard/v0alpha1/dashboard_codec_gen.go new file mode 100644 index 00000000000..6186cb824b5 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type DashboardJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*DashboardJSONCodec) 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 (*DashboardJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &DashboardJSONCodec{} diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go b/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go new file mode 100644 index 00000000000..6e22219dbf4 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_metadata_gen.go @@ -0,0 +1,28 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type DashboardMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewDashboardMetadata creates a new DashboardMetadata object. +func NewDashboardMetadata() *DashboardMetadata { + return &DashboardMetadata{} +} diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go b/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go new file mode 100644 index 00000000000..ab406988c90 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go @@ -0,0 +1,269 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type Dashboard struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the Dashboard + Spec DashboardSpec `json:"spec" yaml:"spec"` + + Status DashboardStatus `json:"status" yaml:"status"` +} + +func (o *Dashboard) GetSpec() any { + return o.Spec +} + +func (o *Dashboard) SetSpec(spec any) error { + cast, ok := spec.(DashboardSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *Dashboard) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *Dashboard) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *Dashboard) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(DashboardStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type DashboardStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *Dashboard) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *Dashboard) 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 *Dashboard) 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 *Dashboard) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *Dashboard) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *Dashboard) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *Dashboard) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *Dashboard) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *Dashboard) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *Dashboard) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *Dashboard) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *Dashboard) DeepCopyObject() runtime.Object { + return o.Copy() +} + +// Interface compliance compile-time check +var _ resource.Object = &Dashboard{} + +// +k8s:openapi-gen=true +type DashboardList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Dashboard `json:"items" yaml:"items"` +} + +func (o *DashboardList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *DashboardList) Copy() resource.ListObject { + cpy := &DashboardList{ + TypeMeta: o.TypeMeta, + Items: make([]Dashboard, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*Dashboard); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *DashboardList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *DashboardList) SetItems(items []resource.Object) { + o.Items = make([]Dashboard, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*Dashboard) + } +} + +// Interface compliance compile-time check +var _ resource.ListObject = &DashboardList{} diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go b/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go new file mode 100644 index 00000000000..5b2da44ec05 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v0alpha1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) + kindDashboard = resource.Kind{ + Schema: schemaDashboard, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &DashboardJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func DashboardKind() resource.Kind { + return kindDashboard +} + +// Schema returns a resource.SimpleSchema representation of Dashboard +func DashboardSchema() *resource.SimpleSchema { + return schemaDashboard +} + +// Interface compliance checks +var _ resource.Schema = kindDashboard diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_spec.go b/pkg/apis/dashboard/v0alpha1/dashboard_spec.go new file mode 100644 index 00000000000..ec3bf1aeb1f --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_spec.go @@ -0,0 +1,13 @@ +package v0alpha1 + +import ( + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" +) + +// +k8s:openapi-gen=true +type DashboardSpec = common.Unstructured + +// NewDashboardSpec creates a new Spec object. +func NewDashboardSpec() *DashboardSpec { + return &DashboardSpec{} +} diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_spec_gen.go b/pkg/apis/dashboard/v0alpha1/dashboard_spec_gen.go new file mode 100644 index 00000000000..90130b85cf3 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_spec_gen.go @@ -0,0 +1,3 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 diff --git a/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go b/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go new file mode 100644 index 00000000000..70fbb4e9263 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go @@ -0,0 +1,34 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// ConversionStatus is the status of the conversion of the dashboard. +// +k8s:openapi-gen=true +type DashboardConversionStatus struct { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + Failed bool `json:"failed"` + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + StoredVersion string `json:"storedVersion"` + // The error message from the conversion. + // Empty if the conversion has not failed. + Error string `json:"error"` +} + +// NewDashboardConversionStatus creates a new DashboardConversionStatus object. +func NewDashboardConversionStatus() *DashboardConversionStatus { + return &DashboardConversionStatus{} +} + +// +k8s:openapi-gen=true +type DashboardStatus struct { + // Optional conversion status. + Conversion *DashboardConversionStatus `json:"conversion,omitempty"` +} + +// NewDashboardStatus creates a new DashboardStatus object. +func NewDashboardStatus() *DashboardStatus { + return &DashboardStatus{} +} diff --git a/pkg/apis/dashboard/v0alpha1/deepcopy.go b/pkg/apis/dashboard/v0alpha1/deepcopy.go new file mode 100644 index 00000000000..ffdea428a67 --- /dev/null +++ b/pkg/apis/dashboard/v0alpha1/deepcopy.go @@ -0,0 +1,41 @@ +package v0alpha1 + +// TODO: these should be automatically generated by the SDK. + +func (in *Dashboard) DeepCopyInto(out *Dashboard) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +func (in *Dashboard) DeepCopy() *Dashboard { + if in == nil { + return nil + } + out := new(Dashboard) + in.DeepCopyInto(out) + return out +} + +func (in *DashboardList) DeepCopyInto(out *DashboardList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Dashboard, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +func (in *DashboardList) DeepCopy() *DashboardList { + if in == nil { + return nil + } + out := new(DashboardList) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/dashboard/v0alpha1/doc.go b/pkg/apis/dashboard/v0alpha1/doc.go index 5716d920d2a..748fa7fffd2 100644 --- a/pkg/apis/dashboard/v0alpha1/doc.go +++ b/pkg/apis/dashboard/v0alpha1/doc.go @@ -1,7 +1,10 @@ -// +k8s:deepcopy-gen=package // +k8s:openapi-gen=true // +k8s:defaulter-gen=TypeMeta // +k8s:conversion-gen=github.com/grafana/grafana/pkg/apis/dashboard // +groupName=dashboard.grafana.app +// NOTE (@radiohead): we do not use package-wide deepcopy generation +// because grafana-app-sdk already provides deepcopy functions. +// Kinds which are not generated by the SDK are explicitly opted in to deepcopy generation. + package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" diff --git a/pkg/apis/dashboard/v0alpha1/search.go b/pkg/apis/dashboard/v0alpha1/search.go index 96afdc447ff..5d768dbfc11 100644 --- a/pkg/apis/dashboard/v0alpha1/search.go +++ b/pkg/apis/dashboard/v0alpha1/search.go @@ -6,6 +6,7 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type SearchResults struct { metav1.TypeMeta `json:",inline"` @@ -32,11 +33,13 @@ type SearchResults struct { Facets map[string]FacetResult `json:"facets,omitempty"` } +// +k8s:deepcopy-gen=true type SortBy struct { Field string `json:"field"` Descending bool `json:"desc,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type SortableFields struct { metav1.TypeMeta `json:",inline"` @@ -45,12 +48,14 @@ type SortableFields struct { Fields []SortableField `json:"fields"` } +// +k8s:deepcopy-gen=true type SortableField struct { Field string `json:"string,omitempty"` Display string `json:"display,omitempty"` Type string `json:"type,omitempty"` // string or number } +// +k8s:deepcopy-gen=true type DashboardHit struct { // Dashboard or folder Resource string `json:"resource"` // dashboards | folders @@ -70,6 +75,7 @@ type DashboardHit struct { Explain *common.Unstructured `json:"explain,omitempty"` } +// +k8s:deepcopy-gen=true type FacetResult struct { Field string `json:"field,omitempty"` // The distinct terms @@ -80,6 +86,7 @@ type FacetResult struct { Terms []TermFacet `json:"terms,omitempty"` } +// +k8s:deepcopy-gen=true type TermFacet struct { Term string `json:"term,omitempty"` Count int64 `json:"count,omitempty"` diff --git a/pkg/apis/dashboard/v0alpha1/types.go b/pkg/apis/dashboard/v0alpha1/types.go index af6f62f2e95..ff4e38841d3 100644 --- a/pkg/apis/dashboard/v0alpha1/types.go +++ b/pkg/apis/dashboard/v0alpha1/types.go @@ -7,40 +7,7 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type Dashboard struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata - // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // The dashboard body (unstructured for now) - Spec common.Unstructured `json:"spec"` - - // Optional dashboard status - Status *DashboardStatus `json:"status,omitempty"` -} - -type DashboardStatus struct { - ConversionStatus *ConversionStatus `json:"conversion,omitempty"` -} - -type ConversionStatus struct { - Failed bool `json:"failed,omitempty"` - StoredVersion string `json:"storedVersion,omitempty"` - Error string `json:"error,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DashboardList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata,omitempty"` - - Items []Dashboard `json:"items,omitempty"` -} - +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardVersionList struct { metav1.TypeMeta `json:",inline"` @@ -50,6 +17,7 @@ type DashboardVersionList struct { Items []DashboardVersionInfo `json:"items,omitempty"` } +// +k8s:deepcopy-gen=true type DashboardVersionInfo struct { // The internal ID for this version (will be replaced with resourceVersion) Version int `json:"version"` @@ -67,6 +35,7 @@ type DashboardVersionInfo struct { Message string `json:"message,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:conversion-gen:explicit-from=net/url.Values // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type VersionsQueryOptions struct { @@ -80,6 +49,7 @@ type VersionsQueryOptions struct { Version int64 `json:"version,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanel struct { metav1.TypeMeta `json:",inline"` @@ -95,6 +65,7 @@ type LibraryPanel struct { Status *LibraryPanelStatus `json:"status,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanelList struct { metav1.TypeMeta `json:",inline"` @@ -104,6 +75,7 @@ type LibraryPanelList struct { Items []LibraryPanel `json:"items,omitempty"` } +// +k8s:deepcopy-gen=true type LibraryPanelSpec struct { // The panel type Type string `json:"type"` @@ -131,6 +103,7 @@ type LibraryPanelSpec struct { Targets []data.DataQuery `json:"targets,omitempty"` } +// +k8s:deepcopy-gen=true type LibraryPanelStatus struct { // Translation warnings (mostly things that were in SQL columns but not found in the saved body) Warnings []string `json:"warnings,omitempty"` @@ -140,6 +113,7 @@ type LibraryPanelStatus struct { } // This is like the legacy DTO where access and metadata are all returned in a single call +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardWithAccessInfo struct { Dashboard `json:",inline"` @@ -147,7 +121,7 @@ type DashboardWithAccessInfo struct { Access DashboardAccess `json:"access"` } -// Information about how the requesting user can use a given dashboard +// +k8s:deepcopy-gen=true type DashboardAccess struct { // Metadata fields Slug string `json:"slug,omitempty"` @@ -162,11 +136,13 @@ type DashboardAccess struct { AnnotationsPermissions *AnnotationPermission `json:"annotationsPermissions"` } +// +k8s:deepcopy-gen=true type AnnotationPermission struct { Dashboard AnnotationActions `json:"dashboard"` Organization AnnotationActions `json:"organization"` } +// +k8s:deepcopy-gen=true type AnnotationActions struct { CanAdd bool `json:"canAdd"` CanEdit bool `json:"canEdit"` diff --git a/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go index 82e62ccdae6..8d851c4f6c2 100644 --- a/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go @@ -46,54 +46,6 @@ func (in *AnnotationPermission) DeepCopy() *AnnotationPermission { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConversionStatus) DeepCopyInto(out *ConversionStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConversionStatus. -func (in *ConversionStatus) DeepCopy() *ConversionStatus { - if in == nil { - return nil - } - out := new(ConversionStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Dashboard) DeepCopyInto(out *Dashboard) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - if in.Status != nil { - in, out := &in.Status, &out.Status - *out = new(DashboardStatus) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dashboard. -func (in *Dashboard) DeepCopy() *Dashboard { - if in == nil { - return nil - } - out := new(Dashboard) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Dashboard) 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 *DashboardAccess) DeepCopyInto(out *DashboardAccess) { *out = *in @@ -144,60 +96,6 @@ func (in *DashboardHit) DeepCopy() *DashboardHit { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DashboardList) DeepCopyInto(out *DashboardList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Dashboard, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardList. -func (in *DashboardList) DeepCopy() *DashboardList { - if in == nil { - return nil - } - out := new(DashboardList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DashboardList) 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 *DashboardStatus) DeepCopyInto(out *DashboardStatus) { - *out = *in - if in.ConversionStatus != nil { - in, out := &in.ConversionStatus, &out.ConversionStatus - *out = new(ConversionStatus) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardStatus. -func (in *DashboardStatus) DeepCopy() *DashboardStatus { - if in == nil { - return nil - } - out := new(DashboardStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DashboardVersionInfo) DeepCopyInto(out *DashboardVersionInfo) { *out = *in diff --git a/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go b/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go index 23197ec669b..6249102860d 100644 --- a/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go @@ -8,34 +8,38 @@ package v0alpha1 import ( + commonv0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" 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/pkg/apis/dashboard/v0alpha1.AnnotationActions": schema_pkg_apis_dashboard_v0alpha1_AnnotationActions(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.ConversionStatus": schema_pkg_apis_dashboard_v0alpha1_ConversionStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess": schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardHit": schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardList": schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus": schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionInfo(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.FacetResult": schema_pkg_apis_dashboard_v0alpha1_FacetResult(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanel": schema_pkg_apis_dashboard_v0alpha1_LibraryPanel(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelSpec(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SearchResults": schema_pkg_apis_dashboard_v0alpha1_SearchResults(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortBy": schema_pkg_apis_dashboard_v0alpha1_SortBy(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableField": schema_pkg_apis_dashboard_v0alpha1_SortableField(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableFields": schema_pkg_apis_dashboard_v0alpha1_SortableFields(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.TermFacet": schema_pkg_apis_dashboard_v0alpha1_TermFacet(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": commonv0alpha1.Unstructured{}.OpenAPIDefinition(), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationActions": schema_pkg_apis_dashboard_v0alpha1_AnnotationActions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess": schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardConversionStatus": schema_pkg_apis_dashboard_v0alpha1_DashboardConversionStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardHit": schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardJSONCodec": schema_pkg_apis_dashboard_v0alpha1_DashboardJSONCodec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardList": schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardMetadata": schema_pkg_apis_dashboard_v0alpha1_DashboardMetadata(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus": schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionInfo(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.FacetResult": schema_pkg_apis_dashboard_v0alpha1_FacetResult(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanel": schema_pkg_apis_dashboard_v0alpha1_LibraryPanel(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SearchResults": schema_pkg_apis_dashboard_v0alpha1_SearchResults(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortBy": schema_pkg_apis_dashboard_v0alpha1_SortBy(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableField": schema_pkg_apis_dashboard_v0alpha1_SortableField(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableFields": schema_pkg_apis_dashboard_v0alpha1_SortableFields(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.TermFacet": schema_pkg_apis_dashboard_v0alpha1_TermFacet(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref), } } @@ -100,36 +104,6 @@ func schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref common.Referenc } } -func schema_pkg_apis_dashboard_v0alpha1_ConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "failed": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "storedVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "error": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -152,25 +126,24 @@ func schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref common.ReferenceCallback) }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "The dashboard body (unstructured for now)", + Description: "Spec is the spec of the Dashboard", Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Description: "Optional dashboard status", - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"), }, }, }, - Required: []string{"spec"}, + Required: []string{"metadata", "spec", "status"}, }, }, Dependencies: []string{ @@ -182,8 +155,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref common.ReferenceCall return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "Information about how the requesting user can use a given dashboard", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ "slug": { SchemaProps: spec.SchemaProps{ @@ -248,6 +220,44 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref common.ReferenceCall } } +func schema_pkg_apis_dashboard_v0alpha1_DashboardConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConversionStatus is the status of the conversion of the dashboard.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "failed": { + SchemaProps: spec.SchemaProps{ + Description: "Whether from another version has failed. If true, means that the dashboard is not valid, and the caller should instead fetch the stored version.", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "storedVersion": { + SchemaProps: spec.SchemaProps{ + Description: "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "The error message from the conversion. Empty if the conversion has not failed.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"failed", "storedVersion", "error"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -328,6 +338,17 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref common.ReferenceCallbac } } +func schema_pkg_apis_dashboard_v0alpha1_DashboardJSONCodec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding", + Type: []string{"object"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -368,6 +389,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref common.ReferenceCallba }, }, }, + Required: []string{"metadata", "items"}, }, }, Dependencies: []string{ @@ -375,6 +397,102 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref common.ReferenceCallba } } +func schema_pkg_apis_dashboard_v0alpha1_DashboardMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "metadata contains embedded CommonMetadata and can be extended with custom string fields without external reference as using the CommonMetadata reference breaks thema codegen.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "updateTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "createdBy": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "creationTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "deletionTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "finalizers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "generation": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "updatedBy": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"updateTimestamp", "createdBy", "uid", "creationTimestamp", "finalizers", "resourceVersion", "generation", "updatedBy", "labels"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -383,14 +501,15 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref common.ReferenceCall Properties: map[string]spec.Schema{ "conversion": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.ConversionStatus"), + Description: "Optional conversion status.", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardConversionStatus"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.ConversionStatus"}, + "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardConversionStatus"}, } } @@ -514,21 +633,20 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref common.Refer }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "The dashboard body (unstructured for now)", + Description: "Spec is the spec of the Dashboard", Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Description: "Optional dashboard status", - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"), }, }, "access": { @@ -538,7 +656,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref common.Refer }, }, }, - Required: []string{"spec", "access"}, + Required: []string{"metadata", "spec", "status", "access"}, }, }, Dependencies: []string{ diff --git a/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list index afee90908cc..7061b042725 100644 --- a/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/dashboard/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -1,8 +1,9 @@ API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,DashboardHit,Tags +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,DashboardMetadata,Finalizers API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,FacetResult,Terms API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,LibraryPanelStatus,Warnings API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SearchResults,Hits API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SortableFields,Fields -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,DashboardStatus,ConversionStatus +API rule violation: names_match,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,Unstructured,Object API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SortBy,Descending API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SortableField,Field diff --git a/pkg/apis/dashboard/v1alpha1/constants.go b/pkg/apis/dashboard/v1alpha1/constants.go new file mode 100644 index 00000000000..b3732986f0b --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/constants.go @@ -0,0 +1,18 @@ +package v1alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // Group is the API group used by all kinds in this package + Group = "dashboard.grafana.app" + // Version is the API version used by all kinds in this package + Version = "v1alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: Group, + Version: Version, + } +) diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_codec_gen.go b/pkg/apis/dashboard/v1alpha1/dashboard_codec_gen.go new file mode 100644 index 00000000000..24ee7ecd7bd --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type DashboardJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*DashboardJSONCodec) 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 (*DashboardJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &DashboardJSONCodec{} diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_metadata_gen.go b/pkg/apis/dashboard/v1alpha1/dashboard_metadata_gen.go new file mode 100644 index 00000000000..16ead71d265 --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_metadata_gen.go @@ -0,0 +1,28 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type DashboardMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewDashboardMetadata creates a new DashboardMetadata object. +func NewDashboardMetadata() *DashboardMetadata { + return &DashboardMetadata{} +} diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_object_gen.go b/pkg/apis/dashboard/v1alpha1/dashboard_object_gen.go new file mode 100644 index 00000000000..cdf11cabbd0 --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_object_gen.go @@ -0,0 +1,269 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type Dashboard struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the Dashboard + Spec DashboardSpec `json:"spec" yaml:"spec"` + + Status DashboardStatus `json:"status" yaml:"status"` +} + +func (o *Dashboard) GetSpec() any { + return o.Spec +} + +func (o *Dashboard) SetSpec(spec any) error { + cast, ok := spec.(DashboardSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *Dashboard) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *Dashboard) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *Dashboard) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(DashboardStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type DashboardStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *Dashboard) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *Dashboard) 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 *Dashboard) 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 *Dashboard) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *Dashboard) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *Dashboard) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *Dashboard) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *Dashboard) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *Dashboard) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *Dashboard) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *Dashboard) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *Dashboard) DeepCopyObject() runtime.Object { + return o.Copy() +} + +// Interface compliance compile-time check +var _ resource.Object = &Dashboard{} + +// +k8s:openapi-gen=true +type DashboardList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Dashboard `json:"items" yaml:"items"` +} + +func (o *DashboardList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *DashboardList) Copy() resource.ListObject { + cpy := &DashboardList{ + TypeMeta: o.TypeMeta, + Items: make([]Dashboard, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*Dashboard); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *DashboardList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *DashboardList) SetItems(items []resource.Object) { + o.Items = make([]Dashboard, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*Dashboard) + } +} + +// Interface compliance compile-time check +var _ resource.ListObject = &DashboardList{} diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_schema_gen.go b/pkg/apis/dashboard/v1alpha1/dashboard_schema_gen.go new file mode 100644 index 00000000000..1c09a83e2bd --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v1alpha1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) + kindDashboard = resource.Kind{ + Schema: schemaDashboard, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &DashboardJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func DashboardKind() resource.Kind { + return kindDashboard +} + +// Schema returns a resource.SimpleSchema representation of Dashboard +func DashboardSchema() *resource.SimpleSchema { + return schemaDashboard +} + +// Interface compliance checks +var _ resource.Schema = kindDashboard diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_spec.go b/pkg/apis/dashboard/v1alpha1/dashboard_spec.go new file mode 100644 index 00000000000..0b341e6e55e --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_spec.go @@ -0,0 +1,11 @@ +package v1alpha1 + +import common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" + +// +k8s:openapi-gen=true +type DashboardSpec = common.Unstructured + +// NewDashboardSpec creates a new Spec object. +func NewDashboardSpec() *DashboardSpec { + return &DashboardSpec{} +} diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_spec_gen.go b/pkg/apis/dashboard/v1alpha1/dashboard_spec_gen.go new file mode 100644 index 00000000000..4f48fee9aae --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_spec_gen.go @@ -0,0 +1,3 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 diff --git a/pkg/apis/dashboard/v1alpha1/dashboard_status_gen.go b/pkg/apis/dashboard/v1alpha1/dashboard_status_gen.go new file mode 100644 index 00000000000..556d5ddba53 --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/dashboard_status_gen.go @@ -0,0 +1,34 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +// ConversionStatus is the status of the conversion of the dashboard. +// +k8s:openapi-gen=true +type DashboardConversionStatus struct { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + Failed bool `json:"failed"` + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + StoredVersion string `json:"storedVersion"` + // The error message from the conversion. + // Empty if the conversion has not failed. + Error string `json:"error"` +} + +// NewDashboardConversionStatus creates a new DashboardConversionStatus object. +func NewDashboardConversionStatus() *DashboardConversionStatus { + return &DashboardConversionStatus{} +} + +// +k8s:openapi-gen=true +type DashboardStatus struct { + // Optional conversion status. + Conversion *DashboardConversionStatus `json:"conversion,omitempty"` +} + +// NewDashboardStatus creates a new DashboardStatus object. +func NewDashboardStatus() *DashboardStatus { + return &DashboardStatus{} +} diff --git a/pkg/apis/dashboard/v1alpha1/deepcopy.go b/pkg/apis/dashboard/v1alpha1/deepcopy.go new file mode 100644 index 00000000000..1e05aa647f2 --- /dev/null +++ b/pkg/apis/dashboard/v1alpha1/deepcopy.go @@ -0,0 +1,41 @@ +package v1alpha1 + +// TODO: these should be automatically generated by the SDK. + +func (in *Dashboard) DeepCopyInto(out *Dashboard) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +func (in *Dashboard) DeepCopy() *Dashboard { + if in == nil { + return nil + } + out := new(Dashboard) + in.DeepCopyInto(out) + return out +} + +func (in *DashboardList) DeepCopyInto(out *DashboardList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Dashboard, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +func (in *DashboardList) DeepCopy() *DashboardList { + if in == nil { + return nil + } + out := new(DashboardList) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/dashboard/v1alpha1/doc.go b/pkg/apis/dashboard/v1alpha1/doc.go index 288a406db14..f10d157ec41 100644 --- a/pkg/apis/dashboard/v1alpha1/doc.go +++ b/pkg/apis/dashboard/v1alpha1/doc.go @@ -1,7 +1,10 @@ -// +k8s:deepcopy-gen=package // +k8s:openapi-gen=true // +k8s:defaulter-gen=TypeMeta // +k8s:conversion-gen=github.com/grafana/grafana/pkg/apis/dashboard // +groupName=dashboard.grafana.app +// NOTE (@radiohead): we do not use package-wide deepcopy generation +// because grafana-app-sdk already provides deepcopy functions. +// Kinds which are not generated by the SDK are explicitly opted in to deepcopy generation. + package v1alpha1 // import "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1" diff --git a/pkg/apis/dashboard/v1alpha1/types.go b/pkg/apis/dashboard/v1alpha1/types.go index 0a5144508a1..43844655505 100644 --- a/pkg/apis/dashboard/v1alpha1/types.go +++ b/pkg/apis/dashboard/v1alpha1/types.go @@ -7,40 +7,7 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type Dashboard struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata - // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // The dashboard body (unstructured for now) - Spec common.Unstructured `json:"spec"` - - // Optional dashboard status - Status *DashboardStatus `json:"status,omitempty"` -} - -type DashboardStatus struct { - ConversionStatus *ConversionStatus `json:"conversion,omitempty"` -} - -type ConversionStatus struct { - Failed bool `json:"failed,omitempty"` - StoredVersion string `json:"storedVersion,omitempty"` - Error string `json:"error,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DashboardList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata,omitempty"` - - Items []Dashboard `json:"items,omitempty"` -} - +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardVersionList struct { metav1.TypeMeta `json:",inline"` @@ -50,6 +17,7 @@ type DashboardVersionList struct { Items []DashboardVersionInfo `json:"items,omitempty"` } +// +k8s:deepcopy-gen=true type DashboardVersionInfo struct { // The internal ID for this version (will be replaced with resourceVersion) Version int `json:"version"` @@ -67,6 +35,7 @@ type DashboardVersionInfo struct { Message string `json:"message,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:conversion-gen:explicit-from=net/url.Values // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type VersionsQueryOptions struct { @@ -80,6 +49,7 @@ type VersionsQueryOptions struct { Version int64 `json:"version,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanel struct { metav1.TypeMeta `json:",inline"` @@ -95,6 +65,7 @@ type LibraryPanel struct { Status *LibraryPanelStatus `json:"status,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanelList struct { metav1.TypeMeta `json:",inline"` @@ -104,6 +75,7 @@ type LibraryPanelList struct { Items []LibraryPanel `json:"items,omitempty"` } +// +k8s:deepcopy-gen=true type LibraryPanelSpec struct { // The panel type Type string `json:"type"` @@ -131,6 +103,7 @@ type LibraryPanelSpec struct { Targets []data.DataQuery `json:"targets,omitempty"` } +// +k8s:deepcopy-gen=true type LibraryPanelStatus struct { // Translation warnings (mostly things that were in SQL columns but not found in the saved body) Warnings []string `json:"warnings,omitempty"` @@ -140,6 +113,7 @@ type LibraryPanelStatus struct { } // This is like the legacy DTO where access and metadata are all returned in a single call +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardWithAccessInfo struct { Dashboard `json:",inline"` @@ -148,6 +122,7 @@ type DashboardWithAccessInfo struct { } // Information about how the requesting user can use a given dashboard +// +k8s:deepcopy-gen=true type DashboardAccess struct { // Metadata fields Slug string `json:"slug,omitempty"` @@ -162,11 +137,13 @@ type DashboardAccess struct { AnnotationsPermissions *AnnotationPermission `json:"annotationsPermissions"` } +// +k8s:deepcopy-gen=true type AnnotationPermission struct { Dashboard AnnotationActions `json:"dashboard"` Organization AnnotationActions `json:"organization"` } +// +k8s:deepcopy-gen=true type AnnotationActions struct { CanAdd bool `json:"canAdd"` CanEdit bool `json:"canEdit"` diff --git a/pkg/apis/dashboard/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/dashboard/v1alpha1/zz_generated.deepcopy.go index c94be011e07..8e38161cc58 100644 --- a/pkg/apis/dashboard/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/dashboard/v1alpha1/zz_generated.deepcopy.go @@ -46,54 +46,6 @@ func (in *AnnotationPermission) DeepCopy() *AnnotationPermission { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConversionStatus) DeepCopyInto(out *ConversionStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConversionStatus. -func (in *ConversionStatus) DeepCopy() *ConversionStatus { - if in == nil { - return nil - } - out := new(ConversionStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Dashboard) DeepCopyInto(out *Dashboard) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - if in.Status != nil { - in, out := &in.Status, &out.Status - *out = new(DashboardStatus) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dashboard. -func (in *Dashboard) DeepCopy() *Dashboard { - if in == nil { - return nil - } - out := new(Dashboard) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Dashboard) 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 *DashboardAccess) DeepCopyInto(out *DashboardAccess) { *out = *in @@ -115,60 +67,6 @@ func (in *DashboardAccess) DeepCopy() *DashboardAccess { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DashboardList) DeepCopyInto(out *DashboardList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Dashboard, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardList. -func (in *DashboardList) DeepCopy() *DashboardList { - if in == nil { - return nil - } - out := new(DashboardList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DashboardList) 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 *DashboardStatus) DeepCopyInto(out *DashboardStatus) { - *out = *in - if in.ConversionStatus != nil { - in, out := &in.ConversionStatus, &out.ConversionStatus - *out = new(ConversionStatus) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardStatus. -func (in *DashboardStatus) DeepCopy() *DashboardStatus { - if in == nil { - return nil - } - out := new(DashboardStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DashboardVersionInfo) DeepCopyInto(out *DashboardVersionInfo) { *out = *in diff --git a/pkg/apis/dashboard/v1alpha1/zz_generated.openapi.go b/pkg/apis/dashboard/v1alpha1/zz_generated.openapi.go index e6f99cd895c..f9de16ee169 100644 --- a/pkg/apis/dashboard/v1alpha1/zz_generated.openapi.go +++ b/pkg/apis/dashboard/v1alpha1/zz_generated.openapi.go @@ -8,27 +8,31 @@ package v1alpha1 import ( + v0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" 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/pkg/apis/dashboard/v1alpha1.AnnotationActions": schema_pkg_apis_dashboard_v1alpha1_AnnotationActions(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v1alpha1_AnnotationPermission(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.ConversionStatus": schema_pkg_apis_dashboard_v1alpha1_ConversionStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.Dashboard": schema_pkg_apis_dashboard_v1alpha1_Dashboard(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardAccess": schema_pkg_apis_dashboard_v1alpha1_DashboardAccess(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardList": schema_pkg_apis_dashboard_v1alpha1_DashboardList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardStatus": schema_pkg_apis_dashboard_v1alpha1_DashboardStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v1alpha1_DashboardVersionInfo(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v1alpha1_DashboardVersionList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v1alpha1_DashboardWithAccessInfo(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanel": schema_pkg_apis_dashboard_v1alpha1_LibraryPanel(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v1alpha1_LibraryPanelList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v1alpha1_LibraryPanelSpec(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v1alpha1_LibraryPanelStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v1alpha1_VersionsQueryOptions(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": v0alpha1.Unstructured{}.OpenAPIDefinition(), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.AnnotationActions": schema_pkg_apis_dashboard_v1alpha1_AnnotationActions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v1alpha1_AnnotationPermission(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.Dashboard": schema_pkg_apis_dashboard_v1alpha1_Dashboard(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardAccess": schema_pkg_apis_dashboard_v1alpha1_DashboardAccess(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardConversionStatus": schema_pkg_apis_dashboard_v1alpha1_DashboardConversionStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardJSONCodec": schema_pkg_apis_dashboard_v1alpha1_DashboardJSONCodec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardList": schema_pkg_apis_dashboard_v1alpha1_DashboardList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardMetadata": schema_pkg_apis_dashboard_v1alpha1_DashboardMetadata(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardStatus": schema_pkg_apis_dashboard_v1alpha1_DashboardStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v1alpha1_DashboardVersionInfo(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v1alpha1_DashboardVersionList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v1alpha1_DashboardWithAccessInfo(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanel": schema_pkg_apis_dashboard_v1alpha1_LibraryPanel(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v1alpha1_LibraryPanelList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v1alpha1_LibraryPanelSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v1alpha1_LibraryPanelStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v1alpha1_VersionsQueryOptions(ref), } } @@ -93,36 +97,6 @@ func schema_pkg_apis_dashboard_v1alpha1_AnnotationPermission(ref common.Referenc } } -func schema_pkg_apis_dashboard_v1alpha1_ConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "failed": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "storedVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "error": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_dashboard_v1alpha1_Dashboard(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -145,25 +119,24 @@ func schema_pkg_apis_dashboard_v1alpha1_Dashboard(ref common.ReferenceCallback) }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "The dashboard body (unstructured for now)", + Description: "Spec is the spec of the Dashboard", Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Description: "Optional dashboard status", - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardStatus"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardStatus"), }, }, }, - Required: []string{"spec"}, + Required: []string{"metadata", "spec", "status"}, }, }, Dependencies: []string{ @@ -241,6 +214,55 @@ func schema_pkg_apis_dashboard_v1alpha1_DashboardAccess(ref common.ReferenceCall } } +func schema_pkg_apis_dashboard_v1alpha1_DashboardConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConversionStatus is the status of the conversion of the dashboard.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "failed": { + SchemaProps: spec.SchemaProps{ + Description: "Whether from another version has failed. If true, means that the dashboard is not valid, and the caller should instead fetch the stored version.", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "storedVersion": { + SchemaProps: spec.SchemaProps{ + Description: "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "The error message from the conversion. Empty if the conversion has not failed.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"failed", "storedVersion", "error"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v1alpha1_DashboardJSONCodec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding", + Type: []string{"object"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v1alpha1_DashboardList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -281,6 +303,7 @@ func schema_pkg_apis_dashboard_v1alpha1_DashboardList(ref common.ReferenceCallba }, }, }, + Required: []string{"metadata", "items"}, }, }, Dependencies: []string{ @@ -288,6 +311,102 @@ func schema_pkg_apis_dashboard_v1alpha1_DashboardList(ref common.ReferenceCallba } } +func schema_pkg_apis_dashboard_v1alpha1_DashboardMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "metadata contains embedded CommonMetadata and can be extended with custom string fields without external reference as using the CommonMetadata reference breaks thema codegen.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "updateTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "createdBy": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "creationTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "deletionTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "finalizers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "generation": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "updatedBy": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"updateTimestamp", "createdBy", "uid", "creationTimestamp", "finalizers", "resourceVersion", "generation", "updatedBy", "labels"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v1alpha1_DashboardStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -296,14 +415,15 @@ func schema_pkg_apis_dashboard_v1alpha1_DashboardStatus(ref common.ReferenceCall Properties: map[string]spec.Schema{ "conversion": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.ConversionStatus"), + Description: "Optional conversion status.", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardConversionStatus"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.ConversionStatus"}, + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardConversionStatus"}, } } @@ -427,21 +547,20 @@ func schema_pkg_apis_dashboard_v1alpha1_DashboardWithAccessInfo(ref common.Refer }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "The dashboard body (unstructured for now)", + Description: "Spec is the spec of the Dashboard", Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Description: "Optional dashboard status", - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardStatus"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1.DashboardStatus"), }, }, "access": { @@ -451,7 +570,7 @@ func schema_pkg_apis_dashboard_v1alpha1_DashboardWithAccessInfo(ref common.Refer }, }, }, - Required: []string{"spec", "access"}, + Required: []string{"metadata", "spec", "status", "access"}, }, }, Dependencies: []string{ diff --git a/pkg/apis/dashboard/v1alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/dashboard/v1alpha1/zz_generated.openapi_violation_exceptions.list index bbfa46dae2b..a34c29a7192 100644 --- a/pkg/apis/dashboard/v1alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/dashboard/v1alpha1/zz_generated.openapi_violation_exceptions.list @@ -1,2 +1,3 @@ +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1,DashboardMetadata,Finalizers API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1,LibraryPanelStatus,Warnings -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1,DashboardStatus,ConversionStatus +API rule violation: names_match,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,Unstructured,Object diff --git a/pkg/apis/dashboard/v2alpha1/constants.go b/pkg/apis/dashboard/v2alpha1/constants.go new file mode 100644 index 00000000000..84b030c8994 --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/constants.go @@ -0,0 +1,18 @@ +package v2alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // Group is the API group used by all kinds in this package + Group = "dashboard.grafana.app" + // Version is the API version used by all kinds in this package + Version = "v2alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: Group, + Version: Version, + } +) diff --git a/pkg/apis/dashboard/v2alpha1/dashboard_codec_gen.go b/pkg/apis/dashboard/v2alpha1/dashboard_codec_gen.go new file mode 100644 index 00000000000..a1892865692 --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/dashboard_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v2alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type DashboardJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*DashboardJSONCodec) 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 (*DashboardJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &DashboardJSONCodec{} diff --git a/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go b/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go new file mode 100644 index 00000000000..f4e92178780 --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/dashboard_metadata_gen.go @@ -0,0 +1,28 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v2alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type DashboardMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewDashboardMetadata creates a new DashboardMetadata object. +func NewDashboardMetadata() *DashboardMetadata { + return &DashboardMetadata{} +} diff --git a/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go b/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go new file mode 100644 index 00000000000..504dc5a25f1 --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go @@ -0,0 +1,269 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v2alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type Dashboard struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the Dashboard + Spec DashboardSpec `json:"spec" yaml:"spec"` + + Status DashboardStatus `json:"status" yaml:"status"` +} + +func (o *Dashboard) GetSpec() any { + return o.Spec +} + +func (o *Dashboard) SetSpec(spec any) error { + cast, ok := spec.(DashboardSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *Dashboard) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *Dashboard) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *Dashboard) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(DashboardStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type DashboardStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *Dashboard) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *Dashboard) 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 *Dashboard) 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 *Dashboard) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *Dashboard) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *Dashboard) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *Dashboard) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *Dashboard) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *Dashboard) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *Dashboard) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *Dashboard) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *Dashboard) DeepCopyObject() runtime.Object { + return o.Copy() +} + +// Interface compliance compile-time check +var _ resource.Object = &Dashboard{} + +// +k8s:openapi-gen=true +type DashboardList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Dashboard `json:"items" yaml:"items"` +} + +func (o *DashboardList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *DashboardList) Copy() resource.ListObject { + cpy := &DashboardList{ + TypeMeta: o.TypeMeta, + Items: make([]Dashboard, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*Dashboard); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *DashboardList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *DashboardList) SetItems(items []resource.Object) { + o.Items = make([]Dashboard, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*Dashboard) + } +} + +// Interface compliance compile-time check +var _ resource.ListObject = &DashboardList{} diff --git a/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go b/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go new file mode 100644 index 00000000000..136698cf70f --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v2alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v2alpha1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) + kindDashboard = resource.Kind{ + Schema: schemaDashboard, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &DashboardJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func DashboardKind() resource.Kind { + return kindDashboard +} + +// Schema returns a resource.SimpleSchema representation of Dashboard +func DashboardSchema() *resource.SimpleSchema { + return schemaDashboard +} + +// Interface compliance checks +var _ resource.Schema = kindDashboard diff --git a/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go b/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go new file mode 100644 index 00000000000..ade7b3398b0 --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go @@ -0,0 +1,2373 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v2alpha1 + +import ( + json "encoding/json" + errors "errors" + fmt "fmt" +) + +// +k8s:openapi-gen=true +type DashboardAnnotationQueryKind struct { + Kind string `json:"kind"` + Spec DashboardAnnotationQuerySpec `json:"spec"` +} + +// NewDashboardAnnotationQueryKind creates a new DashboardAnnotationQueryKind object. +func NewDashboardAnnotationQueryKind() *DashboardAnnotationQueryKind { + return &DashboardAnnotationQueryKind{ + Kind: "AnnotationQuery", + Spec: *NewDashboardAnnotationQuerySpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardAnnotationQuerySpec struct { + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + Query *DashboardDataQueryKind `json:"query,omitempty"` + Enable bool `json:"enable"` + Hide bool `json:"hide"` + IconColor string `json:"iconColor"` + Name string `json:"name"` + BuiltIn *bool `json:"builtIn,omitempty"` + Filter *DashboardAnnotationPanelFilter `json:"filter,omitempty"` +} + +// NewDashboardAnnotationQuerySpec creates a new DashboardAnnotationQuerySpec object. +func NewDashboardAnnotationQuerySpec() *DashboardAnnotationQuerySpec { + return &DashboardAnnotationQuerySpec{ + BuiltIn: (func(input bool) *bool { return &input })(false), + } +} + +// +k8s:openapi-gen=true +type DashboardDataSourceRef struct { + // The plugin type-id + Type *string `json:"type,omitempty"` + // Specific datasource instance + Uid *string `json:"uid,omitempty"` +} + +// NewDashboardDataSourceRef creates a new DashboardDataSourceRef object. +func NewDashboardDataSourceRef() *DashboardDataSourceRef { + return &DashboardDataSourceRef{} +} + +// +k8s:openapi-gen=true +type DashboardDataQueryKind struct { + // The kind of a DataQueryKind is the datasource type + Kind string `json:"kind"` + Spec map[string]interface{} `json:"spec"` +} + +// NewDashboardDataQueryKind creates a new DashboardDataQueryKind object. +func NewDashboardDataQueryKind() *DashboardDataQueryKind { + return &DashboardDataQueryKind{} +} + +// +k8s:openapi-gen=true +type DashboardAnnotationPanelFilter struct { + // Should the specified panels be included or excluded + Exclude *bool `json:"exclude,omitempty"` + // Panel IDs that should be included or excluded + Ids []uint8 `json:"ids"` +} + +// NewDashboardAnnotationPanelFilter creates a new DashboardAnnotationPanelFilter object. +func NewDashboardAnnotationPanelFilter() *DashboardAnnotationPanelFilter { + return &DashboardAnnotationPanelFilter{ + Exclude: (func(input bool) *bool { return &input })(false), + } +} + +// "Off" for no shared crosshair or tooltip (default). +// "Crosshair" for shared crosshair. +// "Tooltip" for shared crosshair AND shared tooltip. +// +k8s:openapi-gen=true +type DashboardDashboardCursorSync string + +const ( + DashboardDashboardCursorSyncOff DashboardDashboardCursorSync = "Off" + DashboardDashboardCursorSyncCrosshair DashboardDashboardCursorSync = "Crosshair" + DashboardDashboardCursorSyncTooltip DashboardDashboardCursorSync = "Tooltip" +) + +// Supported dashboard elements +// |* more element types in the future +// +k8s:openapi-gen=true +type DashboardElement = DashboardPanelKindOrLibraryPanelKind + +// NewDashboardElement creates a new DashboardElement object. +func NewDashboardElement() *DashboardElement { + return NewDashboardPanelKindOrLibraryPanelKind() +} + +// +k8s:openapi-gen=true +type DashboardPanelKind struct { + Kind string `json:"kind"` + Spec DashboardPanelSpec `json:"spec"` +} + +// NewDashboardPanelKind creates a new DashboardPanelKind object. +func NewDashboardPanelKind() *DashboardPanelKind { + return &DashboardPanelKind{ + Kind: "Panel", + Spec: *NewDashboardPanelSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardPanelSpec struct { + Id float64 `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Links []DashboardDataLink `json:"links"` + Data DashboardQueryGroupKind `json:"data"` + VizConfig DashboardVizConfigKind `json:"vizConfig"` + Transparent *bool `json:"transparent,omitempty"` +} + +// NewDashboardPanelSpec creates a new DashboardPanelSpec object. +func NewDashboardPanelSpec() *DashboardPanelSpec { + return &DashboardPanelSpec{ + Data: *NewDashboardQueryGroupKind(), + VizConfig: *NewDashboardVizConfigKind(), + } +} + +// +k8s:openapi-gen=true +type DashboardDataLink struct { + Title string `json:"title"` + Url string `json:"url"` + TargetBlank *bool `json:"targetBlank,omitempty"` +} + +// NewDashboardDataLink creates a new DashboardDataLink object. +func NewDashboardDataLink() *DashboardDataLink { + return &DashboardDataLink{} +} + +// +k8s:openapi-gen=true +type DashboardQueryGroupKind struct { + Kind string `json:"kind"` + Spec DashboardQueryGroupSpec `json:"spec"` +} + +// NewDashboardQueryGroupKind creates a new DashboardQueryGroupKind object. +func NewDashboardQueryGroupKind() *DashboardQueryGroupKind { + return &DashboardQueryGroupKind{ + Kind: "QueryGroup", + Spec: *NewDashboardQueryGroupSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardQueryGroupSpec struct { + Queries []DashboardPanelQueryKind `json:"queries"` + Transformations []DashboardTransformationKind `json:"transformations"` + QueryOptions DashboardQueryOptionsSpec `json:"queryOptions"` +} + +// NewDashboardQueryGroupSpec creates a new DashboardQueryGroupSpec object. +func NewDashboardQueryGroupSpec() *DashboardQueryGroupSpec { + return &DashboardQueryGroupSpec{ + QueryOptions: *NewDashboardQueryOptionsSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardPanelQueryKind struct { + Kind string `json:"kind"` + Spec DashboardPanelQuerySpec `json:"spec"` +} + +// NewDashboardPanelQueryKind creates a new DashboardPanelQueryKind object. +func NewDashboardPanelQueryKind() *DashboardPanelQueryKind { + return &DashboardPanelQueryKind{ + Kind: "PanelQuery", + Spec: *NewDashboardPanelQuerySpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardPanelQuerySpec struct { + Query DashboardDataQueryKind `json:"query"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + RefId string `json:"refId"` + Hidden bool `json:"hidden"` +} + +// NewDashboardPanelQuerySpec creates a new DashboardPanelQuerySpec object. +func NewDashboardPanelQuerySpec() *DashboardPanelQuerySpec { + return &DashboardPanelQuerySpec{ + Query: *NewDashboardDataQueryKind(), + } +} + +// +k8s:openapi-gen=true +type DashboardTransformationKind struct { + // The kind of a TransformationKind is the transformation ID + Kind string `json:"kind"` + Spec DashboardDataTransformerConfig `json:"spec"` +} + +// NewDashboardTransformationKind creates a new DashboardTransformationKind object. +func NewDashboardTransformationKind() *DashboardTransformationKind { + return &DashboardTransformationKind{ + Spec: *NewDashboardDataTransformerConfig(), + } +} + +// Transformations allow to manipulate data returned by a query before the system applies a visualization. +// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, +// use the output of one transformation as the input to another transformation, etc. +// +k8s:openapi-gen=true +type DashboardDataTransformerConfig struct { + // Unique identifier of transformer + Id string `json:"id"` + // Disabled transformations are skipped + Disabled *bool `json:"disabled,omitempty"` + // Optional frame matcher. When missing it will be applied to all results + Filter *DashboardMatcherConfig `json:"filter,omitempty"` + // Where to pull DataFrames from as input to transformation + Topic *DashboardDataTopic `json:"topic,omitempty"` + // Options to be passed to the transformer + // Valid options depend on the transformer id + Options interface{} `json:"options"` +} + +// NewDashboardDataTransformerConfig creates a new DashboardDataTransformerConfig object. +func NewDashboardDataTransformerConfig() *DashboardDataTransformerConfig { + return &DashboardDataTransformerConfig{} +} + +// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. +// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. +// +k8s:openapi-gen=true +type DashboardMatcherConfig struct { + // The matcher id. This is used to find the matcher implementation from registry. + Id string `json:"id"` + // The matcher options. This is specific to the matcher implementation. + Options interface{} `json:"options,omitempty"` +} + +// NewDashboardMatcherConfig creates a new DashboardMatcherConfig object. +func NewDashboardMatcherConfig() *DashboardMatcherConfig { + return &DashboardMatcherConfig{ + Id: "", + } +} + +// A topic is attached to DataFrame metadata in query results. +// This specifies where the data should be used. +// +k8s:openapi-gen=true +type DashboardDataTopic string + +const ( + DashboardDataTopicSeries DashboardDataTopic = "series" + DashboardDataTopicAnnotations DashboardDataTopic = "annotations" + DashboardDataTopicAlertStates DashboardDataTopic = "alertStates" +) + +// +k8s:openapi-gen=true +type DashboardQueryOptionsSpec struct { + TimeFrom *string `json:"timeFrom,omitempty"` + MaxDataPoints *int64 `json:"maxDataPoints,omitempty"` + TimeShift *string `json:"timeShift,omitempty"` + QueryCachingTTL *int64 `json:"queryCachingTTL,omitempty"` + Interval *string `json:"interval,omitempty"` + CacheTimeout *string `json:"cacheTimeout,omitempty"` + HideTimeOverride *bool `json:"hideTimeOverride,omitempty"` +} + +// NewDashboardQueryOptionsSpec creates a new DashboardQueryOptionsSpec object. +func NewDashboardQueryOptionsSpec() *DashboardQueryOptionsSpec { + return &DashboardQueryOptionsSpec{} +} + +// +k8s:openapi-gen=true +type DashboardVizConfigKind struct { + // The kind of a VizConfigKind is the plugin ID + Kind string `json:"kind"` + Spec DashboardVizConfigSpec `json:"spec"` +} + +// NewDashboardVizConfigKind creates a new DashboardVizConfigKind object. +func NewDashboardVizConfigKind() *DashboardVizConfigKind { + return &DashboardVizConfigKind{ + Spec: *NewDashboardVizConfigSpec(), + } +} + +// --- Kinds --- +// +k8s:openapi-gen=true +type DashboardVizConfigSpec struct { + PluginVersion string `json:"pluginVersion"` + Options map[string]interface{} `json:"options"` + FieldConfig DashboardFieldConfigSource `json:"fieldConfig"` +} + +// NewDashboardVizConfigSpec creates a new DashboardVizConfigSpec object. +func NewDashboardVizConfigSpec() *DashboardVizConfigSpec { + return &DashboardVizConfigSpec{ + FieldConfig: *NewDashboardFieldConfigSource(), + } +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +// +k8s:openapi-gen=true +type DashboardFieldConfigSource struct { + // Defaults are the options applied to all fields. + Defaults DashboardFieldConfig `json:"defaults"` + // Overrides are the options applied to specific fields overriding the defaults. + Overrides []DashboardV2alpha1FieldConfigSourceOverrides `json:"overrides"` +} + +// NewDashboardFieldConfigSource creates a new DashboardFieldConfigSource object. +func NewDashboardFieldConfigSource() *DashboardFieldConfigSource { + return &DashboardFieldConfigSource{ + Defaults: *NewDashboardFieldConfig(), + } +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +// +k8s:openapi-gen=true +type DashboardFieldConfig struct { + // The display value for this field. This supports template variables blank is auto + DisplayName *string `json:"displayName,omitempty"` + // This can be used by data sources that return and explicit naming structure for values and labels + // When this property is configured, this value is used rather than the default naming strategy. + DisplayNameFromDS *string `json:"displayNameFromDS,omitempty"` + // Human readable field metadata + Description *string `json:"description,omitempty"` + // An explicit path to the field in the datasource. When the frame meta includes a path, + // This will default to `${frame.meta.path}/${field.name} + // + // When defined, this value can be used as an identifier within the datasource scope, and + // may be used to update the results + Path *string `json:"path,omitempty"` + // True if data source can write a value to the path. Auth/authz are supported separately + Writeable *bool `json:"writeable,omitempty"` + // True if data source field supports ad-hoc filters + Filterable *bool `json:"filterable,omitempty"` + // Unit a field should use. The unit you select is applied to all fields except time. + // You can use the units ID availables in Grafana or a custom unit. + // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts + // As custom unit, you can use the following formats: + // `suffix:` for custom unit that should go after value. + // `prefix:` for custom unit that should go before value. + // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. + // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. + // `count:` for a custom count unit. + // `currency:` for custom a currency unit. + Unit *string `json:"unit,omitempty"` + // Specify the number of decimals Grafana includes in the rendered value. + // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. + // For example 1.1234 will display as 1.12 and 100.456 will display as 100. + // To display all decimals, set the unit to `String`. + Decimals *float64 `json:"decimals,omitempty"` + // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + Min *float64 `json:"min,omitempty"` + // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + Max *float64 `json:"max,omitempty"` + // Convert input values into a display string + Mappings []DashboardValueMapping `json:"mappings,omitempty"` + // Map numeric values to states + Thresholds *DashboardThresholdsConfig `json:"thresholds,omitempty"` + // Panel color configuration + Color *DashboardFieldColor `json:"color,omitempty"` + // The behavior when clicking on a result + Links []interface{} `json:"links,omitempty"` + // Alternative to empty string + NoValue *string `json:"noValue,omitempty"` + // custom is specified by the FieldConfig field + // in panel plugin schemas. + Custom map[string]interface{} `json:"custom,omitempty"` +} + +// NewDashboardFieldConfig creates a new DashboardFieldConfig object. +func NewDashboardFieldConfig() *DashboardFieldConfig { + return &DashboardFieldConfig{} +} + +// +k8s:openapi-gen=true +type DashboardValueMapping = DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap + +// NewDashboardValueMapping creates a new DashboardValueMapping object. +func NewDashboardValueMapping() *DashboardValueMapping { + return NewDashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap() +} + +// Maps text values to a color or different display text and color. +// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +// +k8s:openapi-gen=true +type DashboardValueMap struct { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "value" + Type string `json:"type"` + // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } + Options map[string]DashboardValueMappingResult `json:"options"` +} + +// NewDashboardValueMap creates a new DashboardValueMap object. +func NewDashboardValueMap() *DashboardValueMap { + return &DashboardValueMap{ + Type: "value", + } +} + +// Result used as replacement with text and color when the value matches +// +k8s:openapi-gen=true +type DashboardValueMappingResult struct { + // Text to display when the value matches + Text *string `json:"text,omitempty"` + // Text to use when the value matches + Color *string `json:"color,omitempty"` + // Icon to display when the value matches. Only specific visualizations. + Icon *string `json:"icon,omitempty"` + // Position in the mapping array. Only used internally. + Index *int32 `json:"index,omitempty"` +} + +// NewDashboardValueMappingResult creates a new DashboardValueMappingResult object. +func NewDashboardValueMappingResult() *DashboardValueMappingResult { + return &DashboardValueMappingResult{} +} + +// Maps numerical ranges to a display text and color. +// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +// +k8s:openapi-gen=true +type DashboardRangeMap struct { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "range" + Type string `json:"type"` + // Range to match against and the result to apply when the value is within the range + Options DashboardV2alpha1RangeMapOptions `json:"options"` +} + +// NewDashboardRangeMap creates a new DashboardRangeMap object. +func NewDashboardRangeMap() *DashboardRangeMap { + return &DashboardRangeMap{ + Type: "range", + Options: *NewDashboardV2alpha1RangeMapOptions(), + } +} + +// Maps regular expressions to replacement text and a color. +// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +// +k8s:openapi-gen=true +type DashboardRegexMap struct { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "regex" + Type string `json:"type"` + // Regular expression to match against and the result to apply when the value matches the regex + Options DashboardV2alpha1RegexMapOptions `json:"options"` +} + +// NewDashboardRegexMap creates a new DashboardRegexMap object. +func NewDashboardRegexMap() *DashboardRegexMap { + return &DashboardRegexMap{ + Type: "regex", + Options: *NewDashboardV2alpha1RegexMapOptions(), + } +} + +// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. +// See SpecialValueMatch to see the list of special values. +// For example, you can configure a special value mapping so that null values appear as N/A. +// +k8s:openapi-gen=true +type DashboardSpecialValueMap struct { + // TODO (@radiohead): Something broke in cog / app SDK codegen + // And this is no longer producing valid TS / Go output + // type: MappingType & "special" + Type string `json:"type"` + Options DashboardV2alpha1SpecialValueMapOptions `json:"options"` +} + +// NewDashboardSpecialValueMap creates a new DashboardSpecialValueMap object. +func NewDashboardSpecialValueMap() *DashboardSpecialValueMap { + return &DashboardSpecialValueMap{ + Type: "special", + Options: *NewDashboardV2alpha1SpecialValueMapOptions(), + } +} + +// Special value types supported by the `SpecialValueMap` +// +k8s:openapi-gen=true +type DashboardSpecialValueMatch string + +const ( + DashboardSpecialValueMatchTrue DashboardSpecialValueMatch = "true" + DashboardSpecialValueMatchFalse DashboardSpecialValueMatch = "false" + DashboardSpecialValueMatchNull DashboardSpecialValueMatch = "null" + DashboardSpecialValueMatchNaN DashboardSpecialValueMatch = "nan" + DashboardSpecialValueMatchNullAndNaN DashboardSpecialValueMatch = "null+nan" + DashboardSpecialValueMatchEmpty DashboardSpecialValueMatch = "empty" +) + +// +k8s:openapi-gen=true +type DashboardThresholdsConfig struct { + Mode DashboardThresholdsMode `json:"mode"` + Steps []DashboardThreshold `json:"steps"` +} + +// NewDashboardThresholdsConfig creates a new DashboardThresholdsConfig object. +func NewDashboardThresholdsConfig() *DashboardThresholdsConfig { + return &DashboardThresholdsConfig{} +} + +// +k8s:openapi-gen=true +type DashboardThresholdsMode string + +const ( + DashboardThresholdsModeAbsolute DashboardThresholdsMode = "absolute" + DashboardThresholdsModePercentage DashboardThresholdsMode = "percentage" +) + +// +k8s:openapi-gen=true +type DashboardThreshold struct { + Value float64 `json:"value"` + Color string `json:"color"` +} + +// NewDashboardThreshold creates a new DashboardThreshold object. +func NewDashboardThreshold() *DashboardThreshold { + return &DashboardThreshold{} +} + +// Map a field to a color. +// +k8s:openapi-gen=true +type DashboardFieldColor struct { + // The main color scheme mode. + Mode DashboardFieldColorModeId `json:"mode"` + // The fixed color value for fixed or shades color modes. + FixedColor *string `json:"fixedColor,omitempty"` + // Some visualizations need to know how to assign a series color from by value color schemes. + SeriesBy *DashboardFieldColorSeriesByMode `json:"seriesBy,omitempty"` +} + +// NewDashboardFieldColor creates a new DashboardFieldColor object. +func NewDashboardFieldColor() *DashboardFieldColor { + return &DashboardFieldColor{} +} + +// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. +// Continuous color interpolates a color using the percentage of a value relative to min and max. +// Accepted values are: +// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold +// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations +// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations +// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode +// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode +// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode +// `continuous-YlRd`: Continuous Yellow-Red palette mode +// `continuous-BlPu`: Continuous Blue-Purple palette mode +// `continuous-YlBl`: Continuous Yellow-Blue palette mode +// `continuous-blues`: Continuous Blue palette mode +// `continuous-reds`: Continuous Red palette mode +// `continuous-greens`: Continuous Green palette mode +// `continuous-purples`: Continuous Purple palette mode +// `shades`: Shades of a single color. Specify a single color, useful in an override rule. +// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. +// +k8s:openapi-gen=true +type DashboardFieldColorModeId string + +const ( + DashboardFieldColorModeIdThresholds DashboardFieldColorModeId = "thresholds" + DashboardFieldColorModeIdPaletteClassic DashboardFieldColorModeId = "palette-classic" + DashboardFieldColorModeIdPaletteClassicByName DashboardFieldColorModeId = "palette-classic-by-name" + DashboardFieldColorModeIdContinuousGrYlRd DashboardFieldColorModeId = "continuous-GrYlRd" + DashboardFieldColorModeIdContinuousRdYlGr DashboardFieldColorModeId = "continuous-RdYlGr" + DashboardFieldColorModeIdContinuousBlYlRd DashboardFieldColorModeId = "continuous-BlYlRd" + DashboardFieldColorModeIdContinuousYlRd DashboardFieldColorModeId = "continuous-YlRd" + DashboardFieldColorModeIdContinuousBlPu DashboardFieldColorModeId = "continuous-BlPu" + DashboardFieldColorModeIdContinuousYlBl DashboardFieldColorModeId = "continuous-YlBl" + DashboardFieldColorModeIdContinuousBlues DashboardFieldColorModeId = "continuous-blues" + DashboardFieldColorModeIdContinuousReds DashboardFieldColorModeId = "continuous-reds" + DashboardFieldColorModeIdContinuousGreens DashboardFieldColorModeId = "continuous-greens" + DashboardFieldColorModeIdContinuousPurples DashboardFieldColorModeId = "continuous-purples" + DashboardFieldColorModeIdFixed DashboardFieldColorModeId = "fixed" + DashboardFieldColorModeIdShades DashboardFieldColorModeId = "shades" +) + +// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. +// +k8s:openapi-gen=true +type DashboardFieldColorSeriesByMode string + +const ( + DashboardFieldColorSeriesByModeMin DashboardFieldColorSeriesByMode = "min" + DashboardFieldColorSeriesByModeMax DashboardFieldColorSeriesByMode = "max" + DashboardFieldColorSeriesByModeLast DashboardFieldColorSeriesByMode = "last" +) + +// +k8s:openapi-gen=true +type DashboardDynamicConfigValue struct { + Id string `json:"id"` + Value interface{} `json:"value,omitempty"` +} + +// NewDashboardDynamicConfigValue creates a new DashboardDynamicConfigValue object. +func NewDashboardDynamicConfigValue() *DashboardDynamicConfigValue { + return &DashboardDynamicConfigValue{ + Id: "", + } +} + +// +k8s:openapi-gen=true +type DashboardLibraryPanelKind struct { + Kind string `json:"kind"` + Spec DashboardLibraryPanelKindSpec `json:"spec"` +} + +// NewDashboardLibraryPanelKind creates a new DashboardLibraryPanelKind object. +func NewDashboardLibraryPanelKind() *DashboardLibraryPanelKind { + return &DashboardLibraryPanelKind{ + Kind: "LibraryPanel", + Spec: *NewDashboardLibraryPanelKindSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardLibraryPanelKindSpec struct { + // Panel ID for the library panel in the dashboard + Id float64 `json:"id"` + // Title for the library panel in the dashboard + Title string `json:"title"` + LibraryPanel DashboardLibraryPanelRef `json:"libraryPanel"` +} + +// NewDashboardLibraryPanelKindSpec creates a new DashboardLibraryPanelKindSpec object. +func NewDashboardLibraryPanelKindSpec() *DashboardLibraryPanelKindSpec { + return &DashboardLibraryPanelKindSpec{ + LibraryPanel: *NewDashboardLibraryPanelRef(), + } +} + +// A library panel is a reusable panel that you can use in any dashboard. +// When you make a change to a library panel, that change propagates to all instances of where the panel is used. +// Library panels streamline reuse of panels across multiple dashboards. +// +k8s:openapi-gen=true +type DashboardLibraryPanelRef struct { + // Library panel name + Name string `json:"name"` + // Library panel uid + Uid string `json:"uid"` +} + +// NewDashboardLibraryPanelRef creates a new DashboardLibraryPanelRef object. +func NewDashboardLibraryPanelRef() *DashboardLibraryPanelRef { + return &DashboardLibraryPanelRef{} +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutKind struct { + Kind string `json:"kind"` + Spec DashboardGridLayoutSpec `json:"spec"` +} + +// NewDashboardGridLayoutKind creates a new DashboardGridLayoutKind object. +func NewDashboardGridLayoutKind() *DashboardGridLayoutKind { + return &DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: *NewDashboardGridLayoutSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutSpec struct { + Items []DashboardGridLayoutItemKindOrGridLayoutRowKind `json:"items"` +} + +// NewDashboardGridLayoutSpec creates a new DashboardGridLayoutSpec object. +func NewDashboardGridLayoutSpec() *DashboardGridLayoutSpec { + return &DashboardGridLayoutSpec{} +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutItemKind struct { + Kind string `json:"kind"` + Spec DashboardGridLayoutItemSpec `json:"spec"` +} + +// NewDashboardGridLayoutItemKind creates a new DashboardGridLayoutItemKind object. +func NewDashboardGridLayoutItemKind() *DashboardGridLayoutItemKind { + return &DashboardGridLayoutItemKind{ + Kind: "GridLayoutItem", + Spec: *NewDashboardGridLayoutItemSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutItemSpec struct { + X int64 `json:"x"` + Y int64 `json:"y"` + Width int64 `json:"width"` + Height int64 `json:"height"` + // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference + Element DashboardElementReference `json:"element"` + Repeat *DashboardRepeatOptions `json:"repeat,omitempty"` +} + +// NewDashboardGridLayoutItemSpec creates a new DashboardGridLayoutItemSpec object. +func NewDashboardGridLayoutItemSpec() *DashboardGridLayoutItemSpec { + return &DashboardGridLayoutItemSpec{ + Element: *NewDashboardElementReference(), + } +} + +// +k8s:openapi-gen=true +type DashboardElementReference struct { + Kind string `json:"kind"` + Name string `json:"name"` +} + +// NewDashboardElementReference creates a new DashboardElementReference object. +func NewDashboardElementReference() *DashboardElementReference { + return &DashboardElementReference{ + Kind: "ElementReference", + } +} + +// +k8s:openapi-gen=true +type DashboardRepeatOptions struct { + Mode string `json:"mode"` + Value string `json:"value"` + Direction *DashboardRepeatOptionsDirection `json:"direction,omitempty"` + MaxPerRow *int64 `json:"maxPerRow,omitempty"` +} + +// NewDashboardRepeatOptions creates a new DashboardRepeatOptions object. +func NewDashboardRepeatOptions() *DashboardRepeatOptions { + return &DashboardRepeatOptions{} +} + +// other repeat modes will be added in the future: label, frame +// +k8s:openapi-gen=true +const DashboardRepeatMode = "variable" + +// +k8s:openapi-gen=true +type DashboardGridLayoutRowKind struct { + Kind string `json:"kind"` + Spec DashboardGridLayoutRowSpec `json:"spec"` +} + +// NewDashboardGridLayoutRowKind creates a new DashboardGridLayoutRowKind object. +func NewDashboardGridLayoutRowKind() *DashboardGridLayoutRowKind { + return &DashboardGridLayoutRowKind{ + Kind: "GridLayoutRow", + Spec: *NewDashboardGridLayoutRowSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutRowSpec struct { + Y int64 `json:"y"` + Collapsed bool `json:"collapsed"` + Title string `json:"title"` + // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard. + Elements []DashboardGridLayoutItemKind `json:"elements"` + Repeat *DashboardRowRepeatOptions `json:"repeat,omitempty"` +} + +// NewDashboardGridLayoutRowSpec creates a new DashboardGridLayoutRowSpec object. +func NewDashboardGridLayoutRowSpec() *DashboardGridLayoutRowSpec { + return &DashboardGridLayoutRowSpec{} +} + +// +k8s:openapi-gen=true +type DashboardRowRepeatOptions struct { + Mode string `json:"mode"` + Value string `json:"value"` +} + +// NewDashboardRowRepeatOptions creates a new DashboardRowRepeatOptions object. +func NewDashboardRowRepeatOptions() *DashboardRowRepeatOptions { + return &DashboardRowRepeatOptions{} +} + +// +k8s:openapi-gen=true +type DashboardRowsLayoutKind struct { + Kind string `json:"kind"` + Spec DashboardRowsLayoutSpec `json:"spec"` +} + +// NewDashboardRowsLayoutKind creates a new DashboardRowsLayoutKind object. +func NewDashboardRowsLayoutKind() *DashboardRowsLayoutKind { + return &DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: *NewDashboardRowsLayoutSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardRowsLayoutSpec struct { + Rows []DashboardRowsLayoutRowKind `json:"rows"` +} + +// NewDashboardRowsLayoutSpec creates a new DashboardRowsLayoutSpec object. +func NewDashboardRowsLayoutSpec() *DashboardRowsLayoutSpec { + return &DashboardRowsLayoutSpec{} +} + +// +k8s:openapi-gen=true +type DashboardRowsLayoutRowKind struct { + Kind string `json:"kind"` + Spec DashboardRowsLayoutRowSpec `json:"spec"` +} + +// NewDashboardRowsLayoutRowKind creates a new DashboardRowsLayoutRowKind object. +func NewDashboardRowsLayoutRowKind() *DashboardRowsLayoutRowKind { + return &DashboardRowsLayoutRowKind{ + Kind: "RowsLayoutRow", + Spec: *NewDashboardRowsLayoutRowSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardRowsLayoutRowSpec struct { + Title *string `json:"title,omitempty"` + Collapsed bool `json:"collapsed"` + Repeat *DashboardRowRepeatOptions `json:"repeat,omitempty"` + Layout DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind `json:"layout"` +} + +// NewDashboardRowsLayoutRowSpec creates a new DashboardRowsLayoutRowSpec object. +func NewDashboardRowsLayoutRowSpec() *DashboardRowsLayoutRowSpec { + return &DashboardRowsLayoutRowSpec{ + Layout: *NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(), + } +} + +// +k8s:openapi-gen=true +type DashboardResponsiveGridLayoutKind struct { + Kind string `json:"kind"` + Spec DashboardResponsiveGridLayoutSpec `json:"spec"` +} + +// NewDashboardResponsiveGridLayoutKind creates a new DashboardResponsiveGridLayoutKind object. +func NewDashboardResponsiveGridLayoutKind() *DashboardResponsiveGridLayoutKind { + return &DashboardResponsiveGridLayoutKind{ + Kind: "ResponsiveGridLayout", + Spec: *NewDashboardResponsiveGridLayoutSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardResponsiveGridLayoutSpec struct { + Row string `json:"row"` + Col string `json:"col"` + Items []DashboardResponsiveGridLayoutItemKind `json:"items"` +} + +// NewDashboardResponsiveGridLayoutSpec creates a new DashboardResponsiveGridLayoutSpec object. +func NewDashboardResponsiveGridLayoutSpec() *DashboardResponsiveGridLayoutSpec { + return &DashboardResponsiveGridLayoutSpec{} +} + +// +k8s:openapi-gen=true +type DashboardResponsiveGridLayoutItemKind struct { + Kind string `json:"kind"` + Spec DashboardResponsiveGridLayoutItemSpec `json:"spec"` +} + +// NewDashboardResponsiveGridLayoutItemKind creates a new DashboardResponsiveGridLayoutItemKind object. +func NewDashboardResponsiveGridLayoutItemKind() *DashboardResponsiveGridLayoutItemKind { + return &DashboardResponsiveGridLayoutItemKind{ + Kind: "ResponsiveGridLayoutItem", + Spec: *NewDashboardResponsiveGridLayoutItemSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardResponsiveGridLayoutItemSpec struct { + Element DashboardElementReference `json:"element"` + Repeat *DashboardResponsiveGridRepeatOptions `json:"repeat,omitempty"` +} + +// NewDashboardResponsiveGridLayoutItemSpec creates a new DashboardResponsiveGridLayoutItemSpec object. +func NewDashboardResponsiveGridLayoutItemSpec() *DashboardResponsiveGridLayoutItemSpec { + return &DashboardResponsiveGridLayoutItemSpec{ + Element: *NewDashboardElementReference(), + } +} + +// +k8s:openapi-gen=true +type DashboardResponsiveGridRepeatOptions struct { + Mode string `json:"mode"` + Value string `json:"value"` +} + +// NewDashboardResponsiveGridRepeatOptions creates a new DashboardResponsiveGridRepeatOptions object. +func NewDashboardResponsiveGridRepeatOptions() *DashboardResponsiveGridRepeatOptions { + return &DashboardResponsiveGridRepeatOptions{} +} + +// +k8s:openapi-gen=true +type DashboardTabsLayoutKind struct { + Kind string `json:"kind"` + Spec DashboardTabsLayoutSpec `json:"spec"` +} + +// NewDashboardTabsLayoutKind creates a new DashboardTabsLayoutKind object. +func NewDashboardTabsLayoutKind() *DashboardTabsLayoutKind { + return &DashboardTabsLayoutKind{ + Kind: "TabsLayout", + Spec: *NewDashboardTabsLayoutSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardTabsLayoutSpec struct { + Tabs []DashboardTabsLayoutTabKind `json:"tabs"` +} + +// NewDashboardTabsLayoutSpec creates a new DashboardTabsLayoutSpec object. +func NewDashboardTabsLayoutSpec() *DashboardTabsLayoutSpec { + return &DashboardTabsLayoutSpec{} +} + +// +k8s:openapi-gen=true +type DashboardTabsLayoutTabKind struct { + Kind string `json:"kind"` + Spec DashboardTabsLayoutTabSpec `json:"spec"` +} + +// NewDashboardTabsLayoutTabKind creates a new DashboardTabsLayoutTabKind object. +func NewDashboardTabsLayoutTabKind() *DashboardTabsLayoutTabKind { + return &DashboardTabsLayoutTabKind{ + Kind: "TabsLayoutTab", + Spec: *NewDashboardTabsLayoutTabSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardTabsLayoutTabSpec struct { + Title *string `json:"title,omitempty"` + Layout DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind `json:"layout"` +} + +// NewDashboardTabsLayoutTabSpec creates a new DashboardTabsLayoutTabSpec object. +func NewDashboardTabsLayoutTabSpec() *DashboardTabsLayoutTabSpec { + return &DashboardTabsLayoutTabSpec{ + Layout: *NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind(), + } +} + +// Links with references to other dashboards or external resources +// +k8s:openapi-gen=true +type DashboardDashboardLink struct { + // Title to display with the link + Title string `json:"title"` + // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) + // FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType` + Type DashboardDashboardLinkType `json:"type"` + // Icon name to be displayed with the link + Icon string `json:"icon"` + // Tooltip to display when the user hovers their mouse over it + Tooltip string `json:"tooltip"` + // Link URL. Only required/valid if the type is link + Url *string `json:"url,omitempty"` + // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards + Tags []string `json:"tags"` + // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards + AsDropdown bool `json:"asDropdown"` + // If true, the link will be opened in a new tab + TargetBlank bool `json:"targetBlank"` + // If true, includes current template variables values in the link as query params + IncludeVars bool `json:"includeVars"` + // If true, includes current time range in the link as query params + KeepTime bool `json:"keepTime"` +} + +// NewDashboardDashboardLink creates a new DashboardDashboardLink object. +func NewDashboardDashboardLink() *DashboardDashboardLink { + return &DashboardDashboardLink{ + AsDropdown: false, + TargetBlank: false, + IncludeVars: false, + KeepTime: false, + } +} + +// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) +// +k8s:openapi-gen=true +type DashboardDashboardLinkType string + +const ( + DashboardDashboardLinkTypeLink DashboardDashboardLinkType = "link" + DashboardDashboardLinkTypeDashboards DashboardDashboardLinkType = "dashboards" +) + +// Time configuration +// It defines the default time config for the time picker, the refresh picker for the specific dashboard. +// +k8s:openapi-gen=true +type DashboardTimeSettingsSpec struct { + // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". + Timezone *string `json:"timezone,omitempty"` + // Start time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + From string `json:"from"` + // End time range for dashboard. + // Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z". + To string `json:"to"` + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + // v1: refresh + AutoRefresh string `json:"autoRefresh"` + // Interval options available in the refresh picker dropdown. + // v1: timepicker.refresh_intervals + AutoRefreshIntervals []string `json:"autoRefreshIntervals"` + // Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. + // v1: timepicker.quick_ranges , not exposed in the UI + QuickRanges []DashboardTimeRangeOption `json:"quickRanges,omitempty"` + // Whether timepicker is visible or not. + // v1: timepicker.hidden + HideTimepicker bool `json:"hideTimepicker"` + // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". + WeekStart *DashboardTimeSettingsSpecWeekStart `json:"weekStart,omitempty"` + // The month that the fiscal year starts on. 0 = January, 11 = December + FiscalYearStartMonth int64 `json:"fiscalYearStartMonth"` + // Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. + // v1: timepicker.nowDelay + NowDelay *string `json:"nowDelay,omitempty"` +} + +// NewDashboardTimeSettingsSpec creates a new DashboardTimeSettingsSpec object. +func NewDashboardTimeSettingsSpec() *DashboardTimeSettingsSpec { + return &DashboardTimeSettingsSpec{ + Timezone: (func(input string) *string { return &input })("browser"), + From: "now-6h", + To: "now", + AutoRefreshIntervals: []string{"5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"}, + } +} + +// +k8s:openapi-gen=true +type DashboardTimeRangeOption struct { + Display string `json:"display"` + From string `json:"from"` + To string `json:"to"` +} + +// NewDashboardTimeRangeOption creates a new DashboardTimeRangeOption object. +func NewDashboardTimeRangeOption() *DashboardTimeRangeOption { + return &DashboardTimeRangeOption{ + Display: "Last 6 hours", + From: "now-6h", + To: "now", + } +} + +// +k8s:openapi-gen=true +type DashboardVariableKind = DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind + +// NewDashboardVariableKind creates a new DashboardVariableKind object. +func NewDashboardVariableKind() *DashboardVariableKind { + return NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind() +} + +// Query variable kind +// +k8s:openapi-gen=true +type DashboardQueryVariableKind struct { + Kind string `json:"kind"` + Spec DashboardQueryVariableSpec `json:"spec"` +} + +// NewDashboardQueryVariableKind creates a new DashboardQueryVariableKind object. +func NewDashboardQueryVariableKind() *DashboardQueryVariableKind { + return &DashboardQueryVariableKind{ + Kind: "QueryVariable", + Spec: *NewDashboardQueryVariableSpec(), + } +} + +// Query variable specification +// +k8s:openapi-gen=true +type DashboardQueryVariableSpec struct { + Name string `json:"name"` + Current DashboardVariableOption `json:"current"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + Refresh DashboardVariableRefresh `json:"refresh"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + Query DashboardDataQueryKind `json:"query"` + Regex string `json:"regex"` + Sort DashboardVariableSort `json:"sort"` + Definition *string `json:"definition,omitempty"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + IncludeAll bool `json:"includeAll"` + AllValue *string `json:"allValue,omitempty"` + Placeholder *string `json:"placeholder,omitempty"` +} + +// NewDashboardQueryVariableSpec creates a new DashboardQueryVariableSpec object. +func NewDashboardQueryVariableSpec() *DashboardQueryVariableSpec { + return &DashboardQueryVariableSpec{ + Name: "", + Current: DashboardVariableOption{ + Text: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + Value: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + }, + Hide: DashboardVariableHideDontHide, + Refresh: DashboardVariableRefreshNever, + SkipUrlSync: false, + Query: *NewDashboardDataQueryKind(), + Regex: "", + Multi: false, + IncludeAll: false, + } +} + +// Variable option specification +// +k8s:openapi-gen=true +type DashboardVariableOption struct { + // Whether the option is selected or not + Selected *bool `json:"selected,omitempty"` + // Text to be displayed for the option + Text DashboardStringOrArrayOfString `json:"text"` + // Value of the option + Value DashboardStringOrArrayOfString `json:"value"` +} + +// NewDashboardVariableOption creates a new DashboardVariableOption object. +func NewDashboardVariableOption() *DashboardVariableOption { + return &DashboardVariableOption{ + Text: *NewDashboardStringOrArrayOfString(), + Value: *NewDashboardStringOrArrayOfString(), + } +} + +// Determine if the variable shows on dashboard +// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing). +// +k8s:openapi-gen=true +type DashboardVariableHide string + +const ( + DashboardVariableHideDontHide DashboardVariableHide = "dontHide" + DashboardVariableHideHideLabel DashboardVariableHide = "hideLabel" + DashboardVariableHideHideVariable DashboardVariableHide = "hideVariable" +) + +// Options to config when to refresh a variable +// `never`: Never refresh the variable +// `onDashboardLoad`: Queries the data source every time the dashboard loads. +// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes. +// +k8s:openapi-gen=true +type DashboardVariableRefresh string + +const ( + DashboardVariableRefreshNever DashboardVariableRefresh = "never" + DashboardVariableRefreshOnDashboardLoad DashboardVariableRefresh = "onDashboardLoad" + DashboardVariableRefreshOnTimeRangeChanged DashboardVariableRefresh = "onTimeRangeChanged" +) + +// Sort variable options +// Accepted values are: +// `disabled`: No sorting +// `alphabeticalAsc`: Alphabetical ASC +// `alphabeticalDesc`: Alphabetical DESC +// `numericalAsc`: Numerical ASC +// `numericalDesc`: Numerical DESC +// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC +// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC +// `naturalAsc`: Natural ASC +// `naturalDesc`: Natural DESC +// VariableSort enum with default value +// +k8s:openapi-gen=true +type DashboardVariableSort string + +const ( + DashboardVariableSortDisabled DashboardVariableSort = "disabled" + DashboardVariableSortAlphabeticalAsc DashboardVariableSort = "alphabeticalAsc" + DashboardVariableSortAlphabeticalDesc DashboardVariableSort = "alphabeticalDesc" + DashboardVariableSortNumericalAsc DashboardVariableSort = "numericalAsc" + DashboardVariableSortNumericalDesc DashboardVariableSort = "numericalDesc" + DashboardVariableSortAlphabeticalCaseInsensitiveAsc DashboardVariableSort = "alphabeticalCaseInsensitiveAsc" + DashboardVariableSortAlphabeticalCaseInsensitiveDesc DashboardVariableSort = "alphabeticalCaseInsensitiveDesc" + DashboardVariableSortNaturalAsc DashboardVariableSort = "naturalAsc" + DashboardVariableSortNaturalDesc DashboardVariableSort = "naturalDesc" +) + +// Text variable kind +// +k8s:openapi-gen=true +type DashboardTextVariableKind struct { + Kind string `json:"kind"` + Spec DashboardTextVariableSpec `json:"spec"` +} + +// NewDashboardTextVariableKind creates a new DashboardTextVariableKind object. +func NewDashboardTextVariableKind() *DashboardTextVariableKind { + return &DashboardTextVariableKind{ + Kind: "TextVariable", + Spec: *NewDashboardTextVariableSpec(), + } +} + +// Text variable specification +// +k8s:openapi-gen=true +type DashboardTextVariableSpec struct { + Name string `json:"name"` + Current DashboardVariableOption `json:"current"` + Query string `json:"query"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardTextVariableSpec creates a new DashboardTextVariableSpec object. +func NewDashboardTextVariableSpec() *DashboardTextVariableSpec { + return &DashboardTextVariableSpec{ + Name: "", + Current: DashboardVariableOption{ + Text: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + Value: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + }, + Query: "", + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Constant variable kind +// +k8s:openapi-gen=true +type DashboardConstantVariableKind struct { + Kind string `json:"kind"` + Spec DashboardConstantVariableSpec `json:"spec"` +} + +// NewDashboardConstantVariableKind creates a new DashboardConstantVariableKind object. +func NewDashboardConstantVariableKind() *DashboardConstantVariableKind { + return &DashboardConstantVariableKind{ + Kind: "ConstantVariable", + Spec: *NewDashboardConstantVariableSpec(), + } +} + +// Constant variable specification +// +k8s:openapi-gen=true +type DashboardConstantVariableSpec struct { + Name string `json:"name"` + Query string `json:"query"` + Current DashboardVariableOption `json:"current"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardConstantVariableSpec creates a new DashboardConstantVariableSpec object. +func NewDashboardConstantVariableSpec() *DashboardConstantVariableSpec { + return &DashboardConstantVariableSpec{ + Name: "", + Query: "", + Current: DashboardVariableOption{ + Text: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + Value: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + }, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Datasource variable kind +// +k8s:openapi-gen=true +type DashboardDatasourceVariableKind struct { + Kind string `json:"kind"` + Spec DashboardDatasourceVariableSpec `json:"spec"` +} + +// NewDashboardDatasourceVariableKind creates a new DashboardDatasourceVariableKind object. +func NewDashboardDatasourceVariableKind() *DashboardDatasourceVariableKind { + return &DashboardDatasourceVariableKind{ + Kind: "DatasourceVariable", + Spec: *NewDashboardDatasourceVariableSpec(), + } +} + +// Datasource variable specification +// +k8s:openapi-gen=true +type DashboardDatasourceVariableSpec struct { + Name string `json:"name"` + PluginId string `json:"pluginId"` + Refresh DashboardVariableRefresh `json:"refresh"` + Regex string `json:"regex"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + IncludeAll bool `json:"includeAll"` + AllValue *string `json:"allValue,omitempty"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardDatasourceVariableSpec creates a new DashboardDatasourceVariableSpec object. +func NewDashboardDatasourceVariableSpec() *DashboardDatasourceVariableSpec { + return &DashboardDatasourceVariableSpec{ + Name: "", + PluginId: "", + Refresh: DashboardVariableRefreshNever, + Regex: "", + Current: DashboardVariableOption{ + Text: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + Value: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + }, + Multi: false, + IncludeAll: false, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Interval variable kind +// +k8s:openapi-gen=true +type DashboardIntervalVariableKind struct { + Kind string `json:"kind"` + Spec DashboardIntervalVariableSpec `json:"spec"` +} + +// NewDashboardIntervalVariableKind creates a new DashboardIntervalVariableKind object. +func NewDashboardIntervalVariableKind() *DashboardIntervalVariableKind { + return &DashboardIntervalVariableKind{ + Kind: "IntervalVariable", + Spec: *NewDashboardIntervalVariableSpec(), + } +} + +// Interval variable specification +// +k8s:openapi-gen=true +type DashboardIntervalVariableSpec struct { + Name string `json:"name"` + Query string `json:"query"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Auto bool `json:"auto"` + AutoMin string `json:"auto_min"` + AutoCount int64 `json:"auto_count"` + Refresh DashboardVariableRefresh `json:"refresh"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardIntervalVariableSpec creates a new DashboardIntervalVariableSpec object. +func NewDashboardIntervalVariableSpec() *DashboardIntervalVariableSpec { + return &DashboardIntervalVariableSpec{ + Name: "", + Query: "", + Current: DashboardVariableOption{ + Text: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + Value: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + }, + Auto: false, + AutoMin: "", + AutoCount: 0, + Refresh: DashboardVariableRefreshNever, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Custom variable kind +// +k8s:openapi-gen=true +type DashboardCustomVariableKind struct { + Kind string `json:"kind"` + Spec DashboardCustomVariableSpec `json:"spec"` +} + +// NewDashboardCustomVariableKind creates a new DashboardCustomVariableKind object. +func NewDashboardCustomVariableKind() *DashboardCustomVariableKind { + return &DashboardCustomVariableKind{ + Kind: "CustomVariable", + Spec: *NewDashboardCustomVariableSpec(), + } +} + +// Custom variable specification +// +k8s:openapi-gen=true +type DashboardCustomVariableSpec struct { + Name string `json:"name"` + Query string `json:"query"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + IncludeAll bool `json:"includeAll"` + AllValue *string `json:"allValue,omitempty"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardCustomVariableSpec creates a new DashboardCustomVariableSpec object. +func NewDashboardCustomVariableSpec() *DashboardCustomVariableSpec { + return &DashboardCustomVariableSpec{ + Name: "", + Query: "", + Current: *NewDashboardVariableOption(), + Multi: false, + IncludeAll: false, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Group variable kind +// +k8s:openapi-gen=true +type DashboardGroupByVariableKind struct { + Kind string `json:"kind"` + Spec DashboardGroupByVariableSpec `json:"spec"` +} + +// NewDashboardGroupByVariableKind creates a new DashboardGroupByVariableKind object. +func NewDashboardGroupByVariableKind() *DashboardGroupByVariableKind { + return &DashboardGroupByVariableKind{ + Kind: "GroupByVariable", + Spec: *NewDashboardGroupByVariableSpec(), + } +} + +// GroupBy variable specification +// +k8s:openapi-gen=true +type DashboardGroupByVariableSpec struct { + Name string `json:"name"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + Current DashboardVariableOption `json:"current"` + Options []DashboardVariableOption `json:"options"` + Multi bool `json:"multi"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardGroupByVariableSpec creates a new DashboardGroupByVariableSpec object. +func NewDashboardGroupByVariableSpec() *DashboardGroupByVariableSpec { + return &DashboardGroupByVariableSpec{ + Name: "", + Current: DashboardVariableOption{ + Text: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + Value: DashboardStringOrArrayOfString{ + String: (func(input string) *string { return &input })(""), + }, + }, + Multi: false, + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Adhoc variable kind +// +k8s:openapi-gen=true +type DashboardAdhocVariableKind struct { + Kind string `json:"kind"` + Spec DashboardAdhocVariableSpec `json:"spec"` +} + +// NewDashboardAdhocVariableKind creates a new DashboardAdhocVariableKind object. +func NewDashboardAdhocVariableKind() *DashboardAdhocVariableKind { + return &DashboardAdhocVariableKind{ + Kind: "AdhocVariable", + Spec: *NewDashboardAdhocVariableSpec(), + } +} + +// Adhoc variable specification +// +k8s:openapi-gen=true +type DashboardAdhocVariableSpec struct { + Name string `json:"name"` + Datasource *DashboardDataSourceRef `json:"datasource,omitempty"` + BaseFilters []DashboardAdHocFilterWithLabels `json:"baseFilters"` + Filters []DashboardAdHocFilterWithLabels `json:"filters"` + DefaultKeys []DashboardMetricFindValue `json:"defaultKeys"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardAdhocVariableSpec creates a new DashboardAdhocVariableSpec object. +func NewDashboardAdhocVariableSpec() *DashboardAdhocVariableSpec { + return &DashboardAdhocVariableSpec{ + Name: "", + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + +// Define the AdHocFilterWithLabels type +// +k8s:openapi-gen=true +type DashboardAdHocFilterWithLabels struct { + Key string `json:"key"` + Operator string `json:"operator"` + Value string `json:"value"` + Values []string `json:"values,omitempty"` + KeyLabel *string `json:"keyLabel,omitempty"` + ValueLabels []string `json:"valueLabels,omitempty"` + ForceEdit *bool `json:"forceEdit,omitempty"` + // @deprecated + Condition *string `json:"condition,omitempty"` +} + +// NewDashboardAdHocFilterWithLabels creates a new DashboardAdHocFilterWithLabels object. +func NewDashboardAdHocFilterWithLabels() *DashboardAdHocFilterWithLabels { + return &DashboardAdHocFilterWithLabels{} +} + +// Define the MetricFindValue type +// +k8s:openapi-gen=true +type DashboardMetricFindValue struct { + Text string `json:"text"` + Value *DashboardStringOrFloat64 `json:"value,omitempty"` + Group *string `json:"group,omitempty"` + Expandable *bool `json:"expandable,omitempty"` +} + +// NewDashboardMetricFindValue creates a new DashboardMetricFindValue object. +func NewDashboardMetricFindValue() *DashboardMetricFindValue { + return &DashboardMetricFindValue{} +} + +// +k8s:openapi-gen=true +type DashboardSpec struct { + // Title of dashboard. + Annotations []DashboardAnnotationQueryKind `json:"annotations"` + // Configuration of dashboard cursor sync behavior. + // "Off" for no shared crosshair or tooltip (default). + // "Crosshair" for shared crosshair. + // "Tooltip" for shared crosshair AND shared tooltip. + CursorSync DashboardDashboardCursorSync `json:"cursorSync"` + // Description of dashboard. + Description *string `json:"description,omitempty"` + // Whether a dashboard is editable or not. + Editable *bool `json:"editable,omitempty"` + Elements map[string]DashboardElement `json:"elements"` + Layout DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind `json:"layout"` + // Links with references to other dashboards or external websites. + Links []DashboardDashboardLink `json:"links"` + // When set to true, the dashboard will redraw panels at an interval matching the pixel width. + // This will keep data "moving left" regardless of the query refresh rate. This setting helps + // avoid dashboards presenting stale live data. + LiveNow *bool `json:"liveNow,omitempty"` + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + Preload bool `json:"preload"` + // Plugins only. The version of the dashboard installed together with the plugin. + // This is used to determine if the dashboard should be updated when the plugin is updated. + Revision *uint16 `json:"revision,omitempty"` + // Tags associated with dashboard. + Tags []string `json:"tags"` + TimeSettings DashboardTimeSettingsSpec `json:"timeSettings"` + // Title of dashboard. + Title string `json:"title"` + // Configured template variables. + Variables []DashboardVariableKind `json:"variables"` +} + +// NewDashboardSpec creates a new DashboardSpec object. +func NewDashboardSpec() *DashboardSpec { + return &DashboardSpec{ + Editable: (func(input bool) *bool { return &input })(true), + Layout: *NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(), + TimeSettings: *NewDashboardTimeSettingsSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardV2alpha1FieldConfigSourceOverrides struct { + Matcher DashboardMatcherConfig `json:"matcher"` + Properties []DashboardDynamicConfigValue `json:"properties"` +} + +// NewDashboardV2alpha1FieldConfigSourceOverrides creates a new DashboardV2alpha1FieldConfigSourceOverrides object. +func NewDashboardV2alpha1FieldConfigSourceOverrides() *DashboardV2alpha1FieldConfigSourceOverrides { + return &DashboardV2alpha1FieldConfigSourceOverrides{ + Matcher: *NewDashboardMatcherConfig(), + } +} + +// +k8s:openapi-gen=true +type DashboardV2alpha1RangeMapOptions struct { + // Min value of the range. It can be null which means -Infinity + From *float64 `json:"from"` + // Max value of the range. It can be null which means +Infinity + To *float64 `json:"to"` + // Config to apply when the value is within the range + Result DashboardValueMappingResult `json:"result"` +} + +// NewDashboardV2alpha1RangeMapOptions creates a new DashboardV2alpha1RangeMapOptions object. +func NewDashboardV2alpha1RangeMapOptions() *DashboardV2alpha1RangeMapOptions { + return &DashboardV2alpha1RangeMapOptions{ + Result: *NewDashboardValueMappingResult(), + } +} + +// +k8s:openapi-gen=true +type DashboardV2alpha1RegexMapOptions struct { + // Regular expression to match against + Pattern string `json:"pattern"` + // Config to apply when the value matches the regex + Result DashboardValueMappingResult `json:"result"` +} + +// NewDashboardV2alpha1RegexMapOptions creates a new DashboardV2alpha1RegexMapOptions object. +func NewDashboardV2alpha1RegexMapOptions() *DashboardV2alpha1RegexMapOptions { + return &DashboardV2alpha1RegexMapOptions{ + Result: *NewDashboardValueMappingResult(), + } +} + +// +k8s:openapi-gen=true +type DashboardV2alpha1SpecialValueMapOptions struct { + // Special value to match against + Match DashboardSpecialValueMatch `json:"match"` + // Config to apply when the value matches the special value + Result DashboardValueMappingResult `json:"result"` +} + +// NewDashboardV2alpha1SpecialValueMapOptions creates a new DashboardV2alpha1SpecialValueMapOptions object. +func NewDashboardV2alpha1SpecialValueMapOptions() *DashboardV2alpha1SpecialValueMapOptions { + return &DashboardV2alpha1SpecialValueMapOptions{ + Result: *NewDashboardValueMappingResult(), + } +} + +// +k8s:openapi-gen=true +type DashboardRepeatOptionsDirection string + +const ( + DashboardRepeatOptionsDirectionH DashboardRepeatOptionsDirection = "h" + DashboardRepeatOptionsDirectionV DashboardRepeatOptionsDirection = "v" +) + +// +k8s:openapi-gen=true +type DashboardTimeSettingsSpecWeekStart string + +const ( + DashboardTimeSettingsSpecWeekStartSaturday DashboardTimeSettingsSpecWeekStart = "saturday" + DashboardTimeSettingsSpecWeekStartMonday DashboardTimeSettingsSpecWeekStart = "monday" + DashboardTimeSettingsSpecWeekStartSunday DashboardTimeSettingsSpecWeekStart = "sunday" +) + +// +k8s:openapi-gen=true +type DashboardPanelKindOrLibraryPanelKind struct { + PanelKind *DashboardPanelKind `json:"PanelKind,omitempty"` + LibraryPanelKind *DashboardLibraryPanelKind `json:"LibraryPanelKind,omitempty"` +} + +// NewDashboardPanelKindOrLibraryPanelKind creates a new DashboardPanelKindOrLibraryPanelKind object. +func NewDashboardPanelKindOrLibraryPanelKind() *DashboardPanelKindOrLibraryPanelKind { + return &DashboardPanelKindOrLibraryPanelKind{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardPanelKindOrLibraryPanelKind` as JSON. +func (resource DashboardPanelKindOrLibraryPanelKind) MarshalJSON() ([]byte, error) { + if resource.PanelKind != nil { + return json.Marshal(resource.PanelKind) + } + if resource.LibraryPanelKind != nil { + return json.Marshal(resource.LibraryPanelKind) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardPanelKindOrLibraryPanelKind` from JSON. +func (resource *DashboardPanelKindOrLibraryPanelKind) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["kind"] + if !found { + return errors.New("discriminator field 'kind' not found in payload") + } + + switch discriminator { + case "LibraryPanel": + var dashboardLibraryPanelKind DashboardLibraryPanelKind + if err := json.Unmarshal(raw, &dashboardLibraryPanelKind); err != nil { + return err + } + + resource.LibraryPanelKind = &dashboardLibraryPanelKind + return nil + case "Panel": + var dashboardPanelKind DashboardPanelKind + if err := json.Unmarshal(raw, &dashboardPanelKind); err != nil { + return err + } + + resource.PanelKind = &dashboardPanelKind + return nil + } + + return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) +} + +// +k8s:openapi-gen=true +type DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap struct { + ValueMap *DashboardValueMap `json:"ValueMap,omitempty"` + RangeMap *DashboardRangeMap `json:"RangeMap,omitempty"` + RegexMap *DashboardRegexMap `json:"RegexMap,omitempty"` + SpecialValueMap *DashboardSpecialValueMap `json:"SpecialValueMap,omitempty"` +} + +// NewDashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap creates a new DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap object. +func NewDashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap() *DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap { + return &DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap` as JSON. +func (resource DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap) MarshalJSON() ([]byte, error) { + if resource.ValueMap != nil { + return json.Marshal(resource.ValueMap) + } + if resource.RangeMap != nil { + return json.Marshal(resource.RangeMap) + } + if resource.RegexMap != nil { + return json.Marshal(resource.RegexMap) + } + if resource.SpecialValueMap != nil { + return json.Marshal(resource.SpecialValueMap) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap` from JSON. +func (resource *DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["type"] + if !found { + return errors.New("discriminator field 'type' not found in payload") + } + + switch discriminator { + case "range": + var dashboardRangeMap DashboardRangeMap + if err := json.Unmarshal(raw, &dashboardRangeMap); err != nil { + return err + } + + resource.RangeMap = &dashboardRangeMap + return nil + case "regex": + var dashboardRegexMap DashboardRegexMap + if err := json.Unmarshal(raw, &dashboardRegexMap); err != nil { + return err + } + + resource.RegexMap = &dashboardRegexMap + return nil + case "special": + var dashboardSpecialValueMap DashboardSpecialValueMap + if err := json.Unmarshal(raw, &dashboardSpecialValueMap); err != nil { + return err + } + + resource.SpecialValueMap = &dashboardSpecialValueMap + return nil + case "value": + var dashboardValueMap DashboardValueMap + if err := json.Unmarshal(raw, &dashboardValueMap); err != nil { + return err + } + + resource.ValueMap = &dashboardValueMap + return nil + } + + return fmt.Errorf("could not unmarshal resource with `type = %v`", discriminator) +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutItemKindOrGridLayoutRowKind struct { + GridLayoutItemKind *DashboardGridLayoutItemKind `json:"GridLayoutItemKind,omitempty"` + GridLayoutRowKind *DashboardGridLayoutRowKind `json:"GridLayoutRowKind,omitempty"` +} + +// NewDashboardGridLayoutItemKindOrGridLayoutRowKind creates a new DashboardGridLayoutItemKindOrGridLayoutRowKind object. +func NewDashboardGridLayoutItemKindOrGridLayoutRowKind() *DashboardGridLayoutItemKindOrGridLayoutRowKind { + return &DashboardGridLayoutItemKindOrGridLayoutRowKind{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutItemKindOrGridLayoutRowKind` as JSON. +func (resource DashboardGridLayoutItemKindOrGridLayoutRowKind) MarshalJSON() ([]byte, error) { + if resource.GridLayoutItemKind != nil { + return json.Marshal(resource.GridLayoutItemKind) + } + if resource.GridLayoutRowKind != nil { + return json.Marshal(resource.GridLayoutRowKind) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutItemKindOrGridLayoutRowKind` from JSON. +func (resource *DashboardGridLayoutItemKindOrGridLayoutRowKind) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["kind"] + if !found { + return errors.New("discriminator field 'kind' not found in payload") + } + + switch discriminator { + case "GridLayoutItem": + var dashboardGridLayoutItemKind DashboardGridLayoutItemKind + if err := json.Unmarshal(raw, &dashboardGridLayoutItemKind); err != nil { + return err + } + + resource.GridLayoutItemKind = &dashboardGridLayoutItemKind + return nil + case "GridLayoutRow": + var dashboardGridLayoutRowKind DashboardGridLayoutRowKind + if err := json.Unmarshal(raw, &dashboardGridLayoutRowKind); err != nil { + return err + } + + resource.GridLayoutRowKind = &dashboardGridLayoutRowKind + return nil + } + + return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind struct { + GridLayoutKind *DashboardGridLayoutKind `json:"GridLayoutKind,omitempty"` + ResponsiveGridLayoutKind *DashboardResponsiveGridLayoutKind `json:"ResponsiveGridLayoutKind,omitempty"` + TabsLayoutKind *DashboardTabsLayoutKind `json:"TabsLayoutKind,omitempty"` +} + +// NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind creates a new DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind object. +func NewDashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind() *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind { + return &DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` as JSON. +func (resource DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) MarshalJSON() ([]byte, error) { + if resource.GridLayoutKind != nil { + return json.Marshal(resource.GridLayoutKind) + } + if resource.ResponsiveGridLayoutKind != nil { + return json.Marshal(resource.ResponsiveGridLayoutKind) + } + if resource.TabsLayoutKind != nil { + return json.Marshal(resource.TabsLayoutKind) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` from JSON. +func (resource *DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["kind"] + if !found { + return errors.New("discriminator field 'kind' not found in payload") + } + + switch discriminator { + case "GridLayout": + var dashboardGridLayoutKind DashboardGridLayoutKind + if err := json.Unmarshal(raw, &dashboardGridLayoutKind); err != nil { + return err + } + + resource.GridLayoutKind = &dashboardGridLayoutKind + return nil + case "ResponsiveGridLayout": + var dashboardResponsiveGridLayoutKind DashboardResponsiveGridLayoutKind + if err := json.Unmarshal(raw, &dashboardResponsiveGridLayoutKind); err != nil { + return err + } + + resource.ResponsiveGridLayoutKind = &dashboardResponsiveGridLayoutKind + return nil + case "TabsLayout": + var dashboardTabsLayoutKind DashboardTabsLayoutKind + if err := json.Unmarshal(raw, &dashboardTabsLayoutKind); err != nil { + return err + } + + resource.TabsLayoutKind = &dashboardTabsLayoutKind + return nil + } + + return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind struct { + GridLayoutKind *DashboardGridLayoutKind `json:"GridLayoutKind,omitempty"` + RowsLayoutKind *DashboardRowsLayoutKind `json:"RowsLayoutKind,omitempty"` + ResponsiveGridLayoutKind *DashboardResponsiveGridLayoutKind `json:"ResponsiveGridLayoutKind,omitempty"` +} + +// NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind creates a new DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind object. +func NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind() *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind { + return &DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind` as JSON. +func (resource DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind) MarshalJSON() ([]byte, error) { + if resource.GridLayoutKind != nil { + return json.Marshal(resource.GridLayoutKind) + } + if resource.RowsLayoutKind != nil { + return json.Marshal(resource.RowsLayoutKind) + } + if resource.ResponsiveGridLayoutKind != nil { + return json.Marshal(resource.ResponsiveGridLayoutKind) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind` from JSON. +func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["kind"] + if !found { + return errors.New("discriminator field 'kind' not found in payload") + } + + switch discriminator { + case "GridLayout": + var dashboardGridLayoutKind DashboardGridLayoutKind + if err := json.Unmarshal(raw, &dashboardGridLayoutKind); err != nil { + return err + } + + resource.GridLayoutKind = &dashboardGridLayoutKind + return nil + case "ResponsiveGridLayout": + var dashboardResponsiveGridLayoutKind DashboardResponsiveGridLayoutKind + if err := json.Unmarshal(raw, &dashboardResponsiveGridLayoutKind); err != nil { + return err + } + + resource.ResponsiveGridLayoutKind = &dashboardResponsiveGridLayoutKind + return nil + case "RowsLayout": + var dashboardRowsLayoutKind DashboardRowsLayoutKind + if err := json.Unmarshal(raw, &dashboardRowsLayoutKind); err != nil { + return err + } + + resource.RowsLayoutKind = &dashboardRowsLayoutKind + return nil + } + + return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) +} + +// +k8s:openapi-gen=true +type DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind struct { + QueryVariableKind *DashboardQueryVariableKind `json:"QueryVariableKind,omitempty"` + TextVariableKind *DashboardTextVariableKind `json:"TextVariableKind,omitempty"` + ConstantVariableKind *DashboardConstantVariableKind `json:"ConstantVariableKind,omitempty"` + DatasourceVariableKind *DashboardDatasourceVariableKind `json:"DatasourceVariableKind,omitempty"` + IntervalVariableKind *DashboardIntervalVariableKind `json:"IntervalVariableKind,omitempty"` + CustomVariableKind *DashboardCustomVariableKind `json:"CustomVariableKind,omitempty"` + GroupByVariableKind *DashboardGroupByVariableKind `json:"GroupByVariableKind,omitempty"` + AdhocVariableKind *DashboardAdhocVariableKind `json:"AdhocVariableKind,omitempty"` +} + +// NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind creates a new DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind object. +func NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind() *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind { + return &DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind` as JSON. +func (resource DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind) MarshalJSON() ([]byte, error) { + if resource.QueryVariableKind != nil { + return json.Marshal(resource.QueryVariableKind) + } + if resource.TextVariableKind != nil { + return json.Marshal(resource.TextVariableKind) + } + if resource.ConstantVariableKind != nil { + return json.Marshal(resource.ConstantVariableKind) + } + if resource.DatasourceVariableKind != nil { + return json.Marshal(resource.DatasourceVariableKind) + } + if resource.IntervalVariableKind != nil { + return json.Marshal(resource.IntervalVariableKind) + } + if resource.CustomVariableKind != nil { + return json.Marshal(resource.CustomVariableKind) + } + if resource.GroupByVariableKind != nil { + return json.Marshal(resource.GroupByVariableKind) + } + if resource.AdhocVariableKind != nil { + return json.Marshal(resource.AdhocVariableKind) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind` from JSON. +func (resource *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["kind"] + if !found { + return errors.New("discriminator field 'kind' not found in payload") + } + + switch discriminator { + case "AdhocVariable": + var dashboardAdhocVariableKind DashboardAdhocVariableKind + if err := json.Unmarshal(raw, &dashboardAdhocVariableKind); err != nil { + return err + } + + resource.AdhocVariableKind = &dashboardAdhocVariableKind + return nil + case "ConstantVariable": + var dashboardConstantVariableKind DashboardConstantVariableKind + if err := json.Unmarshal(raw, &dashboardConstantVariableKind); err != nil { + return err + } + + resource.ConstantVariableKind = &dashboardConstantVariableKind + return nil + case "CustomVariable": + var dashboardCustomVariableKind DashboardCustomVariableKind + if err := json.Unmarshal(raw, &dashboardCustomVariableKind); err != nil { + return err + } + + resource.CustomVariableKind = &dashboardCustomVariableKind + return nil + case "DatasourceVariable": + var dashboardDatasourceVariableKind DashboardDatasourceVariableKind + if err := json.Unmarshal(raw, &dashboardDatasourceVariableKind); err != nil { + return err + } + + resource.DatasourceVariableKind = &dashboardDatasourceVariableKind + return nil + case "GroupByVariable": + var dashboardGroupByVariableKind DashboardGroupByVariableKind + if err := json.Unmarshal(raw, &dashboardGroupByVariableKind); err != nil { + return err + } + + resource.GroupByVariableKind = &dashboardGroupByVariableKind + return nil + case "IntervalVariable": + var dashboardIntervalVariableKind DashboardIntervalVariableKind + if err := json.Unmarshal(raw, &dashboardIntervalVariableKind); err != nil { + return err + } + + resource.IntervalVariableKind = &dashboardIntervalVariableKind + return nil + case "QueryVariable": + var dashboardQueryVariableKind DashboardQueryVariableKind + if err := json.Unmarshal(raw, &dashboardQueryVariableKind); err != nil { + return err + } + + resource.QueryVariableKind = &dashboardQueryVariableKind + return nil + case "TextVariable": + var dashboardTextVariableKind DashboardTextVariableKind + if err := json.Unmarshal(raw, &dashboardTextVariableKind); err != nil { + return err + } + + resource.TextVariableKind = &dashboardTextVariableKind + return nil + } + + return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) +} + +// +k8s:openapi-gen=true +type DashboardStringOrArrayOfString struct { + String *string `json:"String,omitempty"` + ArrayOfString []string `json:"ArrayOfString,omitempty"` +} + +// NewDashboardStringOrArrayOfString creates a new DashboardStringOrArrayOfString object. +func NewDashboardStringOrArrayOfString() *DashboardStringOrArrayOfString { + return &DashboardStringOrArrayOfString{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardStringOrArrayOfString` as JSON. +func (resource DashboardStringOrArrayOfString) MarshalJSON() ([]byte, error) { + if resource.String != nil { + return json.Marshal(resource.String) + } + + if resource.ArrayOfString != nil { + return json.Marshal(resource.ArrayOfString) + } + + return nil, fmt.Errorf("no value for disjunction of scalars") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardStringOrArrayOfString` from JSON. +func (resource *DashboardStringOrArrayOfString) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + var errList []error + + // String + var String string + if err := json.Unmarshal(raw, &String); err != nil { + errList = append(errList, err) + resource.String = nil + } else { + resource.String = &String + return nil + } + + // ArrayOfString + var ArrayOfString []string + if err := json.Unmarshal(raw, &ArrayOfString); err != nil { + errList = append(errList, err) + resource.ArrayOfString = nil + } else { + resource.ArrayOfString = ArrayOfString + return nil + } + + return errors.Join(errList...) +} + +// +k8s:openapi-gen=true +type DashboardStringOrFloat64 struct { + String *string `json:"String,omitempty"` + Float64 *float64 `json:"Float64,omitempty"` +} + +// NewDashboardStringOrFloat64 creates a new DashboardStringOrFloat64 object. +func NewDashboardStringOrFloat64() *DashboardStringOrFloat64 { + return &DashboardStringOrFloat64{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardStringOrFloat64` as JSON. +func (resource DashboardStringOrFloat64) MarshalJSON() ([]byte, error) { + if resource.String != nil { + return json.Marshal(resource.String) + } + + if resource.Float64 != nil { + return json.Marshal(resource.Float64) + } + + return nil, fmt.Errorf("no value for disjunction of scalars") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardStringOrFloat64` from JSON. +func (resource *DashboardStringOrFloat64) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + var errList []error + + // String + var String string + if err := json.Unmarshal(raw, &String); err != nil { + errList = append(errList, err) + resource.String = nil + } else { + resource.String = &String + return nil + } + + // Float64 + var Float64 float64 + if err := json.Unmarshal(raw, &Float64); err != nil { + errList = append(errList, err) + resource.Float64 = nil + } else { + resource.Float64 = &Float64 + return nil + } + + return errors.Join(errList...) +} + +// +k8s:openapi-gen=true +type DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind struct { + GridLayoutKind *DashboardGridLayoutKind `json:"GridLayoutKind,omitempty"` + RowsLayoutKind *DashboardRowsLayoutKind `json:"RowsLayoutKind,omitempty"` + ResponsiveGridLayoutKind *DashboardResponsiveGridLayoutKind `json:"ResponsiveGridLayoutKind,omitempty"` + TabsLayoutKind *DashboardTabsLayoutKind `json:"TabsLayoutKind,omitempty"` +} + +// NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind creates a new DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind object. +func NewDashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind() *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind { + return &DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` as JSON. +func (resource DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) MarshalJSON() ([]byte, error) { + if resource.GridLayoutKind != nil { + return json.Marshal(resource.GridLayoutKind) + } + if resource.RowsLayoutKind != nil { + return json.Marshal(resource.RowsLayoutKind) + } + if resource.ResponsiveGridLayoutKind != nil { + return json.Marshal(resource.ResponsiveGridLayoutKind) + } + if resource.TabsLayoutKind != nil { + return json.Marshal(resource.TabsLayoutKind) + } + + return nil, fmt.Errorf("no value for disjunction of refs") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind` from JSON. +func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + // FIXME: this is wasteful, we need to find a more efficient way to unmarshal this. + parsedAsMap := make(map[string]interface{}) + if err := json.Unmarshal(raw, &parsedAsMap); err != nil { + return err + } + + discriminator, found := parsedAsMap["kind"] + if !found { + return errors.New("discriminator field 'kind' not found in payload") + } + + switch discriminator { + case "GridLayout": + var dashboardGridLayoutKind DashboardGridLayoutKind + if err := json.Unmarshal(raw, &dashboardGridLayoutKind); err != nil { + return err + } + + resource.GridLayoutKind = &dashboardGridLayoutKind + return nil + case "ResponsiveGridLayout": + var dashboardResponsiveGridLayoutKind DashboardResponsiveGridLayoutKind + if err := json.Unmarshal(raw, &dashboardResponsiveGridLayoutKind); err != nil { + return err + } + + resource.ResponsiveGridLayoutKind = &dashboardResponsiveGridLayoutKind + return nil + case "RowsLayout": + var dashboardRowsLayoutKind DashboardRowsLayoutKind + if err := json.Unmarshal(raw, &dashboardRowsLayoutKind); err != nil { + return err + } + + resource.RowsLayoutKind = &dashboardRowsLayoutKind + return nil + case "TabsLayout": + var dashboardTabsLayoutKind DashboardTabsLayoutKind + if err := json.Unmarshal(raw, &dashboardTabsLayoutKind); err != nil { + return err + } + + resource.TabsLayoutKind = &dashboardTabsLayoutKind + return nil + } + + return fmt.Errorf("could not unmarshal resource with `kind = %v`", discriminator) +} diff --git a/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go b/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go new file mode 100644 index 00000000000..5bd84ac30cc --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go @@ -0,0 +1,34 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v2alpha1 + +// ConversionStatus is the status of the conversion of the dashboard. +// +k8s:openapi-gen=true +type DashboardConversionStatus struct { + // Whether from another version has failed. + // If true, means that the dashboard is not valid, + // and the caller should instead fetch the stored version. + Failed bool `json:"failed"` + // The version which was stored when the dashboard was created / updated. + // Fetching this version should always succeed. + StoredVersion string `json:"storedVersion"` + // The error message from the conversion. + // Empty if the conversion has not failed. + Error string `json:"error"` +} + +// NewDashboardConversionStatus creates a new DashboardConversionStatus object. +func NewDashboardConversionStatus() *DashboardConversionStatus { + return &DashboardConversionStatus{} +} + +// +k8s:openapi-gen=true +type DashboardStatus struct { + // Optional conversion status. + Conversion *DashboardConversionStatus `json:"conversion,omitempty"` +} + +// NewDashboardStatus creates a new DashboardStatus object. +func NewDashboardStatus() *DashboardStatus { + return &DashboardStatus{} +} diff --git a/pkg/apis/dashboard/v2alpha1/deepcopy.go b/pkg/apis/dashboard/v2alpha1/deepcopy.go new file mode 100644 index 00000000000..621d7232a8e --- /dev/null +++ b/pkg/apis/dashboard/v2alpha1/deepcopy.go @@ -0,0 +1,63 @@ +package v2alpha1 + +import "reflect" + +// TODO: these should be automatically generated by the SDK. + +func (in *Dashboard) DeepCopyInto(out *Dashboard) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +func (in *Dashboard) DeepCopy() *Dashboard { + if in == nil { + return nil + } + out := new(Dashboard) + in.DeepCopyInto(out) + return out +} + +func (in *DashboardList) DeepCopyInto(out *DashboardList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Dashboard, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +func (in *DashboardList) DeepCopy() *DashboardList { + if in == nil { + return nil + } + out := new(DashboardList) + in.DeepCopyInto(out) + return out +} + +// TODO: we currently don't generate these methods for spec / status types. +// We probably should do that in the SDK. + +func (in *DashboardSpec) DeepCopyInto(out *DashboardSpec) { + // TODO (@radiohead): since we don't generate this, + // I've added a manual reflection-based implementation for now. + val := reflect.ValueOf(in).Elem() + cpy := reflect.New(val.Type()) + cpy.Elem().Set(val) +} + +func (in *DashboardSpec) DeepCopy() *DashboardSpec { + if in == nil { + return nil + } + out := new(DashboardSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/dashboard/v2alpha1/doc.go b/pkg/apis/dashboard/v2alpha1/doc.go index eac83f16cf9..8866425fdce 100644 --- a/pkg/apis/dashboard/v2alpha1/doc.go +++ b/pkg/apis/dashboard/v2alpha1/doc.go @@ -1,7 +1,10 @@ -// +k8s:deepcopy-gen=package // +k8s:openapi-gen=true // +k8s:defaulter-gen=TypeMeta // +k8s:conversion-gen=github.com/grafana/grafana/pkg/apis/dashboard // +groupName=dashboard.grafana.app +// NOTE (@radiohead): we do not use package-wide deepcopy generation +// because grafana-app-sdk already provides deepcopy functions. +// Kinds which are not generated by the SDK are explicitly opted in to deepcopy generation. + package v2alpha1 // import "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1" diff --git a/pkg/apis/dashboard/v2alpha1/register.go b/pkg/apis/dashboard/v2alpha1/register.go index 8ee9e149d7a..31aa15a72dd 100644 --- a/pkg/apis/dashboard/v2alpha1/register.go +++ b/pkg/apis/dashboard/v2alpha1/register.go @@ -32,7 +32,7 @@ var DashboardResourceInfo = utils.NewResourceInfo(GROUP, VERSION, if dash != nil { return []interface{}{ dash.Name, - dash.Spec.GetNestedString("title"), + dash.Spec.Title, dash.CreationTimestamp.UTC().Format(time.RFC3339), }, nil } @@ -54,14 +54,14 @@ var LibraryPanelResourceInfo = utils.NewResourceInfo(GROUP, VERSION, {Name: "Created At", Type: "date"}, }, Reader: func(obj any) ([]interface{}, error) { - dash, ok := obj.(*LibraryPanel) + panel, ok := obj.(*LibraryPanel) if ok { - if dash != nil { + if panel != nil { return []interface{}{ - dash.Name, - dash.Spec.Title, - dash.Spec.Type, - dash.CreationTimestamp.UTC().Format(time.RFC3339), + panel.Name, + panel.Spec.Title, + panel.Spec.Type, + panel.CreationTimestamp.UTC().Format(time.RFC3339), }, nil } } diff --git a/pkg/apis/dashboard/v2alpha1/types.go b/pkg/apis/dashboard/v2alpha1/types.go index dd25b2e683b..7cc9d29d149 100644 --- a/pkg/apis/dashboard/v2alpha1/types.go +++ b/pkg/apis/dashboard/v2alpha1/types.go @@ -7,40 +7,7 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type Dashboard struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata - // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // The dashboard body (unstructured for now) - Spec common.Unstructured `json:"spec"` - - // Optional dashboard status - Status *DashboardStatus `json:"status,omitempty"` -} - -type DashboardStatus struct { - ConversionStatus *ConversionStatus `json:"conversion,omitempty"` -} - -type ConversionStatus struct { - Failed bool `json:"failed,omitempty"` - StoredVersion string `json:"storedVersion,omitempty"` - Error string `json:"error,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DashboardList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata,omitempty"` - - Items []Dashboard `json:"items,omitempty"` -} - +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardVersionList struct { metav1.TypeMeta `json:",inline"` @@ -50,6 +17,7 @@ type DashboardVersionList struct { Items []DashboardVersionInfo `json:"items,omitempty"` } +// +k8s:deepcopy-gen=true type DashboardVersionInfo struct { // The internal ID for this version (will be replaced with resourceVersion) Version int `json:"version"` @@ -67,6 +35,7 @@ type DashboardVersionInfo struct { Message string `json:"message,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:conversion-gen:explicit-from=net/url.Values // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type VersionsQueryOptions struct { @@ -80,6 +49,7 @@ type VersionsQueryOptions struct { Version int64 `json:"version,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanel struct { metav1.TypeMeta `json:",inline"` @@ -95,6 +65,7 @@ type LibraryPanel struct { Status *LibraryPanelStatus `json:"status,omitempty"` } +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type LibraryPanelList struct { metav1.TypeMeta `json:",inline"` @@ -104,6 +75,7 @@ type LibraryPanelList struct { Items []LibraryPanel `json:"items,omitempty"` } +// +k8s:deepcopy-gen=true type LibraryPanelSpec struct { // The panel type Type string `json:"type"` @@ -131,6 +103,7 @@ type LibraryPanelSpec struct { Targets []data.DataQuery `json:"targets,omitempty"` } +// +k8s:deepcopy-gen=true type LibraryPanelStatus struct { // Translation warnings (mostly things that were in SQL columns but not found in the saved body) Warnings []string `json:"warnings,omitempty"` @@ -140,6 +113,7 @@ type LibraryPanelStatus struct { } // This is like the legacy DTO where access and metadata are all returned in a single call +// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type DashboardWithAccessInfo struct { Dashboard `json:",inline"` @@ -148,6 +122,7 @@ type DashboardWithAccessInfo struct { } // Information about how the requesting user can use a given dashboard +// +k8s:deepcopy-gen=true type DashboardAccess struct { // Metadata fields Slug string `json:"slug,omitempty"` @@ -162,11 +137,13 @@ type DashboardAccess struct { AnnotationsPermissions *AnnotationPermission `json:"annotationsPermissions"` } +// +k8s:deepcopy-gen=true type AnnotationPermission struct { Dashboard AnnotationActions `json:"dashboard"` Organization AnnotationActions `json:"organization"` } +// +k8s:deepcopy-gen=true type AnnotationActions struct { CanAdd bool `json:"canAdd"` CanEdit bool `json:"canEdit"` diff --git a/pkg/apis/dashboard/v2alpha1/zz_generated.deepcopy.go b/pkg/apis/dashboard/v2alpha1/zz_generated.deepcopy.go index 36e22f8aeda..6ce617a1d0b 100644 --- a/pkg/apis/dashboard/v2alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/dashboard/v2alpha1/zz_generated.deepcopy.go @@ -46,54 +46,6 @@ func (in *AnnotationPermission) DeepCopy() *AnnotationPermission { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConversionStatus) DeepCopyInto(out *ConversionStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConversionStatus. -func (in *ConversionStatus) DeepCopy() *ConversionStatus { - if in == nil { - return nil - } - out := new(ConversionStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Dashboard) DeepCopyInto(out *Dashboard) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - if in.Status != nil { - in, out := &in.Status, &out.Status - *out = new(DashboardStatus) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dashboard. -func (in *Dashboard) DeepCopy() *Dashboard { - if in == nil { - return nil - } - out := new(Dashboard) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Dashboard) 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 *DashboardAccess) DeepCopyInto(out *DashboardAccess) { *out = *in @@ -115,60 +67,6 @@ func (in *DashboardAccess) DeepCopy() *DashboardAccess { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DashboardList) DeepCopyInto(out *DashboardList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Dashboard, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardList. -func (in *DashboardList) DeepCopy() *DashboardList { - if in == nil { - return nil - } - out := new(DashboardList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DashboardList) 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 *DashboardStatus) DeepCopyInto(out *DashboardStatus) { - *out = *in - if in.ConversionStatus != nil { - in, out := &in.ConversionStatus, &out.ConversionStatus - *out = new(ConversionStatus) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardStatus. -func (in *DashboardStatus) DeepCopy() *DashboardStatus { - if in == nil { - return nil - } - out := new(DashboardStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DashboardVersionInfo) DeepCopyInto(out *DashboardVersionInfo) { *out = *in diff --git a/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go b/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go index bb6364a92ce..1025adcda86 100644 --- a/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go +++ b/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go @@ -14,21 +14,113 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.AnnotationActions": schema_pkg_apis_dashboard_v2alpha1_AnnotationActions(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v2alpha1_AnnotationPermission(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.ConversionStatus": schema_pkg_apis_dashboard_v2alpha1_ConversionStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.Dashboard": schema_pkg_apis_dashboard_v2alpha1_Dashboard(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAccess": schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardList": schema_pkg_apis_dashboard_v2alpha1_DashboardList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus": schema_pkg_apis_dashboard_v2alpha1_DashboardStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v2alpha1_DashboardVersionInfo(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v2alpha1_DashboardVersionList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v2alpha1_DashboardWithAccessInfo(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanel": schema_pkg_apis_dashboard_v2alpha1_LibraryPanel(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v2alpha1_LibraryPanelList(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v2alpha1_LibraryPanelSpec(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v2alpha1_LibraryPanelStatus(ref), - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v2alpha1_VersionsQueryOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.AnnotationActions": schema_pkg_apis_dashboard_v2alpha1_AnnotationActions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v2alpha1_AnnotationPermission(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.Dashboard": schema_pkg_apis_dashboard_v2alpha1_Dashboard(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAccess": schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2alpha1_DashboardAdHocFilterWithLabels(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationPanelFilter": schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationPanelFilter(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQueryKind": schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQueryKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQuerySpec": schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQuerySpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConstantVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardConstantVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConstantVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardConstantVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConversionStatus": schema_pkg_apis_dashboard_v2alpha1_DashboardConversionStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardCustomVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardCustomVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardCustomVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardCustomVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDashboardLink": schema_pkg_apis_dashboard_v2alpha1_DashboardDashboardLink(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataLink": schema_pkg_apis_dashboard_v2alpha1_DashboardDataLink(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind": schema_pkg_apis_dashboard_v2alpha1_DashboardDataQueryKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef": schema_pkg_apis_dashboard_v2alpha1_DashboardDataSourceRef(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataTransformerConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardDataTransformerConfig(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2alpha1_DashboardDynamicConfigValue(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardElementReference": schema_pkg_apis_dashboard_v2alpha1_DashboardElementReference(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldColor": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldColor(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfigSource(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKindOrGridLayoutRowKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKindOrGridLayoutRowKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2alpha1_DashboardJSONCodec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelKind": schema_pkg_apis_dashboard_v2alpha1_DashboardLibraryPanelKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelKindSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardLibraryPanelKindSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelRef": schema_pkg_apis_dashboard_v2alpha1_DashboardLibraryPanelRef(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardList": schema_pkg_apis_dashboard_v2alpha1_DashboardList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMatcherConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardMatcherConfig(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMetadata": schema_pkg_apis_dashboard_v2alpha1_DashboardMetadata(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMetricFindValue": schema_pkg_apis_dashboard_v2alpha1_DashboardMetricFindValue(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelKind": schema_pkg_apis_dashboard_v2alpha1_DashboardPanelKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelKindOrLibraryPanelKind": schema_pkg_apis_dashboard_v2alpha1_DashboardPanelKindOrLibraryPanelKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelQueryKind": schema_pkg_apis_dashboard_v2alpha1_DashboardPanelQueryKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelQuerySpec": schema_pkg_apis_dashboard_v2alpha1_DashboardPanelQuerySpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardPanelSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryGroupKind": schema_pkg_apis_dashboard_v2alpha1_DashboardQueryGroupKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryGroupSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardQueryGroupSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryOptionsSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardQueryOptionsSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRangeMap": schema_pkg_apis_dashboard_v2alpha1_DashboardRangeMap(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRegexMap": schema_pkg_apis_dashboard_v2alpha1_DashboardRegexMap(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRepeatOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardRepeatOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutItemKind": schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutItemKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutItemSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutItemSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridRepeatOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridRepeatOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardRowRepeatOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutRowKind": schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutRowKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutRowSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutRowSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpecialValueMap": schema_pkg_apis_dashboard_v2alpha1_DashboardSpecialValueMap(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus": schema_pkg_apis_dashboard_v2alpha1_DashboardStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString": schema_pkg_apis_dashboard_v2alpha1_DashboardStringOrArrayOfString(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrFloat64": schema_pkg_apis_dashboard_v2alpha1_DashboardStringOrFloat64(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutTabKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutTabKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutTabSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutTabSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTextVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTextVariableKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTextVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTextVariableSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardThreshold": schema_pkg_apis_dashboard_v2alpha1_DashboardThreshold(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardThresholdsConfig(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeRangeOption(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeSettingsSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RangeMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RangeMapOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RegexMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RegexMapOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1SpecialValueMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1SpecialValueMapOptions(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMap": schema_pkg_apis_dashboard_v2alpha1_DashboardValueMap(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap": schema_pkg_apis_dashboard_v2alpha1_DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult": schema_pkg_apis_dashboard_v2alpha1_DashboardValueMappingResult(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption": schema_pkg_apis_dashboard_v2alpha1_DashboardVariableOption(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v2alpha1_DashboardVersionInfo(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v2alpha1_DashboardVersionList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVizConfigKind": schema_pkg_apis_dashboard_v2alpha1_DashboardVizConfigKind(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVizConfigSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardVizConfigSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v2alpha1_DashboardWithAccessInfo(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanel": schema_pkg_apis_dashboard_v2alpha1_LibraryPanel(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v2alpha1_LibraryPanelList(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v2alpha1_LibraryPanelSpec(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v2alpha1_LibraryPanelStatus(ref), + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v2alpha1_VersionsQueryOptions(ref), } } @@ -93,36 +185,6 @@ func schema_pkg_apis_dashboard_v2alpha1_AnnotationPermission(ref common.Referenc } } -func schema_pkg_apis_dashboard_v2alpha1_ConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "failed": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "storedVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "error": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_dashboard_v2alpha1_Dashboard(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -145,29 +207,29 @@ func schema_pkg_apis_dashboard_v2alpha1_Dashboard(ref common.ReferenceCallback) }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "The dashboard body (unstructured for now)", - Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), + Description: "Spec is the spec of the Dashboard", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Description: "Optional dashboard status", - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus"), }, }, }, - Required: []string{"spec"}, + Required: []string{"metadata", "spec", "status"}, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpec", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -241,6 +303,1929 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref common.ReferenceCall } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardAdHocFilterWithLabels(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Define the AdHocFilterWithLabels type", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "keyLabel": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "valueLabels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "forceEdit": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "condition": { + SchemaProps: spec.SchemaProps{ + Description: "@deprecated", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"key", "operator", "value"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adhoc variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adhoc variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "datasource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"), + }, + }, + "baseFilters": { + 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/pkg/apis/dashboard/v2alpha1.DashboardAdHocFilterWithLabels"), + }, + }, + }, + }, + }, + "filters": { + 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/pkg/apis/dashboard/v2alpha1.DashboardAdHocFilterWithLabels"), + }, + }, + }, + }, + }, + "defaultKeys": { + 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/pkg/apis/dashboard/v2alpha1.DashboardMetricFindValue"), + }, + }, + }, + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "baseFilters", "filters", "defaultKeys", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdHocFilterWithLabels", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMetricFindValue"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationPanelFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exclude": { + SchemaProps: spec.SchemaProps{ + Description: "Should the specified panels be included or excluded", + Type: []string{"boolean"}, + Format: "", + }, + }, + "ids": { + SchemaProps: spec.SchemaProps{ + Description: "Panel IDs that should be included or excluded", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "byte", + }, + }, + }, + }, + }, + }, + Required: []string{"ids"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQueryKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQuerySpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQuerySpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQuerySpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "datasource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"), + }, + }, + "query": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"), + }, + }, + "enable": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "iconColor": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "builtIn": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "filter": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationPanelFilter"), + }, + }, + }, + Required: []string{"enable", "hide", "iconColor", "name"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationPanelFilter", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardConstantVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Constant variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConstantVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConstantVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardConstantVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Constant variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "query": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "query", "current", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConversionStatus is the status of the conversion of the dashboard.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "failed": { + SchemaProps: spec.SchemaProps{ + Description: "Whether from another version has failed. If true, means that the dashboard is not valid, and the caller should instead fetch the stored version.", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "storedVersion": { + SchemaProps: spec.SchemaProps{ + Description: "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "The error message from the conversion. Empty if the conversion has not failed.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"failed", "storedVersion", "error"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardCustomVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Custom variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardCustomVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardCustomVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardCustomVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Custom variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "query": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "options": { + 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/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + }, + }, + }, + "multi": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "includeAll": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "allValue": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "query", "current", "options", "multi", "includeAll", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDashboardLink(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Links with references to other dashboards or external resources", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Title to display with the link", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType`", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "icon": { + SchemaProps: spec.SchemaProps{ + Description: "Icon name to be displayed with the link", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "tooltip": { + SchemaProps: spec.SchemaProps{ + Description: "Tooltip to display when the user hovers their mouse over it", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Description: "Link URL. Only required/valid if the type is link", + Type: []string{"string"}, + Format: "", + }, + }, + "tags": { + SchemaProps: spec.SchemaProps{ + Description: "List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "asDropdown": { + SchemaProps: spec.SchemaProps{ + Description: "If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "targetBlank": { + SchemaProps: spec.SchemaProps{ + Description: "If true, the link will be opened in a new tab", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "includeVars": { + SchemaProps: spec.SchemaProps{ + Description: "If true, includes current template variables values in the link as query params", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "keepTime": { + SchemaProps: spec.SchemaProps{ + Description: "If true, includes current time range in the link as query params", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"title", "type", "icon", "tooltip", "tags", "asDropdown", "targetBlank", "includeVars", "keepTime"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDataLink(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "targetBlank": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"title", "url"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDataQueryKind(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: "The kind of a DataQueryKind is the datasource type", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDataSourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "The plugin type-id", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "Specific datasource instance", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDataTransformerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Transformations allow to manipulate data returned by a query before the system applies a visualization. Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, use the output of one transformation as the input to another transformation, etc.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Description: "Unique identifier of transformer", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "disabled": { + SchemaProps: spec.SchemaProps{ + Description: "Disabled transformations are skipped", + Type: []string{"boolean"}, + Format: "", + }, + }, + "filter": { + SchemaProps: spec.SchemaProps{ + Description: "Optional frame matcher. When missing it will be applied to all results", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMatcherConfig"), + }, + }, + "topic": { + SchemaProps: spec.SchemaProps{ + Description: "Where to pull DataFrames from as input to transformation", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Options to be passed to the transformer Valid options depend on the transformer id", + Type: []string{"object"}, + Format: "", + }, + }, + }, + Required: []string{"id", "options"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMatcherConfig"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Datasource variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Datasource variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "pluginId": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "refresh": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "regex": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "options": { + 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/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + }, + }, + }, + "multi": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "includeAll": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "allValue": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "pluginId", "refresh", "regex", "current", "options", "multi", "includeAll", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardDynamicConfigValue(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + Required: []string{"id"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardElementReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldColor(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Map a field to a color.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Description: "The main color scheme mode.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "fixedColor": { + SchemaProps: spec.SchemaProps{ + Description: "The fixed color value for fixed or shades color modes.", + Type: []string{"string"}, + Format: "", + }, + }, + "seriesBy": { + SchemaProps: spec.SchemaProps{ + Description: "Some visualizations need to know how to assign a series color from by value color schemes.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"mode"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. Each column within this structure is called a field. A field can represent a single time series or table column. Field options allow you to change how the data is displayed in your visualizations.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "displayName": { + SchemaProps: spec.SchemaProps{ + Description: "The display value for this field. This supports template variables blank is auto", + Type: []string{"string"}, + Format: "", + }, + }, + "displayNameFromDS": { + SchemaProps: spec.SchemaProps{ + Description: "This can be used by data sources that return and explicit naming structure for values and labels When this property is configured, this value is used rather than the default naming strategy.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable field metadata", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "An explicit path to the field in the datasource. When the frame meta includes a path, This will default to `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and may be used to update the results", + Type: []string{"string"}, + Format: "", + }, + }, + "writeable": { + SchemaProps: spec.SchemaProps{ + Description: "True if data source can write a value to the path. Auth/authz are supported separately", + Type: []string{"boolean"}, + Format: "", + }, + }, + "filterable": { + SchemaProps: spec.SchemaProps{ + Description: "True if data source field supports ad-hoc filters", + Type: []string{"boolean"}, + Format: "", + }, + }, + "unit": { + SchemaProps: spec.SchemaProps{ + Description: "Unit a field should use. The unit you select is applied to all fields except time. You can use the units ID availables in Grafana or a custom unit. Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts As custom unit, you can use the following formats: `suffix:` for custom unit that should go after value. `prefix:` for custom unit that should go before value. `time:` For custom date time formats type for example `time:YYYY-MM-DD`. `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. `count:` for a custom count unit. `currency:` for custom a currency unit.", + Type: []string{"string"}, + Format: "", + }, + }, + "decimals": { + SchemaProps: spec.SchemaProps{ + Description: "Specify the number of decimals Grafana includes in the rendered value. If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. For example 1.1234 will display as 1.12 and 100.456 will display as 100. To display all decimals, set the unit to `String`.", + Type: []string{"number"}, + Format: "double", + }, + }, + "min": { + SchemaProps: spec.SchemaProps{ + Description: "The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.", + Type: []string{"number"}, + Format: "double", + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.", + Type: []string{"number"}, + Format: "double", + }, + }, + "mappings": { + SchemaProps: spec.SchemaProps{ + Description: "Convert input values into a display string", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"), + }, + }, + }, + }, + }, + "thresholds": { + SchemaProps: spec.SchemaProps{ + Description: "Map numeric values to states", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig"), + }, + }, + "color": { + SchemaProps: spec.SchemaProps{ + Description: "Panel color configuration", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldColor"), + }, + }, + "links": { + SchemaProps: spec.SchemaProps{ + Description: "The behavior when clicking on a result", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "noValue": { + SchemaProps: spec.SchemaProps{ + Description: "Alternative to empty string", + Type: []string{"string"}, + Format: "", + }, + }, + "custom": { + SchemaProps: spec.SchemaProps{ + Description: "custom is specified by the FieldConfig field in panel plugin schemas.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldColor", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. Each column within this structure is called a field. A field can represent a single time series or table column. Field options allow you to change how the data is displayed in your visualizations.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "defaults": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults are the options applied to all fields.", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldConfig"), + }, + }, + "overrides": { + SchemaProps: spec.SchemaProps{ + Description: "Overrides are the options applied to specific fields overriding the defaults.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides"), + }, + }, + }, + }, + }, + }, + Required: []string{"defaults", "overrides"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldConfig", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemKindOrGridLayoutRowKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "GridLayoutItemKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind"), + }, + }, + "GridLayoutRowKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowKind"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutItemSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "x": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "y": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "width": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "height": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "element": { + SchemaProps: spec.SchemaProps{ + Description: "reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardElementReference"), + }, + }, + "repeat": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRepeatOptions"), + }, + }, + }, + Required: []string{"x", "y", "width", "height", "element"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardElementReference", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRepeatOptions"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "GridLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind"), + }, + }, + "ResponsiveGridLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind"), + }, + }, + "TabsLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutKind"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "GridLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind"), + }, + }, + "RowsLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutKind"), + }, + }, + "ResponsiveGridLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "GridLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind"), + }, + }, + "RowsLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutKind"), + }, + }, + "ResponsiveGridLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind"), + }, + }, + "TabsLayoutKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutKind"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutRowSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutRowSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "y": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "collapsed": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "elements": { + SchemaProps: spec.SchemaProps{ + Description: "Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind"), + }, + }, + }, + }, + }, + "repeat": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions"), + }, + }, + }, + Required: []string{"y", "collapsed", "title", "elements"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKindOrGridLayoutRowKind"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutItemKindOrGridLayoutRowKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Group variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupBy variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "datasource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"), + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "options": { + 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/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + }, + }, + }, + "multi": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "current", "options", "multi", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Interval variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Interval variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "query": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "options": { + 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/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + }, + }, + }, + "auto": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "auto_min": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "auto_count": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "refresh": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "query", "current", "options", "auto", "auto_min", "auto_count", "refresh", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardJSONCodec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardLibraryPanelKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelKindSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelKindSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardLibraryPanelKindSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Description: "Panel ID for the library panel in the dashboard", + Default: 0, + Type: []string{"number"}, + Format: "double", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Title for the library panel in the dashboard", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "libraryPanel": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelRef"), + }, + }, + }, + Required: []string{"id", "title", "libraryPanel"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelRef"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardLibraryPanelRef(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A library panel is a reusable panel that you can use in any dashboard. When you make a change to a library panel, that change propagates to all instances of where the panel is used. Library panels streamline reuse of panels across multiple dashboards.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Library panel name", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "Library panel uid", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "uid"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -281,6 +2266,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardList(ref common.ReferenceCallba }, }, }, + Required: []string{"metadata", "items"}, }, }, Dependencies: []string{ @@ -288,6 +2274,1280 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardList(ref common.ReferenceCallba } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardMatcherConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Description: "The matcher id. This is used to find the matcher implementation from registry.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "The matcher options. This is specific to the matcher implementation.", + Type: []string{"object"}, + Format: "", + }, + }, + }, + Required: []string{"id"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "metadata contains embedded CommonMetadata and can be extended with custom string fields without external reference as using the CommonMetadata reference breaks thema codegen.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "updateTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "createdBy": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "creationTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "deletionTimestamp": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "date-time", + }, + }, + "finalizers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "generation": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "updatedBy": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"updateTimestamp", "createdBy", "uid", "creationTimestamp", "finalizers", "resourceVersion", "generation", "updatedBy", "labels"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardMetricFindValue(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Define the MetricFindValue type", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "text": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrFloat64"), + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "expandable": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"text"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrFloat64"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelKindOrLibraryPanelKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "PanelKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelKind"), + }, + }, + "LibraryPanelKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelKind"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardLibraryPanelKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelQueryKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelQuerySpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelQuerySpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelQuerySpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "query": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"), + }, + }, + "datasource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"), + }, + }, + "refId": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "hidden": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"query", "refId", "hidden"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"number"}, + Format: "double", + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "links": { + 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/pkg/apis/dashboard/v2alpha1.DashboardDataLink"), + }, + }, + }, + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryGroupKind"), + }, + }, + "vizConfig": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVizConfigKind"), + }, + }, + "transparent": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"id", "title", "description", "links", "data", "vizConfig"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataLink", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryGroupKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVizConfigKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryGroupKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryGroupSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryGroupSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryGroupSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "queries": { + 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/pkg/apis/dashboard/v2alpha1.DashboardPanelQueryKind"), + }, + }, + }, + }, + }, + "transformations": { + 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/pkg/apis/dashboard/v2alpha1.DashboardTransformationKind"), + }, + }, + }, + }, + }, + "queryOptions": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryOptionsSpec"), + }, + }, + }, + Required: []string{"queries", "transformations", "queryOptions"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelQueryKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryOptionsSpec", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTransformationKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryOptionsSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "timeFrom": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "maxDataPoints": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "timeShift": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "queryCachingTTL": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "interval": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "cacheTimeout": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hideTimeOverride": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Query variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "QueryVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableKind"), + }, + }, + "TextVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTextVariableKind"), + }, + }, + "ConstantVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConstantVariableKind"), + }, + }, + "DatasourceVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableKind"), + }, + }, + "IntervalVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableKind"), + }, + }, + "CustomVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardCustomVariableKind"), + }, + }, + "GroupByVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableKind"), + }, + }, + "AdhocVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableKind"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConstantVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardCustomVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTextVariableKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Query variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "refresh": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "datasource": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"), + }, + }, + "query": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"), + }, + }, + "regex": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "sort": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "definition": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + 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/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + }, + }, + }, + "multi": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "includeAll": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "allValue": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "placeholder": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "current", "hide", "refresh", "skipUrlSync", "query", "regex", "sort", "options", "multi", "includeAll"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRangeMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "And this is no longer producing valid TS / Go output type: MappingType & \"range\"", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Range to match against and the result to apply when the value is within the range", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RangeMapOptions"), + }, + }, + }, + Required: []string{"type", "options"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RangeMapOptions"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRegexMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "And this is no longer producing valid TS / Go output type: MappingType & \"regex\"", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Regular expression to match against and the result to apply when the value matches the regex", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RegexMapOptions"), + }, + }, + }, + Required: []string{"type", "options"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RegexMapOptions"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRepeatOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "direction": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "maxPerRow": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"mode", "value"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutItemKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutItemSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutItemSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutItemSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "element": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardElementReference"), + }, + }, + "repeat": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridRepeatOptions"), + }, + }, + }, + Required: []string{"element"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardElementReference", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridRepeatOptions"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridLayoutSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "row": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "col": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "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/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutItemKind"), + }, + }, + }, + }, + }, + }, + Required: []string{"row", "col", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardResponsiveGridLayoutItemKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardResponsiveGridRepeatOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"mode", "value"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRowRepeatOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"mode", "value"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutRowKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutRowSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutRowSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutRowSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "collapsed": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "repeat": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions"), + }, + }, + "layout": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind"), + }, + }, + }, + Required: []string{"collapsed", "layout"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowRepeatOptions"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardRowsLayoutSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "rows": { + 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/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutRowKind"), + }, + }, + }, + }, + }, + }, + Required: []string{"rows"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRowsLayoutRowKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Title of dashboard.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQueryKind"), + }, + }, + }, + }, + }, + "cursorSync": { + SchemaProps: spec.SchemaProps{ + Description: "Configuration of dashboard cursor sync behavior. \"Off\" for no shared crosshair or tooltip (default). \"Crosshair\" for shared crosshair. \"Tooltip\" for shared crosshair AND shared tooltip.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description of dashboard.", + Type: []string{"string"}, + Format: "", + }, + }, + "editable": { + SchemaProps: spec.SchemaProps{ + Description: "Whether a dashboard is editable or not.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "elements": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelKindOrLibraryPanelKind"), + }, + }, + }, + }, + }, + "layout": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind"), + }, + }, + "links": { + SchemaProps: spec.SchemaProps{ + Description: "Links with references to other dashboards or external websites.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDashboardLink"), + }, + }, + }, + }, + }, + "liveNow": { + SchemaProps: spec.SchemaProps{ + Description: "When set to true, the dashboard will redraw panels at an interval matching the pixel width. This will keep data \"moving left\" regardless of the query refresh rate. This setting helps avoid dashboards presenting stale live data.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "preload": { + SchemaProps: spec.SchemaProps{ + Description: "When set to true, the dashboard will load all panels in the dashboard when it's loaded.", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "revision": { + SchemaProps: spec.SchemaProps{ + Description: "Plugins only. The version of the dashboard installed together with the plugin. This is used to determine if the dashboard should be updated when the plugin is updated.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "tags": { + SchemaProps: spec.SchemaProps{ + Description: "Tags associated with dashboard.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "timeSettings": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTimeSettingsSpec"), + }, + }, + "title": { + SchemaProps: spec.SchemaProps{ + Description: "Title of dashboard.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "variables": { + SchemaProps: spec.SchemaProps{ + Description: "Configured template variables.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind"), + }, + }, + }, + }, + }, + }, + Required: []string{"annotations", "cursorSync", "elements", "layout", "links", "preload", "tags", "timeSettings", "title", "variables"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQueryKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDashboardLink", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardPanelKindOrLibraryPanelKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTimeSettingsSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardSpecialValueMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "And this is no longer producing valid TS / Go output type: MappingType & \"special\"", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1SpecialValueMapOptions"), + }, + }, + }, + Required: []string{"type", "options"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1SpecialValueMapOptions"}, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -296,14 +3556,782 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardStatus(ref common.ReferenceCall Properties: map[string]spec.Schema{ "conversion": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.ConversionStatus"), + Description: "Optional conversion status.", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConversionStatus"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.ConversionStatus"}, + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardConversionStatus"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardStringOrArrayOfString(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "String": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "ArrayOfString": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardStringOrFloat64(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "String": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "Float64": { + SchemaProps: spec.SchemaProps{ + Type: []string{"number"}, + Format: "double", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "tabs": { + 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/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutTabKind"), + }, + }, + }, + }, + }, + }, + Required: []string{"tabs"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutTabKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutTabKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutTabSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTabsLayoutTabSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTabsLayoutTabSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "layout": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind"), + }, + }, + }, + Required: []string{"layout"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTextVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Text variable kind", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTextVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTextVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTextVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Text variable specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"), + }, + }, + "query": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "current", "query", "hide", "skipUrlSync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardThreshold(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "value": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"number"}, + Format: "double", + }, + }, + "color": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"value", "color"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardThresholdsConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "mode": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "steps": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardThreshold"), + }, + }, + }, + }, + }, + }, + Required: []string{"mode", "steps"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardThreshold"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTimeRangeOption(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "display": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "from": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "to": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"display", "from", "to"}, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTimeSettingsSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Time configuration It defines the default time config for the time picker, the refresh picker for the specific dashboard.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "timezone": { + SchemaProps: spec.SchemaProps{ + Description: "Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".", + Type: []string{"string"}, + Format: "", + }, + }, + "from": { + SchemaProps: spec.SchemaProps{ + Description: "Start time range for dashboard. Accepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "to": { + SchemaProps: spec.SchemaProps{ + Description: "End time range for dashboard. Accepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "autoRefresh": { + SchemaProps: spec.SchemaProps{ + Description: "Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\". v1: refresh", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "autoRefreshIntervals": { + SchemaProps: spec.SchemaProps{ + Description: "Interval options available in the refresh picker dropdown. v1: timepicker.refresh_intervals", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "quickRanges": { + SchemaProps: spec.SchemaProps{ + Description: "Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard. v1: timepicker.quick_ranges , not exposed in the UI", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTimeRangeOption"), + }, + }, + }, + }, + }, + "hideTimepicker": { + SchemaProps: spec.SchemaProps{ + Description: "Whether timepicker is visible or not. v1: timepicker.hidden", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "weekStart": { + SchemaProps: spec.SchemaProps{ + Description: "Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".", + Type: []string{"string"}, + Format: "", + }, + }, + "fiscalYearStartMonth": { + SchemaProps: spec.SchemaProps{ + Description: "The month that the fiscal year starts on. 0 = January, 11 = December", + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + "nowDelay": { + SchemaProps: spec.SchemaProps{ + Description: "Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. v1: timepicker.nowDelay", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"from", "to", "autoRefresh", "autoRefreshIntervals", "hideTimepicker", "fiscalYearStartMonth"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardTimeRangeOption"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(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: "The kind of a TransformationKind is the transformation ID", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataTransformerConfig"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDataTransformerConfig"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matcher": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMatcherConfig"), + }, + }, + "properties": { + 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/pkg/apis/dashboard/v2alpha1.DashboardDynamicConfigValue"), + }, + }, + }, + }, + }, + }, + Required: []string{"matcher", "properties"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardDynamicConfigValue", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardMatcherConfig"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RangeMapOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "from": { + SchemaProps: spec.SchemaProps{ + Description: "Min value of the range. It can be null which means -Infinity", + Type: []string{"number"}, + Format: "double", + }, + }, + "to": { + SchemaProps: spec.SchemaProps{ + Description: "Max value of the range. It can be null which means +Infinity", + Type: []string{"number"}, + Format: "double", + }, + }, + "result": { + SchemaProps: spec.SchemaProps{ + Description: "Config to apply when the value is within the range", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"), + }, + }, + }, + Required: []string{"from", "to", "result"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RegexMapOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pattern": { + SchemaProps: spec.SchemaProps{ + Description: "Regular expression to match against", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "result": { + SchemaProps: spec.SchemaProps{ + Description: "Config to apply when the value matches the regex", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"), + }, + }, + }, + Required: []string{"pattern", "result"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1SpecialValueMapOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "match": { + SchemaProps: spec.SchemaProps{ + Description: "Special value to match against", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "result": { + SchemaProps: spec.SchemaProps{ + Description: "Config to apply when the value matches the special value", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"), + }, + }, + }, + Required: []string{"match", "result"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardValueMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "And this is no longer producing valid TS / Go output type: MappingType & \"value\"", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Map with : ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"), + }, + }, + }, + }, + }, + }, + Required: []string{"type", "options"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMappingResult"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ValueMap": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMap"), + }, + }, + "RangeMap": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRangeMap"), + }, + }, + "RegexMap": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRegexMap"), + }, + }, + "SpecialValueMap": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpecialValueMap"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRangeMap", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardRegexMap", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpecialValueMap", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardValueMap"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardValueMappingResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Result used as replacement with text and color when the value matches", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "text": { + SchemaProps: spec.SchemaProps{ + Description: "Text to display when the value matches", + Type: []string{"string"}, + Format: "", + }, + }, + "color": { + SchemaProps: spec.SchemaProps{ + Description: "Text to use when the value matches", + Type: []string{"string"}, + Format: "", + }, + }, + "icon": { + SchemaProps: spec.SchemaProps{ + Description: "Icon to display when the value matches. Only specific visualizations.", + Type: []string{"string"}, + Format: "", + }, + }, + "index": { + SchemaProps: spec.SchemaProps{ + Description: "Position in the mapping array. Only used internally.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardVariableOption(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Variable option specification", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "selected": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the option is selected or not", + Type: []string{"boolean"}, + Format: "", + }, + }, + "text": { + SchemaProps: spec.SchemaProps{ + Description: "Text to be displayed for the option", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"), + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value of the option", + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"), + }, + }, + }, + Required: []string{"text", "value"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"}, } } @@ -404,6 +4432,78 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardVersionList(ref common.Referenc } } +func schema_pkg_apis_dashboard_v2alpha1_DashboardVizConfigKind(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: "The kind of a VizConfigKind is the plugin ID", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVizConfigSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardVizConfigSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2alpha1_DashboardVizConfigSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "--- Kinds ---", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pluginVersion": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "fieldConfig": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldConfigSource"), + }, + }, + }, + Required: []string{"pluginVersion", "options", "fieldConfig"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardFieldConfigSource"}, + } +} + func schema_pkg_apis_dashboard_v2alpha1_DashboardWithAccessInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -427,21 +4527,21 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardWithAccessInfo(ref common.Refer }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, "spec": { SchemaProps: spec.SchemaProps{ - Description: "The dashboard body (unstructured for now)", - Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), + Description: "Spec is the spec of the Dashboard", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Description: "Optional dashboard status", - Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus"), + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus"), }, }, "access": { @@ -451,11 +4551,11 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardWithAccessInfo(ref common.Refer }, }, }, - Required: []string{"spec", "access"}, + Required: []string{"metadata", "spec", "status", "access"}, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAccess", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardAccess", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardSpec", "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1.DashboardStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } diff --git a/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list index 0683c758ce4..4914dd6343d 100644 --- a/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/dashboard/v2alpha1/zz_generated.openapi_violation_exceptions.list @@ -1,2 +1,65 @@ +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardAdHocFilterWithLabels,ValueLabels +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardAdHocFilterWithLabels,Values +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,BaseFilters +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,DefaultKeys +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,Filters +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardCustomVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardDashboardLink,Tags +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardDatasourceVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Links +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Mappings +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardFieldConfigSource,Overrides +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutRowSpec,Elements +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutSpec,Items +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGroupByVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardIntervalVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardMetadata,Finalizers +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardPanelSpec,Links +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryGroupSpec,Queries +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryGroupSpec,Transformations +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableSpec,Options +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardResponsiveGridLayoutSpec,Items +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardRowsLayoutSpec,Rows +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardSpec,Annotations +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardSpec,Links +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardSpec,Tags +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardSpec,Variables +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardStringOrArrayOfString,ArrayOfString +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardTabsLayoutSpec,Tabs +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardThresholdsConfig,Steps +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardTimeSettingsSpec,AutoRefreshIntervals +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardTimeSettingsSpec,QuickRanges +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardV2alpha1FieldConfigSourceOverrides,Properties API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,LibraryPanelStatus,Warnings -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardStatus,ConversionStatus +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutItemKindOrGridLayoutRowKind,GridLayoutItemKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutItemKindOrGridLayoutRowKind,GridLayoutRowKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,GridLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,ResponsiveGridLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,TabsLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind,GridLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind,ResponsiveGridLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKind,RowsLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,GridLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,ResponsiveGridLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,RowsLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind,TabsLayoutKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardIntervalVariableSpec,AutoCount +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardIntervalVariableSpec,AutoMin +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardPanelKindOrLibraryPanelKind,LibraryPanelKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardPanelKindOrLibraryPanelKind,PanelKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,AdhocVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,ConstantVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,CustomVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,DatasourceVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,GroupByVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,IntervalVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,QueryVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,TextVariableKind +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardStringOrArrayOfString,ArrayOfString +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardStringOrArrayOfString,String +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardStringOrFloat64,Float64 +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardStringOrFloat64,String +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,RangeMap +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,RegexMap +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,SpecialValueMap +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,ValueMap diff --git a/pkg/apis/dashboard_manifest.go b/pkg/apis/dashboard_manifest.go new file mode 100644 index 00000000000..bc68fa86ca6 --- /dev/null +++ b/pkg/apis/dashboard_manifest.go @@ -0,0 +1,53 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package apis + +import ( + "encoding/json" + + "github.com/grafana/grafana-app-sdk/app" +) + +var () + +var appManifestData = app.ManifestData{ + AppName: "dashboard", + Group: "dashboard.grafana.app", + Kinds: []app.ManifestKind{ + { + Kind: "Dashboard", + Scope: "Namespaced", + Conversion: false, + Versions: []app.ManifestKindVersion{ + { + Name: "v0alpha1", + }, + + { + Name: "v1alpha1", + }, + + { + Name: "v2alpha1", + }, + }, + }, + }, +} + +func jsonToMap(j string) map[string]any { + m := make(map[string]any) + json.Unmarshal([]byte(j), &j) + return m +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("dashboard") +} diff --git a/pkg/registry/apis/dashboard/large.go b/pkg/registry/apis/dashboard/large.go index 6594145f88d..5464d479dd6 100644 --- a/pkg/registry/apis/dashboard/large.go +++ b/pkg/registry/apis/dashboard/large.go @@ -1,6 +1,7 @@ package dashboard import ( + "encoding/json" "fmt" "k8s.io/apimachinery/pkg/runtime" @@ -35,7 +36,11 @@ func NewDashboardLargeObjectSupport(scheme *runtime.Scheme) *apistore.BasicLarge case *dashboardV1.Dashboard: reduceUnstructredSpec(&dash.Spec) case *dashboardV2.Dashboard: - reduceUnstructredSpec(&dash.Spec) + dash.Spec = dashboardV2.DashboardSpec{ + Title: dash.Spec.Title, + Description: dash.Spec.Description, + Tags: dash.Spec.Tags, + } default: return fmt.Errorf("unsupported dashboard type %T", obj) } @@ -45,23 +50,16 @@ func NewDashboardLargeObjectSupport(scheme *runtime.Scheme) *apistore.BasicLarge }, RebuildSpec: func(obj runtime.Object, blob []byte) error { - body := commonV0.Unstructured{} - err := body.UnmarshalJSON(blob) - if err != nil { - return err - } - switch dash := obj.(type) { case *dashboardV0.Dashboard: - dash.Spec = body + return dash.Spec.UnmarshalJSON(blob) case *dashboardV1.Dashboard: - dash.Spec = body + return dash.Spec.UnmarshalJSON(blob) case *dashboardV2.Dashboard: - dash.Spec = body + return json.Unmarshal(blob, &dash.Spec) default: return fmt.Errorf("unsupported dashboard type %T", obj) } - return nil }, } } diff --git a/pkg/registry/apis/dashboard/mutate.go b/pkg/registry/apis/dashboard/mutate.go index c000204744b..560dcfbedba 100644 --- a/pkg/registry/apis/dashboard/mutate.go +++ b/pkg/registry/apis/dashboard/mutate.go @@ -38,10 +38,7 @@ func (b *DashboardsAPIBuilder) Mutate(ctx context.Context, a admission.Attribute internalID = int64(id) } case *dashboardV2.Dashboard: - if id, ok := v.Spec.Object["id"].(float64); ok { - delete(v.Spec.Object, "id") - internalID = int64(id) - } + // Noop for V2 default: return fmt.Errorf("mutation error: expected to dashboard, got %T", obj) } diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index cedda8820ff..9c750e605a9 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -109,6 +109,16 @@ func RegisterAPIService( } func (b *DashboardsAPIBuilder) GetGroupVersions() []schema.GroupVersion { + if featuremgmt.AnyEnabled(b.features, featuremgmt.FlagUseV2DashboardsAPI) { + // If dashboards v2 is enabled, we want to use v2alpha1 as the default API version. + return []schema.GroupVersion{ + dashboardv2alpha1.DashboardResourceInfo.GroupVersion(), + dashboardv0alpha1.DashboardResourceInfo.GroupVersion(), + dashboardv1alpha1.DashboardResourceInfo.GroupVersion(), + } + } + + // TODO (@radiohead): should we switch to v1alpha1 by default? return []schema.GroupVersion{ dashboardv0alpha1.DashboardResourceInfo.GroupVersion(), dashboardv1alpha1.DashboardResourceInfo.GroupVersion(), diff --git a/pkg/registry/apis/dashboard/register_test.go b/pkg/registry/apis/dashboard/register_test.go index d218c0727c6..80b1dd28665 100644 --- a/pkg/registry/apis/dashboard/register_test.go +++ b/pkg/registry/apis/dashboard/register_test.go @@ -7,11 +7,15 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" + "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1" + "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/user" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" ) @@ -158,3 +162,93 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) { }) } } + +func TestDashboardAPIBuilder_GetGroupVersions(t *testing.T) { + tests := []struct { + name string + enabledFeatures []string + expected []schema.GroupVersion + }{ + { + name: "should return v0alpha1 by default", + enabledFeatures: []string{}, + expected: []schema.GroupVersion{ + v0alpha1.DashboardResourceInfo.GroupVersion(), + v1alpha1.DashboardResourceInfo.GroupVersion(), + v2alpha1.DashboardResourceInfo.GroupVersion(), + }, + }, + { + name: "should return v0alpha1 as the default if some other feature is enabled", + enabledFeatures: []string{ + featuremgmt.FlagKubernetesDashboards, + }, + expected: []schema.GroupVersion{ + v0alpha1.DashboardResourceInfo.GroupVersion(), + v1alpha1.DashboardResourceInfo.GroupVersion(), + v2alpha1.DashboardResourceInfo.GroupVersion(), + }, + }, + { + name: "should return v2alpha1 as the default if dashboards v2 is enabled", + enabledFeatures: []string{ + featuremgmt.FlagUseV2DashboardsAPI, + }, + expected: []schema.GroupVersion{ + v2alpha1.DashboardResourceInfo.GroupVersion(), + v0alpha1.DashboardResourceInfo.GroupVersion(), + v1alpha1.DashboardResourceInfo.GroupVersion(), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + builder := &DashboardsAPIBuilder{ + features: newMockFeatureToggles(t, tt.enabledFeatures...), + } + + require.Equal(t, tt.expected, builder.GetGroupVersions()) + }) + } +} + +type mockFeatureToggles struct { + // We need to make a copy in `GetEnabled` anyway, + // so no need to store the original map as map[string]bool. + enabledFeatures map[string]struct{} +} + +func newMockFeatureToggles(t *testing.T, enabledFeatures ...string) featuremgmt.FeatureToggles { + t.Helper() + + res := &mockFeatureToggles{ + enabledFeatures: make(map[string]struct{}, len(enabledFeatures)), + } + + for _, f := range enabledFeatures { + res.enabledFeatures[f] = struct{}{} + } + + return res +} + +func (m *mockFeatureToggles) IsEnabledGlobally(feature string) bool { + _, ok := m.enabledFeatures[feature] + return ok +} + +func (m *mockFeatureToggles) IsEnabled(ctx context.Context, feature string) bool { + _, ok := m.enabledFeatures[feature] + return ok +} + +func (m *mockFeatureToggles) GetEnabled(ctx context.Context) map[string]bool { + res := make(map[string]bool, len(m.enabledFeatures)) + + for f := range m.enabledFeatures { + res[f] = true + } + + return res +} diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 68d9aceafbc..6c377348ee0 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -207,6 +207,7 @@ require ( github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa // indirect github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect + github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect github.com/grafana/grafana-aws-sdk v0.31.5 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 2a54e8f0a54..1b5e473ad53 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -1265,6 +1265,8 @@ github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6k github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 h1:IR+UNYHqaU31t8/TArJk8K/GlDwOyxMpGNkWCXeZ28g= github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040/go.mod h1:SPLNCARd4xdjCkue0O6hvuoveuS1dGJjDnfxYe405YQ= +github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk= +github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 0a5d4a68146..bc891b4aa28 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -133,6 +133,7 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/grafana/alerting v0.0.0-20250310104713-16b885f1c79e // indirect github.com/grafana/dataplane/sdata v0.0.9 // indirect + github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect github.com/grafana/grafana-aws-sdk v0.31.5 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect @@ -214,7 +215,6 @@ require ( github.com/tjhop/slog-gokit v0.1.3 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect - github.com/ugorji/go/codec v1.2.11 // indirect github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect github.com/unknwon/com v1.0.1 // indirect github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index aef2f25aee9..b7a7baddf76 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -1160,6 +1160,8 @@ github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6k github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 h1:IR+UNYHqaU31t8/TArJk8K/GlDwOyxMpGNkWCXeZ28g= github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040/go.mod h1:SPLNCARd4xdjCkue0O6hvuoveuS1dGJjDnfxYe405YQ= +github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDRgZwHkksFk= +github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= diff --git a/pkg/tests/apis/dashboard/testdata/dashboard-test-v2.yaml b/pkg/tests/apis/dashboard/testdata/dashboard-test-v2.yaml index 1f9efaa0579..540b9dcc919 100644 --- a/pkg/tests/apis/dashboard/testdata/dashboard-test-v2.yaml +++ b/pkg/tests/apis/dashboard/testdata/dashboard-test-v2.yaml @@ -4,3 +4,7 @@ metadata: name: test-v2 spec: title: Test dashboard. Created at v2 + layout: + kind: GridLayout + spec: + items: [] diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json index b0f2258cb24..eec30bc3008 100644 --- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v0alpha1.json @@ -1490,24 +1490,12 @@ } } }, - "com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.ConversionStatus": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "failed": { - "type": "boolean" - }, - "storedVersion": { - "type": "string" - } - } - }, "com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.Dashboard": { "type": "object", "required": [ - "spec" + "metadata", + "spec", + "status" ], "properties": { "apiVersion": { @@ -1519,7 +1507,6 @@ "type": "string" }, "metadata": { - "description": "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "default": {}, "allOf": [ { @@ -1528,7 +1515,7 @@ ] }, "spec": { - "description": "The dashboard body (unstructured for now)", + "description": "Spec is the spec of the Dashboard", "allOf": [ { "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" @@ -1536,7 +1523,7 @@ ] }, "status": { - "description": "Optional dashboard status", + "default": {}, "allOf": [ { "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.DashboardStatus" @@ -1553,7 +1540,6 @@ ] }, "com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.DashboardAccess": { - "description": "Information about how the requesting user can use a given dashboard", "type": "object", "required": [ "canSave", @@ -1597,8 +1583,38 @@ } } }, + "com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.DashboardConversionStatus": { + "description": "ConversionStatus is the status of the conversion of the dashboard.", + "type": "object", + "required": [ + "failed", + "storedVersion", + "error" + ], + "properties": { + "error": { + "description": "The error message from the conversion. Empty if the conversion has not failed.", + "type": "string", + "default": "" + }, + "failed": { + "description": "Whether from another version has failed. If true, means that the dashboard is not valid, and the caller should instead fetch the stored version.", + "type": "boolean", + "default": false + }, + "storedVersion": { + "description": "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", + "type": "string", + "default": "" + } + } + }, "com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.DashboardList": { "type": "object", + "required": [ + "metadata", + "items" + ], "properties": { "apiVersion": { "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", @@ -1640,7 +1656,12 @@ "type": "object", "properties": { "conversion": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.ConversionStatus" + "description": "Optional conversion status.", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.DashboardConversionStatus" + } + ] } } }, @@ -1648,7 +1669,9 @@ "description": "This is like the legacy DTO where access and metadata are all returned in a single call", "type": "object", "required": [ + "metadata", "spec", + "status", "access" ], "properties": { @@ -1669,7 +1692,6 @@ "type": "string" }, "metadata": { - "description": "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "default": {}, "allOf": [ { @@ -1678,7 +1700,7 @@ ] }, "spec": { - "description": "The dashboard body (unstructured for now)", + "description": "Spec is the spec of the Dashboard", "allOf": [ { "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" @@ -1686,7 +1708,7 @@ ] }, "status": { - "description": "Optional dashboard status", + "default": {}, "allOf": [ { "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.dashboard.v0alpha1.DashboardStatus" diff --git a/public/api-merged.json b/public/api-merged.json index 130059510ae..873200a1fb7 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -13055,6 +13055,7 @@ } }, "AnnotationActions": { + "description": "+k8s:deepcopy-gen=true", "type": "object", "properties": { "canAdd": { @@ -13129,6 +13130,7 @@ } }, "AnnotationPermission": { + "description": "+k8s:deepcopy-gen=true", "type": "object", "properties": { "dashboard": { diff --git a/public/openapi3.json b/public/openapi3.json index d791ee2bb10..9d4c63135c3 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -3118,6 +3118,7 @@ "type": "object" }, "AnnotationActions": { + "description": "+k8s:deepcopy-gen=true", "properties": { "canAdd": { "type": "boolean" @@ -3192,6 +3193,7 @@ "type": "object" }, "AnnotationPermission": { + "description": "+k8s:deepcopy-gen=true", "properties": { "dashboard": { "$ref": "#/components/schemas/AnnotationActions" From b4366db1f13271a58f09f02a08c2150ef123017b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 11 Mar 2025 12:03:10 +0100 Subject: [PATCH 165/312] ThemeDrawer: Change theme from anywhere and preview them (#100405) * ThemeDrawer: Change theme from anywhere and preview them * Update * added subtitle * Use new component * Fixes * Fix runtime ony wrong prop * Fixed saving issue * update lang file * Fixed circular dep * fix import --- packages/grafana-data/src/types/config.ts | 1 + pkg/api/dtos/frontend_settings.go | 1 + pkg/api/frontendsettings.go | 1 + pkg/services/navtree/navtreeimpl/navtree.go | 12 -- .../components/AppChrome/MegaMenu/utils.ts | 2 +- .../AppChrome/TopBar/ProfileButton.tsx | 24 +++- .../AppChrome/TopBar/SingleTopBar.tsx | 3 +- .../SharedPreferences/SharedPreferences.tsx | 21 +-- .../ThemeSelector/ThemeSelectorDrawer.tsx | 121 ++++++++++++++++++ .../ThemeSelector/getSelectableThemes.ts | 20 +++ public/app/core/constants.ts | 1 + public/app/core/services/theme.ts | 2 +- public/locales/en-US/grafana.json | 3 +- 13 files changed, 175 insertions(+), 37 deletions(-) create mode 100644 public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx create mode 100644 public/app/core/components/ThemeSelector/getSelectableThemes.ts diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index 4e57dd61656..6cec302e4b4 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -284,4 +284,5 @@ export interface AuthSettings { disableLogin?: boolean; passwordlessEnabled?: boolean; basicAuthStrongPasswordPolicy?: boolean; + disableSignoutMenu?: boolean; } diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go index a9581366b13..9ed646941d6 100644 --- a/pkg/api/dtos/frontend_settings.go +++ b/pkg/api/dtos/frontend_settings.go @@ -34,6 +34,7 @@ type FrontendSettingsAuthDTO struct { DisableLogin bool `json:"disableLogin"` BasicAuthStrongPasswordPolicy bool `json:"basicAuthStrongPasswordPolicy"` PasswordlessEnabled bool `json:"passwordlessEnabled"` + DisableSignoutMenu bool `json:"disableSignoutMenu"` } type FrontendSettingsBuildInfoDTO struct { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index ba0f2f436bd..0993e086edb 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -367,6 +367,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro OktaSkipOrgRoleSync: parseSkipOrgRoleSyncEnabled(oauthProviders[social.OktaProviderName]), DisableLogin: hs.Cfg.DisableLogin, BasicAuthStrongPasswordPolicy: hs.Cfg.BasicAuthStrongPasswordPolicy, + DisableSignoutMenu: hs.Cfg.DisableSignoutMenu, } if hs.Cfg.PasswordlessMagicLinkAuth.Enabled && hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagPasswordlessMagicLinkAuthentication) { diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index d84240556f8..facf246f171 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -296,18 +296,6 @@ func (s *ServiceImpl) getProfileNode(c *contextmodel.ReqContext) *navtree.NavLin }) } - if !s.cfg.DisableSignoutMenu { - // add sign out first - children = append(children, &navtree.NavLink{ - Text: "Sign out", - Id: "sign-out", - Url: s.cfg.AppSubURL + "/logout", - Icon: "arrow-from-right", - Target: "_self", - HideFromTabs: true, - }) - } - return &navtree.NavLink{ Text: c.SignedInUser.GetName(), SubTitle: login, diff --git a/public/app/core/components/AppChrome/MegaMenu/utils.ts b/public/app/core/components/AppChrome/MegaMenu/utils.ts index 2afb61f8cc4..178d6604dca 100644 --- a/public/app/core/components/AppChrome/MegaMenu/utils.ts +++ b/public/app/core/components/AppChrome/MegaMenu/utils.ts @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { NavModelItem } from '@grafana/data'; import { config, reportInteraction } from '@grafana/runtime'; +import { MEGA_MENU_TOGGLE_ID } from 'app/core/constants'; import { t } from 'app/core/internationalization'; import { HOME_NAV_ID } from 'app/core/reducers/navModel'; @@ -9,7 +10,6 @@ import { ShowModalReactEvent } from '../../../../types/events'; import appEvents from '../../../app_events'; import { getFooterLinks } from '../../Footer/Footer'; import { HelpModal } from '../../help/HelpModal'; -import { MEGA_MENU_TOGGLE_ID } from '../TopBar/SingleTopBar'; import { DOCK_MENU_BUTTON_ID, MEGA_MENU_HEADER_TOGGLE_ID } from './MegaMenuHeader'; diff --git a/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx b/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx index 3faa5f24876..937a1fd0f2c 100644 --- a/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx +++ b/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx @@ -8,6 +8,7 @@ import { Dropdown, Menu, MenuItem, ToolbarButton, useStyles2 } from '@grafana/ui import { contextSrv } from 'app/core/core'; import { t } from 'app/core/internationalization'; +import { ThemeSelectorDrawer } from '../../ThemeSelector/ThemeSelectorDrawer'; import { enrichWithInteractionTracking } from '../MegaMenu/utils'; import { NewsContainer } from '../News/NewsDrawer'; @@ -21,6 +22,7 @@ export function ProfileButton({ profileNode }: Props) { const styles = useStyles2(getStyles); const node = enrichWithInteractionTracking(cloneDeep(profileNode), false); const [showNewsDrawer, onToggleShowNewsDrawer] = useToggle(false); + const [showThemeDrawer, onToggleThemeDrawer] = useToggle(false); if (!node) { return null; @@ -28,16 +30,27 @@ export function ProfileButton({ profileNode }: Props) { const renderMenu = () => ( - {config.newsFeedEnabled && ( - <> - + <> + {config.featureToggles.grafanaconThemes && ( + + )} + {config.newsFeedEnabled && ( - - )} + )} + + {!config.auth.disableSignoutMenu && ( + + )} + ); @@ -52,6 +65,7 @@ export function ProfileButton({ profileNode }: Props) { /> {showNewsDrawer && } + {showThemeDrawer && } ); } diff --git a/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx b/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx index 64d67fa326d..01ba7281c20 100644 --- a/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx +++ b/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx @@ -5,6 +5,7 @@ import { memo } from 'react'; import { GrafanaTheme2, NavModelItem } from '@grafana/data'; import { Dropdown, Icon, Stack, ToolbarButton, useStyles2 } from '@grafana/ui'; import { config } from 'app/core/config'; +import { MEGA_MENU_TOGGLE_ID } from 'app/core/constants'; import { useGrafana } from 'app/core/context/GrafanaContext'; import { contextSrv } from 'app/core/core'; import { t } from 'app/core/internationalization'; @@ -25,8 +26,6 @@ import { SignInLink } from './SignInLink'; import { TopNavBarMenu } from './TopNavBarMenu'; import { TopSearchBarCommandPaletteTrigger } from './TopSearchBarCommandPaletteTrigger'; -export const MEGA_MENU_TOGGLE_ID = 'mega-menu-toggle'; - interface Props { sectionNav: NavModelItem; pageNav?: NavModelItem; diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index dcc1a250133..fcac1bdeefb 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { PureComponent } from 'react'; import * as React from 'react'; -import { FeatureState, getBuiltInThemes, ThemeRegistryItem } from '@grafana/data'; +import { FeatureState, ThemeRegistryItem } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { config, reportInteraction } from '@grafana/runtime'; import { Preferences as UserPreferencesDTO } from '@grafana/schema/src/raw/preferences/x/preferences_types.gen'; @@ -26,6 +26,9 @@ import { t, Trans } from 'app/core/internationalization'; import { LANGUAGES, PSEUDO_LOCALE } from 'app/core/internationalization/constants'; import { PreferencesService } from 'app/core/services/PreferencesService'; import { changeTheme } from 'app/core/services/theme'; + +import { getSelectableThemes } from '../ThemeSelector/getSelectableThemes'; + export interface Props { resourceUri: string; disabled?: boolean; @@ -82,21 +85,9 @@ export class SharedPreferences extends PureComponent { navbar: { bookmarkUrls: [] }, }; - const allowedExtraThemes = []; + const themes = getSelectableThemes(); - if (config.featureToggles.extraThemes) { - allowedExtraThemes.push('debug'); - } - - if (config.featureToggles.grafanaconThemes) { - allowedExtraThemes.push('desertbloom'); - allowedExtraThemes.push('gildedgrove'); - allowedExtraThemes.push('sapphiredusk'); - allowedExtraThemes.push('tron'); - allowedExtraThemes.push('gloom'); - } - - this.themeOptions = getBuiltInThemes(allowedExtraThemes).map((theme) => ({ + this.themeOptions = themes.map((theme) => ({ value: theme.id, label: getTranslatedThemeName(theme), })); diff --git a/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx b/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx new file mode 100644 index 00000000000..bc3de6ce4d7 --- /dev/null +++ b/public/app/core/components/ThemeSelector/ThemeSelectorDrawer.tsx @@ -0,0 +1,121 @@ +import { css } from '@emotion/css'; + +import { GrafanaTheme2, ThemeRegistryItem } from '@grafana/data'; +import { Drawer, RadioButtonDot, TextLink, useStyles2, useTheme2 } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; +import { changeTheme } from 'app/core/services/theme'; + +import { ThemePreview } from '../Theme/ThemePreview'; + +import { getSelectableThemes } from './getSelectableThemes'; + +interface Props { + onClose: () => void; +} + +export function ThemeSelectorDrawer({ onClose }: Props) { + const styles = useStyles2(getStyles); + const themes = getSelectableThemes(); + const currentTheme = useTheme2(); + + const onChange = (theme: ThemeRegistryItem) => { + changeTheme(theme.id, false); + }; + + const subTitle = ( + + Enjoying the limited edition themes? Tell us what you'd like to see{' '} + + here. + + + ); + + return ( + +
+ {themes.map((themeOption) => ( + onChange(themeOption)} + isSelected={currentTheme.name === themeOption.name} + /> + ))} +
+
+ ); +} + +interface ThemeCardProps { + themeOption: ThemeRegistryItem; + isSelected?: boolean; + onSelect: () => void; +} + +function ThemeCard({ themeOption, isSelected, onSelect }: ThemeCardProps) { + const theme = themeOption.build(); + const label = getTranslatedThemeName(themeOption); + const styles = useStyles2(getStyles); + + return ( +
+
+ +
+ +
+ ); +} + +const getStyles = (theme: GrafanaTheme2) => { + return { + grid: css({ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', + gridAutoRows: `250px`, + gap: theme.spacing(2), + }), + card: css({ + border: `1px solid ${theme.colors.border.weak}`, + borderRadius: theme.shape.radius.default, + display: 'flex', + flexDirection: 'column', + cursor: 'pointer', + '&:hover': { + border: `1px solid ${theme.colors.border.medium}`, + }, + }), + header: css({ + borderBottom: `1px solid ${theme.colors.border.weak}`, + padding: theme.spacing(1), + // The RadioButtonDot is not correctly implemented at the moment, missing cursor (And click ability for the label and input) + '> label': { + cursor: 'pointer', + }, + }), + }; +}; + +function getTranslatedThemeName(theme: ThemeRegistryItem) { + switch (theme.id) { + case 'dark': + return t('shared.preferences.theme.dark-label', 'Dark'); + case 'light': + return t('shared.preferences.theme.light-label', 'Light'); + case 'system': + return t('shared.preferences.theme.system-label', 'System preference'); + default: + return theme.name; + } +} diff --git a/public/app/core/components/ThemeSelector/getSelectableThemes.ts b/public/app/core/components/ThemeSelector/getSelectableThemes.ts new file mode 100644 index 00000000000..7dc749d9793 --- /dev/null +++ b/public/app/core/components/ThemeSelector/getSelectableThemes.ts @@ -0,0 +1,20 @@ +import { getBuiltInThemes } from '@grafana/data'; +import { config } from '@grafana/runtime'; + +export function getSelectableThemes() { + const allowedExtraThemes = []; + + if (config.featureToggles.extraThemes) { + allowedExtraThemes.push('debug'); + } + + if (config.featureToggles.grafanaconThemes) { + allowedExtraThemes.push('desertbloom'); + allowedExtraThemes.push('gildedgrove'); + allowedExtraThemes.push('sapphiredusk'); + allowedExtraThemes.push('tron'); + allowedExtraThemes.push('gloom'); + } + + return getBuiltInThemes(allowedExtraThemes); +} diff --git a/public/app/core/constants.ts b/public/app/core/constants.ts index 66417d663af..1de5d88801b 100644 --- a/public/app/core/constants.ts +++ b/public/app/core/constants.ts @@ -17,3 +17,4 @@ export const EDIT_PANEL_ID = 23763571993; export const DEFAULT_PER_PAGE_PAGINATION = 40; export const LS_VISUALIZATION_SELECT_TAB_KEY = 'VisualizationSelectPane.ListMode'; +export const MEGA_MENU_TOGGLE_ID = 'mega-menu-toggle'; diff --git a/public/app/core/services/theme.ts b/public/app/core/services/theme.ts index 6368aa07f76..b244b95e012 100644 --- a/public/app/core/services/theme.ts +++ b/public/app/core/services/theme.ts @@ -50,7 +50,7 @@ export async function changeTheme(themeId: string, runtimeOnly?: boolean) { await service.update({ ...currentPref, - theme: newTheme.colors.mode, + theme: themeId, }); } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index a2dd8f62e0d..ee429b59821 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -3123,7 +3123,8 @@ "old-password-required": "Old password is required", "passwords-must-match": "Passwords must match", "strong-password-validation-register": "Password does not comply with the strong password policy" - } + }, + "change-theme": "Change theme" }, "public-dashboard": { "acknowledgment-checkboxes": { From c8f810b422d4a7213fbf64990a5e2ef9faac7ffd Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Tue, 11 Mar 2025 12:04:33 +0100 Subject: [PATCH 166/312] Authz: Check namespace is set in the context (#101723) * Authz: Test List * Anonymous case * Cover rendering * Authz: Check namespace is set in the context * Explicitly request a namespace check in the storage functions * Revert logic --- pkg/services/authz/rbac/service_test.go | 47 +++++++++++++++++-------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/pkg/services/authz/rbac/service_test.go b/pkg/services/authz/rbac/service_test.go index dce4b79da76..b9df0d92322 100644 --- a/pkg/services/authz/rbac/service_test.go +++ b/pkg/services/authz/rbac/service_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/singleflight" + "k8s.io/apiserver/pkg/endpoints/request" "github.com/grafana/authlib/authn" authzv1 "github.com/grafana/authlib/authz/proto/v1" @@ -225,11 +226,10 @@ func TestService_getUserTeams(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() s := setupService() - ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12} userIdentifiers := &store.UserIdentifiers{UID: "test-uid"} - identityStore := &fakeIdentityStore{teams: tc.teams, err: tc.expectedError} + identityStore := &fakeIdentityStore{teams: tc.teams, err: tc.expectedError, disableNsCheck: true} s.identityStore = identityStore if tc.cacheHit { @@ -305,7 +305,7 @@ func TestService_getUserBasicRole(t *testing.T) { ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12} userIdentifiers := &store.UserIdentifiers{UID: "test-uid", ID: 1} - store := &fakeStore{basicRole: &tc.basicRole, err: tc.expectedError} + store := &fakeStore{basicRole: &tc.basicRole, err: tc.expectedError, disableNsCheck: true} s.store = store s.permissionStore = store @@ -367,9 +367,9 @@ func TestService_getUserPermissions(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() s := setupService() + ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12} userID := &store.UserIdentifiers{UID: "test-uid", ID: 112} - ns := types.NamespaceInfo{Value: "stacks-12", OrgID: 1, StackID: 12} action := "dashboards:read" if tc.cacheHit { @@ -380,10 +380,11 @@ func TestService_getUserPermissions(t *testing.T) { userID: userID, basicRole: &store.BasicRole{Role: "viewer", IsAdmin: false}, userPermissions: tc.permissions, + disableNsCheck: true, } s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}, disableNsCheck: true} perms, err := s.getIdentityPermissions(ctx, ns, types.TypeUser, userID.UID, action) require.NoError(t, err) @@ -737,7 +738,7 @@ func TestService_Check(t *testing.T) { } s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{} _, err := s.Check(ctx, tc.req) require.Error(t, err) @@ -785,7 +786,7 @@ func TestService_Check(t *testing.T) { } s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{} resp, err := s.Check(ctx, tc.req) require.NoError(t, err) @@ -838,7 +839,7 @@ func TestService_Check(t *testing.T) { store := &fakeStore{userPermissions: tc.permissions} s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{} resp, err := s.Check(ctx, tc.req) require.NoError(t, err) @@ -1019,7 +1020,7 @@ func TestService_List(t *testing.T) { } s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{} _, err := s.List(ctx, tc.req) require.Error(t, err) @@ -1072,7 +1073,7 @@ func TestService_List(t *testing.T) { } s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{} resp, err := s.List(ctx, tc.req) require.NoError(t, err) @@ -1131,7 +1132,7 @@ func TestService_List(t *testing.T) { store := &fakeStore{userPermissions: tc.permissions} s.store = store s.permissionStore = store - s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}} + s.identityStore = &fakeIdentityStore{} resp, err := s.List(ctx, tc.req) require.NoError(t, err) @@ -1218,6 +1219,8 @@ func strPtr(s string) *string { type fakeStore struct { store.Store + // The namespace has to be set in the handlers for the correct organization to be picked up. + disableNsCheck bool folders []store.Folder basicRole *store.BasicRole userID *store.UserIdentifiers @@ -1227,6 +1230,9 @@ type fakeStore struct { } func (f *fakeStore) GetBasicRoles(ctx context.Context, namespace types.NamespaceInfo, query store.BasicRoleQuery) (*store.BasicRole, error) { + if ns, ok := request.NamespaceFrom(ctx); !f.disableNsCheck && (!ok || ns != namespace.Value) { + return nil, fmt.Errorf("namespace mismatch") + } f.calls++ if f.err { return nil, fmt.Errorf("store error") @@ -1235,6 +1241,9 @@ func (f *fakeStore) GetBasicRoles(ctx context.Context, namespace types.Namespace } func (f *fakeStore) GetUserIdentifiers(ctx context.Context, query store.UserIdentifierQuery) (*store.UserIdentifiers, error) { + if _, ok := request.NamespaceFrom(ctx); !f.disableNsCheck && !ok { + return nil, fmt.Errorf("namespace not found") + } f.calls++ if f.err { return nil, fmt.Errorf("store error") @@ -1243,6 +1252,9 @@ func (f *fakeStore) GetUserIdentifiers(ctx context.Context, query store.UserIden } func (f *fakeStore) GetUserPermissions(ctx context.Context, namespace types.NamespaceInfo, query store.PermissionsQuery) ([]accesscontrol.Permission, error) { + if ns, ok := request.NamespaceFrom(ctx); !f.disableNsCheck && (!ok || ns != namespace.Value) { + return nil, fmt.Errorf("namespace mismatch") + } f.calls++ if f.err { return nil, fmt.Errorf("store error") @@ -1251,6 +1263,9 @@ func (f *fakeStore) GetUserPermissions(ctx context.Context, namespace types.Name } func (f *fakeStore) ListFolders(ctx context.Context, namespace types.NamespaceInfo) ([]store.Folder, error) { + if ns, ok := request.NamespaceFrom(ctx); !f.disableNsCheck && (!ok || ns != namespace.Value) { + return nil, fmt.Errorf("namespace mismatch") + } f.calls++ if f.err { return nil, fmt.Errorf("store error") @@ -1260,12 +1275,16 @@ func (f *fakeStore) ListFolders(ctx context.Context, namespace types.NamespaceIn type fakeIdentityStore struct { legacy.LegacyIdentityStore - teams []int64 - err bool - calls int + teams []int64 + disableNsCheck bool + err bool + calls int } func (f *fakeIdentityStore) ListUserTeams(ctx context.Context, namespace types.NamespaceInfo, query legacy.ListUserTeamsQuery) (*legacy.ListUserTeamsResult, error) { + if ns, ok := request.NamespaceFrom(ctx); !f.disableNsCheck && (!ok || ns != namespace.Value) { + return nil, fmt.Errorf("namespace mismatch") + } f.calls++ if f.err { return nil, fmt.Errorf("identity store error") From 3adb12fb56f684a462817d8690268f43421755ef Mon Sep 17 00:00:00 2001 From: Victor Marin <36818606+mdvictor@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:19:46 +0200 Subject: [PATCH 167/312] Dashboards: Fix timezone change issue in dashboards (#101880) * fix timezone change issue in dashboards * update dashboard initial state after saving --- public/app/features/dashboard-scene/scene/DashboardScene.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 0ffc53520c4..41559cd975a 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -286,9 +286,14 @@ export class DashboardScene extends SceneObjectBase impleme folderUid: folderUid, version: result.version, }, + overlay: undefined, }); this.state.editPanel?.dashboardSaved(); + + this._initialState = sceneUtils.cloneSceneObjectState(this.state); + this._initialUrlState = locationService.getLocation(); + this._changeTracker.startTrackingChanges(); } From 79d079b638f7c31f12d51718ec5ac6afb53fe11c Mon Sep 17 00:00:00 2001 From: Mike Nolta Date: Tue, 11 Mar 2025 07:24:33 -0400 Subject: [PATCH 168/312] Docs: fix missing `@` in `GF_PLUGINS_PREINSTALL` example (#101908) Docs: fix missing `@` in `GF_PLUGINS_PREINSTALL` --- docs/sources/setup-grafana/installation/docker/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/setup-grafana/installation/docker/index.md b/docs/sources/setup-grafana/installation/docker/index.md index 7a7bd091cb9..da578681fca 100644 --- a/docs/sources/setup-grafana/installation/docker/index.md +++ b/docs/sources/setup-grafana/installation/docker/index.md @@ -150,7 +150,7 @@ To install plugins in the Docker container, complete the following steps: ```bash docker run -d -p 3000:3000 --name=grafana \ - -e "GF_PLUGINS_PREINSTALL=grafana-clock-panel 1.0.1" \ + -e "GF_PLUGINS_PREINSTALL=grafana-clock-panel@1.0.1" \ grafana/grafana-enterprise ``` From 927f7befd6a256bbfa5d513f12f874f318ed3b9b Mon Sep 17 00:00:00 2001 From: maicon Date: Tue, 11 Mar 2025 08:33:08 -0300 Subject: [PATCH 169/312] Unistore: Create default permissions through Folder APIServer (#101420) * Unistore: Declare a new storage to set default folder permissions Signed-off-by: Maicon Costa * Remove the setting of default permissions from folder legacy storage Signed-off-by: Maicon Costa * Disable setting of folder permissions when Api Server is enabled Signed-off-by: Maicon Costa * Reverts grafana/grafana#100019 Signed-off-by: Maicon Costa * Add unit test Signed-off-by: Maicon Costa * check error on unit test Signed-off-by: Maicon Costa * Add unit test Signed-off-by: Maicon Costa * Remove unused fields Signed-off-by: Maicon Costa * Add unit tests for folder_storage Signed-off-by: Maicon Costa * Remove duplicated import Signed-off-by: Maicon Costa * Fix unit test Signed-off-by: Maicon Costa --------- Signed-off-by: Maicon Costa --- pkg/api/folder.go | 7 +- pkg/api/folder_test.go | 91 ++++++++++ pkg/registry/apis/folders/folder_storage.go | 161 ++++++++++++++++++ .../apis/folders/folder_storage_test.go | 132 ++++++++++++++ pkg/registry/apis/folders/legacy_storage.go | 48 +----- pkg/registry/apis/folders/register.go | 24 ++- .../accesscontrol/ossaccesscontrol/folder.go | 12 -- .../dashboards/service/dashboard_service.go | 5 +- .../service/dashboard_service_test.go | 70 ++++++++ 9 files changed, 485 insertions(+), 65 deletions(-) create mode 100644 pkg/registry/apis/folders/folder_storage.go create mode 100644 pkg/registry/apis/folders/folder_storage_test.go diff --git a/pkg/api/folder.go b/pkg/api/folder.go index b18db9c7850..682b650cc7b 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -199,8 +199,11 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response return apierrors.ToFolderErrorResponse(err) } - if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil { - hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err) + // Only set default permissions if the Folder API Server is disabled. + if !hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { + if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil { + hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err) + } } // Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index 97701e0f4f0..840ae125594 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -677,3 +677,94 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) { } }) } + +func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) { + folderService := &foldertest.FakeService{} + setUpRBACGuardian(t) + folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}" + + type testCase struct { + description string + expectedCallsToSetPermissions int + expectedCode int + expectedFolder *folder.Folder + permissions []accesscontrol.Permission + featuresArr []any + input string + } + + tcs := []testCase{ + { + description: "folder creation succeeds, via legacy storage", + expectedCallsToSetPermissions: 1, + input: folderWithoutParentInput, + expectedCode: http.StatusOK, + expectedFolder: &folder.Folder{UID: "uid", Title: "Folder"}, + permissions: []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}}, + }, + { + description: "folder creation succeeds, via API Server", + expectedCallsToSetPermissions: 0, + input: folderWithoutParentInput, + expectedCode: http.StatusOK, + expectedFolder: &folder.Folder{UID: "uid", Title: "Folder"}, + permissions: []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}}, + featuresArr: []any{featuremgmt.FlagKubernetesClientDashboardsFolders}, + }, + } + + // we need to save these values because they are defined at `setting` package level + // and modified when we invoke setting.NewCfgFromINIFile + prevCookieSameSiteDisabled := setting.CookieSameSiteDisabled + prevCookieSameSiteMode := setting.CookieSameSiteMode + + cfg := setting.NewCfg() + cfg.Raw.Section("rbac").Key("resources_with_managed_permissions_on_creation").SetValue("folder") + tmpCfg, err := setting.NewCfgFromINIFile(cfg.Raw) + require.NoError(t, err) + cfg.RBAC = tmpCfg.RBAC + + // restore previous values so other tests don't break + // ex: TestHTTPServer_RotateUserAuthToken + setting.CookieSameSiteDisabled = prevCookieSameSiteDisabled + setting.CookieSameSiteMode = prevCookieSameSiteMode + + for _, tc := range tcs { + t.Run(tc.description, func(t *testing.T) { + folderService.ExpectedFolder = tc.expectedFolder + folderPermService := acmock.NewMockedPermissionsService() + folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil) + + srv := SetupAPITestServer(t, func(hs *HTTPServer) { + hs.Cfg = cfg + + featuresArr := append(tc.featuresArr, featuremgmt.FlagNestedFolders) + hs.Features = featuremgmt.WithFeatures( + featuresArr..., + ) + hs.folderService = folderService + hs.folderPermissionsService = folderPermService + hs.accesscontrolService = actest.FakeService{} + }) + + input := strings.NewReader(tc.input) + req := srv.NewPostRequest("/api/folders", input) + req = webtest.RequestWithSignedInUser(req, userWithPermissions(1, tc.permissions)) + resp, err := srv.SendJSON(req) + require.NoError(t, err) + require.Equal(t, tc.expectedCode, resp.StatusCode) + + folder := dtos.Folder{} + err = json.NewDecoder(resp.Body).Decode(&folder) + require.NoError(t, err) + require.NoError(t, resp.Body.Close()) + + folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions) + + if tc.expectedCode == http.StatusOK { + assert.Equal(t, "uid", folder.UID) + assert.Equal(t, "Folder", folder.Title) + } + }) + } +} diff --git a/pkg/registry/apis/folders/folder_storage.go b/pkg/registry/apis/folders/folder_storage.go new file mode 100644 index 00000000000..92156643354 --- /dev/null +++ b/pkg/registry/apis/folders/folder_storage.go @@ -0,0 +1,161 @@ +package folders + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" + + claims "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" + grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" + "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/org" + "github.com/grafana/grafana/pkg/setting" +) + +var ( + _ rest.Scoper = (*folderStorage)(nil) + _ rest.SingularNameProvider = (*folderStorage)(nil) + _ rest.Getter = (*folderStorage)(nil) + _ rest.Lister = (*folderStorage)(nil) + _ rest.Storage = (*folderStorage)(nil) + _ rest.Creater = (*folderStorage)(nil) + _ rest.Updater = (*folderStorage)(nil) + _ rest.GracefulDeleter = (*folderStorage)(nil) +) + +type folderStorage struct { + tableConverter rest.TableConvertor + cfg *setting.Cfg + features featuremgmt.FeatureToggles + folderPermissionsSvc accesscontrol.FolderPermissionsService + store grafanarest.Storage +} + +func (s *folderStorage) New() runtime.Object { + return resourceInfo.NewFunc() +} + +func (s *folderStorage) Destroy() {} + +func (s *folderStorage) NamespaceScoped() bool { + return true // namespace == org +} + +func (s *folderStorage) GetSingularName() string { + return resourceInfo.GetSingularName() +} + +func (s *folderStorage) NewList() runtime.Object { + return resourceInfo.NewListFunc() +} + +func (s *folderStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + return s.tableConverter.ConvertToTable(ctx, object, tableOptions) +} + +func (s *folderStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { + return s.store.List(ctx, options) +} + +func (s *folderStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { + return s.store.Get(ctx, name, options) +} + +func (s *folderStorage) Create(ctx context.Context, + obj runtime.Object, + createValidation rest.ValidateObjectFunc, + options *metav1.CreateOptions, +) (runtime.Object, error) { + obj, err := s.store.Create(ctx, obj, createValidation, options) + if err != nil { + return nil, err + } + + info, err := request.NamespaceInfoFrom(ctx, true) + if err != nil { + return nil, err + } + + user, err := identity.GetRequester(ctx) + if err != nil { + return nil, err + } + + p, ok := obj.(*v0alpha1.Folder) + if !ok { + return nil, fmt.Errorf("expected folder?") + } + + accessor, err := utils.MetaAccessor(p) + if err != nil { + return nil, err + } + + parentUid := accessor.GetFolder() + + err = s.setDefaultFolderPermissions(ctx, info.OrgID, user, p.ObjectMeta.Name, parentUid) + if err != nil { + return nil, err + } + + return obj, nil +} + +func (s *folderStorage) Update(ctx context.Context, + name string, + objInfo rest.UpdatedObjectInfo, + createValidation rest.ValidateObjectFunc, + updateValidation rest.ValidateObjectUpdateFunc, + forceAllowCreate bool, + options *metav1.UpdateOptions, +) (runtime.Object, bool, error) { + return s.store.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options) +} + +// GracefulDeleter +func (s *folderStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { + return s.store.Delete(ctx, name, deleteValidation, options) +} + +// GracefulDeleter +func (s *folderStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) { + return nil, fmt.Errorf("DeleteCollection for folders not implemented") +} + +func (s *folderStorage) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, uid string, parentUID string) error { + if !s.cfg.RBAC.PermissionsOnCreation("folder") { + return nil + } + + var permissions []accesscontrol.SetResourcePermissionCommand + + if user.IsIdentityType(claims.TypeUser) { + userID, err := user.GetInternalID() + if err != nil { + return err + } + + permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ + UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(), + }) + } + isNested := parentUID != "" + if !isNested || !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) { + permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{ + {BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()}, + {BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()}, + }...) + } + _, err := s.folderPermissionsSvc.SetPermissions(ctx, orgID, uid, permissions...) + return err +} diff --git a/pkg/registry/apis/folders/folder_storage_test.go b/pkg/registry/apis/folders/folder_storage_test.go new file mode 100644 index 00000000000..31cc755b6f3 --- /dev/null +++ b/pkg/registry/apis/folders/folder_storage_test.go @@ -0,0 +1,132 @@ +package folders + +import ( + "context" + "testing" + + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" + "github.com/grafana/grafana/pkg/services/accesscontrol" + acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" + "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "gopkg.in/ini.v1" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) { + type testCase struct { + description string + expectedCallsToSetPermissions int + } + + tcs := []testCase{ + { + description: "folder creation succeeds, via legacy storage", + expectedCallsToSetPermissions: 1, + }, + } + + for _, tc := range tcs { + t.Run(tc.description, func(t *testing.T) { + folderPermService := acmock.NewMockedPermissionsService() + folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil) + + cfg := setting.NewCfg() + f := ini.Empty() + f.Section("rbac").Key("resources_with_managed_permissions_on_creation").SetValue("folder") + tempCfg, err := setting.NewCfgFromINIFile(f) + require.NoError(t, err) + cfg.RBAC = tempCfg.RBAC + + fs := folderStorage{ + folderPermissionsSvc: folderPermService, + store: &fakeStorage{}, + cfg: cfg, + } + obj := &v0alpha1.Folder{} + + ctx := request.WithNamespace(context.Background(), "org-2") + ctx = identity.WithRequester(ctx, &user.SignedInUser{ + UserID: 1, + }) + + out, err := fs.Create(ctx, obj, func(ctx context.Context, + obj runtime.Object) error { + return nil + }, + &metav1.CreateOptions{}) + + require.NoError(t, err) + require.NotNil(t, out) + + folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions) + }) + } +} + +var ( + _ rest.Scoper = (*fakeStorage)(nil) + _ rest.SingularNameProvider = (*fakeStorage)(nil) + _ rest.Getter = (*fakeStorage)(nil) + _ rest.Lister = (*fakeStorage)(nil) + _ rest.Storage = (*fakeStorage)(nil) + _ rest.Creater = (*fakeStorage)(nil) + _ rest.Updater = (*fakeStorage)(nil) + _ rest.GracefulDeleter = (*fakeStorage)(nil) +) + +type fakeStorage struct{} + +func (s *fakeStorage) New() runtime.Object { + return nil +} + +func (s *fakeStorage) Destroy() {} + +func (s *fakeStorage) NamespaceScoped() bool { + return true +} + +func (s *fakeStorage) GetSingularName() string { + return "" +} + +func (s *fakeStorage) NewList() runtime.Object { + return nil +} + +func (s *fakeStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + return nil, nil +} + +func (s *fakeStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { + return nil, nil +} + +func (s *fakeStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { + return nil, nil +} + +func (s *fakeStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { + return obj, nil +} + +func (s *fakeStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, + updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { + return nil, false, nil +} + +func (s *fakeStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { + return nil, false, nil +} + +func (s *fakeStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) { + return nil, nil +} diff --git a/pkg/registry/apis/folders/legacy_storage.go b/pkg/registry/apis/folders/legacy_storage.go index 550331c855e..9ec2d7c98b8 100644 --- a/pkg/registry/apis/folders/legacy_storage.go +++ b/pkg/registry/apis/folders/legacy_storage.go @@ -10,18 +10,14 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/rest" - claims "github.com/grafana/authlib/types" "github.com/grafana/grafana/pkg/api/apierrors" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" - "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/dashboards" - "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/folder" - "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -38,12 +34,11 @@ var ( ) type legacyStorage struct { - service folder.Service - namespacer request.NamespaceMapper - tableConverter rest.TableConvertor - cfg *setting.Cfg - features featuremgmt.FeatureToggles - folderPermissionsSvc accesscontrol.FolderPermissionsService + service folder.Service + namespacer request.NamespaceMapper + tableConverter rest.TableConvertor + cfg *setting.Cfg + features featuremgmt.FeatureToggles } func (s *legacyStorage) New() runtime.Object { @@ -200,11 +195,6 @@ func (s *legacyStorage) Create(ctx context.Context, return nil, &statusErr } - err = s.setDefaultFolderPermissions(ctx, info.OrgID, user, out) - if err != nil { - return nil, err - } - // #TODO can we directly convert instead of doing a Get? the result of the Create // has more data than the one of Get so there is more we can include in the k8s resource // this way @@ -216,34 +206,6 @@ func (s *legacyStorage) Create(ctx context.Context, return r, nil } -func (s *legacyStorage) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error { - if !s.cfg.RBAC.PermissionsOnCreation("folder") { - return nil - } - - var permissions []accesscontrol.SetResourcePermissionCommand - - if user.IsIdentityType(claims.TypeUser) { - userID, err := user.GetInternalID() - if err != nil { - return err - } - - permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ - UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(), - }) - } - isNested := folder.ParentUID != "" - if !isNested || !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) { - permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{ - {BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()}, - {BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()}, - }...) - } - _, err := s.folderPermissionsSvc.SetPermissions(ctx, orgID, folder.UID, permissions...) - return err -} - func (s *legacyStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, diff --git a/pkg/registry/apis/folders/register.go b/pkg/registry/apis/folders/register.go index 6d4d64f1d9a..ca3d492815d 100644 --- a/pkg/registry/apis/folders/register.go +++ b/pkg/registry/apis/folders/register.go @@ -148,28 +148,38 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API } legacyStore := &legacyStorage{ - service: b.folderSvc, - namespacer: b.namespacer, + service: b.folderSvc, + namespacer: b.namespacer, + tableConverter: resourceInfo.TableConverter(), + features: b.features, + cfg: b.cfg, + } + + opts.StorageOptions(resourceInfo.GroupResource(), apistore.StorageOptions{ + RequireDeprecatedInternalID: true}) + + folderStore := &folderStorage{ tableConverter: resourceInfo.TableConverter(), folderPermissionsSvc: b.folderPermissionsSvc, features: b.features, cfg: b.cfg, } - opts.StorageOptions(resourceInfo.GroupResource(), apistore.StorageOptions{ - RequireDeprecatedInternalID: true}) - - storage[resourceInfo.StoragePath()] = legacyStore if optsGetter != nil && dualWriteBuilder != nil { store, err := grafanaregistry.NewRegistryStore(scheme, resourceInfo, optsGetter) if err != nil { return err } - storage[resourceInfo.StoragePath()], err = dualWriteBuilder(resourceInfo.GroupResource(), legacyStore, store) + + dw, err := dualWriteBuilder(resourceInfo.GroupResource(), legacyStore, store) if err != nil { return err } + + folderStore.store = dw } + storage[resourceInfo.StoragePath()] = folderStore + storage[resourceInfo.StoragePath("parents")] = &subParentsREST{ getter: storage[resourceInfo.StoragePath()].(rest.Getter), // Get the parents } diff --git a/pkg/services/accesscontrol/ossaccesscontrol/folder.go b/pkg/services/accesscontrol/ossaccesscontrol/folder.go index ee41326c878..67b73ebe764 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/folder.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/folder.go @@ -2,7 +2,6 @@ package ossaccesscontrol import ( "context" - "errors" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -107,17 +106,6 @@ func ProvideFolderPermissions( }) if err != nil { - // if the folder is not found, this may be on the create path, - // where the write path to legacy will then go through the read - // path and try to read from both legacy & unified before it exists on both - if features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) && errors.Is(err, dashboards.ErrFolderNotFound) { - _, err = folderService.GetLegacy(ctx, &folder.GetFolderQuery{ - UID: &resourceID, - OrgID: orgID, - SignedInUser: ident, - }) - return err - } return err } diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 68b49623bd3..31fbfcb428d 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -719,7 +719,10 @@ func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.C return nil, err } - dr.setDefaultFolderPermissions(ctx, dto, f, true) + // Only set default permissions if the Folder API Server is disabled. + if !dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { + dr.setDefaultFolderPermissions(ctx, dto, f, true) + } return f, nil } diff --git a/pkg/services/dashboards/service/dashboard_service_test.go b/pkg/services/dashboards/service/dashboard_service_test.go index 105f3bcfe23..c3593db643c 100644 --- a/pkg/services/dashboards/service/dashboard_service_test.go +++ b/pkg/services/dashboards/service/dashboard_service_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "gopkg.in/ini.v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -18,6 +20,8 @@ import ( dashboardv0alpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/services/accesscontrol" + acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/apiserver/client" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -1112,6 +1116,72 @@ func TestGetDashboardsByPluginID(t *testing.T) { }) } +func TestSetDefaultPermissionsWhenSavingFolderForProvisionedDashboards(t *testing.T) { + fakeStore := dashboards.FakeDashboardStore{} + defer fakeStore.AssertExpectations(t) + + type testCase struct { + description string + expectedCallsToSetPermissions int + featuresArr []any + } + + tcs := []testCase{ + { + description: "folder creation succeeds, via legacy storage", + expectedCallsToSetPermissions: 1, + }, + { + description: "folder creation succeeds, via API Server", + expectedCallsToSetPermissions: 0, + featuresArr: []any{featuremgmt.FlagKubernetesClientDashboardsFolders}, + }, + } + + for _, tc := range tcs { + t.Run(tc.description, func(t *testing.T) { + folderPermService := acmock.NewMockedPermissionsService() + folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil) + + cfg := setting.NewCfg() + f := ini.Empty() + f.Section("rbac").Key("resources_with_managed_permissions_on_creation").SetValue("folder") + tempCfg, err := setting.NewCfgFromINIFile(f) + require.NoError(t, err) + cfg.RBAC = tempCfg.RBAC + + service := &DashboardServiceImpl{ + cfg: cfg, + dashboardStore: &fakeStore, + folderPermissions: folderPermService, + folderService: &foldertest.FakeService{ + ExpectedFolder: &folder.Folder{ + ID: 0, + UID: "general", + }, + }, + log: log.NewNopLogger(), + } + + origNewDashboardGuardian := guardian.New + defer func() { guardian.New = origNewDashboardGuardian }() + guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) + + cmd := &folder.CreateFolderCommand{ + Title: "foo", + OrgID: 1, + } + + service.features = featuremgmt.WithFeatures(tc.featuresArr...) + folder, err := service.SaveFolderForProvisionedDashboards(context.Background(), cmd) + require.NoError(t, err) + require.NotNil(t, folder) + + folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions) + }) + } +} + func TestSaveProvisionedDashboard(t *testing.T) { fakeStore := dashboards.FakeDashboardStore{} defer fakeStore.AssertExpectations(t) From adbc5b2b8813c1ae2b888fe2b726acf72991e935 Mon Sep 17 00:00:00 2001 From: Gilles De Mey Date: Tue, 11 Mar 2025 12:51:48 +0100 Subject: [PATCH 170/312] Alerting: Hide "unauthorized" warning for anonymous users (#101811) * remove nav analytics * revert * Remove new user check for alerting navigation tracking * Delete Analytics.test.ts --- .../alerting/unified/Analytics.test.ts | 41 ------------------- .../features/alerting/unified/Analytics.ts | 24 +---------- 2 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 public/app/features/alerting/unified/Analytics.test.ts diff --git a/public/app/features/alerting/unified/Analytics.test.ts b/public/app/features/alerting/unified/Analytics.test.ts deleted file mode 100644 index 47575f755df..00000000000 --- a/public/app/features/alerting/unified/Analytics.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { dateTime } from '@grafana/data'; -import { getBackendSrv } from '@grafana/runtime'; - -import { USER_CREATION_MIN_DAYS, isNewUser } from './Analytics'; - -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getBackendSrv: jest.fn().mockReturnValue({ - get: jest.fn(), - }), -})); - -describe('isNewUser', function () { - it('should return true if the user has been created within the last week', async () => { - const newUser = { - id: 1, - createdAt: dateTime().subtract(6, 'days'), - }; - - getBackendSrv().get = jest.fn().mockResolvedValue(newUser); - - const isNew = await isNewUser(); - expect(isNew).toBe(true); - expect(getBackendSrv().get).toHaveBeenCalledTimes(1); - expect(getBackendSrv().get).toHaveBeenCalledWith('/api/user'); - }); - - it('should return false if the user has been created prior to the last two weeks', async () => { - const oldUser = { - id: 2, - createdAt: dateTime().subtract(USER_CREATION_MIN_DAYS, 'days'), - }; - - getBackendSrv().get = jest.fn().mockResolvedValue(oldUser); - - const isNew = await isNewUser(); - expect(isNew).toBe(false); - expect(getBackendSrv().get).toHaveBeenCalledTimes(1); - expect(getBackendSrv().get).toHaveBeenCalledWith('/api/user'); - }); -}); diff --git a/public/app/features/alerting/unified/Analytics.ts b/public/app/features/alerting/unified/Analytics.ts index e546d215574..559e7808d96 100644 --- a/public/app/features/alerting/unified/Analytics.ts +++ b/public/app/features/alerting/unified/Analytics.ts @@ -1,7 +1,6 @@ import { isEmpty } from 'lodash'; -import { dateTime } from '@grafana/data'; -import { createMonitoringLogger, getBackendSrv } from '@grafana/runtime'; +import { createMonitoringLogger } from '@grafana/runtime'; import { config, reportInteraction } from '@grafana/runtime/src'; import { contextSrv } from 'app/core/core'; @@ -13,8 +12,6 @@ import { FilterType } from './components/rules/central-state-history/EventListSc import { RulesFilter, getSearchFilterFromQuery } from './search/rulesSearchParser'; import { RuleFormType } from './types/rule-form'; -export const USER_CREATION_MIN_DAYS = 7; - export const LogMessages = { filterByLabel: 'filtering alert instances by label', loadedList: 'loaded Alert Rules list', @@ -152,21 +149,6 @@ function getRulerRulesMetadata(rulerRules: RulerRulesConfigDTO) { }; } -export async function isNewUser() { - try { - const { createdAt } = await getBackendSrv().get(`/api/user`); - - const limitDateForNewUser = dateTime().subtract(USER_CREATION_MIN_DAYS, 'days'); - const userCreationDate = dateTime(createdAt); - - const isNew = limitDateForNewUser.isBefore(userCreationDate); - - return isNew; - } catch { - return true; //if no date is returned, we assume the user is new to prevent tracking actions - } -} - export const trackRuleListNavigation = async ( props: AlertRuleTrackingProps = { grafana_version: config.buildInfo.version, @@ -174,10 +156,6 @@ export const trackRuleListNavigation = async ( user_id: contextSrv.user.id, } ) => { - const isNew = await isNewUser(); - if (isNew) { - return; - } reportInteraction('grafana_alerting_navigation', props); }; From 062a0e7212f495330408359fee7c42e2c6839abc Mon Sep 17 00:00:00 2001 From: Joey <90795735+joey-grafana@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:43:02 +0000 Subject: [PATCH 171/312] Tempo: fallback for intrinsic tags (#101677) * Add intrinsics fallback * Add test * Update tests * Prettier * Remove extra uniq --- .../tempo/SearchTraceQLEditor/utils.test.ts | 11 +++++++++-- .../tempo/SearchTraceQLEditor/utils.ts | 18 +++++++++++------- .../datasource/tempo/language_provider.test.ts | 9 +++++++-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts index e76cdf168e6..57c2dadc2b1 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.test.ts @@ -3,12 +3,14 @@ import { uniq } from 'lodash'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; import { TempoDatasource } from '../datasource'; import TempoLanguageProvider from '../language_provider'; +import { intrinsics } from '../traceql/traceql'; import { filterToQuerySection, generateQueryFromAdHocFilters, getAllTags, getFilteredTags, + getIntrinsicTags, getTagsByScope, getUnscopedTags, } from './utils'; @@ -94,7 +96,7 @@ describe('gets correct tags', () => { it('for all tags', () => { const tags = getAllTags(v2Tags); - expect(tags).toEqual(['cluster', 'container', 'db', 'duration', 'kind', 'name', 'status']); + expect(tags).toEqual(uniq(['cluster', 'container', 'db', 'duration', 'kind', 'name', 'status'].concat(intrinsics))); }); it('for tags by resource scope', () => { @@ -106,6 +108,11 @@ describe('gets correct tags', () => { const tags = getTagsByScope(v2Tags, TraceqlSearchScope.Span); expect(tags).toEqual(['db']); }); + + it('for intrinsic tags', () => { + const tags = getIntrinsicTags(v2Tags); + expect(tags).toEqual(testIntrinsics); + }); }); describe('filterToQuerySection returns the correct query section for a filter', () => { @@ -179,7 +186,7 @@ describe('filterToQuerySection returns the correct query section for a filter', }); export const emptyTags = []; -export const testIntrinsics = ['duration', 'kind', 'name', 'status']; +export const testIntrinsics = uniq(['duration', 'kind', 'name', 'status'].concat(intrinsics)); export const v1Tags = ['bar', 'foo']; export const v2Tags = [ { diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts index e1a599e99b3..5b4313ce6f2 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts @@ -7,6 +7,7 @@ import { VariableFormatID } from '@grafana/schema'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; import { getEscapedSpanNames } from '../datasource'; import TempoLanguageProvider from '../language_provider'; +import { intrinsics } from '../traceql/traceql'; import { Scope } from '../types'; export const interpolateFilters = (filters: TraceqlFilter[], scopedVars?: ScopedVars) => { @@ -132,13 +133,16 @@ export const getUnscopedTags = (scopes: Scope[]) => { }; export const getIntrinsicTags = (scopes: Scope[]) => { - return uniq( - scopes - .map((scope: Scope) => - scope.name && scope.name === TraceqlSearchScope.Intrinsic && scope.tags ? scope.tags : [] - ) - .flat() - ); + let tags = scopes + .map((scope: Scope) => (scope.name && scope.name === TraceqlSearchScope.Intrinsic && scope.tags ? scope.tags : [])) + .flat(); + + // Add the default intrinsic tags to the list of tags. + // This is needed because the /api/v2/search/tags API + // may not always return all the default intrinsic tags + // but generally has the most up to date list. + tags = uniq(tags.concat(intrinsics)); + return tags; }; export const getAllTags = (scopes: Scope[]) => { diff --git a/public/app/plugins/datasource/tempo/language_provider.test.ts b/public/app/plugins/datasource/tempo/language_provider.test.ts index 9bd44f33bed..7aff5eb7b18 100644 --- a/public/app/plugins/datasource/tempo/language_provider.test.ts +++ b/public/app/plugins/datasource/tempo/language_provider.test.ts @@ -1,7 +1,10 @@ +import { uniq } from 'lodash'; + import { v1Tags, v2Tags } from './SearchTraceQLEditor/utils.test'; import { TraceqlSearchScope } from './dataquery.gen'; import { TempoDatasource } from './datasource'; import TempoLanguageProvider from './language_provider'; +import { intrinsics } from './traceql/traceql'; import { Scope } from './types'; describe('Language_provider', () => { @@ -15,7 +18,7 @@ describe('Language_provider', () => { it('for API v2 intrinsic tags', async () => { const lp = setup(undefined, v2Tags); const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Intrinsic); - expect(tags).toEqual(['duration', 'kind', 'name', 'status']); + expect(tags).toEqual(uniq(['duration', 'kind', 'name', 'status'].concat(intrinsics))); }); it('for API v2 resource tags', async () => { @@ -105,7 +108,9 @@ describe('Language_provider', () => { it('for API v2 tags', async () => { const lp = setup(undefined, v2Tags); const tags = lp.getAutocompleteTags(); - expect(tags).toEqual(['cluster', 'container', 'db', 'duration', 'kind', 'name', 'status']); + expect(tags).toEqual( + uniq(['cluster', 'container', 'db', 'duration', 'kind', 'name', 'status'].concat(intrinsics)) + ); }); }); From bbab62ce399c349e314148fd9603f15830a7ecd1 Mon Sep 17 00:00:00 2001 From: Steve Simpson Date: Tue, 11 Mar 2025 13:45:16 +0100 Subject: [PATCH 172/312] Alerting: Select remote write path dependent on metrics backend type. (#101891) The remote write path differs based on whether the data source is actually Prometheus, Mimir, Cortex, or an older version of Cortex. We do not want users to have to specify the path, so this change determines the path as best it can. It may be in the future we have to make this configurable per-datasource to cater for setups where it's impossible to determine the correct path. --- conf/defaults.ini | 3 - conf/sample.ini | 3 - .../fakes/fake_datasource_service.go | 11 ++- pkg/services/ngalert/ngalert.go | 8 +- .../ngalert/schedule/recording_rule_test.go | 11 ++- .../ngalert/writer/datasourcewriter.go | 85 +++++++++++++++-- .../ngalert/writer/datasourcewriter_test.go | 95 +++++++++++++++++-- pkg/services/ngalert/writer/testing.go | 12 +-- pkg/setting/setting_unified_alerting.go | 28 +++--- 9 files changed, 199 insertions(+), 57 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index a216bb0792a..ee703f76d2a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1527,9 +1527,6 @@ timeout = 10s # Only has effect if the grafanaManagedRecordRulesDatasources feature toggle is enabled. default_datasource_uid = -# Suffix to apply to the data source URL for remote write requests. -remote_write_path_suffix = /push - # Optional custom headers to include in recording rule write requests. [recording_rules.custom_headers] # exampleHeader = exampleValue diff --git a/conf/sample.ini b/conf/sample.ini index 21ef0ad88c6..207fa535410 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1509,9 +1509,6 @@ timeout = 30s # Only has effect if the grafanaManagedRecordRulesDatasources feature toggle is enabled. default_datasource_uid = -# Suffix to apply to the data source URL for remote write requests. -remote_write_path_suffix = /push - # Optional custom headers to include in recording rule write requests. [recording_rules.custom_headers] # exampleHeader = exampleValue diff --git a/pkg/services/datasources/fakes/fake_datasource_service.go b/pkg/services/datasources/fakes/fake_datasource_service.go index f117f7b4af8..43a71852c43 100644 --- a/pkg/services/datasources/fakes/fake_datasource_service.go +++ b/pkg/services/datasources/fakes/fake_datasource_service.go @@ -74,11 +74,12 @@ func (s *FakeDataSourceService) AddDataSource(ctx context.Context, cmd *datasour s.lastID = int64(len(s.DataSources) - 1) } dataSource := &datasources.DataSource{ - ID: s.lastID + 1, - Name: cmd.Name, - Type: cmd.Type, - UID: cmd.UID, - OrgID: cmd.OrgID, + ID: s.lastID + 1, + Name: cmd.Name, + Type: cmd.Type, + UID: cmd.UID, + OrgID: cmd.OrgID, + JsonData: cmd.JsonData, } s.DataSources = append(s.DataSources, dataSource) return dataSource, nil diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index 9f4a62656f6..690c58c291b 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -760,14 +760,12 @@ func createRecordingWriter(featureToggles featuremgmt.FeatureToggles, settings s if settings.Enabled { if featureToggles.IsEnabledGlobally(featuremgmt.FlagGrafanaManagedRecordingRulesDatasources) { cfg := writer.DatasourceWriterConfig{ - Timeout: settings.Timeout, - DefaultDatasourceUID: settings.DefaultDatasourceUID, - RemoteWritePathSuffix: settings.RemoteWritePathSuffix, + Timeout: settings.Timeout, + DefaultDatasourceUID: settings.DefaultDatasourceUID, } logger.Info("Setting up remote write using data sources", - "timeout", cfg.Timeout, "default_datasource_uid", cfg.DefaultDatasourceUID, - "remote_write_path_suffix", cfg.RemoteWritePathSuffix) + "timeout", cfg.Timeout, "default_datasource_uid", cfg.DefaultDatasourceUID) return writer.NewDatasourceWriter(cfg, datasourceService, httpClientProvider, clock, logger, m), nil } else { diff --git a/pkg/services/ngalert/schedule/recording_rule_test.go b/pkg/services/ngalert/schedule/recording_rule_test.go index 6979d8469b7..a3f2a90cbae 100644 --- a/pkg/services/ngalert/schedule/recording_rule_test.go +++ b/pkg/services/ngalert/schedule/recording_rule_test.go @@ -18,6 +18,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasources" @@ -574,15 +575,15 @@ func setupDatasourceWriter(t *testing.T, target *writer.TestRemoteWriteTarget, r dss := &dsfakes.FakeDataSourceService{} p1, _ := dss.AddDataSource(context.Background(), &datasources.AddDataSourceCommand{ - UID: dsUID, - Type: datasources.DS_PROMETHEUS, + UID: dsUID, + Type: datasources.DS_PROMETHEUS, + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Prometheus"}`)), }) p1.URL = target.DatasourceURL() cfg := writer.DatasourceWriterConfig{ - Timeout: time.Second * 5, - DefaultDatasourceUID: "", - RemoteWritePathSuffix: writer.RemoteWriteSuffix, + Timeout: time.Second * 5, + DefaultDatasourceUID: "", } return writer.NewDatasourceWriter(cfg, dss, provider, clock.NewMock(), diff --git a/pkg/services/ngalert/writer/datasourcewriter.go b/pkg/services/ngalert/writer/datasourcewriter.go index a69b157c80b..9be600ae157 100644 --- a/pkg/services/ngalert/writer/datasourcewriter.go +++ b/pkg/services/ngalert/writer/datasourcewriter.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" "net/url" + "path" + "strings" "time" "github.com/benbjohnson/clock" @@ -35,9 +37,6 @@ type DatasourceWriterConfig struct { // This exists to cater for upgrading from old versions of Grafana, where rule // definitions may not have a target data source specified. DefaultDatasourceUID string - - // RemoteWritePathSuffix is the path suffix for remote write, normally /push. - RemoteWritePathSuffix string } type DatasourceWriter struct { @@ -78,6 +77,73 @@ func (w *DatasourceWriter) decrypt(ds *datasources.DataSource) (map[string]strin return decryptedJsonData, err } +func getPrometheusType(ds *datasources.DataSource) string { + if ds.JsonData == nil { + return "" + } + jsonData := ds.JsonData.Get("prometheusType") + if jsonData == nil { + return "" + } + str, err := jsonData.String() + if err != nil { + return "" + } + return str +} + +func getRemoteWriteURL(ds *datasources.DataSource) (*url.URL, error) { + u, err := url.Parse(ds.URL) + if err != nil { + return nil, err + } + + if getPrometheusType(ds) == "Prometheus" { + return u.JoinPath("/api/v1/write"), nil + } + + // All other cases assume Mimir/Cortex, as these systems are much more likely to be + // used as a remote write target, where as Prometheus does not recommend it. + + // Mimir/Cortex are more complicated, as Grafana has to be configured with the + // base URL for where the Prometheus API is located, e.g. /api/prom or /prometheus. + // + // - For "legacy" routes, /push is located on the same level as /api/v1/query. + // + // For example: + // Grafana will be configured with /api/prom + // The query API is at /api/prom/api/v1/query + // The push API is at /api/prom/push + // + // - For "new" routes, /push is located at the Mimir root, not Prometheus root. + // + // For example: + // Grafana will be configured with e.g. /prometheus + // The query API is at /prometheus/api/v1/query + // But push API is at /push + // + // Unfortunately, the prefixes can also be configured, + + cleanPath := path.Clean(u.Path) + + // If the suffix is /api/prom, assume Mimir/Cortex with legacy routes. + if strings.HasSuffix(cleanPath, "/api/prom") { + u.Path = path.Join(u.Path, "/push") + return u, nil + } + + // If the suffix is /prometheus, assume Mimir/Cortex with new routes. + if strings.HasSuffix(cleanPath, "/prometheus") { + u.Path = path.Join(path.Dir(u.Path), "/api/v1/push") + return u, nil + } + + // The user has configured an unknown prefix, so fall back to taking + // the host as the Mimir root. This is less than ideal. + u.Path = "/api/v1/push" + return u, nil +} + func (w *DatasourceWriter) makeWriter(ctx context.Context, orgID int64, dsUID string) (*PrometheusWriter, error) { ds, err := w.datasources.GetDataSource(ctx, &datasources.GetDataSourceQuery{ UID: dsUID, @@ -101,13 +167,11 @@ func (w *DatasourceWriter) makeWriter(ctx context.Context, orgID int64, dsUID st return nil, err } - u, err := url.Parse(is.URL) + u, err := getRemoteWriteURL(ds) if err != nil { return nil, err } - u = u.JoinPath(w.cfg.RemoteWritePathSuffix) - cfg := PrometheusWriterConfig{ URL: u.String(), HTTPOptions: httpclient.Options{ @@ -121,6 +185,15 @@ func (w *DatasourceWriter) makeWriter(ctx context.Context, orgID int64, dsUID st return nil, err } + w.l.Debug("Created Prometheus remote writer", + "datasource_uid", dsUID, + "type", ds.Type, + "prometheusType", getPrometheusType(ds), + "url", cfg.URL, + "tls", cfg.HTTPOptions.TLS != nil, + "basic_auth", cfg.HTTPOptions.BasicAuth != nil, + "timeout", cfg.Timeout) + return NewPrometheusWriter( cfg, w.httpClientProvider, diff --git a/pkg/services/ngalert/writer/datasourcewriter_test.go b/pkg/services/ngalert/writer/datasourcewriter_test.go index 6727d352997..165f735d10d 100644 --- a/pkg/services/ngalert/writer/datasourcewriter_test.go +++ b/pkg/services/ngalert/writer/datasourcewriter_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasources" @@ -43,16 +44,20 @@ func setupDataSources(t *testing.T) *testDataSources { }) p1, _ := res.AddDataSource(context.Background(), &datasources.AddDataSourceCommand{ - UID: "prom-1", - Type: datasources.DS_PROMETHEUS, + UID: "prom-1", + Type: datasources.DS_PROMETHEUS, + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Prometheus"}`)), }) - p1.URL = res.prom1.srv.URL + "/api/v1" + p1.URL = res.prom1.srv.URL + res.prom1.ExpectedPath = "/api/v1/write" p2, _ := res.AddDataSource(context.Background(), &datasources.AddDataSourceCommand{ - UID: "prom-2", - Type: datasources.DS_PROMETHEUS, + UID: "prom-2", + Type: datasources.DS_PROMETHEUS, + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Mimir"}`)), }) - p2.URL = res.prom2.srv.URL + "/api/v1" + p2.URL = res.prom2.srv.URL + "/api/prom" + res.prom2.ExpectedPath = "/api/prom/push" // Add a non-Prometheus datasource. _, _ = res.AddDataSource(context.Background(), &datasources.AddDataSourceCommand{ @@ -70,9 +75,8 @@ func TestDatasourceWriter(t *testing.T) { datasources := setupDataSources(t) cfg := DatasourceWriterConfig{ - Timeout: time.Second * 5, - DefaultDatasourceUID: "prom-2", - RemoteWritePathSuffix: "/write", + Timeout: time.Second * 5, + DefaultDatasourceUID: "prom-2", } met := metrics.NewRemoteWriterMetrics(prometheus.NewRegistry()) @@ -117,3 +121,76 @@ func TestDatasourceWriter(t *testing.T) { require.NoError(t, err) }) } + +func TestDatasourceWriterGetRemoteWriteURL(t *testing.T) { + tc := []struct { + name string + ds datasources.DataSource + url string + }{ + { + "prometheus", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Prometheus"}`)), + URL: "http://example.com", + }, + "http://example.com/api/v1/write", + }, + { + "prometheus with prefix", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Prometheus"}`)), + URL: "http://example.com/myprom", + }, + "http://example.com/myprom/api/v1/write", + }, + { + "mimir/cortex legacy routes", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Anything"}`)), + URL: "http://example.com/api/prom", + }, + "http://example.com/api/prom/push", + }, + { + "mimir/cortex legacy routes with prefix", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Anything"}`)), + URL: "http://example.com/myprom/api/prom", + }, + "http://example.com/myprom/api/prom/push", + }, + { + "mimir/cortex new routes", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Anything"}`)), + URL: "http://example.com/prometheus", + }, + "http://example.com/api/v1/push", + }, + { + "mimir/cortex new routes with prefix", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Anything"}`)), + URL: "http://example.com/mymimir/prometheus", + }, + "http://example.com/mymimir/api/v1/push", + }, + { + "mimir/cortex with unknown suffix", + datasources.DataSource{ + JsonData: simplejson.MustJson([]byte(`{"prometheusType":"Anything"}`)), + URL: "http://example.com/foo/bar", + }, + "http://example.com/api/v1/push", + }, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + res, err := getRemoteWriteURL(&tt.ds) + require.NoError(t, err) + require.Equal(t, tt.url, res.String()) + }) + } +} diff --git a/pkg/services/ngalert/writer/testing.go b/pkg/services/ngalert/writer/testing.go index 1996e712247..2100c9cfc7a 100644 --- a/pkg/services/ngalert/writer/testing.go +++ b/pkg/services/ngalert/writer/testing.go @@ -12,10 +12,7 @@ import ( "github.com/stretchr/testify/require" ) -const RemoteWritePrefix = "/api/v1" -const RemoteWriteSuffix = "/write" - -const RemoteWriteEndpoint = RemoteWritePrefix + RemoteWriteSuffix +const RemoteWriteEndpoint = "/api/v1/write" type TestRemoteWriteTarget struct { srv *httptest.Server @@ -23,6 +20,8 @@ type TestRemoteWriteTarget struct { mtx sync.Mutex RequestsCount int LastRequestBody string + + ExpectedPath string } func NewTestRemoteWriteTarget(t *testing.T) *TestRemoteWriteTarget { @@ -31,10 +30,11 @@ func NewTestRemoteWriteTarget(t *testing.T) *TestRemoteWriteTarget { target := &TestRemoteWriteTarget{ RequestsCount: 0, LastRequestBody: "", + ExpectedPath: RemoteWriteEndpoint, } handler := func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != RemoteWriteEndpoint { + if r.URL.Path != target.ExpectedPath { require.Fail(t, "Received unexpected request for endpoint %s", r.URL.Path) } @@ -63,7 +63,7 @@ func (s *TestRemoteWriteTarget) Close() { } func (s *TestRemoteWriteTarget) DatasourceURL() string { - return s.srv.URL + RemoteWritePrefix + return s.srv.URL } func (s *TestRemoteWriteTarget) ClientSettings() setting.RecordingRuleSettings { diff --git a/pkg/setting/setting_unified_alerting.go b/pkg/setting/setting_unified_alerting.go index bf359c34855..33f002cb571 100644 --- a/pkg/setting/setting_unified_alerting.go +++ b/pkg/setting/setting_unified_alerting.go @@ -132,14 +132,13 @@ type UnifiedAlertingSettings struct { } type RecordingRuleSettings struct { - Enabled bool - URL string - BasicAuthUsername string - BasicAuthPassword string - CustomHeaders map[string]string - Timeout time.Duration - DefaultDatasourceUID string - RemoteWritePathSuffix string + Enabled bool + URL string + BasicAuthUsername string + BasicAuthPassword string + CustomHeaders map[string]string + Timeout time.Duration + DefaultDatasourceUID string } // RemoteAlertmanagerSettings contains the configuration needed @@ -437,13 +436,12 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error { rr := iniFile.Section("recording_rules") uaCfgRecordingRules := RecordingRuleSettings{ - Enabled: rr.Key("enabled").MustBool(false), - URL: rr.Key("url").MustString(""), - BasicAuthUsername: rr.Key("basic_auth_username").MustString(""), - BasicAuthPassword: rr.Key("basic_auth_password").MustString(""), - Timeout: rr.Key("timeout").MustDuration(defaultRecordingRequestTimeout), - DefaultDatasourceUID: rr.Key("default_datasource_uid").MustString(""), - RemoteWritePathSuffix: rr.Key("remote_write_path_suffix").MustString("/push"), + Enabled: rr.Key("enabled").MustBool(false), + URL: rr.Key("url").MustString(""), + BasicAuthUsername: rr.Key("basic_auth_username").MustString(""), + BasicAuthPassword: rr.Key("basic_auth_password").MustString(""), + Timeout: rr.Key("timeout").MustDuration(defaultRecordingRequestTimeout), + DefaultDatasourceUID: rr.Key("default_datasource_uid").MustString(""), } rrHeaders := iniFile.Section("recording_rules.custom_headers") From 2712686a368a18ec788e601d38c9235ac6e658bd Mon Sep 17 00:00:00 2001 From: Alex Bikfalvi Date: Tue, 11 Mar 2025 13:45:26 +0100 Subject: [PATCH 173/312] feat(datasource/Tempo): Instrument Tempo query latency measurements (#101285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Instrument Tempo query latency measurements Add comprehensive latency tracking and reporting for Tempo queries using reportInteraction: - Add latency measurements for TraceQL metrics queries - Add latency measurements for TraceID queries - Add latency measurements for TraceQL search queries - Track both streaming and non-streaming query performance - Include success/error states and relevant metadata in reports - Measure latency in milliseconds for more precise tracking This instrumentation will help monitor query performance and identify potential bottlenecks in trace queries. Signed-off-by: Alex Bikfalvi * fixup! feat: Instrument Tempo query latency measurements Signed-off-by: Alex Bikfalvi * prettier fix --------- Signed-off-by: Alex Bikfalvi Co-authored-by: André Pereira --- .../plugins/datasource/tempo/datasource.ts | 216 +++++++++++++++++- 1 file changed, 210 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index ee8727ce74f..de07cc54e45 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -1,6 +1,6 @@ import { groupBy } from 'lodash'; import { EMPTY, forkJoin, from, lastValueFrom, merge, Observable, of } from 'rxjs'; -import { catchError, concatMap, map, mergeMap, toArray } from 'rxjs/operators'; +import { catchError, concatMap, finalize, map, mergeMap, toArray } from 'rxjs/operators'; import semver from 'semver'; import { @@ -97,6 +97,16 @@ interface ServiceMapQueryResponseWithRates { edges: DataFrame; } +interface TempoQueryMetrics { + success: boolean; + streaming?: boolean; + latencyMs: number; + query?: string; + error?: string; + statusCode?: number; + statusText?: string; +} + export class TempoDatasource extends DataSourceWithBackend { tracesToLogs?: TraceToLogsOptions; serviceMap?: { @@ -363,8 +373,7 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_traceql_response', options, { + success: true, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: queryFromFilters ?? '', + }); return { data: formatTraceQLResponse( response.data.traces, @@ -442,6 +458,15 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_traceql_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: queryFromFilters ?? '', + error: getErrorMessage(err.message), + statusCode: err.status, + statusText: err.statusText, + }); return of({ error: { message: getErrorMessage(err.data.message) }, data: [] }); }) ) @@ -569,7 +594,11 @@ export class TempoDatasource extends DataSourceWithBackend, targets: TempoQuery[]): Observable { + handleTraceIdQuery( + options: DataQueryRequest, + targets: TempoQuery[], + query: string + ): Observable { const validTargets = targets .filter((t) => t.query) .map((t): TempoQuery => ({ ...t, query: t.query?.trim(), queryType: 'traceId' })); @@ -577,13 +606,41 @@ export class TempoDatasource extends DataSourceWithBackend { if (response.error) { + reportTempoQueryMetrics('grafana_traces_traceID_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(response.error.message), + statusCode: response.error.status, + statusText: response.error.statusText, + }); return response; } + reportTempoQueryMetrics('grafana_traces_traceID_response', options, { + success: true, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + }); return transformTrace(response, this.instanceSettings, this.nodeGraph?.enabled); + }), + catchError((error) => { + reportTempoQueryMetrics('grafana_traces_traceID_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(error.message), + statusCode: error.status, + statusText: error.statusText, + }); + throw error; }) ); } @@ -595,6 +652,7 @@ export class TempoDatasource extends DataSourceWithBackend => { + const startTime = performance.now(); if (this.isStreamingSearchEnabled()) { return this.handleStreamingQuery(options, targets.traceql, queryValue); } else { @@ -606,11 +664,26 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_traceql_response', options, { + success: true, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: queryValue ?? '', + }); return { data: formatTraceQLResponse(response.data.traces, this.instanceSettings, targets.traceql[0].tableType), }; }), catchError((err) => { + reportTempoQueryMetrics('grafana_traces_traceql_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: queryValue ?? '', + error: getErrorMessage(err.message), + statusCode: err.status, + statusText: err.statusText, + }); return of({ error: { message: getErrorMessage(err.data.message) }, data: [] }); }) ); @@ -619,7 +692,8 @@ export class TempoDatasource extends DataSourceWithBackend, - targets: TempoQuery[] + targets: TempoQuery[], + query: string ): Observable { const validTargets = targets .filter((t) => t.query) @@ -630,12 +704,28 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_traceql_metrics_response', options, { + success: true, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + }); return enhanceTraceQlMetricsResponse(response, this.instanceSettings); }), catchError((err) => { + reportTempoQueryMetrics('grafana_traces_traceql_metrics_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(err.data.message), + statusCode: err.status, + statusText: err.statusText, + }); return of({ error: { message: getErrorMessage(err.data.message) }, data: [] }); }) ); @@ -659,6 +749,7 @@ export class TempoDatasource extends DataSourceWithBackend { if (!response.data.summaries) { + reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(`No summary data for '${groupBy}'.`), + }); return { error: { message: getErrorMessage(`No summary data for '${groupBy}'.`), @@ -678,6 +776,13 @@ export class TempoDatasource extends DataSourceWithBackend summary.series.length > 0); if (!hasSeries) { + reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(`No series data. Ensure you are using an up to date version of Tempo`), + }); return { error: { message: getErrorMessage(`No series data. Ensure you are using an up to date version of Tempo`), @@ -685,11 +790,26 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { + success: false, + streaming: false, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(error.data.message), + statusCode: error.status, + statusText: error.statusText, + }); return of({ error: { message: getErrorMessage(error.data.message) }, data: emptyResponse, @@ -709,6 +829,7 @@ export class TempoDatasource extends DataSourceWithBackend doTempoSearchStreaming( @@ -718,6 +839,28 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_traceql_response', options, { + success: false, + streaming: true, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(error.data.message), + statusCode: error.status, + statusText: error.statusText, + }); + // Re-throw the error to maintain the error chain + throw error; + }), + finalize(() => { + reportTempoQueryMetrics('grafana_traces_traceql_response', options, { + success: true, + streaming: true, + query: query ?? '', + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + }); + }) ); } @@ -732,6 +875,7 @@ export class TempoDatasource extends DataSourceWithBackend doTempoMetricsStreaming( @@ -740,6 +884,28 @@ export class TempoDatasource extends DataSourceWithBackend { + reportTempoQueryMetrics('grafana_traces_traceql_metrics_response', options, { + success: false, + streaming: true, + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + query: query ?? '', + error: getErrorMessage(error.data.message), + statusCode: error.status, + statusText: error.statusText, + }); + // Re-throw the error to maintain the error chain + throw error; + }), + finalize(() => { + reportTempoQueryMetrics('grafana_traces_traceql_metrics_response', options, { + success: true, + streaming: true, + query: query ?? '', + latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond + }); + }) ); } @@ -1442,6 +1608,44 @@ function getServiceGraphViewDataFrames( return df; } +/** + * Reports metrics for Tempo query interactions. + * + * @param options - The data query request options containing app and other context + * @param metrics - Object containing metrics to report: + * - success: Whether the query was successful + * - streaming: (optional) Whether streaming was used + * - latencyMs: Query execution time in milliseconds + * - query: (optional) The query string that was executed + * - error: (optional) Error message if query failed + * - statusCode: (optional) HTTP status code if query failed + * - statusText: (optional) HTTP status text if query failed + * @param interactionName - (optional) Name of the interaction to report. + * Defaults to 'grafana_traces_traceql_response' + * + * @example + * ```typescript + * reportTempoQueryMetrics(options, { + * success: true, + * streaming: true, + * latencyMs: Math.round(performance.now() - startTime), + * query: 'my query' + * }); + * ``` + */ +function reportTempoQueryMetrics( + interactionName: string, + options: DataQueryRequest, + metrics: TempoQueryMetrics +) { + reportInteraction(interactionName, { + datasourceType: 'tempo', + app: options.app ?? '', + grafana_version: config.buildInfo.version, + ...metrics, + }); +} + export function buildExpr( metric: { expr: string; params: string[]; topk?: number }, extraParams: string, From 0519cfa66d6697aa9cc12a0063a01aefb9918423 Mon Sep 17 00:00:00 2001 From: Ed Poole Date: Tue, 11 Mar 2025 13:09:08 +0000 Subject: [PATCH 174/312] Fix/theme gradients (#101934) * Brighten the DesertBloom gradient * Adjust gradient values so they're consistently rgba --- .../grafana-data/src/themes/themeDefinitions/desertbloom.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts b/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts index 0d8ce37e045..8c08ca75da1 100644 --- a/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts +++ b/packages/grafana-data/src/themes/themeDefinitions/desertbloom.ts @@ -61,9 +61,10 @@ const desertBloomTheme: NewThemeOptions = { disabledBackground: 'rgba(168, 156, 134, 0.06)', disabledOpacity: 0.38, }, + gradients: { - brandHorizontal: 'linear-gradient(270deg, #FF6F61 0%, #ece0d1 100%)', - brandVertical: 'linear-gradient(0.01deg, #FF6F61 0.01%, #ece0d1 99.99%)', + brandHorizontal: 'linear-gradient(270deg,rgba(255, 111, 97, 1) 0%, rgba(255, 167, 58, 1) 100%)', + brandVertical: 'linear-gradient(0deg, rgba(255, 111, 97, 1) 0%, rgba(255, 167, 58, 1) 100%)', }, contrastThreshold: 3, hoverFactor: 0.03, From 6b2c73141df74430eb72f1e6b6c166f5f896e715 Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Tue, 11 Mar 2025 13:13:00 +0000 Subject: [PATCH 175/312] Alerting: Improve clarity of recording rule creation (#100700) * Add description below group and namespace fields to make creation clearer * Make DS managed recording rules clearer * Change link for recording rule on empty state to Grafana managed * Tweak empty state * Tidy up logic for display of recording rule buttons * Update .betterer.results --- .betterer.results | 6 +- .../rule-editor/GroupAndNamespaceFields.tsx | 15 +++- .../unified/components/rules/CloudRules.tsx | 5 +- .../unified/components/rules/NoRulesCTA.tsx | 69 ++++++++++++++++--- public/locales/en-US/grafana.json | 2 + 5 files changed, 82 insertions(+), 15 deletions(-) diff --git a/.betterer.results b/.betterer.results index b03dac144aa..4df170e7dc1 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2387,10 +2387,8 @@ exports[`better eslint`] = { ], "public/app/features/alerting/unified/components/rules/CloudRules.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], "public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], diff --git a/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx b/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx index dee970cf598..6bbac094cbb 100644 --- a/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx @@ -45,6 +45,10 @@ export const GroupAndNamespaceFields = ({ rulesSourceName }: Props) => { @@ -71,7 +75,16 @@ export const GroupAndNamespaceFields = ({ rulesSourceName }: Props) => { }} /> - + ( - New recording rule + + New data source-managed recording rule + ); } diff --git a/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx b/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx index 38a4da4ce30..f33db5d9962 100644 --- a/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx +++ b/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx @@ -1,8 +1,65 @@ -import { EmptyState, LinkButton, Stack, TextLink } from '@grafana/ui'; -import { Trans } from 'app/core/internationalization'; +import { config } from '@grafana/runtime'; +import { Dropdown, EmptyState, LinkButton, Menu, MenuItem, Stack, TextLink } from '@grafana/ui'; +import { Trans, t } from 'app/core/internationalization'; import { useRulesAccess } from '../../utils/accessControlHooks'; +const RecordingRulesButtons = () => { + const { canCreateGrafanaRules, canCreateCloudRules } = useRulesAccess(); + const grafanaRecordingRulesEnabled = config.featureToggles.grafanaManagedRecordingRules; + const canCreateAll = canCreateGrafanaRules && canCreateCloudRules && grafanaRecordingRulesEnabled; + + // User can create Grafana and DS-managed recording rules, show a dropdown + if (canCreateAll) { + return ( + + + + + } + > + + New recording rule + + + ); + } + + // ...Otherwise, just show the buttons for each type of recording rule + // (this will just be one or the other) + return ( + <> + {canCreateGrafanaRules && grafanaRecordingRulesEnabled && ( + + + New Grafana-managed recording rule + + + )} + {canCreateCloudRules && ( + + + New data source-managed recording rule + + + )} + + ); +}; + export const NoRulesSplash = () => { const { canCreateGrafanaRules, canCreateCloudRules } = useRulesAccess(); const canCreateAnything = canCreateGrafanaRules || canCreateCloudRules; @@ -14,17 +71,13 @@ export const NoRulesSplash = () => { variant="call-to-action" button={ canCreateAnything ? ( - + {canCreateAnything && ( New alert rule )} - {canCreateCloudRules && ( - - New recording rule - - )} + ) : null } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index ee429b59821..2deedc01267 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "New alert rule", + "new-ds-managed-recording-rule": "New data source-managed recording rule", + "new-grafana-recording-rule": "New Grafana-managed recording rule", "new-recording-rule": "New recording rule", "provisioning": "You can also define rules through file provisioning or Terraform. <2>Learn more" }, From c74a5fcbedba9d0c4717ce0348065da3cb487d4f Mon Sep 17 00:00:00 2001 From: Will Browne Date: Tue, 11 Mar 2025 14:24:20 +0000 Subject: [PATCH 176/312] Chore: Avoid simplejson usage in `xorm` module (#101943) avoid simplejson usage --- pkg/util/xorm/go.mod | 2 -- pkg/util/xorm/go.sum | 4 ---- pkg/util/xorm/xorm_test.go | 7 +++---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/pkg/util/xorm/go.mod b/pkg/util/xorm/go.mod index 310a1bbc263..fb55683da79 100644 --- a/pkg/util/xorm/go.mod +++ b/pkg/util/xorm/go.mod @@ -5,7 +5,6 @@ go 1.23.7 require ( cloud.google.com/go/spanner v1.75.0 github.com/googleapis/go-sql-spanner v1.11.1 - github.com/grafana/grafana v5.4.5+incompatible github.com/mattn/go-sqlite3 v1.14.22 github.com/stretchr/testify v1.10.0 xorm.io/builder v0.3.6 @@ -23,7 +22,6 @@ require ( cloud.google.com/go/monitoring v1.23.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/pkg/util/xorm/go.sum b/pkg/util/xorm/go.sum index 7febbb40654..f93449ff49b 100644 --- a/pkg/util/xorm/go.sum +++ b/pkg/util/xorm/go.sum @@ -632,8 +632,6 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -834,8 +832,6 @@ github.com/googleapis/go-sql-spanner v1.11.1 h1:z3ThtKV5HFvaNv9UGc26+ggS+lS0dsCA github.com/googleapis/go-sql-spanner v1.11.1/go.mod h1:fuA5q4yMS3SZiVfRr5bvksPNk7zUn/irbQW62H/ffZw= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/grafana/grafana v5.4.5+incompatible h1:xNuhSBxLgwDwesuQIAhQu1QCk6tD0TAghKHE36/hxrs= -github.com/grafana/grafana v5.4.5+incompatible/go.mod h1:U8QyUclJHj254BFcuw45p6sg7eeGYX44qn1ShYo5rGE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= diff --git a/pkg/util/xorm/xorm_test.go b/pkg/util/xorm/xorm_test.go index 4dee7eb8294..8b8d5aeb03b 100644 --- a/pkg/util/xorm/xorm_test.go +++ b/pkg/util/xorm/xorm_test.go @@ -1,12 +1,11 @@ package xorm import ( + "encoding/json" "testing" _ "github.com/mattn/go-sqlite3" "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/components/simplejson" ) func TestBasicOperationsWithSqlite(t *testing.T) { @@ -38,7 +37,7 @@ func testBasicOperations(t *testing.T, eng *Engine) { require.NoError(t, err) require.NotZero(t, obj.Id) - obj.Json = simplejson.MustJson([]byte(`{"test": "test", "key": null}`)) + obj.Json = json.RawMessage(`{"test": "test", "key": null}`) _, err = sess.Update(obj) require.NoError(t, err) }) @@ -47,5 +46,5 @@ func testBasicOperations(t *testing.T, eng *Engine) { type TestStruct struct { Id int64 Comment string - Json *simplejson.Json + Json json.RawMessage } From d9cb6e632dfb9c36e9e72ce65959f260a60c69d5 Mon Sep 17 00:00:00 2001 From: Matthew Thorning Date: Tue, 11 Mar 2025 14:31:43 +0000 Subject: [PATCH 177/312] Navigation: Add the `IsNew` badge to the IRM menu item (#101926) add the `IsNew` badge to the IRM menu item --- pkg/services/navtree/navtreeimpl/applinks.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/services/navtree/navtreeimpl/applinks.go b/pkg/services/navtree/navtreeimpl/applinks.go index 4c71eb150ed..c3a385fd67d 100644 --- a/pkg/services/navtree/navtreeimpl/applinks.go +++ b/pkg/services/navtree/navtreeimpl/applinks.go @@ -239,6 +239,9 @@ func (s *ServiceImpl) addPluginToSection(c *contextmodel.ReqContext, treeRoot *n alertsAndIncidentsChildren = append(alertsAndIncidentsChildren, alertingNode) treeRoot.RemoveSection(alertingNode) } + if appLink.Id == "plugin-page-grafana-irm-app" { + appLink.IsNew = true + } alertsAndIncidentsChildren = append(alertsAndIncidentsChildren, appLink) treeRoot.AddSection(&navtree.NavLink{ Text: "Alerts & IRM", From 82610288b1170c0c7565c8e4b9134b8f6593c1eb Mon Sep 17 00:00:00 2001 From: Yulia Shanyrova Date: Tue, 11 Mar 2025 15:51:25 +0100 Subject: [PATCH 178/312] Plugins: Move raiseanissueurl from plugin object to plugin details (#101428) * move raiseanissueurl from plugin object to plugin details * updated the test for PluginDetailsPane; --- public/app/features/plugins/admin/api.ts | 1 + .../components/PluginDetailsPanel.test.tsx | 32 +++++++++++++++++++ .../admin/components/PluginDetailsPanel.tsx | 10 ++++-- public/app/features/plugins/admin/helpers.ts | 5 --- public/app/features/plugins/admin/types.ts | 3 +- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/public/app/features/plugins/admin/api.ts b/public/app/features/plugins/admin/api.ts index f79968d47ae..4520989603f 100644 --- a/public/app/features/plugins/admin/api.ts +++ b/public/app/features/plugins/admin/api.ts @@ -39,6 +39,7 @@ export async function getPluginDetails(id: string): Promise=9.0.0', statusContext: 'stable', @@ -118,4 +134,20 @@ describe('PluginDetailsPanel', () => { const panel = screen.getByTestId('plugin-details-panel'); expect(panel).toHaveStyle({ width: '300px' }); }); + + it('should render license, documentation, repository, raise issue links', () => { + render(); + const repositoryLink = screen.getByText('Repository'); + const licenseLink = screen.getByText('License'); + const documentationLink = screen.getByText('Documentation'); + const raiseIssueLink = screen.getByText('Raise issue'); + expect(repositoryLink).toBeInTheDocument(); + expect(repositoryLink).toHaveAttribute('href', 'https://github.com/grafana/test-plugin'); + expect(licenseLink).toBeInTheDocument(); + expect(licenseLink).toHaveAttribute('href', 'https://github.com/grafana/test-plugin/blob/main/LICENSE'); + expect(documentationLink).toBeInTheDocument(); + expect(documentationLink).toHaveAttribute('href', 'https://test-plugin.com/docs'); + expect(raiseIssueLink).toBeInTheDocument(); + expect(raiseIssueLink).toHaveAttribute('href', 'https://github.com/grafana/test-plugin/issues/new'); + }); }); diff --git a/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx b/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx index 7d7612dc814..8440b37c76d 100644 --- a/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx +++ b/public/app/features/plugins/admin/components/PluginDetailsPanel.tsx @@ -99,8 +99,14 @@ export function PluginDetailsPanel(props: Props): React.ReactElement | null { Repository )} - {plugin.raiseAnIssueUrl && ( - + {plugin.details?.raiseAnIssueUrl && ( + Raise an issue )} diff --git a/public/app/features/plugins/admin/helpers.ts b/public/app/features/plugins/admin/helpers.ts index 28386a73cbd..8b0bfe80cfd 100644 --- a/public/app/features/plugins/admin/helpers.ts +++ b/public/app/features/plugins/admin/helpers.ts @@ -122,7 +122,6 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C versionSignatureType, versionSignedByOrgName, url, - raiseAnIssueUrl, } = plugin; const isDisabled = !!error || isDisabledSecretsPlugin(typeCode); @@ -161,7 +160,6 @@ export function mapRemoteToCatalog(plugin: RemotePlugin, error?: PluginError): C isFullyInstalled: isDisabled, latestVersion: plugin.version, url, - raiseAnIssueUrl, }; } @@ -178,7 +176,6 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat hasUpdate, accessControl, angularDetected, - raiseAnIssueUrl, } = plugin; const isDisabled = !!error || isDisabledSecretsPlugin(type); @@ -213,7 +210,6 @@ export function mapLocalToCatalog(plugin: LocalPlugin, error?: PluginError): Cat isFullyInstalled: true, iam: plugin.iam, latestVersion: plugin.latestVersion, - raiseAnIssueUrl, }; } @@ -278,7 +274,6 @@ export function mapToCatalogPlugin(local?: LocalPlugin, remote?: RemotePlugin, e iam: local?.iam, latestVersion: local?.latestVersion || remote?.version || '', url: remote?.url || '', - raiseAnIssueUrl: remote?.raiseAnIssueUrl || local?.raiseAnIssueUrl, }; } diff --git a/public/app/features/plugins/admin/types.ts b/public/app/features/plugins/admin/types.ts index 96e1d2451d2..4476c824216 100644 --- a/public/app/features/plugins/admin/types.ts +++ b/public/app/features/plugins/admin/types.ts @@ -65,7 +65,6 @@ export interface CatalogPlugin extends WithAccessControlMetadata { iam?: IdentityAccessManagement; isProvisioned?: boolean; url?: string; - raiseAnIssueUrl?: string; } export interface CatalogPluginDetails { @@ -83,6 +82,7 @@ export interface CatalogPluginDetails { lastCommitDate?: string; licenseUrl?: string; documentationUrl?: string; + raiseAnIssueUrl?: string; signatureType?: PluginSignatureType; signature?: PluginSignatureStatus; } @@ -197,7 +197,6 @@ export type LocalPlugin = WithAccessControlMetadata & { dependencies: PluginDependencies; angularDetected: boolean; iam?: IdentityAccessManagement; - raiseAnIssueUrl?: string; }; interface IdentityAccessManagement { From c8c17683ed9fdca0270086504db3258edd1b2998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Fern=C3=A1ndez?= Date: Tue, 11 Mar 2025 15:55:30 +0100 Subject: [PATCH 179/312] ThemeDemo: Use `Combobox` instead of `Select` (#101947) --- packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx b/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx index ba7146bcf23..0e6315899df 100644 --- a/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx +++ b/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx @@ -9,6 +9,7 @@ import { useTheme2 } from '../../themes/ThemeContext'; import { allButtonVariants, Button } from '../Button'; import { Card } from '../Card/Card'; import { CollapsableSection } from '../Collapse/CollapsableSection'; +import { Combobox } from '../Combobox/Combobox'; import { Field } from '../Forms/Field'; import { InlineField } from '../Forms/InlineField'; import { InlineFieldRow } from '../Forms/InlineFieldRow'; @@ -17,7 +18,6 @@ import { Icon } from '../Icon/Icon'; import { Input } from '../Input/Input'; import { BackgroundColor, BorderColor, Box, BoxShadow } from '../Layout/Box/Box'; import { Stack } from '../Layout/Stack/Stack'; -import { Select } from '../Select/Select'; import { Switch } from '../Switch/Switch'; import { Text, TextProps } from '../Text/Text'; @@ -150,8 +150,8 @@ export const ThemeDemo = () => { - - {}} /> + {}} /> ); From 5bfe046da957d32cfd57f0dd50d27c7eb28befef Mon Sep 17 00:00:00 2001 From: Pepe Cano <825430+ppcano@users.noreply.github.com> Date: Tue, 11 Mar 2025 15:58:25 +0100 Subject: [PATCH 182/312] docs(alerting): clarify behaviour when provisioning the policy tree (#101937) --- .../export-alerting-resources/index.md | 6 +----- .../file-provisioning/index.md | 6 +----- .../terraform-provisioning/index.md | 6 +----- docs/sources/shared/alerts/alerting_provisioning.md | 2 ++ docs/sources/shared/alerts/warning-provisioning-tree.md | 9 +++++++++ 5 files changed, 14 insertions(+), 15 deletions(-) create mode 100644 docs/sources/shared/alerts/warning-provisioning-tree.md diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md index 3a6f3576452..a7fac56ebe9 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md @@ -197,11 +197,7 @@ However, you can export it by manually copying the content and name of the notif All notification policies are provisioned through a single resource: the root of the notification policy tree. -{{% admonition type="warning" %}} - -Since the policy tree is a single resource, provisioning it overwrites a policy tree created through any other means. - -{{< /admonition >}} +{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="" >}} To export the notification policy tree from the Grafana UI, complete the following steps. diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md index 850600cc074..f7cb4a0e174 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md @@ -702,11 +702,7 @@ Create or reset the notification policy tree using provisioning files in your Gr In Grafana, the entire notification policy tree is considered a single, large resource. Add new specific policies as sub-policies under the root policy. Since specific policies may depend on each other, you cannot provision subsets of the policy tree; the entire tree must be defined in a single place. -{{% admonition type="warning" %}} - -Since the policy tree is a single resource, provisioning it will overwrite a policy tree created through any other means. - -{{< /admonition >}} +{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="" >}} 1. Find the notification policy tree in Grafana. 1. [Export](ref:export_policies) and download a provisioning file for your notification policy tree. diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md index 0821f5ec0b4..2a5f5d97211 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md @@ -341,11 +341,7 @@ In this section, we'll create Terraform configurations for each alerting resourc [Notification policies](ref:notification-policy) defines how to route alert instances to your contact points. -{{% admonition type="warning" %}} - -Since the policy tree is a single resource, provisioning the `grafana_notification_policy` resource will overwrite a policy tree created through any other means. - -{{< /admonition >}} +{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="" >}} 1. Find the default notification policy tree. Alternatively, consider writing the resource in code as demonstrated in the example below. diff --git a/docs/sources/shared/alerts/alerting_provisioning.md b/docs/sources/shared/alerts/alerting_provisioning.md index 134bd086f97..40d3c72622d 100644 --- a/docs/sources/shared/alerts/alerting_provisioning.md +++ b/docs/sources/shared/alerts/alerting_provisioning.md @@ -1386,6 +1386,8 @@ Status: Conflict ### Sets the notification policy tree. (_RoutePutPolicyTree_) +{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="" >}} + ``` PUT /api/v1/provisioning/policies ``` diff --git a/docs/sources/shared/alerts/warning-provisioning-tree.md b/docs/sources/shared/alerts/warning-provisioning-tree.md new file mode 100644 index 00000000000..36ecf9b0fa6 --- /dev/null +++ b/docs/sources/shared/alerts/warning-provisioning-tree.md @@ -0,0 +1,9 @@ +--- +title: 'Warning Provisioning Tree' +--- + +{{% admonition type="warning" %}} + +Since the policy tree is a single resource, provisioning it will overwrite all policies in the notification policy tree. However, it does not affect internal policies created when alert rules directly select a contact point. + +{{< /admonition >}} From f6f6ae449615cfb866aed4afe53dc4a0cde83f25 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 11 Mar 2025 16:27:17 +0100 Subject: [PATCH 183/312] Zanzana: Update docs with subresources description (#101948) * Zanzana: Update docs with subresources description * clarify resource name --- pkg/services/authz/zanzana/schema/README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pkg/services/authz/zanzana/schema/README.md b/pkg/services/authz/zanzana/schema/README.md index 4462e0f18e0..3a41e96a11a 100644 --- a/pkg/services/authz/zanzana/schema/README.md +++ b/pkg/services/authz/zanzana/schema/README.md @@ -5,8 +5,8 @@ Here's some notes about [OpenFGA authorization model](https://openfga.dev/docs/m ## GroupResource level permissions A relation to a group_resource object grants access to all objects of the GroupResource. -They take the form of `{ “user”: “user:1”, relation: “read”, object:”group_resource:dashboard.grafana.app/dashboard” }`. This -example would grant `user:1` access to all `dashboard.grafana.app/dashboard` in the namespace. +They take the form of `{ “user”: “user:1”, relation: “read”, object:”group_resource:dashboard.grafana.app/dashboards” }`. This +example would grant `user:1` access to all `dashboard.grafana.app/dashboards` in the namespace. ## Folder level permissions @@ -20,11 +20,19 @@ This context holds all GroupResources in a list e.g. `{ "group_resources": ["das ## Resource level permissions -Most of our resource should use the generic resource type. +Most of our resource should use the generic resource type. -To grant a user direct access to a specific resource we store `{ “user”: “user:1”, relation: “read”, object:”resource:dashboard.grafana.app/dashboard/” }` with additional context. +To grant a user direct access to a specific resource we store `{ “user”: “user:1”, relation: “read”, object:”resource:dashboard.grafana.app/dashboards/” }` with additional context. This context store the GroupResource. `{ "group_resource": "dashboard.grafana.app/dashboards" }`. This is required so we can filter them out for list requests. +## Subresources + +Subresources enable more granular permissions for the resources. Example might be access to public dashboards or access to dashboard settings. + +To grant a user access to the subresource of the specific resource we store following tuple: `{ “user”: “user:1”, relation: “read”, object:”resource:dashboard.grafana.app/dashboards//” }` with additional context `{ "group_resource": "dashboard.grafana.app/dashboards/" }` + +It's also possible to grant user access to all subresources for specific resource type. It can be done with following tuple: `{ “user”: “user:1”, relation: “read”, object:”resource:dashboard.grafana.app/dashboards/” }`. + ## Managed permissions In the RBAC model managed permissions stored as a special "managed" role permissions. OpenFGA model allows to assign permissions directly to users, so it produces following tuples: @@ -58,4 +66,3 @@ type folder ``` According to the schema, user can get `read` access to folder if it has `read` relation granted directly to the folder or its parent folders. - From 13d1f0259762a9fb212b252ee7463cae606b008f Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 11 Mar 2025 09:40:15 -0600 Subject: [PATCH 184/312] Frontend Sandbox: Do not perform authenticated queries for non authenticated users (#101946) * Do not perform authenticated queries for non authenticated users * Empty commit --- .../sandbox/sandbox_plugin_loader_registry.test.ts | 9 +++++++++ .../plugins/sandbox/sandbox_plugin_loader_registry.ts | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts index 8138baf6b6f..9e1f8a11b0d 100644 --- a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts +++ b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.test.ts @@ -1,5 +1,6 @@ import { PluginMeta, PluginSignatureStatus, PluginSignatureType } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { contextSrv } from 'app/core/services/context_srv'; import { getPluginDetails } from '../admin/api'; import { CatalogPluginDetails } from '../admin/types'; @@ -30,6 +31,7 @@ jest.mock('../admin/api', () => ({ const getPluginSettingsMock = jest.mocked(getPluginSettings); const getPluginDetailsMock = jest.mocked(getPluginDetails); +const mockContextSrv = jest.mocked(contextSrv); const fakePluginSettings: PluginMeta = { id: 'test-plugin', @@ -45,6 +47,7 @@ describe('Sandbox eligibility checks', () => { jest.clearAllMocks(); getPluginDetailsMock.mockReset(); getPluginSettingsMock.mockReset(); + mockContextSrv.isSignedIn = true; // restore default check setSandboxEnabledCheck(isPluginFrontendSandboxEnabled); @@ -63,6 +66,12 @@ describe('Sandbox eligibility checks', () => { expect(result).toBe(false); }); + test('isPluginFrontendSandboxEligible returns false for unsigned users', async () => { + mockContextSrv.isSignedIn = false; + const isEligible = await isPluginFrontendSandboxEligible({ pluginId: 'test-plugin' }); + expect(isEligible).toBe(false); + }); + test('shouldLoadPluginInFrontendSandbox returns false when feature toggle is off', async () => { config.featureToggles.pluginsFrontendSandbox = false; const result = await shouldLoadPluginInFrontendSandbox({ pluginId: 'test-plugin' }); diff --git a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts index 2dbef3be1e1..6001734a905 100644 --- a/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts +++ b/public/app/features/plugins/sandbox/sandbox_plugin_loader_registry.ts @@ -1,5 +1,6 @@ import { PluginSignatureType } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { contextSrv } from 'app/core/core'; import { getPluginDetails } from '../admin/api'; import { getPluginSettings } from '../pluginSettings'; @@ -62,6 +63,10 @@ export async function isPluginFrontendSandboxEligible({ return false; } + if (!contextSrv.isSignedIn) { + return false; + } + // grafana signature and internal plugins are not allowed in the sandbox return isPluginSignatureEligibleForSandbox({ pluginId }); } From 59d87fe3f1c0dd59353e6176bf55bd4a3c37c0bf Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Tue, 11 Mar 2025 10:15:58 -0600 Subject: [PATCH 185/312] Unified Storage: Use match all query instead of wildcard for not-in requirement query (#101953) use match all query insteaed of wildcard --- pkg/storage/unified/search/bleve.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/storage/unified/search/bleve.go b/pkg/storage/unified/search/bleve.go index 630c9832315..322abff0516 100644 --- a/pkg/storage/unified/search/bleve.go +++ b/pkg/storage/unified/search/bleve.go @@ -803,7 +803,7 @@ func requirementQuery(req *resource.Requirement, prefix string) (query.Query, *r boolQuery.AddMustNot(mustNotQueries...) // must still have a value - notEmptyQuery := bleve.NewWildcardQuery("*") + notEmptyQuery := bleve.NewMatchAllQuery() boolQuery.AddMust(notEmptyQuery) return boolQuery, nil From 7e4beb2074ae23e083312218c1177af35c815d80 Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Tue, 11 Mar 2025 12:40:44 -0400 Subject: [PATCH 186/312] Alerting: API to return deleted rules (#101429) --- pkg/services/ngalert/api/api_ruler.go | 20 ++++ pkg/services/ngalert/api/persist.go | 1 + pkg/services/ngalert/store/alert_rule.go | 34 +++++++ pkg/services/ngalert/store/alert_rule_test.go | 57 +++++++++++ pkg/services/ngalert/tests/fakes/rules.go | 13 +++ pkg/tests/api/alerting/api_ruler_test.go | 98 +++++++++++++++++++ pkg/tests/api/alerting/testing.go | 10 ++ 7 files changed, 233 insertions(+) diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index 2d88ae849e1..e5ece7154e3 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -261,6 +261,26 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *contextmodel.ReqContext, namespa // RouteGetRulesConfig returns all alert rules that are available to the current user func (srv RulerSrv) RouteGetRulesConfig(c *contextmodel.ReqContext) response.Response { + if strings.ToLower(c.Query("deleted")) == "true" { + if !srv.featureManager.IsEnabledGlobally(featuremgmt.FlagAlertRuleRestore) { + return ErrResp(http.StatusBadRequest, errors.New("restore of deleted rules is not enabled"), "") + } + if !c.SignedInUser.HasRole(identity.RoleAdmin) { + return ErrResp(http.StatusForbidden, errors.New("only admins can get deleted rules"), "") + } + rules, err := srv.store.ListDeletedRules(c.Req.Context(), c.SignedInUser.GetOrgID()) + if err != nil { + return ErrResp(http.StatusInternalServerError, err, "failed to get deleted rules") + } + result := apimodels.NamespaceConfigResponse{} + if len(rules) > 0 { + result[""] = []apimodels.GettableRuleGroupConfig{ + toGettableRuleGroupConfig("", rules, map[string]ngmodels.Provenance{}, srv.resolveUserIdToNameFn(c.Req.Context())), + } + } + return response.JSON(http.StatusOK, result) + } + namespaceMap, err := srv.store.GetUserVisibleNamespaces(c.Req.Context(), c.SignedInUser.GetOrgID(), c.SignedInUser) if err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to get namespaces visible to the user") diff --git a/pkg/services/ngalert/api/persist.go b/pkg/services/ngalert/api/persist.go index b169be59c07..30b79b666dd 100644 --- a/pkg/services/ngalert/api/persist.go +++ b/pkg/services/ngalert/api/persist.go @@ -23,6 +23,7 @@ type RuleStore interface { GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) (*ngmodels.AlertRule, error) GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) ([]*ngmodels.AlertRule, error) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error) + ListDeletedRules(ctx context.Context, orgID int64) ([]*ngmodels.AlertRule, error) // InsertAlertRules will insert all alert rules passed into the function // and return the map of uuid to id. diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 47ab409c63a..d4fb0202a78 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -236,6 +236,40 @@ func (st DBstore) GetAlertRuleVersions(ctx context.Context, orgID int64, guid st return alertRules, nil } +// ListDeletedRules retrieves a list of deleted alert rules for the specified organization ID from the database. +// It ensures that only the latest version of each rule is included and filters out invalid or duplicated versions. +// Returns a slice of *models.AlertRule or an error if the operation fails. +func (st DBstore) ListDeletedRules(ctx context.Context, orgID int64) ([]*ngmodels.AlertRule, error) { + alertRules := make([]*ngmodels.AlertRule, 0) + err := st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { + // take only the latest versions of each rule by GUID + rows, err := sess.Table(alertRuleVersion{}).Where("rule_org_id = ? AND rule_uid = ''", orgID).Rows(alertRuleVersion{}) + if err != nil { + return err + } + // Deserialize each rule separately in case any of them contain invalid JSON. + for rows.Next() { + rule := new(alertRuleVersion) + err = rows.Scan(rule) + if err != nil { + st.Logger.Error("Invalid rule version found in DB store, ignoring it", "func", "GetAlertRuleVersions", "error", err) + continue + } + converted, err := alertRuleToModelsAlertRule(alertRuleVersionToAlertRule(*rule), st.Logger) + if err != nil { + st.Logger.Error("Invalid rule found in DB store, cannot convert, ignoring it", "func", "GetAlertRuleVersions", "error", err, "version_id", rule.ID) + continue + } + alertRules = append(alertRules, &converted) + } + return nil + }) + if err != nil { + return nil, err + } + return alertRules, nil +} + // GetRuleByID retrieves models.AlertRule by ID. // It returns models.ErrAlertRuleNotFound if no alert rule is found for the provided ID. func (st DBstore) GetRuleByID(ctx context.Context, query ngmodels.GetAlertRuleByIDQuery) (result *ngmodels.AlertRule, err error) { diff --git a/pkg/services/ngalert/store/alert_rule_test.go b/pkg/services/ngalert/store/alert_rule_test.go index 2b6d7d76971..c3e94e7e806 100644 --- a/pkg/services/ngalert/store/alert_rule_test.go +++ b/pkg/services/ngalert/store/alert_rule_test.go @@ -1954,6 +1954,63 @@ func TestIntegration_ListAlertRules(t *testing.T) { }) } +func TestIntegration_ListDeletedRules(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + cfg := setting.NewCfg() + cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{ + BaseInterval: 1 * time.Second, + RuleVersionRecordLimit: -1, + } + sqlStore := db.InitTestDB(t) + folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures()) + b := &fakeBus{} + store := createTestStore(sqlStore, folderService, &logtest.Fake{}, cfg.UnifiedAlerting, b) + store.FeatureToggles = featuremgmt.WithFeatures(featuremgmt.FlagAlertRuleRestore) + + orgID := int64(1) + gen := models.RuleGen + gen = gen.With(gen.WithIntervalMatching(store.Cfg.BaseInterval), gen.WithOrgID(orgID)) + + result, err := store.InsertAlertRules(context.Background(), &models.AlertingUserUID, []models.AlertRule{gen.Generate()}) + require.NoError(t, err) + rule, err := store.GetAlertRuleByUID(context.Background(), &models.GetAlertRuleByUIDQuery{UID: result[0].UID}) + require.NoError(t, err) + + rule2 := models.CopyRule(rule, gen.WithTitle(util.GenerateShortUID())) + err = store.UpdateAlertRules(context.Background(), &models.AlertingUserUID, []models.UpdateRule{ + { + Existing: rule, + New: *rule2, + }, + }) + require.NoError(t, err) + rule2, err = store.GetAlertRuleByUID(context.Background(), &models.GetAlertRuleByUIDQuery{UID: result[0].UID}) + require.NoError(t, err) + + versions, err := store.GetAlertRuleVersions(context.Background(), orgID, rule.GUID) + require.NoError(t, err) + require.Len(t, versions, 2) + + t.Run("should not return if rule is not deleted", func(t *testing.T) { + list, err := store.ListDeletedRules(context.Background(), orgID) + require.NoError(t, err) + require.Empty(t, list) + }) + + err = store.DeleteAlertRulesByUID(context.Background(), orgID, &models.AlertingUserUID, rule.UID) + require.NoError(t, err) + + t.Run("should return the last deleted rule", func(t *testing.T) { + list, err := store.ListDeletedRules(context.Background(), orgID) + require.NoError(t, err) + require.Len(t, list, 1) + assert.Empty(t, list[0].UID) + assert.Empty(t, rule2.Diff(list[0], "ID", "UID", "DashboardUID", "PanelID")) + }) +} + func createTestStore( sqlStore db.DB, folderService folder.Service, diff --git a/pkg/services/ngalert/tests/fakes/rules.go b/pkg/services/ngalert/tests/fakes/rules.go index 10bc282e8a8..24d9959a0e2 100644 --- a/pkg/services/ngalert/tests/fakes/rules.go +++ b/pkg/services/ngalert/tests/fakes/rules.go @@ -24,6 +24,7 @@ type RuleStore struct { // OrgID -> RuleGroup -> Namespace -> Rules Rules map[int64][]*models.AlertRule History map[string][]*models.AlertRule + Deleted map[int64][]*models.AlertRule Hook func(cmd any) error // use Hook if you need to intercept some query and return an error RecordedOps []any Folders map[int64][]*folder.Folder @@ -460,3 +461,15 @@ func (f *RuleStore) GetAlertRuleVersions(_ context.Context, orgID int64, guid st return f.History[guid], nil } + +func (f *RuleStore) ListDeletedRules(_ context.Context, orgID int64) ([]*models.AlertRule, error) { + f.mtx.Lock() + defer f.mtx.Unlock() + defer func() { + f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{Name: "ListDeletedRules", Params: []any{orgID}}) + }() + if err := f.Hook(orgID); err != nil { + return nil, err + } + return f.Deleted[orgID], nil +} diff --git a/pkg/tests/api/alerting/api_ruler_test.go b/pkg/tests/api/alerting/api_ruler_test.go index 9f03a070f53..3cdf2d2a20f 100644 --- a/pkg/tests/api/alerting/api_ruler_test.go +++ b/pkg/tests/api/alerting/api_ruler_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "math/rand" "net/http" "path" @@ -15,6 +16,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/prometheus/alertmanager/pkg/labels" @@ -4645,6 +4647,102 @@ func TestIntegrationRuleVersions(t *testing.T) { }) } +func TestIntegrationRuleSoftDelete(t *testing.T) { + testinfra.SQLiteIntegrationTest(t) + + // Setup Grafana and its Database + dir, p := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + DisableLegacyAlerting: true, + EnableUnifiedAlerting: true, + EnableQuota: true, + DisableAnonymous: true, + AppModeProduction: true, + EnableFeatureToggles: []string{featuremgmt.FlagAlertRuleRestore}, + }) + + grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p) + + createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleAdmin), + Password: "admin", + Login: "admin", + }) + + createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleEditor), + Password: "password", + Login: "editor", + }) + + adminClient := newAlertingApiClient(grafanaListedAddr, "admin", "admin") + editorClient := newAlertingApiClient(grafanaListedAddr, "editor", "password") + + deleted, status, data := adminClient.GetDeletedRulesWithStatus(t) + requireStatusCode(t, http.StatusOK, status, data) + require.Emptyf(t, deleted, "Expected empty list of deleted rules, got %v", deleted) + + // Create the namespace we'll save our alerts to. + adminClient.CreateFolder(t, "folder1", "folder1") + + var group apimodels.RuleGroupConfigResponse + { // create rules and some history + postGroupRaw, err := testData.ReadFile(path.Join("test-data", "rulegroup-1-post.json")) + require.NoError(t, err) + var group1 apimodels.PostableRuleGroupConfig + require.NoError(t, json.Unmarshal(postGroupRaw, &group1)) + + // Create rule under folder1 + response := adminClient.PostRulesGroup(t, "folder1", &group1) + require.NotEmptyf(t, response.Created, "Expected created to be set") + + // create some versions of the rule + for i := 0; i < 3; i++ { + groups, status := adminClient.GetRulesGroup(t, "folder1", group1.Name) + require.Equal(t, http.StatusAccepted, status) + group1 = convertGettableRuleGroupToPostable(groups.GettableRuleGroupConfig) + group1.Rules[0].Annotations[util.GenerateShortUID()] = util.GenerateShortUID() + _ = adminClient.PostRulesGroup(t, "folder1", &group1) + } + group, status = adminClient.GetRulesGroup(t, "folder1", group1.Name) + require.Equal(t, http.StatusAccepted, status) + } + + // deleting group by using editor user + status, body := editorClient.DeleteRulesGroup(t, "folder1", group.Name) + require.Equalf(t, http.StatusAccepted, status, "failed to delete group. Response: %s", body) + + t.Run("should see deleted rules", func(t *testing.T) { + rules, status, raw := adminClient.GetDeletedRulesWithStatus(t) + requireStatusCode(t, http.StatusOK, status, raw) + + require.Containsf(t, rules, "", "All rules should be in empty folder but got %v", slices.Collect(maps.Keys(rules))) + require.Lenf(t, rules[""], 1, "All deleted rules should be in single group but got %d", len(rules[""])) + require.Equalf(t, "", rules[""][0].Name, "All deleted rules should be in empty group but got %v", rules[""][0].Name) + + require.Len(t, rules[""][0].Rules, len(group.Rules)) + require.Empty(t, cmp.Diff(group.Rules, rules[""][0].Rules, cmpopts.IgnoreFields(apimodels.GettableGrafanaRule{}, "UID", "Version", "Updated", "UpdatedBy"))) + rule := rules[""][0].Rules[0] + require.Equalf(t, "editor", rule.GrafanaManagedAlert.UpdatedBy.Name, "Field 'UpdatedBy' should be set by editor but got %v ", rule.GrafanaManagedAlert.UpdatedBy) + }) + + t.Run("only admin should be able to see deleted rules", func(t *testing.T) { + t.Run("editor", func(t *testing.T) { + _, status, raw := editorClient.GetDeletedRulesWithStatus(t) + requireStatusCode(t, http.StatusForbidden, status, raw) + }) + t.Run("viewer", func(t *testing.T) { + createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleViewer), + Password: "password", + Login: "viewer", + }) + client := newAlertingApiClient(grafanaListedAddr, "viewer", "password") + _, status, raw := client.GetDeletedRulesWithStatus(t) + requireStatusCode(t, http.StatusForbidden, status, raw) + }) + }) +} + func newTestingRuleConfig(t *testing.T) apimodels.PostableRuleGroupConfig { interval, err := model.ParseDuration("1m") require.NoError(t, err) diff --git a/pkg/tests/api/alerting/testing.go b/pkg/tests/api/alerting/testing.go index f613d7a5547..25a25fd4e58 100644 --- a/pkg/tests/api/alerting/testing.go +++ b/pkg/tests/api/alerting/testing.go @@ -647,6 +647,16 @@ func (a apiClient) GetAllRulesWithStatus(t *testing.T) (apimodels.NamespaceConfi return result, resp.StatusCode, b } +func (a apiClient) GetDeletedRulesWithStatus(t *testing.T) (apimodels.NamespaceConfigResponse, int, string) { + t.Helper() + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules", a.url), nil) + require.NoError(t, err) + q := req.URL.Query() + q.Add("deleted", "true") + req.URL.RawQuery = q.Encode() + return sendRequestJSON[apimodels.NamespaceConfigResponse](t, req, http.StatusOK) +} + func (a apiClient) ExportRulesWithStatus(t *testing.T, params *apimodels.AlertRulesExportParameters) (int, string) { t.Helper() u, err := url.Parse(fmt.Sprintf("%s/api/ruler/grafana/api/v1/export/rules", a.url)) From 42ae2fb02695281956e3787f7dbaa0cbb58e6d08 Mon Sep 17 00:00:00 2001 From: Will Assis <35489495+gassiss@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:56:34 -0300 Subject: [PATCH 187/312] fix(unified-storage): add missing dashboard legacy_id when in legacy read mode (#101944) * add missing dashboard legacy_id when in modes 0-2 --- .../dashboard/legacysearcher/search_client.go | 8 +++++- .../legacysearcher/search_client_test.go | 27 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/pkg/registry/apis/dashboard/legacysearcher/search_client.go b/pkg/registry/apis/dashboard/legacysearcher/search_client.go index 1f98dc3a1e1..c80919971c6 100644 --- a/pkg/registry/apis/dashboard/legacysearcher/search_client.go +++ b/pkg/registry/apis/dashboard/legacysearcher/search_client.go @@ -213,6 +213,11 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour searchFields.Field(resource.SEARCH_FIELD_TITLE), searchFields.Field(resource.SEARCH_FIELD_FOLDER), searchFields.Field(resource.SEARCH_FIELD_TAGS), + { + Name: unisearch.DASHBOARD_LEGACY_ID, + Type: resource.ResourceTableColumnDefinition_INT64, + Description: "Deprecated legacy id of the dashboard", + }, { Name: sortByField, Type: resource.ResourceTableColumnDefinition_INT64, @@ -270,7 +275,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour list.Results.Rows = append(list.Results.Rows, &resource.ResourceTableRow{ Key: getResourceKey(dashboard, req.Options.Key.Namespace), - Cells: [][]byte{[]byte(dashboard.Title), []byte(dashboard.FolderUID), tags, []byte(strconv.FormatInt(dashboard.SortMeta, 10))}, + Cells: [][]byte{[]byte(dashboard.Title), []byte(dashboard.FolderUID), tags, []byte(strconv.FormatInt(dashboard.ID, 10)), []byte(strconv.FormatInt(dashboard.SortMeta, 10))}, }) } @@ -306,6 +311,7 @@ func formatQueryResult(res []dashboards.DashboardSearchProjection) []*dashboards hit, exists := hits[key] if !exists { hit = &dashboards.DashboardSearchProjection{ + ID: item.ID, UID: item.UID, Title: item.Title, FolderUID: item.FolderUID, diff --git a/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go b/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go index 4b56d2e9b6f..ffbc973ba07 100644 --- a/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go +++ b/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go @@ -43,8 +43,8 @@ func TestDashboardSearchClient_Search(t *testing.T) { Type: "dash-db", // should set type based off of key Sort: sorter, }).Return([]dashboards.DashboardSearchProjection{ - {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", Term: "term"}, - {UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2"}, + {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", Term: "term"}, + {ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2"}, }, nil).Once() req := &resource.ResourceSearchRequest{ @@ -72,6 +72,11 @@ func TestDashboardSearchClient_Search(t *testing.T) { searchFields.Field(resource.SEARCH_FIELD_TITLE), searchFields.Field(resource.SEARCH_FIELD_FOLDER), searchFields.Field(resource.SEARCH_FIELD_TAGS), + { + Name: unisearch.DASHBOARD_LEGACY_ID, + Type: resource.ResourceTableColumnDefinition_INT64, + Description: "Deprecated legacy id of the dashboard", + }, { Name: "", // sort by should be empty if title is what we sorted by Type: resource.ResourceTableColumnDefinition_INT64, @@ -88,6 +93,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { []byte("Test Dashboard"), []byte("folder1"), tags, + []byte("1"), []byte(strconv.FormatInt(0, 10)), }, }, @@ -101,6 +107,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { []byte("Test Dashboard2"), []byte("folder2"), emptyTags, + []byte("2"), []byte(strconv.FormatInt(0, 10)), }, }, @@ -120,7 +127,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { Type: "dash-db", Sort: sortOptionAsc, }).Return([]dashboards.DashboardSearchProjection{ - {UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50)}, + {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50)}, }, nil).Once() req := &resource.ResourceSearchRequest{ @@ -145,6 +152,11 @@ func TestDashboardSearchClient_Search(t *testing.T) { searchFields.Field(resource.SEARCH_FIELD_TITLE), searchFields.Field(resource.SEARCH_FIELD_FOLDER), searchFields.Field(resource.SEARCH_FIELD_TAGS), + { + Name: unisearch.DASHBOARD_LEGACY_ID, + Type: resource.ResourceTableColumnDefinition_INT64, + Description: "Deprecated legacy id of the dashboard", + }, { Name: "views_total", Type: resource.ResourceTableColumnDefinition_INT64, @@ -161,6 +173,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { []byte("Test Dashboard"), []byte("folder"), emptyTags, + []byte("1"), []byte(strconv.FormatInt(50, 10)), }, }, @@ -180,7 +193,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { Type: "dash-db", Sort: sortOptionAsc, }).Return([]dashboards.DashboardSearchProjection{ - {UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2)}, + {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2)}, }, nil).Once() req := &resource.ResourceSearchRequest{ @@ -205,6 +218,11 @@ func TestDashboardSearchClient_Search(t *testing.T) { searchFields.Field(resource.SEARCH_FIELD_TITLE), searchFields.Field(resource.SEARCH_FIELD_FOLDER), searchFields.Field(resource.SEARCH_FIELD_TAGS), + { + Name: unisearch.DASHBOARD_LEGACY_ID, + Type: resource.ResourceTableColumnDefinition_INT64, + Description: "Deprecated legacy id of the dashboard", + }, { Name: "errors_last_30_days", Type: resource.ResourceTableColumnDefinition_INT64, @@ -221,6 +239,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { []byte("Test Dashboard"), []byte("folder"), emptyTags, + []byte("1"), []byte(strconv.FormatInt(2, 10)), }, }, From 7a3415148e579c102c0d0f171c0fb26fbe6ac58c Mon Sep 17 00:00:00 2001 From: Sam Jewell <2903904+samjewell@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:14:33 +0000 Subject: [PATCH 188/312] SQL Expressions: Add cell-limit for input dataframes (#101700) * expr: Add row limit to SQL expressions Adds a configurable row limit to SQL expressions to prevent memory issues with large result sets. The limit is configured via the `sql_expression_row_limit` setting in the `[expressions]` section of grafana.ini, with a default of 100,000 rows. The limit is enforced by checking the total number of rows across all input tables before executing the SQL query. If the total exceeds the limit, the query fails with an error message indicating the limit was exceeded. * revert addition of newline * Switch to table-driven tests * Remove single-frame test-cases. We only need to test for the multi frame case. Single frame is a subset of the multi-frame case * Add helper function Simplify the way tests are set up and written * Support convention, that limit: 0 is no limit * Set the row-limit in one place only * Update default limit to 20k rows As per some discussion here: https://raintank-corp.slack.com/archives/C071A5XCFST/p1741611647001369?thread_ts=1740047619.804869&cid=C071A5XCFST * Test row-limit is applied from config Make sure we protect this from regressions This is perhaps a brittle test, somewhat coupled to the code here. But it's good enough to prevent regressions at least. * Add public documentation for the limit * Limit total number of cells instead of rows * Use named-return for totalRows As @kylebrandt requested during review of #101700 * Leave DF cells as zero values during limits tests When testing the cell limit we don't interact with the cell values at all, so we leave them at their zero values both to speed up tests, and to simplify and clarify that their values aren't used. * Set SQLCmd limit at object creation - don't mutate * Test that SQL node receives limit when built And that it receives it from the Grafana config * Improve TODO message for new Expression Parser * Fix failing test by always creating config on the Service --- .../setup-grafana/configure-grafana/_index.md | 4 + pkg/expr/graph.go | 2 +- pkg/expr/graph_test.go | 2 + pkg/expr/nodes.go | 4 +- pkg/expr/reader.go | 4 +- pkg/expr/service_test.go | 65 ++++++++ pkg/expr/sql_command.go | 38 ++++- pkg/expr/sql_command_test.go | 142 +++++++++++++++++- pkg/setting/setting.go | 4 + 9 files changed, 253 insertions(+), 12 deletions(-) diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 5c7b92515ec..9f16b072e63 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -2753,6 +2753,10 @@ Set the default start of the week, valid values are: `saturday`, `sunday`, `mond Set this to `false` to disable expressions and hide them in the Grafana UI. Default is `true`. +#### `sql_expression_cell_limit` + +Set the maximum number of cells that can be passed to a SQL expression. Default is `100000`. + ### `[geomap]` This section controls the defaults settings for **Geomap Plugin**. diff --git a/pkg/expr/graph.go b/pkg/expr/graph.go index 6632a6b74c1..ae0ec6f9660 100644 --- a/pkg/expr/graph.go +++ b/pkg/expr/graph.go @@ -277,7 +277,7 @@ func (s *Service) buildGraph(req *Request) (*simple.DirectedGraph, error) { case TypeDatasourceNode: node, err = s.buildDSNode(dp, rn, req) case TypeCMDNode: - node, err = buildCMDNode(rn, s.features) + node, err = buildCMDNode(rn, s.features, s.cfg.SQLExpressionCellLimit) case TypeMLNode: if s.features.IsEnabledGlobally(featuremgmt.FlagMlExpressions) { node, err = s.buildMLNode(dp, rn, req) diff --git a/pkg/expr/graph_test.go b/pkg/expr/graph_test.go index fafca8f6876..dfa9f5f5b0a 100644 --- a/pkg/expr/graph_test.go +++ b/pkg/expr/graph_test.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/setting" ) func TestServicebuildPipeLine(t *testing.T) { @@ -234,6 +235,7 @@ func TestServicebuildPipeLine(t *testing.T) { } s := Service{ features: featuremgmt.WithFeatures(featuremgmt.FlagExpressionParser), + cfg: setting.NewCfg(), } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/expr/nodes.go b/pkg/expr/nodes.go index 1159ef13d04..dea3b10e659 100644 --- a/pkg/expr/nodes.go +++ b/pkg/expr/nodes.go @@ -106,7 +106,7 @@ func (gn *CMDNode) Execute(ctx context.Context, now time.Time, vars mathexp.Vars return gn.Command.Execute(ctx, now, vars, s.tracer) } -func buildCMDNode(rn *rawNode, toggles featuremgmt.FeatureToggles) (*CMDNode, error) { +func buildCMDNode(rn *rawNode, toggles featuremgmt.FeatureToggles, sqlExpressionCellLimit int64) (*CMDNode, error) { commandType, err := GetExpressionCommandType(rn.Query) if err != nil { return nil, fmt.Errorf("invalid command type in expression '%v': %w", rn.RefID, err) @@ -163,7 +163,7 @@ func buildCMDNode(rn *rawNode, toggles featuremgmt.FeatureToggles) (*CMDNode, er case TypeThreshold: node.Command, err = UnmarshalThresholdCommand(rn, toggles) case TypeSQL: - node.Command, err = UnmarshalSQLCommand(rn) + node.Command, err = UnmarshalSQLCommand(rn, sqlExpressionCellLimit) default: return nil, fmt.Errorf("expression command type '%v' in expression '%v' not implemented", commandType, rn.RefID) } diff --git a/pkg/expr/reader.go b/pkg/expr/reader.go index ef18d9d8c2b..10a219f0692 100644 --- a/pkg/expr/reader.go +++ b/pkg/expr/reader.go @@ -134,7 +134,9 @@ func (h *ExpressionQueryReader) ReadQuery( err = iter.ReadVal(q) if err == nil { eq.Properties = q - eq.Command, err = NewSQLCommand(common.RefID, q.Expression) + // TODO: Cascade limit from Grafana config in this (new Expression Parser) branch of the code + cellLimit := 0 // zero means no limit + eq.Command, err = NewSQLCommand(common.RefID, q.Expression, int64(cellLimit)) } case QueryTypeThreshold: diff --git a/pkg/expr/service_test.go b/pkg/expr/service_test.go index 2fe6f6e1e9c..a343cdaf7b4 100644 --- a/pkg/expr/service_test.go +++ b/pkg/expr/service_test.go @@ -146,6 +146,71 @@ func TestDSQueryError(t *testing.T) { require.Equal(t, fp(42), res.Responses["C"].Frames[0].Fields[0].At(0)) } +func TestSQLExpressionCellLimitFromConfig(t *testing.T) { + tests := []struct { + name string + configCellLimit int64 + expectedLimit int64 + }{ + { + name: "should pass default cell limit (0) to SQL command", + configCellLimit: 0, + expectedLimit: 0, + }, + { + name: "should pass custom cell limit to SQL command", + configCellLimit: 5000, + expectedLimit: 5000, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a request with an SQL expression + sqlQuery := Query{ + RefID: "A", + DataSource: dataSourceModel(), + JSON: json.RawMessage(`{ "datasource": { "uid": "__expr__", "type": "__expr__"}, "type": "sql", "expression": "SELECT 1 AS n" }`), + TimeRange: AbsoluteTimeRange{ + From: time.Time{}, + To: time.Time{}, + }, + } + + queries := []Query{sqlQuery} + + // Create service with specified cell limit + cfg := setting.NewCfg() + cfg.ExpressionsEnabled = true + cfg.SQLExpressionCellLimit = tt.configCellLimit + + features := featuremgmt.WithFeatures(featuremgmt.FlagSqlExpressions) + + // Create service with our configured limit + s := &Service{ + cfg: cfg, + features: features, + converter: &ResultConverter{ + Features: features, + }, + } + + req := &Request{Queries: queries, User: &user.SignedInUser{}} + + // Build the pipeline + pipeline, err := s.BuildPipeline(req) + require.NoError(t, err) + + node := pipeline[0] + cmdNode := node.(*CMDNode) + sqlCmd := cmdNode.Command.(*SQLCommand) + + // Verify the SQL command has the correct limit + require.Equal(t, tt.expectedLimit, sqlCmd.limit, "SQL command has incorrect cell limit") + }) + } +} + func fp(f float64) *float64 { return &f } diff --git a/pkg/expr/sql_command.go b/pkg/expr/sql_command.go index 069d2a39e91..0b4d7ab698e 100644 --- a/pkg/expr/sql_command.go +++ b/pkg/expr/sql_command.go @@ -19,10 +19,11 @@ type SQLCommand struct { query string varsToQuery []string refID string + limit int64 } // NewSQLCommand creates a new SQLCommand. -func NewSQLCommand(refID, rawSQL string) (*SQLCommand, error) { +func NewSQLCommand(refID, rawSQL string, limit int64) (*SQLCommand, error) { if rawSQL == "" { return nil, errutil.BadRequest("sql-missing-query", errutil.WithPublicMessage("missing SQL query")) @@ -40,15 +41,17 @@ func NewSQLCommand(refID, rawSQL string) (*SQLCommand, error) { if tables != nil { logger.Debug("REF tables", "tables", tables, "sql", rawSQL) } + return &SQLCommand{ query: rawSQL, varsToQuery: tables, refID: refID, + limit: limit, }, nil } // UnmarshalSQLCommand creates a SQLCommand from Grafana's frontend query. -func UnmarshalSQLCommand(rn *rawNode) (*SQLCommand, error) { +func UnmarshalSQLCommand(rn *rawNode, limit int64) (*SQLCommand, error) { if rn.TimeRange == nil { logger.Error("time range must be specified for refID", "refID", rn.RefID) return nil, fmt.Errorf("time range must be specified for refID %s", rn.RefID) @@ -65,7 +68,7 @@ func UnmarshalSQLCommand(rn *rawNode) (*SQLCommand, error) { return nil, fmt.Errorf("expected sql expression to be type string, but got type %T", expressionRaw) } - return NewSQLCommand(rn.RefID, expression) + return NewSQLCommand(rn.RefID, expression, limit) } // NeedsVars returns the variable names (refIds) that are dependencies @@ -91,12 +94,23 @@ func (gr *SQLCommand) Execute(ctx context.Context, now time.Time, vars mathexp.V allFrames = append(allFrames, frames...) } - rsp := mathexp.Results{} - - db := sql.DB{} + totalCells := totalCells(allFrames) + // limit of 0 or less means no limit (following convention) + if gr.limit > 0 && totalCells > gr.limit { + return mathexp.Results{}, + fmt.Errorf( + "SQL expression: total cell count across all input tables exceeds limit of %d. Total cells: %d", + gr.limit, + totalCells, + ) + } logger.Debug("Executing query", "query", gr.query, "frames", len(allFrames)) + + db := sql.DB{} frame, err := db.QueryFrames(ctx, gr.refID, gr.query, allFrames) + + rsp := mathexp.Results{} if err != nil { logger.Error("Failed to query frames", "error", err.Error()) rsp.Error = err @@ -121,3 +135,15 @@ func (gr *SQLCommand) Execute(ctx context.Context, now time.Time, vars mathexp.V func (gr *SQLCommand) Type() string { return TypeSQL.String() } + +func totalCells(frames []*data.Frame) (total int64) { + for _, frame := range frames { + if frame != nil { + // Calculate cells as rows × columns + rows := int64(frame.Rows()) + cols := int64(len(frame.Fields)) + total += rows * cols + } + } + return +} diff --git a/pkg/expr/sql_command_test.go b/pkg/expr/sql_command_test.go index 3e0c5527721..07387a46612 100644 --- a/pkg/expr/sql_command_test.go +++ b/pkg/expr/sql_command_test.go @@ -1,13 +1,21 @@ package expr import ( + "context" + "fmt" + "net/http" "strings" "testing" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana/pkg/expr/mathexp" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/trace" ) func TestNewCommand(t *testing.T) { - t.Skip() - cmd, err := NewSQLCommand("a", "select a from foo, bar") + cmd, err := NewSQLCommand("a", "select a from foo, bar", 0) if err != nil && strings.Contains(err.Error(), "feature is not enabled") { return } @@ -25,3 +33,133 @@ func TestNewCommand(t *testing.T) { return } } + +// Helper function for creating test data +func createFrameWithRowsAndCols(rows int, cols int) *data.Frame { + frame := data.NewFrame("dummy") + + for c := 0; c < cols; c++ { + values := make([]string, rows) + frame.Fields = append(frame.Fields, data.NewField(fmt.Sprintf("col%d", c), nil, values)) + } + + return frame +} + +func TestSQLCommandCellLimits(t *testing.T) { + tests := []struct { + name string + limit int64 + frames []*data.Frame + vars []string + expectError bool + errorContains string + }{ + { + name: "single (long) frame within cell limit", + limit: 10, + frames: []*data.Frame{ + createFrameWithRowsAndCols(10, 1), // 10 cells + }, + vars: []string{"foo"}, + }, + { + name: "single (wide) frame within cell limit", + limit: 10, + frames: []*data.Frame{ + createFrameWithRowsAndCols(1, 10), // 10 cells + }, + vars: []string{"foo"}, + }, + { + name: "multiple frames within cell limit", + limit: 12, + frames: []*data.Frame{ + createFrameWithRowsAndCols(2, 3), // 6 cells + createFrameWithRowsAndCols(2, 3), // 6 cells + }, + vars: []string{"foo", "bar"}, + }, + { + name: "single (long) frame exceeds cell limit", + limit: 9, + frames: []*data.Frame{ + createFrameWithRowsAndCols(10, 1), // 10 cells > 9 limit + }, + vars: []string{"foo"}, + expectError: true, + errorContains: "exceeds limit", + }, + { + name: "single (wide) frame exceeds cell limit", + limit: 9, + frames: []*data.Frame{ + createFrameWithRowsAndCols(1, 10), // 10 cells > 9 limit + }, + vars: []string{"foo"}, + expectError: true, + errorContains: "exceeds limit", + }, + { + name: "multiple frames exceed cell limit", + limit: 11, + frames: []*data.Frame{ + createFrameWithRowsAndCols(2, 3), // 6 cells + createFrameWithRowsAndCols(2, 3), // 6 cells + }, + vars: []string{"foo", "bar"}, + expectError: true, + errorContains: "exceeds limit", + }, + { + name: "limit of 0 means no limit: allow large frame", + limit: 0, + frames: []*data.Frame{ + createFrameWithRowsAndCols(200000, 1), // 200,000 cells + }, + vars: []string{"foo", "bar"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd, err := NewSQLCommand("a", "select a from foo, bar", tt.limit) + require.NoError(t, err, "Failed to create SQL command") + + vars := mathexp.Vars{} + + for i, frame := range tt.frames { + vars[tt.vars[i]] = mathexp.Results{ + Values: mathexp.Values{mathexp.TableData{Frame: frame}}, + } + } + + _, err = cmd.Execute(context.Background(), time.Now(), vars, &testTracer{}) + + if tt.expectError { + require.Error(t, err) + require.Contains(t, err.Error(), tt.errorContains) + } else { + require.NoError(t, err) + } + }) + } +} + +type testTracer struct { + trace.Tracer +} + +func (t *testTracer) Start(ctx context.Context, name string, s ...trace.SpanStartOption) (context.Context, trace.Span) { + return ctx, &testSpan{} +} +func (t *testTracer) Inject(context.Context, http.Header, trace.Span) { + +} + +type testSpan struct { + trace.Span +} + +func (ts *testSpan) End(opt ...trace.SpanEndOption) { +} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index cc1b10b9054..c5805d8520d 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -419,6 +419,9 @@ type Cfg struct { // ExpressionsEnabled specifies whether expressions are enabled. ExpressionsEnabled bool + // SQLExpressionCellLimit is the maximum number of cells (rows × columns, across all frames) that can be accepted by a SQL expression. + SQLExpressionCellLimit int64 + ImageUploadProvider string // LiveMaxConnections is a maximum number of WebSocket connections to @@ -780,6 +783,7 @@ func (cfg *Cfg) readAnnotationSettings() error { func (cfg *Cfg) readExpressionsSettings() { expressions := cfg.Raw.Section("expressions") cfg.ExpressionsEnabled = expressions.Key("enabled").MustBool(true) + cfg.SQLExpressionCellLimit = expressions.Key("sql_expression_cell_limit").MustInt64(100000) } type AnnotationCleanupSettings struct { From e645a7d8ff3f5b31d1e04a1efcbce10d027cdefd Mon Sep 17 00:00:00 2001 From: Denis Vodopianov Date: Tue, 11 Mar 2025 18:25:52 +0100 Subject: [PATCH 189/312] Chore: update golang version in .drone.yaml (#101894) --- .drone.yml | 206 ++++++++++++++++---------------- public/api-enterprise-spec.json | 82 +++++++++++-- public/api-merged.json | 52 +++++++- public/openapi3.json | 52 +++++++- scripts/drone/variables.star | 2 +- 5 files changed, 276 insertions(+), 118 deletions(-) diff --git a/.drone.yml b/.drone.yml index dbcc1662f84..6340d739fd6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,7 +25,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - ./bin/build verify-drone @@ -75,7 +75,7 @@ steps: - go install github.com/bazelbuild/buildtools/buildifier@latest - buildifier --lint=warn -mode=check -r . depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: lint-starlark trigger: event: @@ -437,7 +437,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -446,21 +446,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend - commands: - apk add --update build-base @@ -469,7 +469,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend-integration trigger: event: @@ -524,7 +524,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - echo $(/usr/bin/github-app-external-token) > /github-app/token @@ -569,16 +569,16 @@ steps: - apk add --update make - make gen-go depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: validate-openapi-spec trigger: event: @@ -655,7 +655,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -665,7 +665,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -674,7 +674,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - yarn install --immutable || yarn install --immutable @@ -712,7 +712,7 @@ steps: - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.7 --yarn-cache=$$YARN_CACHE_FOLDER + -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.1 --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD @@ -1110,7 +1110,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -1124,7 +1124,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1133,14 +1133,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -1161,7 +1161,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -1182,7 +1182,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -1198,7 +1198,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -1214,7 +1214,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -1230,7 +1230,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: remote-alertmanager-integration-tests trigger: event: @@ -1312,7 +1312,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue trigger: event: @@ -1433,7 +1433,7 @@ steps: && return 1; fi depends_on: - clone-enterprise - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: swagger-gen trigger: event: @@ -1538,7 +1538,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1549,7 +1549,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - clone-enterprise - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1559,14 +1559,14 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - clone-enterprise - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - apk add --update build-base @@ -1574,7 +1574,7 @@ steps: - go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES} depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: sqlite-benchmark-integration-tests - commands: - apk add --update build-base @@ -1586,7 +1586,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: postgres-benchmark-integration-tests - commands: - apk add --update build-base @@ -1597,7 +1597,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: mysql-8.0-benchmark-integration-tests trigger: event: @@ -1669,7 +1669,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue trigger: branch: main @@ -1852,7 +1852,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1861,21 +1861,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend - commands: - apk add --update build-base @@ -1884,7 +1884,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend-integration trigger: branch: main @@ -1929,22 +1929,22 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - apk add --update make - make gen-go depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: validate-openapi-spec - commands: - ./bin/build verify-drone @@ -2076,7 +2076,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -2086,7 +2086,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2095,7 +2095,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - yarn install --immutable || yarn install --immutable @@ -2132,7 +2132,7 @@ steps: - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.7 --yarn-cache=$$YARN_CACHE_FOLDER + -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.1 --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD @@ -2607,7 +2607,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -2621,7 +2621,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2630,14 +2630,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -2658,7 +2658,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -2679,7 +2679,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -2695,7 +2695,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -2711,7 +2711,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -2727,7 +2727,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: remote-alertmanager-integration-tests trigger: branch: main @@ -2996,7 +2996,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -3005,21 +3005,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend - commands: - apk add --update build-base @@ -3028,7 +3028,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend-integration trigger: branch: @@ -3071,22 +3071,22 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - apk add --update make - make gen-go depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: validate-openapi-spec trigger: branch: @@ -3165,7 +3165,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -3179,7 +3179,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -3188,14 +3188,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -3216,7 +3216,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -3237,7 +3237,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -3253,7 +3253,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -3269,7 +3269,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -3285,7 +3285,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: remote-alertmanager-integration-tests trigger: branch: @@ -3385,7 +3385,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3517,7 +3517,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3658,7 +3658,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - ./bin/build artifacts packages --artifacts-editions=oss --tag $${DRONE_TAG} --src-bucket @@ -3750,7 +3750,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - yarn install --immutable || yarn install --immutable @@ -3850,7 +3850,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - depends_on: - compile-build-cmd @@ -3947,7 +3947,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: compile-build-cmd - commands: - ./bin/build publish grafana-com --edition oss ${DRONE_TAG} @@ -4009,7 +4009,7 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.23.7 + GO_VERSION: 1.24.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4084,7 +4084,7 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.23.7 + GO_VERSION: 1.24.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4201,7 +4201,7 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.23.7 + GO_VERSION: 1.24.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4352,7 +4352,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4361,21 +4361,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend - commands: - apk add --update build-base @@ -4384,7 +4384,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: test-backend-integration trigger: cron: @@ -4438,7 +4438,7 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.23.7 + GO_VERSION: 1.24.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4582,7 +4582,7 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.23.7 + GO_VERSION: 1.24.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4689,7 +4689,7 @@ steps: - export GITHUB_TOKEN=$(cat /github-app/token) - dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} - --go-version=1.23.7 + --go-version=1.24.1 depends_on: - github-app-generate-token environment: @@ -4710,7 +4710,7 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.23.7 + GO_VERSION: 1.24.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4848,7 +4848,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4857,14 +4857,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -4885,7 +4885,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -4906,7 +4906,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -4922,7 +4922,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -4938,7 +4938,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -4954,7 +4954,7 @@ steps: environment: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 - image: golang:1.23.7-alpine + image: golang:1.24.1-alpine name: remote-alertmanager-integration-tests trigger: event: @@ -5257,7 +5257,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:27-cli - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine/git:2.40.1 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.23.7-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.1-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.11.0-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 @@ -5295,7 +5295,7 @@ steps: - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL docker:27-cli - trivy --exit-code 1 --severity HIGH,CRITICAL alpine/git:2.40.1 - - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.23.7-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.1-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:22.11.0-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 @@ -5564,6 +5564,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: 33f2e5615dfd7889899f9f8f16f7716190fa637fe98f1efd7e29607f8946be7d +hmac: f55fddb4c6faf30b232ae778ec6c022c9f3d32955879e8a764c715642712c5ea ... diff --git a/public/api-enterprise-spec.json b/public/api-enterprise-spec.json index 3faeab0aa9a..a036e242e30 100644 --- a/public/api-enterprise-spec.json +++ b/public/api-enterprise-spec.json @@ -2779,6 +2779,7 @@ } }, "AnnotationActions": { + "description": "+k8s:deepcopy-gen=true", "type": "object", "properties": { "canAdd": { @@ -2853,6 +2854,7 @@ } }, "AnnotationPermission": { + "description": "+k8s:deepcopy-gen=true", "type": "object", "properties": { "dashboard": { @@ -3206,6 +3208,24 @@ "type": "string" } }, + "InhibitAnyPolicy": { + "description": "InhibitAnyPolicy and InhibitAnyPolicyZero indicate the presence and value\nof the inhibitAnyPolicy extension.\n\nThe value of InhibitAnyPolicy indicates the number of additional\ncertificates in the path after this certificate that may use the\nanyPolicy policy OID to indicate a match with any other policy.\n\nWhen parsing a certificate, a positive non-zero InhibitAnyPolicy means\nthat the field was specified, -1 means it was unset, and\nInhibitAnyPolicyZero being true mean that the field was explicitly set to\nzero. The case of InhibitAnyPolicy==0 with InhibitAnyPolicyZero==false\nshould be treated equivalent to -1 (unset).", + "type": "integer", + "format": "int64" + }, + "InhibitAnyPolicyZero": { + "description": "InhibitAnyPolicyZero indicates that InhibitAnyPolicy==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, + "InhibitPolicyMapping": { + "description": "InhibitPolicyMapping and InhibitPolicyMappingZero indicate the presence\nand value of the inhibitPolicyMapping field of the policyConstraints\nextension.\n\nThe value of InhibitPolicyMapping indicates the number of additional\ncertificates in the path after this certificate that may use policy\nmapping.\n\nWhen parsing a certificate, a positive non-zero InhibitPolicyMapping\nmeans that the field was specified, -1 means it was unset, and\nInhibitPolicyMappingZero being true mean that the field was explicitly\nset to zero. The case of InhibitPolicyMapping==0 with\nInhibitPolicyMappingZero==false should be treated equivalent to -1\n(unset).", + "type": "integer", + "format": "int64" + }, + "InhibitPolicyMappingZero": { + "description": "InhibitPolicyMappingZero indicates that InhibitPolicyMapping==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, "IsCA": { "type": "boolean" }, @@ -3270,19 +3290,26 @@ } }, "Policies": { - "description": "Policies contains all policy identifiers included in the certificate.\nIn Go 1.22, encoding/gob cannot handle and ignores this field.", + "description": "Policies contains all policy identifiers included in the certificate.\nSee CreateCertificate for context about how this field and the PolicyIdentifiers field\ninteract.\nIn Go 1.22, encoding/gob cannot handle and ignores this field.", "type": "array", "items": { "type": "string" } }, "PolicyIdentifiers": { - "description": "PolicyIdentifiers contains asn1.ObjectIdentifiers, the components\nof which are limited to int32. If a certificate contains a policy which\ncannot be represented by asn1.ObjectIdentifier, it will not be included in\nPolicyIdentifiers, but will be present in Policies, which contains all parsed\npolicy OIDs.", + "description": "PolicyIdentifiers contains asn1.ObjectIdentifiers, the components\nof which are limited to int32. If a certificate contains a policy which\ncannot be represented by asn1.ObjectIdentifier, it will not be included in\nPolicyIdentifiers, but will be present in Policies, which contains all parsed\npolicy OIDs.\nSee CreateCertificate for context about how this field and the Policies field\ninteract.", "type": "array", "items": { "$ref": "#/definitions/ObjectIdentifier" } }, + "PolicyMappings": { + "description": "PolicyMappings contains a list of policy mappings included in the certificate.", + "type": "array", + "items": { + "$ref": "#/definitions/PolicyMapping" + } + }, "PublicKey": {}, "PublicKeyAlgorithm": { "$ref": "#/definitions/PublicKeyAlgorithm" @@ -3322,6 +3349,15 @@ "format": "uint8" } }, + "RequireExplicitPolicy": { + "description": "RequireExplicitPolicy and RequireExplicitPolicyZero indicate the presence\nand value of the requireExplicitPolicy field of the policyConstraints\nextension.\n\nThe value of RequireExplicitPolicy indicates the number of additional\ncertificates in the path after this certificate before an explicit policy\nis required for the rest of the path. When an explicit policy is required,\neach subsequent certificate in the path must contain a required policy OID,\nor a policy OID which has been declared as equivalent through the policy\nmapping extension.\n\nWhen parsing a certificate, a positive non-zero RequireExplicitPolicy\nmeans that the field was specified, -1 means it was unset, and\nRequireExplicitPolicyZero being true mean that the field was explicitly\nset to zero. The case of RequireExplicitPolicy==0 with\nRequireExplicitPolicyZero==false should be treated equivalent to -1\n(unset).", + "type": "integer", + "format": "int64" + }, + "RequireExplicitPolicyZero": { + "description": "RequireExplicitPolicyZero indicates that RequireExplicitPolicy==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, "SerialNumber": { "type": "string" }, @@ -4047,6 +4083,9 @@ "annotationsPermissions": { "$ref": "#/definitions/AnnotationPermission" }, + "apiVersion": { + "type": "string" + }, "canAdmin": { "type": "boolean" }, @@ -4737,6 +4776,9 @@ "type": "integer", "format": "int64" }, + "managedBy": { + "$ref": "#/definitions/ManagerKind" + }, "orgId": { "type": "integer", "format": "int64" @@ -4752,10 +4794,6 @@ "$ref": "#/definitions/Folder" } }, - "repository": { - "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", - "type": "string" - }, "title": { "type": "string" }, @@ -4785,11 +4823,10 @@ "type": "integer", "format": "int64" }, - "parentUid": { - "type": "string" + "managedBy": { + "$ref": "#/definitions/ManagerKind" }, - "repository": { - "description": "When the folder belongs to a repository\nNOTE: this is only populated when folders are managed by unified storage", + "parentUid": { "type": "string" }, "title": { @@ -5536,6 +5573,11 @@ } } }, + "ManagerKind": { + "description": "It can be a user or a tool or a generic API client.\n+enum", + "type": "string", + "title": "ManagerKind is the type of manager, which is responsible for managing the resource." + }, "MassDeleteAnnotationsCmd": { "type": "object", "properties": { @@ -6175,6 +6217,20 @@ "$ref": "#/definitions/Playlist" } }, + "PolicyMapping": { + "type": "object", + "title": "PolicyMapping represents a policy mapping entry in the policyMappings extension.", + "properties": { + "IssuerDomainPolicy": { + "description": "IssuerDomainPolicy contains a policy OID the issuing certificate considers\nequivalent to SubjectDomainPolicy in the subject certificate.", + "type": "string" + }, + "SubjectDomainPolicy": { + "description": "SubjectDomainPolicy contains a OID the issuing certificate considers\nequivalent to IssuerDomainPolicy in the subject certificate.", + "type": "string" + } + } + }, "PostAnnotationsCmd": { "type": "object", "required": [ @@ -9946,6 +10002,12 @@ "type": "object" } }, + "notAcceptableError": { + "description": "NotAcceptableError is returned when the server cannot produce a response matching the accepted formats.", + "schema": { + "$ref": "#/definitions/ErrorResponseBody" + } + }, "notFoundError": { "description": "NotFoundError is returned when the requested resource was not found.", "schema": { diff --git a/public/api-merged.json b/public/api-merged.json index 873200a1fb7..44d88382eb6 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -13615,6 +13615,24 @@ "type": "string" } }, + "InhibitAnyPolicy": { + "description": "InhibitAnyPolicy and InhibitAnyPolicyZero indicate the presence and value\nof the inhibitAnyPolicy extension.\n\nThe value of InhibitAnyPolicy indicates the number of additional\ncertificates in the path after this certificate that may use the\nanyPolicy policy OID to indicate a match with any other policy.\n\nWhen parsing a certificate, a positive non-zero InhibitAnyPolicy means\nthat the field was specified, -1 means it was unset, and\nInhibitAnyPolicyZero being true mean that the field was explicitly set to\nzero. The case of InhibitAnyPolicy==0 with InhibitAnyPolicyZero==false\nshould be treated equivalent to -1 (unset).", + "type": "integer", + "format": "int64" + }, + "InhibitAnyPolicyZero": { + "description": "InhibitAnyPolicyZero indicates that InhibitAnyPolicy==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, + "InhibitPolicyMapping": { + "description": "InhibitPolicyMapping and InhibitPolicyMappingZero indicate the presence\nand value of the inhibitPolicyMapping field of the policyConstraints\nextension.\n\nThe value of InhibitPolicyMapping indicates the number of additional\ncertificates in the path after this certificate that may use policy\nmapping.\n\nWhen parsing a certificate, a positive non-zero InhibitPolicyMapping\nmeans that the field was specified, -1 means it was unset, and\nInhibitPolicyMappingZero being true mean that the field was explicitly\nset to zero. The case of InhibitPolicyMapping==0 with\nInhibitPolicyMappingZero==false should be treated equivalent to -1\n(unset).", + "type": "integer", + "format": "int64" + }, + "InhibitPolicyMappingZero": { + "description": "InhibitPolicyMappingZero indicates that InhibitPolicyMapping==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, "IsCA": { "type": "boolean" }, @@ -13679,19 +13697,26 @@ } }, "Policies": { - "description": "Policies contains all policy identifiers included in the certificate.\nIn Go 1.22, encoding/gob cannot handle and ignores this field.", + "description": "Policies contains all policy identifiers included in the certificate.\nSee CreateCertificate for context about how this field and the PolicyIdentifiers field\ninteract.\nIn Go 1.22, encoding/gob cannot handle and ignores this field.", "type": "array", "items": { "type": "string" } }, "PolicyIdentifiers": { - "description": "PolicyIdentifiers contains asn1.ObjectIdentifiers, the components\nof which are limited to int32. If a certificate contains a policy which\ncannot be represented by asn1.ObjectIdentifier, it will not be included in\nPolicyIdentifiers, but will be present in Policies, which contains all parsed\npolicy OIDs.", + "description": "PolicyIdentifiers contains asn1.ObjectIdentifiers, the components\nof which are limited to int32. If a certificate contains a policy which\ncannot be represented by asn1.ObjectIdentifier, it will not be included in\nPolicyIdentifiers, but will be present in Policies, which contains all parsed\npolicy OIDs.\nSee CreateCertificate for context about how this field and the Policies field\ninteract.", "type": "array", "items": { "$ref": "#/definitions/ObjectIdentifier" } }, + "PolicyMappings": { + "description": "PolicyMappings contains a list of policy mappings included in the certificate.", + "type": "array", + "items": { + "$ref": "#/definitions/PolicyMapping" + } + }, "PublicKey": {}, "PublicKeyAlgorithm": { "$ref": "#/definitions/PublicKeyAlgorithm" @@ -13731,6 +13756,15 @@ "format": "uint8" } }, + "RequireExplicitPolicy": { + "description": "RequireExplicitPolicy and RequireExplicitPolicyZero indicate the presence\nand value of the requireExplicitPolicy field of the policyConstraints\nextension.\n\nThe value of RequireExplicitPolicy indicates the number of additional\ncertificates in the path after this certificate before an explicit policy\nis required for the rest of the path. When an explicit policy is required,\neach subsequent certificate in the path must contain a required policy OID,\nor a policy OID which has been declared as equivalent through the policy\nmapping extension.\n\nWhen parsing a certificate, a positive non-zero RequireExplicitPolicy\nmeans that the field was specified, -1 means it was unset, and\nRequireExplicitPolicyZero being true mean that the field was explicitly\nset to zero. The case of RequireExplicitPolicy==0 with\nRequireExplicitPolicyZero==false should be treated equivalent to -1\n(unset).", + "type": "integer", + "format": "int64" + }, + "RequireExplicitPolicyZero": { + "description": "RequireExplicitPolicyZero indicates that RequireExplicitPolicy==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, "SerialNumber": { "type": "string" }, @@ -18116,6 +18150,20 @@ "$ref": "#/definitions/Playlist" } }, + "PolicyMapping": { + "type": "object", + "title": "PolicyMapping represents a policy mapping entry in the policyMappings extension.", + "properties": { + "IssuerDomainPolicy": { + "description": "IssuerDomainPolicy contains a policy OID the issuing certificate considers\nequivalent to SubjectDomainPolicy in the subject certificate.", + "type": "string" + }, + "SubjectDomainPolicy": { + "description": "SubjectDomainPolicy contains a OID the issuing certificate considers\nequivalent to IssuerDomainPolicy in the subject certificate.", + "type": "string" + } + } + }, "PostAnnotationsCmd": { "type": "object", "required": [ diff --git a/public/openapi3.json b/public/openapi3.json index 9d4c63135c3..ccfd199e14e 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -3676,6 +3676,24 @@ }, "type": "array" }, + "InhibitAnyPolicy": { + "description": "InhibitAnyPolicy and InhibitAnyPolicyZero indicate the presence and value\nof the inhibitAnyPolicy extension.\n\nThe value of InhibitAnyPolicy indicates the number of additional\ncertificates in the path after this certificate that may use the\nanyPolicy policy OID to indicate a match with any other policy.\n\nWhen parsing a certificate, a positive non-zero InhibitAnyPolicy means\nthat the field was specified, -1 means it was unset, and\nInhibitAnyPolicyZero being true mean that the field was explicitly set to\nzero. The case of InhibitAnyPolicy==0 with InhibitAnyPolicyZero==false\nshould be treated equivalent to -1 (unset).", + "format": "int64", + "type": "integer" + }, + "InhibitAnyPolicyZero": { + "description": "InhibitAnyPolicyZero indicates that InhibitAnyPolicy==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, + "InhibitPolicyMapping": { + "description": "InhibitPolicyMapping and InhibitPolicyMappingZero indicate the presence\nand value of the inhibitPolicyMapping field of the policyConstraints\nextension.\n\nThe value of InhibitPolicyMapping indicates the number of additional\ncertificates in the path after this certificate that may use policy\nmapping.\n\nWhen parsing a certificate, a positive non-zero InhibitPolicyMapping\nmeans that the field was specified, -1 means it was unset, and\nInhibitPolicyMappingZero being true mean that the field was explicitly\nset to zero. The case of InhibitPolicyMapping==0 with\nInhibitPolicyMappingZero==false should be treated equivalent to -1\n(unset).", + "format": "int64", + "type": "integer" + }, + "InhibitPolicyMappingZero": { + "description": "InhibitPolicyMappingZero indicates that InhibitPolicyMapping==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, "IsCA": { "type": "boolean" }, @@ -3740,19 +3758,26 @@ "type": "array" }, "Policies": { - "description": "Policies contains all policy identifiers included in the certificate.\nIn Go 1.22, encoding/gob cannot handle and ignores this field.", + "description": "Policies contains all policy identifiers included in the certificate.\nSee CreateCertificate for context about how this field and the PolicyIdentifiers field\ninteract.\nIn Go 1.22, encoding/gob cannot handle and ignores this field.", "items": { "type": "string" }, "type": "array" }, "PolicyIdentifiers": { - "description": "PolicyIdentifiers contains asn1.ObjectIdentifiers, the components\nof which are limited to int32. If a certificate contains a policy which\ncannot be represented by asn1.ObjectIdentifier, it will not be included in\nPolicyIdentifiers, but will be present in Policies, which contains all parsed\npolicy OIDs.", + "description": "PolicyIdentifiers contains asn1.ObjectIdentifiers, the components\nof which are limited to int32. If a certificate contains a policy which\ncannot be represented by asn1.ObjectIdentifier, it will not be included in\nPolicyIdentifiers, but will be present in Policies, which contains all parsed\npolicy OIDs.\nSee CreateCertificate for context about how this field and the Policies field\ninteract.", "items": { "$ref": "#/components/schemas/ObjectIdentifier" }, "type": "array" }, + "PolicyMappings": { + "description": "PolicyMappings contains a list of policy mappings included in the certificate.", + "items": { + "$ref": "#/components/schemas/PolicyMapping" + }, + "type": "array" + }, "PublicKey": {}, "PublicKeyAlgorithm": { "$ref": "#/components/schemas/PublicKeyAlgorithm" @@ -3792,6 +3817,15 @@ }, "type": "array" }, + "RequireExplicitPolicy": { + "description": "RequireExplicitPolicy and RequireExplicitPolicyZero indicate the presence\nand value of the requireExplicitPolicy field of the policyConstraints\nextension.\n\nThe value of RequireExplicitPolicy indicates the number of additional\ncertificates in the path after this certificate before an explicit policy\nis required for the rest of the path. When an explicit policy is required,\neach subsequent certificate in the path must contain a required policy OID,\nor a policy OID which has been declared as equivalent through the policy\nmapping extension.\n\nWhen parsing a certificate, a positive non-zero RequireExplicitPolicy\nmeans that the field was specified, -1 means it was unset, and\nRequireExplicitPolicyZero being true mean that the field was explicitly\nset to zero. The case of RequireExplicitPolicy==0 with\nRequireExplicitPolicyZero==false should be treated equivalent to -1\n(unset).", + "format": "int64", + "type": "integer" + }, + "RequireExplicitPolicyZero": { + "description": "RequireExplicitPolicyZero indicates that RequireExplicitPolicy==0 should be\ninterpreted as an actual maximum path length of zero. Otherwise, that\ncombination is interpreted as InhibitAnyPolicy not being set.", + "type": "boolean" + }, "SerialNumber": { "type": "string" }, @@ -8179,6 +8213,20 @@ }, "type": "array" }, + "PolicyMapping": { + "properties": { + "IssuerDomainPolicy": { + "description": "IssuerDomainPolicy contains a policy OID the issuing certificate considers\nequivalent to SubjectDomainPolicy in the subject certificate.", + "type": "string" + }, + "SubjectDomainPolicy": { + "description": "SubjectDomainPolicy contains a OID the issuing certificate considers\nequivalent to IssuerDomainPolicy in the subject certificate.", + "type": "string" + } + }, + "title": "PolicyMapping represents a policy mapping entry in the policyMappings extension.", + "type": "object" + }, "PostAnnotationsCmd": { "properties": { "dashboardId": { diff --git a/scripts/drone/variables.star b/scripts/drone/variables.star index c737ef65d61..51da1d676ba 100644 --- a/scripts/drone/variables.star +++ b/scripts/drone/variables.star @@ -3,7 +3,7 @@ global variables """ grabpl_version = "v3.1.2" -golang_version = "1.23.7" +golang_version = "1.24.1" # nodejs_version should match what's in ".nvmrc", but without the v prefix. nodejs_version = "22.11.0" From 4dbd1846c70c839fa71a06b7fb72d82d784aeb14 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Tue, 11 Mar 2025 17:28:36 +0000 Subject: [PATCH 190/312] Chore: bump codeql versions used in pr checks (#101957) * bump codeql versions used in pr checks * update supported versions * use glob syntax * wider glob --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/pr-codeql-analysis-go.yml | 4 ++-- .github/workflows/pr-codeql-analysis-javascript.yml | 4 ++-- .github/workflows/pr-codeql-analysis-python.yml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c1f90ceb831..8c8b1abde50 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -8,7 +8,7 @@ name: "CodeQL" on: workflow_dispatch: push: - branches: [main, v1.8.x, v2.0.x, v2.1.x, v2.6.x, v3.0.x, v3.1.x, v4.0.x, v4.1.x, v4.2.x, v4.3.x, v4.4.x, v4.5.x, v4.6.x, v4.7.x, v5.0.x, v5.1.x, v5.2.x, v5.3.x, v5.4.x, v6.0.x, v6.1.x, v6.2.x, v6.3.x, v6.4.x, v6.5.x, v6.6.x, v6.7.x, v7.0.x, v7.1.x, v7.2.x] + branches: [main, v*.*.*] paths-ignore: - '**/*.cue' - '**/*.json' diff --git a/.github/workflows/pr-codeql-analysis-go.yml b/.github/workflows/pr-codeql-analysis-go.yml index ce9082f4400..46645b7fa3f 100644 --- a/.github/workflows/pr-codeql-analysis-go.yml +++ b/.github/workflows/pr-codeql-analysis-go.yml @@ -40,7 +40,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: "go" @@ -50,4 +50,4 @@ jobs: make build-go - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-codeql-analysis-javascript.yml b/.github/workflows/pr-codeql-analysis-javascript.yml index 6c5264c926a..d24b7db9671 100644 --- a/.github/workflows/pr-codeql-analysis-javascript.yml +++ b/.github/workflows/pr-codeql-analysis-javascript.yml @@ -28,9 +28,9 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: "javascript" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-codeql-analysis-python.yml b/.github/workflows/pr-codeql-analysis-python.yml index aea55365afc..4e8b1b14747 100644 --- a/.github/workflows/pr-codeql-analysis-python.yml +++ b/.github/workflows/pr-codeql-analysis-python.yml @@ -26,9 +26,9 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: "python" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 85b0b47efdd811b46af2715aec7a54943d985ea6 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Tue, 11 Mar 2025 19:53:28 +0100 Subject: [PATCH 191/312] Alerting: Allow disabling provenance in the Prometheus conversion API (#101573) When creating Grafana-managed alerts from Prometheus rule definitions with mimirtool or cortextool, the rules are marked as "provisioned" and are not editable in the Grafana UI. This PR allows changing this by providing an extra header: --extra-header="X-Disable-Provenance=true". When provenance is disabled, we do not keep the original rule definition in YAML, so it is impossible to read it back using the Prometheus conversion API (mimirtool/cortextool). This is intentional because if we did keep it and the rule was later changed in the UI, its Prometheus YAML definition would no longer reflect the latest version of the alert rule, as it would be unchanged. --- .../ngalert/api/api_convert_prometheus.go | 38 +++++- .../api/api_convert_prometheus_test.go | 115 +++++++++++++++++ pkg/services/ngalert/prom/convert.go | 32 +++-- pkg/services/ngalert/prom/convert_test.go | 68 ++++++++++ .../ngalert/provisioning/alert_rules_test.go | 36 ++++++ .../provisioning/validation/provenance.go | 20 ++- .../validation/provenance_test.go | 60 +++++++++ .../alerting/api_convert_prometheus_test.go | 117 ++++++++++++++++++ 8 files changed, 466 insertions(+), 20 deletions(-) diff --git a/pkg/services/ngalert/api/api_convert_prometheus.go b/pkg/services/ngalert/api/api_convert_prometheus.go index 3c7878bc500..57d312768cb 100644 --- a/pkg/services/ngalert/api/api_convert_prometheus.go +++ b/pkg/services/ngalert/api/api_convert_prometheus.go @@ -188,11 +188,12 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusDeleteNamespace(c *contex } logger.Info("Deleting all Prometheus-imported rule groups", "folder_uid", namespace.UID, "folder_title", namespaceTitle) + provenance := getProvenance(c) filterOpts := &provisioning.FilterOptions{ NamespaceUIDs: []string{namespace.UID}, ImportedPrometheusRule: util.Pointer(true), } - err = srv.alertRuleService.DeleteRuleGroups(c.Req.Context(), c.SignedInUser, models.ProvenanceConvertedPrometheus, filterOpts) + err = srv.alertRuleService.DeleteRuleGroups(c.Req.Context(), c.SignedInUser, provenance, filterOpts) if errors.Is(err, models.ErrAlertRuleGroupNotFound) { return response.Empty(http.StatusNotFound) } @@ -218,7 +219,8 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusDeleteRuleGroup(c *contex } logger.Info("Deleting Prometheus-imported rule group", "folder_uid", folder.UID, "folder_title", namespaceTitle, "group", group) - err = srv.alertRuleService.DeleteRuleGroup(c.Req.Context(), c.SignedInUser, folder.UID, group, models.ProvenanceConvertedPrometheus) + provenance := getProvenance(c) + err = srv.alertRuleService.DeleteRuleGroup(c.Req.Context(), c.SignedInUser, folder.UID, group, provenance) if errors.Is(err, models.ErrAlertRuleGroupNotFound) { return response.Empty(http.StatusNotFound) } @@ -352,13 +354,21 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostRuleGroup(c *contextm return errorToResponse(err) } - group, err := srv.convertToGrafanaRuleGroup(c, ds, ns.UID, promGroup, logger) + provenance := getProvenance(c) + + // If the provenance is not ConvertedPrometheus, we don't keep the original rule definition. + // This is because the rules can be modified through the UI, which may break compatibility + // with the Prometheus format. We only preserve the original rule definition + // to ensure we can return them in this API in Prometheus format. + keepOriginalRuleDefinition := provenance == models.ProvenanceConvertedPrometheus + + group, err := srv.convertToGrafanaRuleGroup(c, ds, ns.UID, promGroup, keepOriginalRuleDefinition, logger) if err != nil { logger.Error("Failed to convert Prometheus rules to Grafana rules", "error", err) return errorToResponse(err) } - err = srv.alertRuleService.ReplaceRuleGroup(c.Req.Context(), c.SignedInUser, *group, models.ProvenanceConvertedPrometheus) + err = srv.alertRuleService.ReplaceRuleGroup(c.Req.Context(), c.SignedInUser, *group, provenance) if err != nil { logger.Error("Failed to replace rule group", "error", err) return errorToResponse(err) @@ -387,7 +397,14 @@ func (srv *ConvertPrometheusSrv) getOrCreateNamespace(c *contextmodel.ReqContext return ns, nil } -func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup(c *contextmodel.ReqContext, ds *datasources.DataSource, namespaceUID string, promGroup apimodels.PrometheusRuleGroup, logger log.Logger) (*models.AlertRuleGroup, error) { +func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup( + c *contextmodel.ReqContext, + ds *datasources.DataSource, + namespaceUID string, + promGroup apimodels.PrometheusRuleGroup, + keepOriginalRuleDefinition bool, + logger log.Logger, +) (*models.AlertRuleGroup, error) { logger.Info("Converting Prometheus rules to Grafana rules", "rules", len(promGroup.Rules), "folder_uid", namespaceUID, "datasource_uid", ds.UID, "datasource_type", ds.Type) rules := make([]prom.PrometheusRule, len(promGroup.Rules)) @@ -429,6 +446,7 @@ func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup(c *contextmodel.ReqCo AlertRules: prom.RulesConfig{ IsPaused: pauseAlertRules, }, + KeepOriginalRuleDefinition: util.Pointer(keepOriginalRuleDefinition), }, ) if err != nil { @@ -537,3 +555,13 @@ func promGroupHasRecordingRules(promGroup apimodels.PrometheusRuleGroup) bool { } return false } + +// getProvenance determines the provenance value to use for rules created via the Prometheus conversion API. +// If the X-Disable-Provenance header is present in the request, returns ProvenanceNone, +// otherwise returns ProvenanceConvertedPrometheus. +func getProvenance(ctx *contextmodel.ReqContext) models.Provenance { + if _, disabled := ctx.Req.Header[disableProvenanceHeaderName]; disabled { + return models.ProvenanceNone + } + return models.ProvenanceConvertedPrometheus +} diff --git a/pkg/services/ngalert/api/api_convert_prometheus_test.go b/pkg/services/ngalert/api/api_convert_prometheus_test.go index c6b0a33166a..fda8be8cecd 100644 --- a/pkg/services/ngalert/api/api_convert_prometheus_test.go +++ b/pkg/services/ngalert/api/api_convert_prometheus_test.go @@ -144,6 +144,11 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) { promDefinition, err := r.PrometheusRuleDefinition() require.NoError(t, err) require.Equal(t, expectedDef, promDefinition) + + // Verify provenance was set to ProvenanceConvertedPrometheus + prov, err := provenanceStore.GetProvenance(context.Background(), r, 1) + require.NoError(t, err) + require.Equal(t, models.ProvenanceConvertedPrometheus, prov) } }) @@ -341,6 +346,41 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) { }) } }) + + t.Run("with disable provenance header should use ProvenanceNone", func(t *testing.T) { + provenanceStore := fakes.NewFakeProvisioningStore() + srv, _, ruleStore, folderService := createConvertPrometheusSrv(t, withProvenanceStore(provenanceStore)) + + // Create a folder in the root + fldr := randFolder() + fldr.ParentUID = "" + folderService.ExpectedFolder = fldr + folderService.ExpectedFolders = []*folder.Folder{fldr} + ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr) + + // Create request with the X-Disable-Provenance header + rc := createRequestCtx() + rc.Req.Header.Set("X-Disable-Provenance", "true") + + response := srv.RouteConvertPrometheusPostRuleGroup(rc, fldr.Title, simpleGroup) + require.Equal(t, http.StatusAccepted, response.Status()) + + // Get the created rules + rules, err := ruleStore.ListAlertRules(context.Background(), &models.ListAlertRulesQuery{ + OrgID: 1, + }) + require.NoError(t, err) + require.Len(t, rules, 2) + + // Verify provenance was set to ProvenanceNone + for _, r := range rules { + prov, err := provenanceStore.GetProvenance(context.Background(), r, 1) + require.NoError(t, err) + require.Equal(t, models.ProvenanceNone, prov, "Provenance should be ProvenanceNone when X-Disable-Provenance header is set") + // Prometheus rule definition should not be saved when provenance is disabled + require.Nil(t, r.Metadata.PrometheusStyleRule) + } + }) } func TestRouteConvertPrometheusGetRuleGroup(t *testing.T) { @@ -743,6 +783,29 @@ func TestRouteConvertPrometheusDeleteNamespace(t *testing.T) { require.NoError(t, err) require.NotNil(t, remaining) }) + + t.Run("with disable provenance header should still be able to delete rules", func(t *testing.T) { + provenanceStore := fakes.NewFakeProvisioningStore() + srv, ruleStore, fldr, rule := initNamespace("prometheus definition", withProvenanceStore(provenanceStore)) + + // Mark the rule as provisioned with API provenance + err := provenanceStore.SetProvenance(context.Background(), rule, 1, models.ProvenanceConvertedPrometheus) + require.NoError(t, err) + + rc := createRequestCtx() + rc.Req.Header.Set("X-Disable-Provenance", "true") + + response := srv.RouteConvertPrometheusDeleteNamespace(rc, fldr.Title) + require.Equal(t, http.StatusAccepted, response.Status()) + + // Verify the rule was deleted + remaining, err := ruleStore.GetAlertRuleByUID(context.Background(), &models.GetAlertRuleByUIDQuery{ + UID: rule.UID, + OrgID: rule.OrgID, + }) + require.Error(t, err) + require.Nil(t, remaining) + }) }) } @@ -854,6 +917,29 @@ func TestRouteConvertPrometheusDeleteRuleGroup(t *testing.T) { require.NoError(t, err) require.NotNil(t, remaining) }) + + t.Run("with disable provenance header should still be able to delete rules", func(t *testing.T) { + provenanceStore := fakes.NewFakeProvisioningStore() + srv, ruleStore, fldr, rule := initGroup("", groupName, withProvenanceStore(provenanceStore)) + + // Mark the rule as provisioned with API provenance + err := provenanceStore.SetProvenance(context.Background(), rule, 1, models.ProvenanceConvertedPrometheus) + require.NoError(t, err) + + rc := createRequestCtx() + rc.Req.Header.Set("X-Disable-Provenance", "true") + + response := srv.RouteConvertPrometheusDeleteRuleGroup(rc, fldr.Title, groupName) + require.Equal(t, http.StatusAccepted, response.Status()) + + // Verify the rule was deleted + remaining, err := ruleStore.GetAlertRuleByUID(context.Background(), &models.GetAlertRuleByUIDQuery{ + UID: rule.UID, + OrgID: rule.OrgID, + }) + require.Error(t, err) + require.Nil(t, remaining) + }) }) } @@ -995,3 +1081,32 @@ func TestGetWorkingFolderUID(t *testing.T) { require.Equal(t, specifiedFolderUID, folderUID) }) } + +func TestGetProvenance(t *testing.T) { + t.Run("should return ProvenanceConvertedPrometheus when header is not present", func(t *testing.T) { + rc := createRequestCtx() + // Ensure the header is not present + rc.Req.Header.Del(disableProvenanceHeaderName) + + provenance := getProvenance(rc) + require.Equal(t, models.ProvenanceConvertedPrometheus, provenance) + }) + + t.Run("should return ProvenanceNone when header is present", func(t *testing.T) { + rc := createRequestCtx() + // Set the disable provenance header + rc.Req.Header.Set(disableProvenanceHeaderName, "true") + + provenance := getProvenance(rc) + require.Equal(t, models.ProvenanceNone, provenance) + }) + + t.Run("should return ProvenanceNone when header is present with any value", func(t *testing.T) { + rc := createRequestCtx() + // Set the disable provenance header with an empty value + rc.Req.Header.Set(disableProvenanceHeaderName, "") + + provenance := getProvenance(rc) + require.Equal(t, models.ProvenanceNone, provenance) + }) +} diff --git a/pkg/services/ngalert/prom/convert.go b/pkg/services/ngalert/prom/convert.go index ccbbf89e23b..f64b17bfc5e 100644 --- a/pkg/services/ngalert/prom/convert.go +++ b/pkg/services/ngalert/prom/convert.go @@ -37,8 +37,12 @@ type Config struct { EvaluationOffset *time.Duration ExecErrState models.ExecutionErrorState NoDataState models.NoDataState - RecordingRules RulesConfig - AlertRules RulesConfig + // KeepOriginalRuleDefinition indicates whether the original Prometheus rule definition + // if saved to the alert rule metadata. If not, then it will not be possible to convert + // the alert rule back to Prometheus format. + KeepOriginalRuleDefinition *bool + RecordingRules RulesConfig + AlertRules RulesConfig } // RulesConfig contains configuration that applies to either recording or alerting rules. @@ -51,10 +55,11 @@ var ( defaultEvaluationOffset = 0 * time.Minute defaultConfig = Config{ - FromTimeRange: &defaultTimeRange, - EvaluationOffset: &defaultEvaluationOffset, - ExecErrState: models.ErrorErrState, - NoDataState: models.OK, + FromTimeRange: &defaultTimeRange, + EvaluationOffset: &defaultEvaluationOffset, + ExecErrState: models.ErrorErrState, + NoDataState: models.OK, + KeepOriginalRuleDefinition: util.Pointer(true), } ) @@ -87,7 +92,9 @@ func NewConverter(cfg Config) (*Converter, error) { if cfg.NoDataState == "" { cfg.NoDataState = defaultConfig.NoDataState } - + if cfg.KeepOriginalRuleDefinition == nil { + cfg.KeepOriginalRuleDefinition = defaultConfig.KeepOriginalRuleDefinition + } if cfg.DatasourceType != datasources.DS_PROMETHEUS && cfg.DatasourceType != datasources.DS_LOKI { return nil, fmt.Errorf("invalid datasource type: %s", cfg.DatasourceType) } @@ -233,11 +240,12 @@ func (p *Converter) convertRule(orgID int64, namespaceUID string, promGroup Prom RuleGroup: promGroup.Name, IsPaused: isPaused, Record: record, - Metadata: models.AlertRuleMetadata{ - PrometheusStyleRule: &models.PrometheusStyleRule{ - OriginalRuleDefinition: string(originalRuleDefinition), - }, - }, + } + + if p.cfg.KeepOriginalRuleDefinition != nil && *p.cfg.KeepOriginalRuleDefinition { + result.Metadata.PrometheusStyleRule = &models.PrometheusStyleRule{ + OriginalRuleDefinition: string(originalRuleDefinition), + } } return result, nil diff --git a/pkg/services/ngalert/prom/convert_test.go b/pkg/services/ngalert/prom/convert_test.go index 2d7e2b26535..2281a9da1c0 100644 --- a/pkg/services/ngalert/prom/convert_test.go +++ b/pkg/services/ngalert/prom/convert_test.go @@ -618,3 +618,71 @@ func TestPrometheusRulesToGrafana_UID(t *testing.T) { }) }) } + +func TestPrometheusRulesToGrafana_KeepOriginalRuleDefinition(t *testing.T) { + orgID := int64(1) + namespace := "namespace" + + promGroup := PrometheusRuleGroup{ + Name: "test-group", + Rules: []PrometheusRule{ + { + Alert: "test-alert", + Expr: "up == 0", + }, + }, + } + + testCases := []struct { + name string + keepOriginalRuleDefinition *bool + expectDefinition bool + }{ + { + name: "keep original rule definition is true", + keepOriginalRuleDefinition: util.Pointer(true), + expectDefinition: true, + }, + { + name: "keep original rule definition is false", + keepOriginalRuleDefinition: util.Pointer(false), + expectDefinition: false, + }, + { + name: "keep original rule definition is nil (should use default)", + keepOriginalRuleDefinition: nil, + expectDefinition: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg := Config{ + DatasourceUID: "datasource-uid", + DatasourceType: datasources.DS_PROMETHEUS, + DefaultInterval: 1 * time.Minute, + KeepOriginalRuleDefinition: tc.keepOriginalRuleDefinition, + } + + converter, err := NewConverter(cfg) + require.NoError(t, err) + + // Convert the Prometheus rule to Grafana + grafanaGroup, err := converter.PrometheusRulesToGrafana(orgID, namespace, promGroup) + require.NoError(t, err) + require.Len(t, grafanaGroup.Rules, 1) + + if tc.expectDefinition { + originalRuleDefinition, err := yaml.Marshal(promGroup.Rules[0]) + require.NoError(t, err) + require.Equal( + t, + string(originalRuleDefinition), + grafanaGroup.Rules[0].Metadata.PrometheusStyleRule.OriginalRuleDefinition, + ) + } else { + require.Nil(t, grafanaGroup.Rules[0].Metadata.PrometheusStyleRule) + } + }) + } +} diff --git a/pkg/services/ngalert/provisioning/alert_rules_test.go b/pkg/services/ngalert/provisioning/alert_rules_test.go index c98c293fd34..68991883c03 100644 --- a/pkg/services/ngalert/provisioning/alert_rules_test.go +++ b/pkg/services/ngalert/provisioning/alert_rules_test.go @@ -674,6 +674,42 @@ func TestAlertRuleService(t *testing.T) { to: models.ProvenanceNone, errNil: false, }, + { + name: "should be able to update from provenance none to 'converted prometheus'", + from: models.ProvenanceNone, + to: models.ProvenanceConvertedPrometheus, + errNil: true, + }, + { + name: "should be able to update from provenance 'converted prometheus' to none", + from: models.ProvenanceConvertedPrometheus, + to: models.ProvenanceNone, + errNil: true, + }, + { + name: "should not be able to update from provenance 'converted prometheus' to api", + from: models.ProvenanceConvertedPrometheus, + to: models.ProvenanceAPI, + errNil: false, + }, + { + name: "should not be able to update from provenance 'converted prometheus' to file", + from: models.ProvenanceConvertedPrometheus, + to: models.ProvenanceFile, + errNil: false, + }, + { + name: "should not be able to update from provenance api to 'converted prometheus'", + from: models.ProvenanceAPI, + to: models.ProvenanceConvertedPrometheus, + errNil: false, + }, + { + name: "should not be able to update from provenance file to 'converted prometheus'", + from: models.ProvenanceFile, + to: models.ProvenanceConvertedPrometheus, + errNil: false, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/pkg/services/ngalert/provisioning/validation/provenance.go b/pkg/services/ngalert/provisioning/validation/provenance.go index 387cdd56b3e..b57dcee37db 100644 --- a/pkg/services/ngalert/provisioning/validation/provenance.go +++ b/pkg/services/ngalert/provisioning/validation/provenance.go @@ -7,9 +7,23 @@ import ( // CanUpdateProvenanceInRuleGroup checks if a provenance can be updated for a rule group and its alerts. // ReplaceRuleGroup function intends to replace an entire rule group: inserting, updating, and removing rules. func CanUpdateProvenanceInRuleGroup(storedProvenance, provenance models.Provenance) bool { - return storedProvenance == provenance || - storedProvenance == models.ProvenanceNone || - (storedProvenance == models.ProvenanceAPI && provenance == models.ProvenanceNone) + // Same provenance is always allowed + if storedProvenance == provenance { + return true + } + + // Can always update stored ProvenanceNone + if storedProvenance == models.ProvenanceNone { + return true + } + + // Can reset to ProvenanceNone from specific provenances + if provenance == models.ProvenanceNone { + return storedProvenance == models.ProvenanceAPI || + storedProvenance == models.ProvenanceConvertedPrometheus + } + + return false } type ProvenanceStatusTransitionValidator = func(from, to models.Provenance) error diff --git a/pkg/services/ngalert/provisioning/validation/provenance_test.go b/pkg/services/ngalert/provisioning/validation/provenance_test.go index b98ce241b03..04b9c61eccb 100644 --- a/pkg/services/ngalert/provisioning/validation/provenance_test.go +++ b/pkg/services/ngalert/provisioning/validation/provenance_test.go @@ -15,6 +15,7 @@ func TestValidateProvenanceRelaxed(t *testing.T) { models.ProvenanceNone, models.ProvenanceAPI, models.ProvenanceFile, + models.ProvenanceConvertedPrometheus, models.Provenance(fmt.Sprintf("random-%s", util.GenerateShortUID())), } t.Run("all transitions from 'none' are allowed", func(t *testing.T) { @@ -49,3 +50,62 @@ func TestValidateProvenanceRelaxed(t *testing.T) { } }) } + +func TestCanUpdateProvenanceInRuleGroup(t *testing.T) { + all := []models.Provenance{ + models.ProvenanceNone, + models.ProvenanceAPI, + models.ProvenanceFile, + models.ProvenanceConvertedPrometheus, + models.Provenance(fmt.Sprintf("random-%s", util.GenerateShortUID())), + } + + t.Run("same provenance transitions are allowed", func(t *testing.T) { + for _, provenance := range all { + assert.True(t, CanUpdateProvenanceInRuleGroup(provenance, provenance)) + } + }) + + t.Run("all transitions from 'none' are allowed", func(t *testing.T) { + for _, provenance := range all { + assert.True(t, CanUpdateProvenanceInRuleGroup(models.ProvenanceNone, provenance)) + } + }) + + t.Run("only specific provenances can transition to 'none'", func(t *testing.T) { + allowed := []models.Provenance{ + models.ProvenanceAPI, + models.ProvenanceConvertedPrometheus, + } + + for _, from := range allowed { + assert.True(t, CanUpdateProvenanceInRuleGroup(from, models.ProvenanceNone), + "transition %s -> 'none' should be allowed", from) + } + + notAllowed := []models.Provenance{ + models.ProvenanceFile, + models.Provenance(fmt.Sprintf("random-%s", util.GenerateShortUID())), + } + + for _, from := range notAllowed { + assert.False(t, CanUpdateProvenanceInRuleGroup(from, models.ProvenanceNone), + "transition %s -> 'none' should not be allowed", from) + } + }) + + t.Run("transitions between different provenances are not allowed", func(t *testing.T) { + for _, from := range all { + if from == models.ProvenanceNone { + continue // always allowed + } + for _, to := range all { + if from == to || to == models.ProvenanceNone { + continue // always allowed + } + assert.False(t, CanUpdateProvenanceInRuleGroup(from, to), + "transition %s -> '%s' should not be allowed", from, to) + } + } + }) +} diff --git a/pkg/tests/api/alerting/api_convert_prometheus_test.go b/pkg/tests/api/alerting/api_convert_prometheus_test.go index ec881bc09c5..e07382e6bca 100644 --- a/pkg/tests/api/alerting/api_convert_prometheus_test.go +++ b/pkg/tests/api/alerting/api_convert_prometheus_test.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/services/datasources" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" + "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/tests/testinfra" @@ -603,6 +604,122 @@ func TestIntegrationConvertPrometheusEndpoints_FolderUIDHeader(t *testing.T) { }) } +func TestIntegrationConvertPrometheusEndpoints_Provenance(t *testing.T) { + runTest := func(t *testing.T, enableLokiPaths bool) { + testinfra.SQLiteIntegrationTest(t) + + // Setup Grafana and its Database + dir, gpath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + DisableLegacyAlerting: true, + EnableUnifiedAlerting: true, + DisableAnonymous: true, + AppModeProduction: true, + EnableFeatureToggles: []string{"alertingConversionAPI", "grafanaManagedRecordingRulesDatasources", "grafanaManagedRecordingRules"}, + EnableRecordingRules: true, + }) + + grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, gpath) + + // Create admin user + createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleAdmin), + Password: "password", + Login: "admin", + }) + adminClient := newAlertingApiClient(grafanaListedAddr, "admin", "password") + adminClient.prometheusConversionUseLokiPaths = enableLokiPaths + + ds := adminClient.CreateDatasource(t, datasources.DS_PROMETHEUS) + + t.Run("default provenance is ProvenanceConvertedPrometheus", func(t *testing.T) { + namespace := "test-namespace-provenance-" + util.GenerateShortUID() + + // We have to create a folder to get its UID to use in the ruler API later to fetch the rule group. + namespaceUID := util.GenerateShortUID() + adminClient.CreateFolder(t, namespaceUID, namespace) + + adminClient.ConvertPrometheusPostRuleGroup(t, namespace, ds.Body.Datasource.UID, promGroup1, nil) + + // Get the rule group using the ruler API and check its provenance + ruleGroup, status := adminClient.GetRulesGroup(t, namespaceUID, promGroup1.Name) + require.Equal(t, http.StatusAccepted, status) + for _, rule := range ruleGroup.Rules { + require.Equal(t, apimodels.Provenance(models.ProvenanceConvertedPrometheus), rule.GrafanaManagedAlert.Provenance) + } + }) + + t.Run("with disable provenance header should use ProvenanceNone", func(t *testing.T) { + namespace := "test-namespace-provenance-" + util.GenerateShortUID() + + // We have to create a folder to get its UID to use in the ruler API later to fetch the rule group. + namespaceUID := util.GenerateShortUID() + adminClient.CreateFolder(t, namespaceUID, namespace) + + // Create rule group with the X-Disable-Provenance header + headers := map[string]string{ + "X-Disable-Provenance": "true", + } + adminClient.ConvertPrometheusPostRuleGroup(t, namespace, ds.Body.Datasource.UID, promGroup1, headers) + + // Get the rule group using the ruler API and check its provenance + ruleGroup, status := adminClient.GetRulesGroup(t, namespaceUID, promGroup1.Name) + require.Equal(t, http.StatusAccepted, status) + for _, rule := range ruleGroup.Rules { + require.Equal(t, apimodels.Provenance(models.ProvenanceNone), rule.GrafanaManagedAlert.Provenance) + } + }) + + t.Run("can delete rule groups with X-Disable-Provenance header", func(t *testing.T) { + namespace := "test-namespace-delete-provenance-" + util.GenerateShortUID() + namespaceUID := util.GenerateShortUID() + adminClient.CreateFolder(t, namespaceUID, namespace) + + // Create a rule group + adminClient.ConvertPrometheusPostRuleGroup(t, namespace, ds.Body.Datasource.UID, promGroup1, nil) + + // Now try to delete with X-Disable-Provenance header + // This should succeed + headers := map[string]string{ + "X-Disable-Provenance": "true", + } + adminClient.ConvertPrometheusDeleteRuleGroup(t, namespace, promGroup1.Name, headers) + + // Verify the rule group is gone + _, status, _ := adminClient.GetRulesGroupWithStatus(t, namespaceUID, promGroup1.Name) + require.Equal(t, http.StatusNotFound, status) + }) + + t.Run("can delete namespaces with X-Disable-Provenance header", func(t *testing.T) { + namespace := "test-namespace-delete-ns-provenance-" + util.GenerateShortUID() + namespaceUID := util.GenerateShortUID() + adminClient.CreateFolder(t, namespaceUID, namespace) + + // Create a rule group with provenance=ProvenanceConvertedPrometheus + adminClient.ConvertPrometheusPostRuleGroup(t, namespace, ds.Body.Datasource.UID, promGroup1, nil) + + // Now delete with X-Disable-Provenance header + // This should succeed + headers := map[string]string{ + "X-Disable-Provenance": "true", + } + adminClient.ConvertPrometheusDeleteNamespace(t, namespace, headers) + + // Verify the namespace has no rule groups + namespaces := adminClient.ConvertPrometheusGetAllRules(t, nil) + _, exists := namespaces[namespace] + require.False(t, exists) + }) + } + + t.Run("with the mimirtool paths", func(t *testing.T) { + runTest(t, false) + }) + + t.Run("with the cortextool Loki paths", func(t *testing.T) { + runTest(t, true) + }) +} + func TestIntegrationConvertPrometheusEndpoints_Delete(t *testing.T) { runTest := func(t *testing.T, enableLokiPaths bool) { testinfra.SQLiteIntegrationTest(t) From 700f1225df6e8ae1f0f3b31c60a05959eced9c4b Mon Sep 17 00:00:00 2001 From: Isabella Siu Date: Tue, 11 Mar 2025 16:09:22 -0400 Subject: [PATCH 192/312] AWS Datasources: Update grafana assume role docs to remove unnecessary flags (#101086) Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> --- .../datasources/aws-cloudwatch/aws-authentication/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/datasources/aws-cloudwatch/aws-authentication/index.md b/docs/sources/datasources/aws-cloudwatch/aws-authentication/index.md index 9ae64f44856..6cebd2089fd 100644 --- a/docs/sources/datasources/aws-cloudwatch/aws-authentication/index.md +++ b/docs/sources/datasources/aws-cloudwatch/aws-authentication/index.md @@ -164,7 +164,7 @@ Grafana Assume Role is currently in [private preview](https://grafana.com/docs/r It's currently only available for Amazon CloudWatch. -To get early access this feature, reach out to Customer Support and ask for the `awsDatasourcesTempCredentials` feature toggle to be enabled and the `cloudwatchRemoteDatasource` and `athenaRemoteDatasource` feature toggles to be disabled on your account. +To gain early access to this feature, contact Customer Support and ask for the `awsDatasourcesTempCredentials` feature toggle to be enabled on your account. {{% /admonition %}} The Grafana Assume Role authentication provider lets you authenticate with AWS without having to create and maintain long term AWS users or rotate their access and secret keys. Instead, you can create an IAM role that has permissions to access CloudWatch and a trust relationship with Grafana's AWS account. Grafana's AWS account then makes an STS request to AWS to create temporary credentials to access your AWS data. It makes this STS request by passing along an `externalID` that's unique per Cloud account, to ensure that Grafana Cloud users can only access their own AWS data. For more information, refer to the [AWS documentation on external ID](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html). From 9870718c3a9804031a8daf4a6b208d0f0ac19aea Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Tue, 11 Mar 2025 20:31:47 +0000 Subject: [PATCH 193/312] Alerting: Enable `jsx-no-useless-fragment` rule (#101884) * Add no-useless-fragment rule for alerting code * Auto-fix most no-useless-fragment cases * Manually fix remaining no-useless-fragment cases * Fix `invalid` passing to Field component * Allow AlertingPageWrapper to have optional children --- eslint.config.js | 1 + .../features/alerting/unified/RuleViewer.tsx | 10 +- .../components/AlertingPageWrapper.tsx | 7 +- .../contact-points/ContactPoint.tsx | 16 +- .../EditDefaultPolicyForm.tsx | 54 +-- .../notification-policies/Policy.tsx | 352 +++++++++--------- .../receivers/AlertInstanceModalSelector.tsx | 4 +- .../components/receivers/TemplateForm.tsx | 22 +- .../receivers/form/GenerateAlertDataModal.tsx | 48 ++- .../receivers/form/ReceiverForm.tsx | 58 ++- .../receivers/form/fields/DeletedSubform.tsx | 2 +- .../rule-editor/NotificationsStep.tsx | 12 +- .../alert-rule-form/ModifyExportRuleForm.tsx | 46 ++- .../NotificationRouteDetailsModal.tsx | 12 +- .../CloudDataSourceSelector.tsx | 62 ++- .../components/rules/EditRuleGroupModal.tsx | 219 ++++++----- .../rules/Filter/RulesFilter.v2.tsx | 94 +++-- .../unified/components/rules/NoRulesCTA.tsx | 20 +- .../components/rules/RulesFilter.test.tsx | 2 +- .../central-state-history/EventDetails.tsx | 24 +- .../rules/state-history/LokiStateHistory.tsx | 18 +- .../alerting/unified/home/Insights.tsx | 34 +- .../components/RuleGroupActionsMenu.tsx | 28 +- 23 files changed, 549 insertions(+), 596 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 8c4910b7310..3b29e36521b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -262,6 +262,7 @@ module.exports = [ 'prefer-const': 'error', 'react/no-unused-prop-types': 'error', 'react/self-closing-comp': 'error', + 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }], 'unicorn/no-unused-properties': 'error', }, }, diff --git a/public/app/features/alerting/unified/RuleViewer.tsx b/public/app/features/alerting/unified/RuleViewer.tsx index 9beaea7325a..4d4889eab84 100644 --- a/public/app/features/alerting/unified/RuleViewer.tsx +++ b/public/app/features/alerting/unified/RuleViewer.tsx @@ -15,7 +15,7 @@ import { stringifyErrorLike } from './utils/misc'; import { getRuleIdFromPathname, parse as parseRuleId } from './utils/rule-id'; import { withPageErrorBoundary } from './withPageErrorBoundary'; -const RuleViewer = (): JSX.Element => { +const RuleViewer = () => { const params = useParams(); const id = getRuleIdFromPathname(params); @@ -48,11 +48,7 @@ const RuleViewer = (): JSX.Element => { } if (loading) { - return ( - - <> - - ); + return ; } if (rule) { @@ -73,7 +69,7 @@ const RuleViewer = (): JSX.Element => { } // we should never get to this state - return <>; + return null; }; export const defaultPageNav: NavModelItem = { diff --git a/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx b/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx index 68b011d7d22..e6dd4a486f8 100644 --- a/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx +++ b/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, ReactNode } from 'react'; import { useLocation } from 'react-use'; import { Page } from 'app/core/components/Page/Page'; @@ -12,9 +12,10 @@ import { NoAlertManagerWarning } from './NoAlertManagerWarning'; /** * This is the main alerting page wrapper, used by the alertmanager page wrapper and the alert rules list view */ -interface AlertingPageWrapperProps extends PageProps { +type AlertingPageWrapperProps = Omit & { isLoading?: boolean; -} + children?: ReactNode; +}; export const AlertingPageWrapper = ({ children, isLoading, ...rest }: AlertingPageWrapperProps) => ( diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx index c7a637e489f..dfe2d07589c 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx @@ -223,15 +223,13 @@ const ContactPointReceiverMetadataRow = ({ diagnostics, sendingResolved }: Conta {/* this is shown when the last delivery failed – we don't show any additional metadata */} {failedToSend ? ( - <> - - - - Last delivery attempt failed - - - - + + + + Last delivery attempt failed + + + ) : ( <> {/* this is shown when we have a last delivery attempt */} diff --git a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx index 475b6f358d3..ea90e5397fe 100644 --- a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx @@ -51,32 +51,34 @@ export const AmRootRouteForm = ({ actionButtons, alertManagerSourceName, onSubmi }); return (
- - <> -
- ( - handleContactPointSelect(changeValue, onChange), - }} - selectedContactPointName={value} - /> - )} - control={control} - name="receiver" - rules={{ required: { value: true, message: 'Required.' } }} - /> - or - - Create a contact point - -
- + +
+ ( + handleContactPointSelect(changeValue, onChange), + }} + selectedContactPointName={value} + /> + )} + control={control} + name="receiver" + rules={{ required: { value: true, message: 'Required.' } }} + /> + or + + Create a contact point + +
{ const showMore = moreCount > 0; return ( - <> - -
- {/* continueMatching and showMatchesAllLabelsWarning are mutually exclusive so the icons can't overlap */} - {continueMatching && } - {showMatchesAllLabelsWarning && } + +
+ {/* continueMatching and showMatchesAllLabelsWarning are mutually exclusive so the icons can't overlap */} + {continueMatching && } + {showMatchesAllLabelsWarning && } -
- - {/* Matchers and actions */} -
- - {hasChildPolicies ? ( - - ) : null} - {isImmutablePolicy ? ( - isAutogeneratedPolicyRoot ? ( - - ) : ( - - ) - ) : hasMatchers ? ( - - ) : ( - - No matchers - - )} - - {/* TODO maybe we should move errors to the gutter instead? */} - {errors.length > 0 && } - {provisioned && } - - {!isAutoGenerated && !readOnly && ( - - - {isDefaultPolicy ? ( - - ) : ( - - onAddPolicy(currentRoute, 'above')} - /> - onAddPolicy(currentRoute, 'below')} - /> - - onAddPolicy(currentRoute, 'child')} - /> - - } - > - - - )} - - - )} - {dropdownMenuActions.length > 0 && ( - {dropdownMenuActions}}> - - - )} - - -
- - {/* Metadata row */} - -
-
-
-
- {showPolicyChildren && ( - <> - {pageOfChildren.map((child) => { - const childInheritedProperties = getInheritedProperties(currentRoute, child, inheritedProperties); - // This child is autogenerated if it's the autogenerated root or if it's a child of an autogenerated policy. - const isThisChildAutoGenerated = isAutoGeneratedRootAndSimplifiedEnabled(child) || isAutoGenerated; - /* pass the "readOnly" prop from the parent, because for any child policy , if its parent it's not editable, - then the child policy should not be editable either */ - const isThisChildReadOnly = readOnly || provisioned || isAutoGenerated; - - return ( - + + {/* Matchers and actions */} +
+ + {hasChildPolicies ? ( + - ); - })} - {showMore && ( - - )} - - )} + ) : null} + {isImmutablePolicy ? ( + isAutogeneratedPolicyRoot ? ( + + ) : ( + + ) + ) : hasMatchers ? ( + + ) : ( + + No matchers + + )} + + {/* TODO maybe we should move errors to the gutter instead? */} + {errors.length > 0 && } + {provisioned && } + + {!isAutoGenerated && !readOnly && ( + + + {isDefaultPolicy ? ( + + ) : ( + + onAddPolicy(currentRoute, 'above')} + /> + onAddPolicy(currentRoute, 'below')} + /> + + onAddPolicy(currentRoute, 'child')} + /> + + } + > + + + )} + + + )} + {dropdownMenuActions.length > 0 && ( + {dropdownMenuActions}}> + + + )} + + +
+ + {/* Metadata row */} + +
- {showExportDrawer && } -
- +
+
+ {showPolicyChildren && ( + <> + {pageOfChildren.map((child) => { + const childInheritedProperties = getInheritedProperties(currentRoute, child, inheritedProperties); + // This child is autogenerated if it's the autogenerated root or if it's a child of an autogenerated policy. + const isThisChildAutoGenerated = isAutoGeneratedRootAndSimplifiedEnabled(child) || isAutoGenerated; + /* pass the "readOnly" prop from the parent, because for any child policy , if its parent it's not editable, + then the child policy should not be editable either */ + const isThisChildReadOnly = readOnly || provisioned || isAutoGenerated; + + return ( + + ); + })} + {showMore && ( + + )} + + )} +
+ {showExportDrawer && } +
); }; @@ -513,14 +511,12 @@ function MetadataRow({ )} {timingOptions && } {hasInheritedProperties && ( - <> - - - Inherited - - - - + + + Inherited + + + )}
diff --git a/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx b/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx index cd3e7c5d284..96bca41b8db 100644 --- a/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx +++ b/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx @@ -113,9 +113,7 @@ export function AlertInstanceModalSelector({ >
{ruleName}
- <> - {filteredRules[ruleName][0].labels.grafana_folder ?? ''} - + {filteredRules[ruleName][0].labels.grafana_folder ?? ''}
); diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx index 29186ded954..fdc83f84204 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx @@ -317,18 +317,16 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
{/* preview column – full height and half-width */} {isGrafanaAlertManager && ( - <> -
-
- -
- +
+
+ +
)}
diff --git a/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx b/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx index 417ccb4c3ba..4726ab7d27a 100644 --- a/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx @@ -97,31 +97,29 @@ export const GenerateAlertDataModal = ({ isOpen, onDismiss, onAccept }: Props) = setStatus('firing'); }} > - <> - - -
- -
-
- -
-
- setStatus(value)} /> - -
-
-
- + + +
+ +
+
+ +
+
+ setStatus(value)} /> + +
+
+
{alerts.length > 0 && ( diff --git a/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx b/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx index b3bee2c5074..74c2058a999 100644 --- a/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx @@ -197,38 +197,36 @@ export function ReceiverForm({ /> ); })} - <> + {isEditable && ( + + )} +
{isEditable && ( - + <> + {isSubmitting && ( + + )} + {!isSubmitting && } + )} -
- {isEditable && ( - <> - {isSubmitting && ( - - )} - {!isSubmitting && } - - )} - - Cancel - -
- + + Cancel + +
); diff --git a/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx b/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx index 256b20ecfa3..bb5537cbbee 100644 --- a/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx @@ -17,5 +17,5 @@ export function DeletedSubForm({ pathPrefix }: Props): JSX.Element { register(`${pathPrefix}.__deleted`); }, [register, pathPrefix]); - return <>; + return <>{null}; } diff --git a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx index 5bdcb964f13..7d1d429b312 100644 --- a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx @@ -240,16 +240,12 @@ function NeedHelpInfoForNotificationPolicy() { contentText={ - <> - Firing alert instances are routed to notification policies based on matching labels. The default - notification policy matches all alert instances. - + Firing alert instances are routed to notification policies based on matching labels. The default + notification policy matches all alert instances. - <> - Custom labels change the way your notifications are routed. First, add labels to your alert rule and then - connect them to your notification policy by adding label matchers. - + Custom labels change the way your notifications are routed. First, add labels to your alert rule and then + connect them to your notification policy by adding label matchers. - - -
e.preventDefault()}> -
- - {/* Step 1 */} - - {/* Step 2 */} - - {/* Step 3-4-5 */} - + + + e.preventDefault()}> +
+ + {/* Step 1 */} + + {/* Step 2 */} + + {/* Step 3-4-5 */} + - {/* Step 4 & 5 */} - - {/* Notifications step*/} - - {/* Annotations only for cloud and Grafana */} - - -
- - {exportData && } -
- + {/* Step 4 & 5 */} + + {/* Notifications step*/} + + {/* Annotations only for cloud and Grafana */} + +
+
+ + {exportData && } +
); } diff --git a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx index 8650ba3e3fb..35741d4b2ee 100644 --- a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx @@ -81,13 +81,11 @@ export function NotificationRouteDetailsModal({ {isDefault &&
Default policy
}
{!isDefault && ( - <> - - + )}
diff --git a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx index 5bcbaa7e620..c450216bb64 100644 --- a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx @@ -23,38 +23,36 @@ export const CloudDataSourceSelector = ({ disabled, onChangeCloudDatasource }: C const ruleFormType = watch('type'); return ( - <> -
- {(ruleFormType === RuleFormType.cloudAlerting || ruleFormType === RuleFormType.cloudRecording) && ( - - ( - { - // reset expression as they don't need to persist after changing datasources - setValue('expression', ''); - onChange(ds?.name ?? null); - onChangeCloudDatasource(ds?.uid ?? null); - }} - /> - )} - name="dataSourceName" - control={control} - rules={{ - required: { value: true, message: 'Please select a data source' }, - }} - /> - - )} -
- +
+ {(ruleFormType === RuleFormType.cloudAlerting || ruleFormType === RuleFormType.cloudRecording) && ( + + ( + { + // reset expression as they don't need to persist after changing datasources + setValue('expression', ''); + onChange(ds?.name ?? null); + onChangeCloudDatasource(ds?.uid ?? null); + }} + /> + )} + name="dataSourceName" + control={control} + rules={{ + required: { value: true, message: 'Please select a data source' }, + }} + /> + + )} +
); }; diff --git a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx index ae15d299c37..0d748604cf4 100644 --- a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx +++ b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx @@ -316,124 +316,113 @@ export function EditRuleGroupModalForm(props: ModalFormProps): React.ReactElemen return (
- <> - {!props.hideFolder && ( - - - {nameSpaceLabel} - - } - invalid={Boolean(errors.namespaceName) ? true : undefined} - error={errors.namespaceName?.message} - > - - - {isGrafanaManagedGroup && props.folderUrl && ( - - )} - - )} - - Evaluation group - - } - invalid={!!errors.groupName} - error={errors.groupName?.message} - > - - - - Evaluation interval - - } - invalid={Boolean(errors.groupInterval) ? true : undefined} - error={errors.groupInterval?.message} - > - + {!props.hideFolder && ( + + + {nameSpaceLabel} + + } + invalid={Boolean(errors.namespaceName) ? true : undefined} + error={errors.namespaceName?.message} + > - setValue('groupInterval', value, { shouldValidate: true, shouldDirty: true })} - /> - - - - {/* if we're dealing with a Grafana-managed group, check if the evaluation interval is valid / permitted */} - {isGrafanaManagedGroup && checkEvaluationIntervalGlobalLimit(watch('groupInterval')).exceedsLimit && ( - - )} - - {!hasSomeNoRecordingRules &&
This group does not contain alert rules.
} - {hasSomeNoRecordingRules && ( - <> -
List of rules that belong to this group
-
- #Eval column represents the number of evaluations needed before alert starts firing. -
- - - )} - {error && {stringifyErrorLike(error)}} -
- - - - -
- + icon="folder-open" + target="_blank" + /> + )} + + )} + + Evaluation group + + } + invalid={!!errors.groupName} + error={errors.groupName?.message} + > + + + + Evaluation interval + + } + invalid={Boolean(errors.groupInterval) ? true : undefined} + error={errors.groupInterval?.message} + > + + + setValue('groupInterval', value, { shouldValidate: true, shouldDirty: true })} + /> + + + + {/* if we're dealing with a Grafana-managed group, check if the evaluation interval is valid / permitted */} + {isGrafanaManagedGroup && checkEvaluationIntervalGlobalLimit(watch('groupInterval')).exceedsLimit && ( + + )} + + {!hasSomeNoRecordingRules &&
This group does not contain alert rules.
} + {hasSomeNoRecordingRules && ( + <> +
List of rules that belong to this group
+
+ #Eval column represents the number of evaluations needed before alert starts firing. +
+ + + )} + {error && {stringifyErrorLike(error)}} +
+ + + + +
); diff --git a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx index 1c682cc689e..aa5c9edafec 100644 --- a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx +++ b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx @@ -158,54 +158,52 @@ const SavedSearches = () => { const applySearch = useCallback((name: string) => {}, []); return ( - <> - - - - columns={[ - { - id: 'name', - header: 'Saved search name', - cell: ({ row }) => ( - - {row.original.name} - {row.original.default ? : null} - - ), - }, - { - id: 'actions', - cell: ({ row }) => ( - - - - - ), - }, - ]} - data={[ - { - name: 'My saved search', - default: true, - }, - { - name: 'Another saved search', - }, - { - name: 'This one has a really long name and some emojis too 🥒', - }, - ]} - getRowId={(row) => row.name} - /> - - - + + + + columns={[ + { + id: 'name', + header: 'Saved search name', + cell: ({ row }) => ( + + {row.original.name} + {row.original.default ? : null} + + ), + }, + { + id: 'actions', + cell: ({ row }) => ( + + + + + ), + }, + ]} + data={[ + { + name: 'My saved search', + default: true, + }, + { + name: 'Another saved search', + }, + { + name: 'This one has a really long name and some emojis too 🥒', + }, + ]} + getRowId={(row) => row.name} + /> + + ); }; diff --git a/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx b/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx index f33db5d9962..37d977b004d 100644 --- a/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx +++ b/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx @@ -82,17 +82,15 @@ export const NoRulesSplash = () => { ) : null } > - <> - - You can also define rules through file provisioning or Terraform.{' '} - - Learn more - - - + + You can also define rules through file provisioning or Terraform.{' '} + + Learn more + +
); diff --git a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx index 1484cfaa4ec..720e0ed1275 100644 --- a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx @@ -16,7 +16,7 @@ jest.mock('./MultipleDataSourcePicker', () => { const original = jest.requireActual('./MultipleDataSourcePicker'); return { ...original, - MultipleDataSourcePicker: () => <>, + MultipleDataSourcePicker: () => null, }; }); diff --git a/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx b/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx index a037703c420..cfc14895d4c 100644 --- a/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx +++ b/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx @@ -220,19 +220,17 @@ const Annotations = ({ rule }: AnnotationsProps) => { return null; } return ( - <> -
- {Object.entries(annotations).map(([name, value]) => { - const capitalizedName = capitalize(name); - return ( - - {capitalizedName} - - - ); - })} -
- +
+ {Object.entries(annotations).map(([name, value]) => { + const capitalizedName = capitalize(name); + return ( + + {capitalizedName} + + + ); + })} +
); }; interface ValueInTransitionProps { diff --git a/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx b/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx index 76ba417dd20..a449b7ceee3 100644 --- a/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx +++ b/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx @@ -114,16 +114,14 @@ const LokiStateHistory = ({ ruleUID }: Props) => { )} {isEmpty(frameSubset) ? ( - <> -
- {emptyStateMessage} - {totalRecordsCount > 0 && ( - - )} -
- +
+ {emptyStateMessage} + {totalRecordsCount > 0 && ( + + )} +
) : ( <>
diff --git a/public/app/features/alerting/unified/home/Insights.tsx b/public/app/features/alerting/unified/home/Insights.tsx index 9d85797751b..14bd54b0682 100644 --- a/public/app/features/alerting/unified/home/Insights.tsx +++ b/public/app/features/alerting/unified/home/Insights.tsx @@ -176,24 +176,22 @@ export function getInsightsScenes() { component: SectionSubheader, props: { children: ( - <> - - Monitor the status of your system{' '} - - Alerting insights provides pre-built dashboards to monitor your alerting data. -
-
- You can identify patterns in why things go wrong and discover trends in alerting performance - within your organization. -
- } - > - - - - + + Monitor the status of your system{' '} + + Alerting insights provides pre-built dashboards to monitor your alerting data. +
+
+ You can identify patterns in why things go wrong and discover trends in alerting performance within + your organization. +
+ } + > + + + ), }, }), diff --git a/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx b/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx index 7c2735ea5cd..5b61f83d543 100644 --- a/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx +++ b/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx @@ -3,20 +3,18 @@ import { t } from 'app/core/internationalization'; export function RuleGroupActionsMenu() { return ( - <> - - - - - - - - } - > - - - + + + + + + + + } + > + + ); } From 943b73a68200dfccb227bcc9906fc6d1916bfc87 Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Tue, 11 Mar 2025 16:58:26 -0400 Subject: [PATCH 194/312] Alerting: Add scheduled clean-up of deleted rules (#101963) * add scheduled clean up of deleted rules --------- Signed-off-by: Yuri Tseretyan --- conf/defaults.ini | 8 ++ conf/sample.ini | 8 ++ pkg/server/wire.go | 3 + pkg/services/cleanup/cleanup.go | 22 +++- pkg/services/ngalert/store/alert_rule.go | 20 ++- pkg/services/ngalert/store/alert_rule_test.go | 124 +++++++++++++++++- pkg/setting/setting_unified_alerting.go | 8 ++ 7 files changed, 190 insertions(+), 3 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index ee703f76d2a..8fc8296fead 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1406,6 +1406,14 @@ resolved_alert_retention = 15m # 0 value means no limit rule_version_record_limit = 0 +# The retention period for deleted alerting rules. +# Determines how long deleted rules are retained before being permanently removed. +# The retention duration must be specified using a time format with unit suffixes +# such as ms, s, m, h, d (e.g., 30d for 30 days). +# Default: 30d +# 0 value means that rules are deleted permanently immediately. +deleted_rule_retention = 30d + [unified_alerting.screenshots] # Enable screenshots in notifications. You must have either installed the Grafana image rendering # plugin, or set up Grafana to use a remote rendering service. diff --git a/conf/sample.ini b/conf/sample.ini index 207fa535410..152fbf6fb96 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1389,6 +1389,14 @@ # 0 value means no limit ;rule_version_record_limit= 0 +# The retention period for deleted alerting rules. +# Determines how long deleted rules are retained before being permanently removed. +# The retention duration must be specified using a time format with unit suffixes +# such as ms, s, m, h, d (e.g., 30d for 30 days). +# Default: 30d +# 0 value means that rules are deleted permanently immediately. +;deleted_rule_retention = 30d + [unified_alerting.screenshots] # Enable screenshots in notifications. You must have either installed the Grafana image rendering # plugin, or set up Grafana to use a remote rendering service. diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 82b7e891669..c24a40e80d4 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -10,6 +10,7 @@ import ( "github.com/google/wire" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/api" "github.com/grafana/grafana/pkg/api/avatar" "github.com/grafana/grafana/pkg/api/routing" @@ -421,6 +422,7 @@ var wireSet = wire.NewSet( prefimpl.ProvideService, oauthtoken.ProvideService, wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)), + wire.Bind(new(cleanup.AlertRuleService), new(*ngstore.DBstore)), ) var wireCLISet = wire.NewSet( @@ -453,6 +455,7 @@ var wireTestSet = wire.NewSet( oauthtoken.ProvideService, oauthtokentest.ProvideService, wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtokentest.Service)), + wire.Bind(new(cleanup.AlertRuleService), new(*ngstore.DBstore)), ) func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Server, error) { diff --git a/pkg/services/cleanup/cleanup.go b/pkg/services/cleanup/cleanup.go index 240bcf532fd..479d34a6a75 100644 --- a/pkg/services/cleanup/cleanup.go +++ b/pkg/services/cleanup/cleanup.go @@ -27,6 +27,10 @@ import ( "github.com/grafana/grafana/pkg/setting" ) +type AlertRuleService interface { + CleanUpDeletedAlertRules(ctx context.Context) (int64, error) +} + type CleanUpService struct { log log.Logger tracer tracing.Tracer @@ -41,12 +45,13 @@ type CleanUpService struct { tempUserService tempuser.Service annotationCleaner annotations.Cleaner dashboardService dashboards.DashboardService + alertRuleService AlertRuleService } func ProvideService(cfg *setting.Cfg, serverLockService *serverlock.ServerLockService, shortURLService shorturls.Service, sqlstore db.DB, queryHistoryService queryhistory.Service, dashboardVersionService dashver.Service, dashSnapSvc dashboardsnapshots.Service, deleteExpiredImageService *image.DeleteExpiredService, - tempUserService tempuser.Service, tracer tracing.Tracer, annotationCleaner annotations.Cleaner, dashboardService dashboards.DashboardService) *CleanUpService { + tempUserService tempuser.Service, tracer tracing.Tracer, annotationCleaner annotations.Cleaner, dashboardService dashboards.DashboardService, service AlertRuleService) *CleanUpService { s := &CleanUpService{ Cfg: cfg, ServerLockService: serverLockService, @@ -61,6 +66,7 @@ func ProvideService(cfg *setting.Cfg, serverLockService *serverlock.ServerLockSe tracer: tracer, annotationCleaner: annotationCleaner, dashboardService: dashboardService, + alertRuleService: service, } return s } @@ -112,6 +118,10 @@ func (srv *CleanUpService) clean(ctx context.Context) { cleanupJobs = append(cleanupJobs, cleanUpJob{"delete stale short URLs", srv.deleteStaleShortURLs}) } + if srv.Cfg.UnifiedAlerting.DeletedRuleRetention > 0 { + cleanupJobs = append(cleanupJobs, cleanUpJob{"cleanup trash alert rules", srv.cleanUpTrashAlertRules}) + } + logger := srv.log.FromContext(ctx) logger.Debug("Starting cleanup jobs", "jobs", fmt.Sprintf("%v", cleanupJobs)) @@ -313,3 +323,13 @@ func (srv *CleanUpService) cleanUpTrashDashboards(ctx context.Context) { logger.Debug("Cleaned up deleted dashboards", "dashboards affected", affected) } } + +func (srv *CleanUpService) cleanUpTrashAlertRules(ctx context.Context) { + logger := srv.log.FromContext(ctx) + affected, err := srv.alertRuleService.CleanUpDeletedAlertRules(ctx) + if err != nil { + logger.Error("Problem cleaning up deleted alert rules", "error", err) + } else { + logger.Debug("Cleaned up deleted alert rules", "rows affected", affected) + } +} diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index d4fb0202a78..ccba09c5a3b 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -73,7 +73,7 @@ func (st DBstore) DeleteAlertRulesByUID(ctx context.Context, orgID int64, user * logger.Debug("Deleted alert rule state", "count", rows) var versions []alertRuleVersion - if st.FeatureToggles.IsEnabledGlobally(featuremgmt.FlagAlertRuleRestore) { + if st.FeatureToggles.IsEnabledGlobally(featuremgmt.FlagAlertRuleRestore) && st.Cfg.DeletedRuleRetention > 0 { // save deleted version only if retention is greater than 0 versions, err = st.getLatestVersionOfRulesByUID(ctx, orgID, ruleUID) if err != nil { logger.Error("Failed to get latest version of deleted alert rules. The recovery will not be possible", "error", err) @@ -1243,6 +1243,24 @@ func (st DBstore) GetNamespacesByRuleUID(ctx context.Context, orgID int64, uids return result, err } +func (st DBstore) CleanUpDeletedAlertRules(ctx context.Context) (int64, error) { + affectedRows := int64(-1) + err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { + expire := TimeNow().Add(-st.Cfg.DeletedRuleRetention) + st.Logger.Debug("Permanently remove expired deleted rules", "deletedBefore", expire) + result, err := sess.Exec("DELETE FROM alert_rule_version WHERE rule_uid='' AND created <= ?", expire) + if err != nil { + return err + } + affectedRows, err = result.RowsAffected() + if err != nil { + st.Logger.Warn("Failed to get rows affected by the delete operation", "error", err) + } + return nil + }) + return affectedRows, err +} + func getINSubQueryArgs[T any](inputSlice []T) ([]any, []string) { args := make([]any, 0, len(inputSlice)) in := make([]string, 0, len(inputSlice)) diff --git a/pkg/services/ngalert/store/alert_rule_test.go b/pkg/services/ngalert/store/alert_rule_test.go index c3e94e7e806..46fc19eb1fb 100644 --- a/pkg/services/ngalert/store/alert_rule_test.go +++ b/pkg/services/ngalert/store/alert_rule_test.go @@ -784,13 +784,15 @@ func TestIntegration_DeleteAlertRulesByUID(t *testing.T) { require.Empty(t, savedInstances) }) - t.Run("should remove all version and insert one with empty rule_uid", func(t *testing.T) { + t.Run("should remove all version and insert one with empty rule_uid when DeletedRuleRetention is set", func(t *testing.T) { orgID := int64(rand.Intn(1000)) gen = gen.With(gen.WithOrgID(orgID)) // Create a new store to pass the custom bus to check the signal b := &fakeBus{} logger := log.New("test-dbstore") + cfg.UnifiedAlerting.DeletedRuleRetention = 1000 * time.Hour + store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b) store.FeatureToggles = featuremgmt.WithFeatures(featuremgmt.FlagAlertRuleRestore) @@ -848,6 +850,59 @@ func TestIntegration_DeleteAlertRulesByUID(t *testing.T) { return nil }) }) + + t.Run("should remove all versions and not keep history if DeletedRuleRetention = 0", func(t *testing.T) { + orgID := int64(rand.Intn(1000)) + gen = gen.With(gen.WithOrgID(orgID)) + // Create a new store to pass the custom bus to check the signal + b := &fakeBus{} + logger := log.New("test-dbstore") + + cfg.UnifiedAlerting.DeletedRuleRetention = 0 + + store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b) + store.FeatureToggles = featuremgmt.WithFeatures(featuremgmt.FlagAlertRuleRestore) + + result, err := store.InsertAlertRules(context.Background(), &models.AlertingUserUID, gen.GenerateMany(3)) + uids := make([]string, 0, len(result)) + for _, rule := range result { + uids = append(uids, rule.UID) + } + require.NoError(t, err) + rules, err := store.ListAlertRules(context.Background(), &models.ListAlertRulesQuery{OrgID: orgID, RuleUIDs: uids}) + require.NoError(t, err) + + updates := make([]models.UpdateRule, 0, len(rules)) + for _, rule := range rules { + rule2 := models.CopyRule(rule, gen.WithTitle(util.GenerateShortUID())) + updates = append(updates, models.UpdateRule{ + Existing: rule, + New: *rule2, + }) + } + err = store.UpdateAlertRules(context.Background(), &models.AlertingUserUID, updates) + require.NoError(t, err) + + versions, err := store.GetAlertRuleVersions(context.Background(), orgID, rules[0].GUID) + require.NoError(t, err) + require.Len(t, versions, 2) + + err = store.DeleteAlertRulesByUID(context.Background(), orgID, util.Pointer(models.UserUID("test")), uids...) + require.NoError(t, err) + + guids := make([]string, 0, len(rules)) + for _, rule := range rules { + guids = append(guids, rule.GUID) + } + + _ = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { + var versions []alertRuleVersion + err = sess.Table(alertRuleVersion{}).Where(`rule_uid = ''`).In("rule_guid", guids).Find(&versions) + require.NoError(t, err) + require.Emptyf(t, versions, "some rules were not permanently deleted") // should be one version per GUID + return nil + }) + }) } func TestIntegrationInsertAlertRules(t *testing.T) { @@ -1962,6 +2017,7 @@ func TestIntegration_ListDeletedRules(t *testing.T) { cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{ BaseInterval: 1 * time.Second, RuleVersionRecordLimit: -1, + DeletedRuleRetention: 10 * time.Hour, } sqlStore := db.InitTestDB(t) folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures()) @@ -2011,6 +2067,72 @@ func TestIntegration_ListDeletedRules(t *testing.T) { }) } +func TestIntegration_CleanUpDeletedAlertRules(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + oldClk := TimeNow + t.Cleanup(func() { + TimeNow = oldClk + }) + + t0 := time.Now().UTC().Truncate(time.Second) + TimeNow = func() time.Time { + return t0 + } + + sqlStore := db.InitTestDB(t, sqlstore.InitTestDBOpt{ + Cfg: nil, + }) + cfg := setting.NewCfg() + cfg.UnifiedAlerting.BaseInterval = 1 * time.Second + cfg.UnifiedAlerting.RuleVersionRecordLimit = -1 + cfg.UnifiedAlerting.DeletedRuleRetention = 10 * time.Second + + folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures()) + logger := log.New("test-dbstore") + store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, &fakeBus{}) + store.FeatureToggles = featuremgmt.WithFeatures(featuremgmt.FlagAlertRuleRestore) + + gen := models.RuleGen + orgID := int64(rand.Intn(1000)) + + gen = gen.With(gen.WithOrgID(orgID)) + + result, err := store.InsertAlertRules(context.Background(), &models.AlertingUserUID, gen.GenerateMany(3)) + uids := make([]string, 0, len(result)) + for _, rule := range result { + uids = append(uids, rule.UID) + } + require.NoError(t, err) + + // simulate rule deletion at different time. + // t0, t0+10s, t0+20s + for idx, uid := range uids { + TimeNow = func() time.Time { + return t0.Add(time.Duration(idx) * 10 * time.Second) + } + err = store.DeleteAlertRulesByUID(context.Background(), orgID, util.Pointer(models.UserUID("test")), uid) + require.NoError(t, err) + } + + before, err := store.ListDeletedRules(context.Background(), orgID) + require.NoError(t, err) + require.Len(t, before, 3) + + // retention is 10s, now=t+20s, therefore, only one row should be deleted + _, err = store.CleanUpDeletedAlertRules(context.Background()) + require.NoError(t, err) + + after, err := store.ListDeletedRules(context.Background(), orgID) + require.NoError(t, err) + assert.Len(t, after, 1) + for _, rule := range after { + assert.GreaterOrEqual(t, rule.Updated, TimeNow().Add(-cfg.UnifiedAlerting.DeletedRuleRetention)) + } +} + func createTestStore( sqlStore db.DB, folderService folder.Service, diff --git a/pkg/setting/setting_unified_alerting.go b/pkg/setting/setting_unified_alerting.go index 33f002cb571..b14d22b97bc 100644 --- a/pkg/setting/setting_unified_alerting.go +++ b/pkg/setting/setting_unified_alerting.go @@ -129,6 +129,9 @@ type UnifiedAlertingSettings struct { // should be stored in the database for each alert_rule in an organization including the current one. // 0 value means no limit RuleVersionRecordLimit int + + // DeletedRuleRetention defines the maximum duration to retain deleted alerting rules before permanent removal. + DeletedRuleRetention time.Duration } type RecordingRuleSettings struct { @@ -477,6 +480,11 @@ func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error { return fmt.Errorf("setting 'rule_version_record_limit' is invalid, only 0 or a positive integer are allowed") } + uaCfg.DeletedRuleRetention = ua.Key("deleted_rule_retention").MustDuration(30 * 24 * time.Hour) + if uaCfg.DeletedRuleRetention < 0 { + return fmt.Errorf("setting 'deleted_rule_retention' is invalid, only 0 or a positive duration are allowed") + } + cfg.UnifiedAlerting = uaCfg return nil } From 7dd6f526306608941ec65a5bed248c0ca6f75c2a Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Tue, 11 Mar 2025 22:12:06 +0100 Subject: [PATCH 195/312] Alerting: Add MissingSeriesEvalsToResolve option to the AlertRule (#101184) --- go.work.sum | 4 +- pkg/services/ngalert/models/alert_rule.go | 61 ++- .../ngalert/models/alert_rule_test.go | 79 +++- pkg/services/ngalert/models/testing.go | 57 +-- .../ngalert/schedule/registry_test.go | 3 + pkg/services/ngalert/state/manager.go | 16 +- .../ngalert/state/manager_private_test.go | 382 +++++++++++++++++- pkg/services/ngalert/state/manager_test.go | 2 +- pkg/services/ngalert/store/compat.go | 148 +++---- pkg/services/ngalert/store/models.go | 67 +-- .../sqlstore/migrations/migrations.go | 2 + ...rt_rule_missing_series_evals_to_resolve.go | 17 + 12 files changed, 665 insertions(+), 173 deletions(-) create mode 100644 pkg/services/sqlstore/migrations/ualert/alert_rule_missing_series_evals_to_resolve.go diff --git a/go.work.sum b/go.work.sum index c9cfa4ae15a..f29575e7e8d 100644 --- a/go.work.sum +++ b/go.work.sum @@ -692,7 +692,6 @@ github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiG github.com/couchbase/moss v0.2.0 h1:VCYrMzFwEryyhRSeI+/b3tRBSeTpi/8gn5Kf6dxqn+o= github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= @@ -923,6 +922,7 @@ github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= @@ -1197,7 +1197,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -1460,7 +1459,6 @@ golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index 69219b8b09f..022599a7158 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -294,6 +294,11 @@ type AlertRule struct { IsPaused bool NotificationSettings []NotificationSettings Metadata AlertRuleMetadata + // MissingSeriesEvalsToResolve specifies the number of consecutive evaluation intervals + // required before resolving an alert state (a dimension) when data is missing. + // If nil, alerts resolve after 2 missing evaluation intervals + // (i.e., resolution occurs during the second evaluation where data is absent). + MissingSeriesEvalsToResolve *int } type AlertRuleMetadata struct { @@ -578,6 +583,18 @@ func (alertRule *AlertRule) GetGroupKey() AlertRuleGroupKey { return AlertRuleGroupKey{OrgID: alertRule.OrgID, NamespaceUID: alertRule.NamespaceUID, RuleGroup: alertRule.RuleGroup} } +// GetMissingSeriesEvalsToResolve returns the number of consecutive evaluation intervals +// to wait before resolving an alert rule instance when its data is missing. +// If not configured, it returns the default value (2), which means the alert +// resolves after missing for two evaluation intervals. +func (alertRule *AlertRule) GetMissingSeriesEvalsToResolve() int { + if alertRule.MissingSeriesEvalsToResolve == nil { + return 2 // default value + } + + return *alertRule.MissingSeriesEvalsToResolve +} + // PreSave sets default values and loads the updated model for each alert query. func (alertRule *AlertRule) PreSave(timeNow func() time.Time, userUID *UserUID) error { for i, q := range alertRule.Data { @@ -659,6 +676,10 @@ func validateAlertRuleFields(rule *AlertRule) error { return err } + if rule.MissingSeriesEvalsToResolve != nil && *rule.MissingSeriesEvalsToResolve <= 0 { + return fmt.Errorf("%w: field `missing_series_evals_to_resolve` must be greater than 0", ErrAlertRuleFailedValidation) + } + return nil } @@ -708,25 +729,26 @@ func (alertRule *AlertRule) Copy() *AlertRule { return nil } result := AlertRule{ - ID: alertRule.ID, - GUID: alertRule.GUID, - OrgID: alertRule.OrgID, - Title: alertRule.Title, - Condition: alertRule.Condition, - Updated: alertRule.Updated, - UpdatedBy: alertRule.UpdatedBy, - IntervalSeconds: alertRule.IntervalSeconds, - Version: alertRule.Version, - UID: alertRule.UID, - NamespaceUID: alertRule.NamespaceUID, - RuleGroup: alertRule.RuleGroup, - RuleGroupIndex: alertRule.RuleGroupIndex, - NoDataState: alertRule.NoDataState, - ExecErrState: alertRule.ExecErrState, - For: alertRule.For, - Record: alertRule.Record, - IsPaused: alertRule.IsPaused, - Metadata: alertRule.Metadata, + ID: alertRule.ID, + GUID: alertRule.GUID, + OrgID: alertRule.OrgID, + Title: alertRule.Title, + Condition: alertRule.Condition, + Updated: alertRule.Updated, + UpdatedBy: alertRule.UpdatedBy, + IntervalSeconds: alertRule.IntervalSeconds, + Version: alertRule.Version, + UID: alertRule.UID, + NamespaceUID: alertRule.NamespaceUID, + RuleGroup: alertRule.RuleGroup, + RuleGroupIndex: alertRule.RuleGroupIndex, + NoDataState: alertRule.NoDataState, + ExecErrState: alertRule.ExecErrState, + For: alertRule.For, + Record: alertRule.Record, + IsPaused: alertRule.IsPaused, + Metadata: alertRule.Metadata, + MissingSeriesEvalsToResolve: alertRule.MissingSeriesEvalsToResolve, } if alertRule.DashboardUID != nil { @@ -789,6 +811,7 @@ func ClearRecordingRuleIgnoredFields(rule *AlertRule) { rule.Condition = "" rule.For = 0 rule.NotificationSettings = nil + rule.MissingSeriesEvalsToResolve = nil } // GetAlertRuleByUIDQuery is the query for retrieving/deleting an alert rule by UID and organisation ID. diff --git a/pkg/services/ngalert/models/alert_rule_test.go b/pkg/services/ngalert/models/alert_rule_test.go index b8e47df7bbf..978b6d49127 100644 --- a/pkg/services/ngalert/models/alert_rule_test.go +++ b/pkg/services/ngalert/models/alert_rule_test.go @@ -18,6 +18,7 @@ import ( "golang.org/x/exp/maps" "gopkg.in/yaml.v3" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util/cmputil" ) @@ -386,6 +387,7 @@ func TestPatchPartialAlertRule(t *testing.T) { }) } +// nolint:gocyclo func TestDiff(t *testing.T) { t.Run("should return nil if there is no diff", func(t *testing.T) { rule1 := RuleGen.GenerateRef() @@ -406,7 +408,9 @@ func TestDiff(t *testing.T) { t.Run("should find diff in simple fields", func(t *testing.T) { rule1 := RuleGen.GenerateRef() - rule2 := RuleGen.GenerateRef() + rule2 := RuleGen.With( + RuleGen.WithMissingSeriesEvalsToResolve(*rule1.MissingSeriesEvalsToResolve + 1), + ).GenerateRef() diffs := rule1.Diff(rule2, "Data", "Annotations", "Labels", "NotificationSettings", "Metadata") // these fields will be tested separately @@ -540,6 +544,13 @@ func TestDiff(t *testing.T) { assert.Equal(t, rule2.Record, diff[0].Right.String()) difCnt++ } + if rule1.MissingSeriesEvalsToResolve != rule2.MissingSeriesEvalsToResolve { + diff := diffs.GetDiffsForField("MissingSeriesEvalsToResolve") + assert.Len(t, diff, 1) + assert.Equal(t, *rule1.MissingSeriesEvalsToResolve, int(diff[0].Left.Int())) + assert.Equal(t, *rule2.MissingSeriesEvalsToResolve, int(diff[0].Right.Int())) + difCnt++ + } require.Lenf(t, diffs, difCnt, "Got some unexpected diffs. Either add to ignore or add assert to it") @@ -963,6 +974,21 @@ func TestAlertRuleGetKeyWithGroup(t *testing.T) { }) } +func TestAlertRuleGetMissingSeriesEvalsToResolve(t *testing.T) { + t.Run("should return the default 2 if MissingSeriesEvalsToResolve is nil", func(t *testing.T) { + rule := RuleGen.GenerateRef() + rule.MissingSeriesEvalsToResolve = nil + require.Equal(t, 2, rule.GetMissingSeriesEvalsToResolve()) + }) + + t.Run("should return the correct value", func(t *testing.T) { + rule := RuleGen.With( + RuleMuts.WithMissingSeriesEvalsToResolve(3), + ).GenerateRef() + require.Equal(t, 3, rule.GetMissingSeriesEvalsToResolve()) + }) +} + func TestAlertRuleCopy(t *testing.T) { t.Run("should return a copy of the rule", func(t *testing.T) { for i := 0; i < 100; i++ { @@ -1084,3 +1110,54 @@ func TestAlertRule_PrometheusRuleDefinition(t *testing.T) { }) } } + +func TestMissingSeriesEvalsToResolveValidation(t *testing.T) { + testCases := []struct { + name string + missingSeriesEvalsToResolve *int + expectedErrorContains string + }{ + { + name: "should allow nil value", + missingSeriesEvalsToResolve: nil, + }, + { + name: "should reject negative value", + missingSeriesEvalsToResolve: util.Pointer(-1), + expectedErrorContains: "field `missing_series_evals_to_resolve` must be greater than 0", + }, + { + name: "should reject 0", + missingSeriesEvalsToResolve: util.Pointer(0), + expectedErrorContains: "field `missing_series_evals_to_resolve` must be greater than 0", + }, + { + name: "should accept positive value", + missingSeriesEvalsToResolve: util.Pointer(2), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + baseIntervalSeconds := int64(10) + cfg := setting.UnifiedAlertingSettings{ + BaseInterval: time.Duration(baseIntervalSeconds) * time.Second, + } + + rule := RuleGen.With( + RuleMuts.WithIntervalSeconds(baseIntervalSeconds * 2), + ).Generate() + rule.MissingSeriesEvalsToResolve = tc.missingSeriesEvalsToResolve + + err := rule.ValidateAlertRule(cfg) + + if tc.expectedErrorContains != "" { + require.Error(t, err) + require.ErrorIs(t, err, ErrAlertRuleFailedValidation) + require.Contains(t, err.Error(), tc.expectedErrorContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/services/ngalert/models/testing.go b/pkg/services/ngalert/models/testing.go index eb9d838f8df..242aafc0025 100644 --- a/pkg/services/ngalert/models/testing.go +++ b/pkg/services/ngalert/models/testing.go @@ -103,29 +103,30 @@ func (g *AlertRuleGenerator) Generate() AlertRule { } rule := AlertRule{ - ID: 0, - GUID: uuid.NewString(), - OrgID: rand.Int63n(1500) + 1, // Prevent OrgID=0 as this does not pass alert rule validation. - Title: fmt.Sprintf("title-%s", util.GenerateShortUID()), - Condition: "A", - Data: []AlertQuery{g.GenerateQuery()}, - Updated: time.Now().Add(-time.Duration(rand.Intn(100) + 1)), - UpdatedBy: updatedBy, - IntervalSeconds: rand.Int63n(60) + 1, - Version: rand.Int63n(1500), // Don't generate a rule ID too big for postgres - UID: util.GenerateShortUID(), - NamespaceUID: util.GenerateShortUID(), - DashboardUID: dashUID, - PanelID: panelID, - RuleGroup: fmt.Sprintf("group-%s,", util.GenerateShortUID()), - RuleGroupIndex: rand.Intn(1500), - NoDataState: randNoDataState(), - ExecErrState: randErrState(), - For: forInterval, - Annotations: annotations, - Labels: labels, - NotificationSettings: ns, - Metadata: GenerateMetadata(), + ID: 0, + GUID: uuid.NewString(), + OrgID: rand.Int63n(1500) + 1, // Prevent OrgID=0 as this does not pass alert rule validation. + Title: fmt.Sprintf("title-%s", util.GenerateShortUID()), + Condition: "A", + Data: []AlertQuery{g.GenerateQuery()}, + Updated: time.Now().Add(-time.Duration(rand.Intn(100) + 1)), + UpdatedBy: updatedBy, + IntervalSeconds: rand.Int63n(60) + 1, + Version: rand.Int63n(1500), // Don't generate a rule ID too big for postgres + UID: util.GenerateShortUID(), + NamespaceUID: util.GenerateShortUID(), + DashboardUID: dashUID, + PanelID: panelID, + RuleGroup: fmt.Sprintf("group-%s,", util.GenerateShortUID()), + RuleGroupIndex: rand.Intn(1500), + NoDataState: randNoDataState(), + ExecErrState: randErrState(), + For: forInterval, + Annotations: annotations, + Labels: labels, + NotificationSettings: ns, + Metadata: GenerateMetadata(), + MissingSeriesEvalsToResolve: util.Pointer(2), } for _, mutator := range g.mutators { @@ -499,6 +500,15 @@ func (a *AlertRuleMutators) WithSameGroup() AlertRuleMutator { } } +func (a *AlertRuleMutators) WithMissingSeriesEvalsToResolve(timesOfInterval int) AlertRuleMutator { + return func(rule *AlertRule) { + if timesOfInterval <= 0 { + panic("timesOfInterval must be greater than 0") + } + rule.MissingSeriesEvalsToResolve = util.Pointer(timesOfInterval) + } +} + func (a *AlertRuleMutators) WithNotificationSettingsGen(ns func() NotificationSettings) AlertRuleMutator { return func(rule *AlertRule) { rule.NotificationSettings = []NotificationSettings{ns()} @@ -1343,6 +1353,7 @@ func ConvertToRecordingRule(rule *AlertRule) { rule.ExecErrState = "" rule.For = 0 rule.NotificationSettings = nil + rule.MissingSeriesEvalsToResolve = nil } func nameToUid(name string) string { // Avoid legacy_storage.NameToUid import cycle. diff --git a/pkg/services/ngalert/schedule/registry_test.go b/pkg/services/ngalert/schedule/registry_test.go index 2a52f67b264..01acc80fa41 100644 --- a/pkg/services/ngalert/schedule/registry_test.go +++ b/pkg/services/ngalert/schedule/registry_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/services/ngalert/models" + "github.com/grafana/grafana/pkg/util" ) func TestSchedulableAlertRulesRegistry(t *testing.T) { @@ -211,6 +212,7 @@ func TestRuleWithFolderFingerprint(t *testing.T) { SimplifiedNotificationsSection: false, }, }, + MissingSeriesEvalsToResolve: util.Pointer(2), } r2 := &models.AlertRule{ ID: 2, @@ -255,6 +257,7 @@ func TestRuleWithFolderFingerprint(t *testing.T) { SimplifiedQueryAndExpressionsSection: true, }, }, + MissingSeriesEvalsToResolve: util.Pointer(1), } excludedFields := map[string]struct{}{ diff --git a/pkg/services/ngalert/state/manager.go b/pkg/services/ngalert/state/manager.go index f66002bbb3b..e23d3bce143 100644 --- a/pkg/services/ngalert/state/manager.go +++ b/pkg/services/ngalert/state/manager.go @@ -519,7 +519,7 @@ func (st *Manager) deleteStaleStatesFromCache(logger log.Logger, evaluatedAt tim // If we are removing two or more stale series it makes sense to share the resolved image as the alert rule is the same. // TODO: We will need to change this when we support images without screenshots as each series will have a different image staleStates := st.cache.deleteRuleStates(alertRule.GetKey(), func(s *State) bool { - return stateIsStale(evaluatedAt, s.LastEvaluationTime, alertRule.IntervalSeconds) + return stateIsStale(evaluatedAt, s.LastEvaluationTime, alertRule.IntervalSeconds, alertRule.GetMissingSeriesEvalsToResolve()) }) resolvedStates := make([]StateTransition, 0, len(staleStates)) @@ -551,8 +551,18 @@ func (st *Manager) deleteStaleStatesFromCache(logger log.Logger, evaluatedAt tim return resolvedStates } -func stateIsStale(evaluatedAt time.Time, lastEval time.Time, intervalSeconds int64) bool { - return !lastEval.Add(2 * time.Duration(intervalSeconds) * time.Second).After(evaluatedAt) +// stateIsStale determines whether the evaluation state is considered stale. +// A state is considered stale if the data has been missing for at least missingSeriesEvalsToResolve evaluation intervals. +func stateIsStale(evaluatedAt time.Time, lastEval time.Time, intervalSeconds int64, missingSeriesEvalsToResolve int) bool { + // If the last evaluation time equals the current evaluation time, the state is not stale. + if evaluatedAt.Equal(lastEval) { + return false + } + + resolveIfMissingDuration := time.Duration(int64(missingSeriesEvalsToResolve)*intervalSeconds) * time.Second + + // timeSinceLastEval >= resolveIfMissingDuration + return evaluatedAt.Sub(lastEval) >= resolveIfMissingDuration } func StatesToRuleStatus(states []*State) ngModels.RuleStatus { diff --git a/pkg/services/ngalert/state/manager_private_test.go b/pkg/services/ngalert/state/manager_private_test.go index 16de5cd57ea..42fcddf41a3 100644 --- a/pkg/services/ngalert/state/manager_private_test.go +++ b/pkg/services/ngalert/state/manager_private_test.go @@ -31,40 +31,81 @@ func TestStateIsStale(t *testing.T) { now := time.Now() intervalSeconds := rand.Int63n(10) + 5 + threeIntervals := time.Duration(intervalSeconds) * time.Second * 3 + fourIntervals := time.Duration(intervalSeconds) * time.Second * 4 + fiveIntervals := time.Duration(intervalSeconds) * time.Second * 5 + testCases := []struct { - name string - lastEvaluation time.Time - expectedResult bool + name string + lastEvaluation time.Time + expectedResult bool + missingSeriesEvalsToResolve int }{ { - name: "false if last evaluation is now", - lastEvaluation: now, - expectedResult: false, + name: "false if last evaluation is now", + lastEvaluation: now, + missingSeriesEvalsToResolve: 2, + expectedResult: false, }, { - name: "false if last evaluation is 1 interval before now", - lastEvaluation: now.Add(-time.Duration(intervalSeconds)), - expectedResult: false, + name: "false if last evaluation is 1 interval before now", + lastEvaluation: now.Add(-time.Duration(intervalSeconds)), + missingSeriesEvalsToResolve: 2, + expectedResult: false, }, { - name: "false if last evaluation is little less than 2 interval before now", - lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 2).Add(100 * time.Millisecond), - expectedResult: false, + name: "false if last evaluation is little less than 2 interval before now", + lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 2).Add(100 * time.Millisecond), + missingSeriesEvalsToResolve: 2, + expectedResult: false, }, { - name: "true if last evaluation is 2 intervals from now", - lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 2), - expectedResult: true, + name: "true if last evaluation is 2 intervals from now", + lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 2), + missingSeriesEvalsToResolve: 2, + expectedResult: true, }, { - name: "true if last evaluation is 3 intervals from now", - lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 3), - expectedResult: true, + name: "true if last evaluation is 3 intervals from now", + lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 3), + missingSeriesEvalsToResolve: 2, + expectedResult: true, + }, + { + name: "false if last evaluation is within custom resolve after missing for", + lastEvaluation: now.Add(-threeIntervals), + missingSeriesEvalsToResolve: 4, + expectedResult: false, + }, + { + name: "true if last evaluation equals custom resolve after missing for", + lastEvaluation: now.Add(-fourIntervals), + missingSeriesEvalsToResolve: 4, + expectedResult: true, + }, + { + name: "true if last evaluation exceeds custom resolve after missing for", + lastEvaluation: now.Add(-fiveIntervals), + missingSeriesEvalsToResolve: 4, + expectedResult: true, + }, + { + name: "when missingSeriesEvalsToResolve is 1 and the state is just created", + lastEvaluation: now, + missingSeriesEvalsToResolve: 1, + expectedResult: false, + }, + { + name: "when missingSeriesEvalsToResolve is 1 and the state is created in the past", + lastEvaluation: now.Add(-time.Duration(intervalSeconds) * time.Second * 1), + missingSeriesEvalsToResolve: 1, + expectedResult: true, }, } + for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.expectedResult, stateIsStale(now, tc.lastEvaluation, intervalSeconds)) + require.Equal(t, tc.expectedResult, stateIsStale(now, tc.lastEvaluation, intervalSeconds, tc.missingSeriesEvalsToResolve)) }) } } @@ -115,6 +156,7 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { t1 := tN(1) t2 := tN(2) t3 := tN(3) + t4 := tN(4) baseRule := &ngmodels.AlertRule{ OrgID: 1, @@ -738,6 +780,308 @@ func TestProcessEvalResults_StateTransitions(t *testing.T) { }, }, }, + { + desc: "t1[1:alerting] t2[NoData] t3[NoData] at t2,t3", + alertRule: baseRule, + results: map[time.Time]eval.Results{ + t1: { + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels1)), + }, + t2: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + t3: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + }, + expectedTransitions: map[time.Time][]StateTransition{ + t1: { + { + PreviousState: eval.Normal, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Alerting, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t1.Add(ResendDelay * 4), + LastEvaluationTime: t1, + LastSentAt: &t1, + }, + }, + }, + t2: { + { + PreviousState: eval.Normal, + State: &State{ + Labels: labels["system + rule + no-data"], + State: eval.NoData, + LatestResult: newEvaluation(t2, eval.NoData), + StartsAt: t2, + EndsAt: t2.Add(ResendDelay * 4), + LastEvaluationTime: t2, + LastSentAt: &t2, + }, + }, + }, + t3: { + { + PreviousState: eval.NoData, + State: &State{ + Labels: labels["system + rule + no-data"], + State: eval.NoData, + LatestResult: newEvaluation(t3, eval.NoData), + StartsAt: t2, + EndsAt: t3.Add(ResendDelay * 4), + LastSentAt: &t2, + LastEvaluationTime: t3, + }, + }, + // This is the transition of the alerting state from t1 to Normal + // after 2 evaluations as it became stale. + { + PreviousState: eval.Alerting, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Normal, + StateReason: ngmodels.StateReasonMissingSeries, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t3, + LastEvaluationTime: t3, + ResolvedAt: &t3, + LastSentAt: &t3, + }, + }, + }, + }, + }, + { + desc: "t1[1:alerting] t2[NoData] t3[NoData] t4[NoData] with missing_series_evals_to_resolve=3 at t3,t4", + alertRule: baseRuleWith(ngmodels.RuleMuts.WithMissingSeriesEvalsToResolve(3)), + results: map[time.Time]eval.Results{ + t1: { + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels1)), + }, + t2: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + t3: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + t4: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + }, + expectedTransitions: map[time.Time][]StateTransition{ + t3: { + { + PreviousState: eval.NoData, + State: &State{ + Labels: labels["system + rule + no-data"], + State: eval.NoData, + LatestResult: newEvaluation(t3, eval.NoData), + StartsAt: t2, + EndsAt: t3.Add(ResendDelay * 4), + LastEvaluationTime: t3, + LastSentAt: &t2, + }, + }, + }, + t4: { + { + PreviousState: eval.NoData, + State: &State{ + Labels: labels["system + rule + no-data"], + State: eval.NoData, + LatestResult: newEvaluation(t4, eval.NoData), + StartsAt: t2, + EndsAt: t4.Add(ResendDelay * 4), + LastSentAt: &t2, + LastEvaluationTime: t4, + }, + }, + // This is the transition of the alerting state from t1 to Normal + // after 3 evaluations as it became stale. + { + PreviousState: eval.Alerting, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Normal, + StateReason: ngmodels.StateReasonMissingSeries, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t4, + LastEvaluationTime: t4, + ResolvedAt: &t4, + LastSentAt: &t4, + }, + }, + }, + }, + }, + { + desc: "t1[1:alerting] t2[NoData] t3[NoData] with missing_series_evals_to_resolve=1 at t2,t3", + alertRule: baseRuleWith(ngmodels.RuleMuts.WithMissingSeriesEvalsToResolve(1)), + results: map[time.Time]eval.Results{ + t1: { + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels1)), + }, + t2: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + t3: { + newResult(eval.WithState(eval.NoData), eval.WithLabels(noDataLabels)), + }, + }, + expectedTransitions: map[time.Time][]StateTransition{ + t1: { + { + PreviousState: eval.Normal, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Alerting, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t1.Add(ResendDelay * 4), + LastEvaluationTime: t1, + LastSentAt: &t1, + }, + }, + }, + t2: { + { + PreviousState: eval.Normal, + State: &State{ + Labels: labels["system + rule + no-data"], + State: eval.NoData, + LatestResult: newEvaluation(t2, eval.NoData), + StartsAt: t2, + EndsAt: t2.Add(ResendDelay * 4), + LastEvaluationTime: t2, + LastSentAt: &t2, + }, + }, + // This is the transition of the alerting state from t1 to Normal + // after 2 evaluations as it became stale. + { + PreviousState: eval.Alerting, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Normal, + StateReason: ngmodels.StateReasonMissingSeries, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t2, + LastEvaluationTime: t2, + ResolvedAt: &t2, + LastSentAt: &t2, + }, + }, + }, + t3: { + { + PreviousState: eval.NoData, + State: &State{ + Labels: labels["system + rule + no-data"], + State: eval.NoData, + LatestResult: newEvaluation(t3, eval.NoData), + StartsAt: t2, + EndsAt: t3.Add(ResendDelay * 4), + LastSentAt: &t2, + LastEvaluationTime: t3, + }, + }, + }, + }, + }, + { + desc: "t1[1:alerting,2:alerting] t2[1:alerting] t3[1:alerting] with missing_series_evals_to_resolve=1 at t2,t3", + alertRule: baseRuleWith(ngmodels.RuleMuts.WithMissingSeriesEvalsToResolve(1)), + results: map[time.Time]eval.Results{ + t1: { + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels1)), + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels2)), + }, + t2: { + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels2)), + }, + t3: { + newResult(eval.WithState(eval.Alerting), eval.WithLabels(labels2)), + }, + }, + expectedTransitions: map[time.Time][]StateTransition{ + t1: { + { + PreviousState: eval.Normal, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Alerting, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t1.Add(ResendDelay * 4), + LastEvaluationTime: t1, + LastSentAt: &t1, + }, + }, + { + PreviousState: eval.Normal, + State: &State{ + Labels: labels["system + rule + labels2"], + State: eval.Alerting, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t1.Add(ResendDelay * 4), + LastEvaluationTime: t1, + LastSentAt: &t1, + }, + }, + }, + t2: { + { + PreviousState: eval.Alerting, + State: &State{ + Labels: labels["system + rule + labels2"], + State: eval.Alerting, + LatestResult: newEvaluation(t2, eval.Alerting), + StartsAt: t1, + EndsAt: t2.Add(ResendDelay * 4), + LastEvaluationTime: t2, + LastSentAt: &t1, + }, + }, + // This is the transition of the alerting state from t1 to Normal + // after 2 evaluations as it became stale. + { + PreviousState: eval.Alerting, + State: &State{ + Labels: labels["system + rule + labels1"], + State: eval.Normal, + StateReason: ngmodels.StateReasonMissingSeries, + LatestResult: newEvaluation(t1, eval.Alerting), + StartsAt: t1, + EndsAt: t2, + LastEvaluationTime: t2, + ResolvedAt: &t2, + LastSentAt: &t2, + }, + }, + }, + t3: { + { + PreviousState: eval.Alerting, + State: &State{ + Labels: labels["system + rule + labels2"], + State: eval.Alerting, + LatestResult: newEvaluation(t3, eval.Alerting), + StartsAt: t1, + EndsAt: t3.Add(ResendDelay * 4), + LastEvaluationTime: t3, + LastSentAt: &t1, + }, + }, + }, + }, + }, { desc: "t1[{}:normal] t2[{}:alerting] at t2", alertRule: baseRule, diff --git a/pkg/services/ngalert/state/manager_test.go b/pkg/services/ngalert/state/manager_test.go index b6b863f40df..1af185871b8 100644 --- a/pkg/services/ngalert/state/manager_test.go +++ b/pkg/services/ngalert/state/manager_test.go @@ -1906,7 +1906,7 @@ func TestStaleResults(t *testing.T) { st := state.NewManager(cfg, state.NewNoopPersister()) gen := models.RuleGen - rule := gen.With(gen.WithFor(0)).GenerateRef() + rule := gen.With(gen.WithFor(0), gen.WithMissingSeriesEvalsToResolve(2)).GenerateRef() initResults := eval.Results{ eval.ResultGen(eval.WithState(eval.Alerting), eval.WithEvaluatedAt(clk.Now()))(), diff --git a/pkg/services/ngalert/store/compat.go b/pkg/services/ngalert/store/compat.go index 8021233ee93..8d489a2b240 100644 --- a/pkg/services/ngalert/store/compat.go +++ b/pkg/services/ngalert/store/compat.go @@ -18,23 +18,24 @@ func alertRuleToModelsAlertRule(ar alertRule, l log.Logger) (models.AlertRule, e } result := models.AlertRule{ - ID: ar.ID, - OrgID: ar.OrgID, - GUID: ar.GUID, - Title: ar.Title, - Condition: ar.Condition, - Data: data, - Updated: ar.Updated, - IntervalSeconds: ar.IntervalSeconds, - Version: ar.Version, - UID: ar.UID, - NamespaceUID: ar.NamespaceUID, - DashboardUID: ar.DashboardUID, - PanelID: ar.PanelID, - RuleGroup: ar.RuleGroup, - RuleGroupIndex: ar.RuleGroupIndex, - For: ar.For, - IsPaused: ar.IsPaused, + ID: ar.ID, + OrgID: ar.OrgID, + GUID: ar.GUID, + Title: ar.Title, + Condition: ar.Condition, + Data: data, + Updated: ar.Updated, + IntervalSeconds: ar.IntervalSeconds, + Version: ar.Version, + UID: ar.UID, + NamespaceUID: ar.NamespaceUID, + DashboardUID: ar.DashboardUID, + PanelID: ar.PanelID, + RuleGroup: ar.RuleGroup, + RuleGroupIndex: ar.RuleGroupIndex, + For: ar.For, + IsPaused: ar.IsPaused, + MissingSeriesEvalsToResolve: ar.MissingSeriesEvalsToResolve, } if ar.UpdatedBy != nil { @@ -107,24 +108,25 @@ func parseNotificationSettings(s string) ([]models.NotificationSettings, error) func alertRuleFromModelsAlertRule(ar models.AlertRule) (alertRule, error) { result := alertRule{ - ID: ar.ID, - GUID: ar.GUID, - OrgID: ar.OrgID, - Title: ar.Title, - Condition: ar.Condition, - Updated: ar.Updated, - IntervalSeconds: ar.IntervalSeconds, - Version: ar.Version, - UID: ar.UID, - NamespaceUID: ar.NamespaceUID, - DashboardUID: ar.DashboardUID, - PanelID: ar.PanelID, - RuleGroup: ar.RuleGroup, - RuleGroupIndex: ar.RuleGroupIndex, - NoDataState: ar.NoDataState.String(), - ExecErrState: ar.ExecErrState.String(), - For: ar.For, - IsPaused: ar.IsPaused, + ID: ar.ID, + GUID: ar.GUID, + OrgID: ar.OrgID, + Title: ar.Title, + Condition: ar.Condition, + Updated: ar.Updated, + IntervalSeconds: ar.IntervalSeconds, + Version: ar.Version, + UID: ar.UID, + NamespaceUID: ar.NamespaceUID, + DashboardUID: ar.DashboardUID, + PanelID: ar.PanelID, + RuleGroup: ar.RuleGroup, + RuleGroupIndex: ar.RuleGroupIndex, + NoDataState: ar.NoDataState.String(), + ExecErrState: ar.ExecErrState.String(), + For: ar.For, + IsPaused: ar.IsPaused, + MissingSeriesEvalsToResolve: ar.MissingSeriesEvalsToResolve, } if ar.UpdatedBy != nil { @@ -181,30 +183,31 @@ func alertRuleFromModelsAlertRule(ar models.AlertRule) (alertRule, error) { func alertRuleToAlertRuleVersion(rule alertRule) alertRuleVersion { return alertRuleVersion{ - RuleOrgID: rule.OrgID, - RuleGUID: rule.GUID, - RuleUID: rule.UID, - RuleNamespaceUID: rule.NamespaceUID, - RuleGroup: rule.RuleGroup, - RuleGroupIndex: rule.RuleGroupIndex, - ParentVersion: 0, - RestoredFrom: 0, - Version: rule.Version, - Created: rule.Updated, // assuming the Updated time as the creation time - CreatedBy: rule.UpdatedBy, - Title: rule.Title, - Condition: rule.Condition, - Data: rule.Data, - IntervalSeconds: rule.IntervalSeconds, - Record: rule.Record, - NoDataState: rule.NoDataState, - ExecErrState: rule.ExecErrState, - For: rule.For, - Annotations: rule.Annotations, - Labels: rule.Labels, - IsPaused: rule.IsPaused, - NotificationSettings: rule.NotificationSettings, - Metadata: rule.Metadata, + RuleOrgID: rule.OrgID, + RuleGUID: rule.GUID, + RuleUID: rule.UID, + RuleNamespaceUID: rule.NamespaceUID, + RuleGroup: rule.RuleGroup, + RuleGroupIndex: rule.RuleGroupIndex, + ParentVersion: 0, + RestoredFrom: 0, + Version: rule.Version, + Created: rule.Updated, // assuming the Updated time as the creation time + CreatedBy: rule.UpdatedBy, + Title: rule.Title, + Condition: rule.Condition, + Data: rule.Data, + IntervalSeconds: rule.IntervalSeconds, + Record: rule.Record, + NoDataState: rule.NoDataState, + ExecErrState: rule.ExecErrState, + For: rule.For, + Annotations: rule.Annotations, + Labels: rule.Labels, + IsPaused: rule.IsPaused, + NotificationSettings: rule.NotificationSettings, + Metadata: rule.Metadata, + MissingSeriesEvalsToResolve: rule.MissingSeriesEvalsToResolve, } } @@ -224,18 +227,19 @@ func alertRuleVersionToAlertRule(version alertRuleVersion) alertRule { NamespaceUID: version.RuleNamespaceUID, // Versions do not store Dashboard\Panel as separate column. // However, these fields are part of annotations and information in these fields is redundant - DashboardUID: nil, - PanelID: nil, - RuleGroup: version.RuleGroup, - RuleGroupIndex: version.RuleGroupIndex, - Record: version.Record, - NoDataState: version.NoDataState, - ExecErrState: version.ExecErrState, - For: version.For, - Annotations: version.Annotations, - Labels: version.Labels, - IsPaused: version.IsPaused, - NotificationSettings: version.NotificationSettings, - Metadata: version.Metadata, + DashboardUID: nil, + PanelID: nil, + RuleGroup: version.RuleGroup, + RuleGroupIndex: version.RuleGroupIndex, + Record: version.Record, + NoDataState: version.NoDataState, + ExecErrState: version.ExecErrState, + For: version.For, + Annotations: version.Annotations, + Labels: version.Labels, + IsPaused: version.IsPaused, + NotificationSettings: version.NotificationSettings, + Metadata: version.Metadata, + MissingSeriesEvalsToResolve: version.MissingSeriesEvalsToResolve, } } diff --git a/pkg/services/ngalert/store/models.go b/pkg/services/ngalert/store/models.go index 8c5d44a9d8e..63cb4e21cd4 100644 --- a/pkg/services/ngalert/store/models.go +++ b/pkg/services/ngalert/store/models.go @@ -4,31 +4,32 @@ import "time" // alertRule represents a record in alert_rule table type alertRule struct { - ID int64 `xorm:"pk autoincr 'id'"` - GUID string `xorm:"guid"` - OrgID int64 `xorm:"org_id"` - Title string - Condition string - Data string - Updated time.Time - UpdatedBy *string `xorm:"updated_by"` - IntervalSeconds int64 - Version int64 `xorm:"version"` // this tag makes xorm add optimistic lock (see https://xorm.io/docs/chapter-06/1.lock/) - UID string `xorm:"uid"` - NamespaceUID string `xorm:"namespace_uid"` - DashboardUID *string `xorm:"dashboard_uid"` - PanelID *int64 `xorm:"panel_id"` - RuleGroup string - RuleGroupIndex int `xorm:"rule_group_idx"` - Record string - NoDataState string - ExecErrState string - For time.Duration - Annotations string - Labels string - IsPaused bool - NotificationSettings string `xorm:"notification_settings"` - Metadata string `xorm:"metadata"` + ID int64 `xorm:"pk autoincr 'id'"` + GUID string `xorm:"guid"` + OrgID int64 `xorm:"org_id"` + Title string + Condition string + Data string + Updated time.Time + UpdatedBy *string `xorm:"updated_by"` + IntervalSeconds int64 + Version int64 `xorm:"version"` // this tag makes xorm add optimistic lock (see https://xorm.io/docs/chapter-06/1.lock/) + UID string `xorm:"uid"` + NamespaceUID string `xorm:"namespace_uid"` + DashboardUID *string `xorm:"dashboard_uid"` + PanelID *int64 `xorm:"panel_id"` + RuleGroup string + RuleGroupIndex int `xorm:"rule_group_idx"` + Record string + NoDataState string + ExecErrState string + For time.Duration + Annotations string + Labels string + IsPaused bool + NotificationSettings string `xorm:"notification_settings"` + Metadata string `xorm:"metadata"` + MissingSeriesEvalsToResolve *int `xorm:"missing_series_evals_to_resolve"` } func (a alertRule) TableName() string { @@ -59,12 +60,13 @@ type alertRuleVersion struct { ExecErrState string // ideally this field should have been apimodels.ApiDuration // but this is currently not possible because of circular dependencies - For time.Duration - Annotations string - Labels string - IsPaused bool - NotificationSettings string `xorm:"notification_settings"` - Metadata string `xorm:"metadata"` + For time.Duration + Annotations string + Labels string + IsPaused bool + NotificationSettings string `xorm:"notification_settings"` + Metadata string `xorm:"metadata"` + MissingSeriesEvalsToResolve *int `xorm:"missing_series_evals_to_resolve"` } // EqualSpec compares two alertRuleVersion objects for equality based on their specifications and returns true if they match. @@ -88,7 +90,8 @@ func (a alertRuleVersion) EqualSpec(b alertRuleVersion) bool { a.Labels == b.Labels && a.IsPaused == b.IsPaused && a.NotificationSettings == b.NotificationSettings && - a.Metadata == b.Metadata + a.Metadata == b.Metadata && + a.MissingSeriesEvalsToResolve == b.MissingSeriesEvalsToResolve } func (a alertRuleVersion) TableName() string { diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index 47cc25c2888..edf822bb0b6 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -147,4 +147,6 @@ func (oss *OSSMigrations) AddMigration(mg *Migrator) { ualert.AddAlertRuleStateTable(mg) ualert.AddAlertRuleGuidMigration(mg) + + ualert.AddAlertRuleMissingSeriesEvalsToResolve(mg) } diff --git a/pkg/services/sqlstore/migrations/ualert/alert_rule_missing_series_evals_to_resolve.go b/pkg/services/sqlstore/migrations/ualert/alert_rule_missing_series_evals_to_resolve.go new file mode 100644 index 00000000000..a114a12aaa3 --- /dev/null +++ b/pkg/services/sqlstore/migrations/ualert/alert_rule_missing_series_evals_to_resolve.go @@ -0,0 +1,17 @@ +package ualert + +import "github.com/grafana/grafana/pkg/services/sqlstore/migrator" + +// AddAlertRuleMissingSeriesEvalsToResolve adds missing_series_evals_to_resolve column to alert_rule and alert_rule_version tables. +func AddAlertRuleMissingSeriesEvalsToResolve(mg *migrator.Migrator) { + column := &migrator.Column{Name: "missing_series_evals_to_resolve", Type: migrator.DB_SmallInt, Nullable: true} + + mg.AddMigration( + "add missing_series_evals_to_resolve column to alert_rule", + migrator.NewAddColumnMigration(migrator.Table{Name: "alert_rule"}, column), + ) + mg.AddMigration( + "add missing_series_evals_to_resolve column to alert_rule_version", + migrator.NewAddColumnMigration(migrator.Table{Name: "alert_rule_version"}, column), + ) +} From f296b66b3771f059da50a6de3e76256e68d9b330 Mon Sep 17 00:00:00 2001 From: Jev Forsberg <46619047+baldm0mma@users.noreply.github.com> Date: Tue, 11 Mar 2025 15:27:54 -0600 Subject: [PATCH 196/312] Chore: Migrate storybook verification to GHAs (#101968) * baldm0mma/ add storybook-verification workflow file * baldm0mma/ build out storybook jobs to drone spec * baldm0mma/ add node fallback and remove runner id * baldm0mma/ replace with cypress action * baldm0mma/ update codeowners * baldm0mma/ add workflow dispatch for testing * baldm0mma/ update trigger for testing * baldm0mma/ update path * baldm0mma/ update paths * baldm0mma/ update node file --- .github/CODEOWNERS | 1 + .github/workflows/storybook-verification.yml | 40 ++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 .github/workflows/storybook-verification.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ff10207c35..d6ebd9a6a09 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -798,6 +798,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/remove-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend /.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/storybook-verification.yml @grafana/grafana-frontend-platform /.github/workflows/update-changelog.yml @grafana/grafana-developer-enablement-squad /.github/workflows/update-make-docs.yml @grafana/docs-tooling /.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring diff --git a/.github/workflows/storybook-verification.yml b/.github/workflows/storybook-verification.yml new file mode 100644 index 00000000000..72eb6a4ad4c --- /dev/null +++ b/.github/workflows/storybook-verification.yml @@ -0,0 +1,40 @@ +name: Verify Storybook + +on: + pull_request: + paths: + - 'packages/grafana-ui/**' + - '.github/workflows/storybook-verification.yml' + - '!docs/**' + - '!*.md' + +jobs: + verify-storybook: + name: Verify Storybook + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + + - name: Run Storybook and E2E tests + uses: cypress-io/github-action@v6 + with: + browser: chrome + start: yarn storybook --quiet + wait-on: 'http://localhost:9001' + wait-on-timeout: 60 + command: yarn e2e:storybook + install: false + env: + HOST: localhost + PORT: 9001 From 1ceab26cb4d23a22dadb9dab7f150648354b91d3 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 16:41:35 -0600 Subject: [PATCH 197/312] baldm0mma/ add pr-lint-build-docs.yml --- .github/workflows/pr-lint-build-docs.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/workflows/pr-lint-build-docs.yml diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml new file mode 100644 index 00000000000..c0e3d6d638b --- /dev/null +++ b/.github/workflows/pr-lint-build-docs.yml @@ -0,0 +1,9 @@ +name: Lint and Build Documentation + +on: + pull_request: + paths: + - '*.md' + - 'docs/**' + - 'packages/**/*.md' + - 'latest.json' From 687c06419295d40de29d636f544a28cf5f0eb23b Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 16:43:09 -0600 Subject: [PATCH 198/312] baldm0mma/ update node version --- .github/workflows/pr-lint-build-docs.yml | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index c0e3d6d638b..7e91af0ba15 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -7,3 +7,49 @@ on: - 'docs/**' - 'packages/**/*.md' - 'latest.json' + +jobs: + docs: + name: Build & Verify Docs + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable || yarn install --immutable + + - name: Lint docs + run: yarn run prettier:checkDocs + env: + NODE_OPTIONS: --max_old_space_size=8192 + + - name: Build docs website + uses: docker://grafana/docs-base:latest + with: + entrypoint: /bin/sh + args: | + -c "mkdir -p /github/workspace/hugo/content/docs/grafana/latest && \ + echo -e '---\\nredirectURL: /docs/grafana/latest/\\ntype: redirect\\nversioned: true\\n---\\n' > /github/workspace/hugo/content/docs/grafana/_index.md && \ + cp -r /github/workspace/docs/sources/* /github/workspace/hugo/content/docs/grafana/latest/ && \ + cd /github/workspace/hugo && make prod" + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.24.1' + + - name: Verify generated CUE code + run: | + make gen-cue + if [ -n "$(git diff)" ]; then + echo "Generated CUE code is not in sync with its inputs. Please run 'make gen-cue' and commit the changes." + git diff + exit 1 + fi From 12e1ae0751d15c95c56656c54cd338f77c637fd9 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 16:43:27 -0600 Subject: [PATCH 199/312] baldm0mma/ remove double yarn dip --- .github/workflows/pr-lint-build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 7e91af0ba15..61edc5f90b0 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -23,7 +23,7 @@ jobs: cache: 'yarn' - name: Install dependencies - run: yarn install --immutable || yarn install --immutable + run: yarn install --immutable - name: Lint docs run: yarn run prettier:checkDocs From ff74cb954fd307650f9366bd669c6a81afd774c6 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 16:46:08 -0600 Subject: [PATCH 200/312] baldm0mma/ remove cue gen and verification step --- .github/workflows/pr-lint-build-docs.yml | 28 +++++------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 61edc5f90b0..12f47ce6f67 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -1,4 +1,4 @@ -name: Lint and Build Documentation +name: Documentation on: pull_request: @@ -31,25 +31,9 @@ jobs: NODE_OPTIONS: --max_old_space_size=8192 - name: Build docs website - uses: docker://grafana/docs-base:latest - with: - entrypoint: /bin/sh - args: | - -c "mkdir -p /github/workspace/hugo/content/docs/grafana/latest && \ - echo -e '---\\nredirectURL: /docs/grafana/latest/\\ntype: redirect\\nversioned: true\\n---\\n' > /github/workspace/hugo/content/docs/grafana/_index.md && \ - cp -r /github/workspace/docs/sources/* /github/workspace/hugo/content/docs/grafana/latest/ && \ - cd /github/workspace/hugo && make prod" - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '1.24.1' - - - name: Verify generated CUE code run: | - make gen-cue - if [ -n "$(git diff)" ]; then - echo "Generated CUE code is not in sync with its inputs. Please run 'make gen-cue' and commit the changes." - git diff - exit 1 - fi + mkdir -p hugo/content/docs/grafana/latest + echo -e '---\nredirectURL: /docs/grafana/latest/\ntype: redirect\nversioned: true\n---\n' > hugo/content/docs/grafana/_index.md + cp -r docs/sources/* hugo/content/docs/grafana/latest/ + + docker run --rm -v $(pwd):/src grafana/docs-base:latest /bin/sh -c "cd /src/hugo && make prod" From 4d6d37d20f087531ee7b0ba4b7142c2c0d320082 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 16:52:14 -0600 Subject: [PATCH 201/312] baldm0mma/ remove make installation --- .github/workflows/pr-lint-build-docs.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 12f47ce6f67..4a3154e2a76 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -37,3 +37,11 @@ jobs: cp -r docs/sources/* hugo/content/docs/grafana/latest/ docker run --rm -v $(pwd):/src grafana/docs-base:latest /bin/sh -c "cd /src/hugo && make prod" + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Verify generated CUE code + run: CODEGEN_VERIFY=1 make gen-cue From 10621c40d3729ac4cc7c32b2768f2cf26390f940 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 16:59:39 -0600 Subject: [PATCH 202/312] baldm0mma/ annotate mem lim --- .github/workflows/pr-lint-build-docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 4a3154e2a76..056cf593afc 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -28,6 +28,7 @@ jobs: - name: Lint docs run: yarn run prettier:checkDocs env: + # Increase the memory limit for Node.js processes to 8GB to handle the larger docs files NODE_OPTIONS: --max_old_space_size=8192 - name: Build docs website From 2f893faf039d02a99f722aed09bfe462b28416e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 23:02:02 +0000 Subject: [PATCH 203/312] Update dependency @babel/runtime to v7.26.10 [SECURITY] (#101975) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 46 +++++++--------------------------------------- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index eed73cf59f3..d77bd8cef07 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "devDependencies": { "@babel/core": "7.26.9", "@babel/preset-env": "7.26.9", - "@babel/runtime": "7.26.9", + "@babel/runtime": "7.26.10", "@betterer/betterer": "5.4.0", "@betterer/cli": "5.4.0", "@cypress/webpack-preprocessor": "6.0.2", diff --git a/yarn.lock b/yarn.lock index a65a3d0f5dc..4c463770276 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,7 +81,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.3, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.2": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.3, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.24.2, @babel/code-frame@npm:^7.26.2": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -362,17 +362,6 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.25.9": - version: 7.26.7 - resolution: "@babel/parser@npm:7.26.7" - dependencies: - "@babel/types": "npm:^7.26.7" - bin: - parser: ./bin/babel-parser.js - checksum: 10/3ccc384366ca9a9b49c54f5b24c9d8cff9a505f2fbdd1cfc04941c8e1897084cc32f100e77900c12bc14a176cf88daa3c155faad680d9a23491b997fd2a59ffc - languageName: node - linkType: hard - "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.9": version: 7.25.9 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.9" @@ -1427,27 +1416,16 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.26.9, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": - version: 7.26.9 - resolution: "@babel/runtime@npm:7.26.9" +"@babel/runtime@npm:7.26.10, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.26.10 + resolution: "@babel/runtime@npm:7.26.10" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/08edd07d774eafbf157fdc8450ed6ddd22416fdd8e2a53e4a00349daba1b502c03ab7f7ad3ad3a7c46b9a24d99b5697591d0f852ee2f84642082ef7dda90b83d + checksum: 10/9d7ff8e96abe3791047c1138789c742411e3ef19c4d7ca18ce916f83cec92c06ec5dc64401759f6dd1e377cf8a01bbd2c62e033eb7550f435cf6579768d0d4a5 languageName: node linkType: hard -"@babel/template@npm:^7.22.5, @babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9, @babel/template@npm:^7.3.3": - version: 7.25.9 - resolution: "@babel/template@npm:7.25.9" - dependencies: - "@babel/code-frame": "npm:^7.25.9" - "@babel/parser": "npm:^7.25.9" - "@babel/types": "npm:^7.25.9" - checksum: 10/e861180881507210150c1335ad94aff80fd9e9be6202e1efa752059c93224e2d5310186ddcdd4c0f0b0fc658ce48cb47823f15142b5c00c8456dde54f5de80b2 - languageName: node - linkType: hard - -"@babel/template@npm:^7.26.9": +"@babel/template@npm:^7.22.5, @babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9, @babel/template@npm:^7.26.9, @babel/template@npm:^7.3.3": version: 7.26.9 resolution: "@babel/template@npm:7.26.9" dependencies: @@ -1483,16 +1461,6 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.26.7": - version: 7.26.7 - resolution: "@babel/types@npm:7.26.7" - dependencies: - "@babel/helper-string-parser": "npm:^7.25.9" - "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10/2264efd02cc261ca5d1c5bc94497c8995238f28afd2b7483b24ea64dd694cf46b00d51815bf0c87f0d0061ea221569c77893aeecb0d4b4bb254e9c2f938d7669 - languageName: node - linkType: hard - "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -18071,7 +18039,7 @@ __metadata: dependencies: "@babel/core": "npm:7.26.9" "@babel/preset-env": "npm:7.26.9" - "@babel/runtime": "npm:7.26.9" + "@babel/runtime": "npm:7.26.10" "@betterer/betterer": "npm:5.4.0" "@betterer/cli": "npm:5.4.0" "@bsull/augurs": "npm:^0.9.0" From 868aabeac21ac8d61c69c701faed7b66e6ff6b69 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 17:02:28 -0600 Subject: [PATCH 204/312] Revert "baldm0mma/ annotate mem lim" This reverts commit 10621c40d3729ac4cc7c32b2768f2cf26390f940. --- .github/workflows/pr-lint-build-docs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 056cf593afc..4a3154e2a76 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -28,7 +28,6 @@ jobs: - name: Lint docs run: yarn run prettier:checkDocs env: - # Increase the memory limit for Node.js processes to 8GB to handle the larger docs files NODE_OPTIONS: --max_old_space_size=8192 - name: Build docs website From 172a4ca43b65dd7e84106a3f0746e93adb4ad0c2 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 17:03:06 -0600 Subject: [PATCH 205/312] Revert "baldm0mma/ remove make installation" This reverts commit 4d6d37d20f087531ee7b0ba4b7142c2c0d320082. --- .github/workflows/pr-lint-build-docs.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 4a3154e2a76..12f47ce6f67 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -37,11 +37,3 @@ jobs: cp -r docs/sources/* hugo/content/docs/grafana/latest/ docker run --rm -v $(pwd):/src grafana/docs-base:latest /bin/sh -c "cd /src/hugo && make prod" - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' - - - name: Verify generated CUE code - run: CODEGEN_VERIFY=1 make gen-cue From 92cf578dc3263cb6d6f2df6793917c4dac6d57c5 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 17:03:19 -0600 Subject: [PATCH 206/312] Revert "baldm0mma/ remove cue gen and verification step" This reverts commit ff74cb954fd307650f9366bd669c6a81afd774c6. --- .github/workflows/pr-lint-build-docs.yml | 28 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 12f47ce6f67..61edc5f90b0 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -1,4 +1,4 @@ -name: Documentation +name: Lint and Build Documentation on: pull_request: @@ -31,9 +31,25 @@ jobs: NODE_OPTIONS: --max_old_space_size=8192 - name: Build docs website + uses: docker://grafana/docs-base:latest + with: + entrypoint: /bin/sh + args: | + -c "mkdir -p /github/workspace/hugo/content/docs/grafana/latest && \ + echo -e '---\\nredirectURL: /docs/grafana/latest/\\ntype: redirect\\nversioned: true\\n---\\n' > /github/workspace/hugo/content/docs/grafana/_index.md && \ + cp -r /github/workspace/docs/sources/* /github/workspace/hugo/content/docs/grafana/latest/ && \ + cd /github/workspace/hugo && make prod" + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.24.1' + + - name: Verify generated CUE code run: | - mkdir -p hugo/content/docs/grafana/latest - echo -e '---\nredirectURL: /docs/grafana/latest/\ntype: redirect\nversioned: true\n---\n' > hugo/content/docs/grafana/_index.md - cp -r docs/sources/* hugo/content/docs/grafana/latest/ - - docker run --rm -v $(pwd):/src grafana/docs-base:latest /bin/sh -c "cd /src/hugo && make prod" + make gen-cue + if [ -n "$(git diff)" ]; then + echo "Generated CUE code is not in sync with its inputs. Please run 'make gen-cue' and commit the changes." + git diff + exit 1 + fi From a2464f7e392ecbafdaa33ea937df971a59e7f5c9 Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 17:03:30 -0600 Subject: [PATCH 207/312] Revert "baldm0mma/ remove double yarn dip" This reverts commit 12e1ae0751d15c95c56656c54cd338f77c637fd9. --- .github/workflows/pr-lint-build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 61edc5f90b0..7e91af0ba15 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -23,7 +23,7 @@ jobs: cache: 'yarn' - name: Install dependencies - run: yarn install --immutable + run: yarn install --immutable || yarn install --immutable - name: Lint docs run: yarn run prettier:checkDocs From c5c26cb62f09c95b8ced43de68a23990101557aa Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 17:03:47 -0600 Subject: [PATCH 208/312] Revert "baldm0mma/ update node version" This reverts commit 687c06419295d40de29d636f544a28cf5f0eb23b. --- .github/workflows/pr-lint-build-docs.yml | 46 ------------------------ 1 file changed, 46 deletions(-) diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml index 7e91af0ba15..c0e3d6d638b 100644 --- a/.github/workflows/pr-lint-build-docs.yml +++ b/.github/workflows/pr-lint-build-docs.yml @@ -7,49 +7,3 @@ on: - 'docs/**' - 'packages/**/*.md' - 'latest.json' - -jobs: - docs: - name: Build & Verify Docs - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: 'package.json' - cache: 'yarn' - - - name: Install dependencies - run: yarn install --immutable || yarn install --immutable - - - name: Lint docs - run: yarn run prettier:checkDocs - env: - NODE_OPTIONS: --max_old_space_size=8192 - - - name: Build docs website - uses: docker://grafana/docs-base:latest - with: - entrypoint: /bin/sh - args: | - -c "mkdir -p /github/workspace/hugo/content/docs/grafana/latest && \ - echo -e '---\\nredirectURL: /docs/grafana/latest/\\ntype: redirect\\nversioned: true\\n---\\n' > /github/workspace/hugo/content/docs/grafana/_index.md && \ - cp -r /github/workspace/docs/sources/* /github/workspace/hugo/content/docs/grafana/latest/ && \ - cd /github/workspace/hugo && make prod" - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '1.24.1' - - - name: Verify generated CUE code - run: | - make gen-cue - if [ -n "$(git diff)" ]; then - echo "Generated CUE code is not in sync with its inputs. Please run 'make gen-cue' and commit the changes." - git diff - exit 1 - fi From 9ed864a94479e08dcf96ff2e2fa18d37a3a7435a Mon Sep 17 00:00:00 2001 From: jev forsberg Date: Tue, 11 Mar 2025 17:03:59 -0600 Subject: [PATCH 209/312] Revert "baldm0mma/ add pr-lint-build-docs.yml" This reverts commit 1ceab26cb4d23a22dadb9dab7f150648354b91d3. --- .github/workflows/pr-lint-build-docs.yml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .github/workflows/pr-lint-build-docs.yml diff --git a/.github/workflows/pr-lint-build-docs.yml b/.github/workflows/pr-lint-build-docs.yml deleted file mode 100644 index c0e3d6d638b..00000000000 --- a/.github/workflows/pr-lint-build-docs.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Lint and Build Documentation - -on: - pull_request: - paths: - - '*.md' - - 'docs/**' - - 'packages/**/*.md' - - 'latest.json' From 2bec167be5559ce84f2fd1775606f98cabfa212a Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 02:30:46 +0200 Subject: [PATCH 210/312] I18n: Download translations from Crowdin (#101984) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/cs-CZ/grafana.json | 5 ++++- public/locales/de-DE/grafana.json | 5 ++++- public/locales/es-ES/grafana.json | 5 ++++- public/locales/fr-FR/grafana.json | 5 ++++- public/locales/hu-HU/grafana.json | 5 ++++- public/locales/id-ID/grafana.json | 5 ++++- public/locales/it-IT/grafana.json | 5 ++++- public/locales/ja-JP/grafana.json | 5 ++++- public/locales/ko-KR/grafana.json | 5 ++++- public/locales/nl-NL/grafana.json | 5 ++++- public/locales/pl-PL/grafana.json | 5 ++++- public/locales/pt-BR/grafana.json | 5 ++++- public/locales/pt-PT/grafana.json | 5 ++++- public/locales/ru-RU/grafana.json | 5 ++++- public/locales/sv-SE/grafana.json | 5 ++++- public/locales/tr-TR/grafana.json | 5 ++++- public/locales/zh-Hans/grafana.json | 5 ++++- public/locales/zh-Hant/grafana.json | 5 ++++- 18 files changed, 72 insertions(+), 18 deletions(-) diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index af5d7e65575..eda5329aff4 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -383,6 +383,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3143,7 +3145,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index f58adc0d513..dac2fef8922 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "Altes Passwort ist erforderlich", "passwords-must-match": "Passwörter müssen übereinstimmen", "strong-password-validation-register": "Passwort entspricht nicht den strengen Kennwortrichtlinien" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index ca06ff0f505..dee14dc57cc 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "Se requiere la contraseña antigua", "passwords-must-match": "Las contraseñas deben coincidir", "strong-password-validation-register": "La contraseña no cumple con la política de contraseñas seguras" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index cec4d85c830..f7e3ce2684b 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "Vous devez saisir l'ancien mot de passe", "passwords-must-match": "Les mots de passe doivent être identiques", "strong-password-validation-register": "Selon notre politique, votre mot de passe n'est pas suffisamment sécurisé" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index f3a7ee625b6..19430667f64 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index 7d47740aff8..7821be91c18 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -377,6 +377,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3113,7 +3115,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index f3a7ee625b6..19430667f64 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index 7d47740aff8..7821be91c18 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -377,6 +377,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3113,7 +3115,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index 7d47740aff8..7821be91c18 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -377,6 +377,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3113,7 +3115,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index f3a7ee625b6..19430667f64 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index af5d7e65575..eda5329aff4 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -383,6 +383,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3143,7 +3145,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 3a29acad057..5a281c2145e 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "A senha antiga é obrigatória", "passwords-must-match": "As senhas devem corresponder", "strong-password-validation-register": "A senha não está de acordo com a política de senha forte" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index f3a7ee625b6..19430667f64 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index af5d7e65575..eda5329aff4 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -383,6 +383,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3143,7 +3145,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index f3a7ee625b6..19430667f64 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index f3a7ee625b6..19430667f64 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -379,6 +379,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3123,7 +3125,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index 4d5daac76cc..bf7cbd6980c 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -377,6 +377,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3113,7 +3115,8 @@ "old-password-required": "旧密码是必需项", "passwords-must-match": "密码必须一致", "strong-password-validation-register": "密码不符合强密码政策" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index 7d47740aff8..7821be91c18 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -377,6 +377,8 @@ "list-view": { "empty": { "new-alert-rule": "", + "new-ds-managed-recording-rule": "", + "new-grafana-recording-rule": "", "new-recording-rule": "", "provisioning": "" }, @@ -3113,7 +3115,8 @@ "old-password-required": "", "passwords-must-match": "", "strong-password-validation-register": "" - } + }, + "change-theme": "" }, "public-dashboard": { "acknowledgment-checkboxes": { From f02803b02765f082b3a319d788e38bc22b70cc5d Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Wed, 12 Mar 2025 07:00:49 +0100 Subject: [PATCH 211/312] Openapi: Remove duplicate group (#101933) Remove duplicate group --- pkg/tests/apis/openapi_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/tests/apis/openapi_test.go b/pkg/tests/apis/openapi_test.go index 83852b6bc4d..28c17981efa 100644 --- a/pkg/tests/apis/openapi_test.go +++ b/pkg/tests/apis/openapi_test.go @@ -74,9 +74,6 @@ func TestIntegrationOpenAPIs(t *testing.T) { }, { Group: "investigations.grafana.app", Version: "v0alpha1", - }, { - Group: "folder.grafana.app", - Version: "v0alpha1", }} for _, gv := range groups { VerifyOpenAPISnapshots(t, dir, gv, h) From cd7b66e2e82cf4ee93ef776997efa058629d3aee Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 12 Mar 2025 10:01:55 +0300 Subject: [PATCH 212/312] Provisioning: Add RTK client in main (#101991) add frontend --- pkg/apis/folder/v0alpha1/register.go | 3 +- pkg/apis/provisioning/v0alpha1/types.go | 27 +- .../v0alpha1/zz_generated.deepcopy.go | 32 +- .../v0alpha1/zz_generated.openapi.go | 76 +- ...enerated.openapi_violation_exceptions.list | 3 +- .../provisioning/v0alpha1/resourcecount.go | 15 +- .../provisioning.grafana.app-v0alpha1.json | 1830 ++++++++++++++++- pkg/tests/apis/openapi_test.go | 3 - .../provisioning/api/endpoints.gen.ts | 470 ++++- 9 files changed, 2421 insertions(+), 38 deletions(-) diff --git a/pkg/apis/folder/v0alpha1/register.go b/pkg/apis/folder/v0alpha1/register.go index 4f317ac272b..27f1e0ed670 100644 --- a/pkg/apis/folder/v0alpha1/register.go +++ b/pkg/apis/folder/v0alpha1/register.go @@ -3,10 +3,11 @@ package v0alpha1 import ( "fmt" - "github.com/grafana/grafana/pkg/apimachinery/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/grafana/grafana/pkg/apimachinery/utils" ) const ( diff --git a/pkg/apis/provisioning/v0alpha1/types.go b/pkg/apis/provisioning/v0alpha1/types.go index 8a6f2478636..170b655d999 100644 --- a/pkg/apis/provisioning/v0alpha1/types.go +++ b/pkg/apis/provisioning/v0alpha1/types.go @@ -4,6 +4,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" + "github.com/grafana/grafana/pkg/apimachinery/utils" ) // When this code is changed, make sure to update the code generation. @@ -347,15 +348,31 @@ type ResourceStats struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` + // Stats across all unified storage + // When legacy storage is still used, this will offer a shim // +listType=atomic - Items []ResourceCount `json:"items,omitempty"` + Instance []ResourceCount `json:"instance,omitempty"` + + // Stats for each manager + // +listType=atomic + Managed []ManagerStats `json:"managed,omitempty"` +} + +type ManagerStats struct { + // Manager kind + Kind utils.ManagerKind `json:"kind,omitempty"` + + // Manager identity + Identity string `json:"id,omitempty"` + + // stats + Stats []ResourceCount `json:"stats"` } type ResourceCount struct { - Repository string `json:"repository,omitempty"` - Group string `json:"group"` - Resource string `json:"resource"` - Count int64 `json:"count"` + Group string `json:"group"` + Resource string `json:"resource"` + Count int64 `json:"count"` } // HistoryList is a list of versions of a resource diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go index 6cdc255840a..6ab9dfc8a11 100644 --- a/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go @@ -352,6 +352,27 @@ func (in *LocalRepositoryConfig) DeepCopy() *LocalRepositoryConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagerStats) DeepCopyInto(out *ManagerStats) { + *out = *in + if in.Stats != nil { + in, out := &in.Stats, &out.Stats + *out = make([]ResourceCount, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagerStats. +func (in *ManagerStats) DeepCopy() *ManagerStats { + if in == nil { + return nil + } + out := new(ManagerStats) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MigrateJobOptions) DeepCopyInto(out *MigrateJobOptions) { *out = *in @@ -656,11 +677,18 @@ func (in *ResourceStats) DeepCopyInto(out *ResourceStats) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items + if in.Instance != nil { + in, out := &in.Instance, &out.Instance *out = make([]ResourceCount, len(*in)) copy(*out, *in) } + if in.Managed != nil { + in, out := &in.Managed, &out.Managed + *out = make([]ManagerStats, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go index 91e3ae5de2d..ec6e3c0cdbc 100644 --- a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go @@ -28,6 +28,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec": schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref), "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobStatus": schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref), "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_LocalRepositoryConfig(ref), + "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ManagerStats": schema_pkg_apis_provisioning_v0alpha1_ManagerStats(ref), "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MigrateJobOptions": schema_pkg_apis_provisioning_v0alpha1_MigrateJobOptions(ref), "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions": schema_pkg_apis_provisioning_v0alpha1_PullRequestJobOptions(ref), "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Repository": schema_pkg_apis_provisioning_v0alpha1_Repository(ref), @@ -761,6 +762,49 @@ func schema_pkg_apis_provisioning_v0alpha1_LocalRepositoryConfig(ref common.Refe } } +func schema_pkg_apis_provisioning_v0alpha1_ManagerStats(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: "Manager kind", + Type: []string{"string"}, + Format: "", + }, + }, + "id": { + SchemaProps: spec.SchemaProps{ + Description: "Manager identity", + Type: []string{"string"}, + Format: "", + }, + }, + "stats": { + SchemaProps: spec.SchemaProps{ + Description: "stats", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount"), + }, + }, + }, + }, + }, + }, + Required: []string{"stats"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount"}, + } +} + func schema_pkg_apis_provisioning_v0alpha1_MigrateJobOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1187,12 +1231,6 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceCount(ref common.ReferenceCal SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ - "repository": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, "group": { SchemaProps: spec.SchemaProps{ Default: "", @@ -1468,14 +1506,15 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref common.ReferenceCal Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), }, }, - "items": { + "instance": { VendorExtensible: spec.VendorExtensible{ Extensions: spec.Extensions{ "x-kubernetes-list-type": "atomic", }, }, SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, + Description: "Stats across all unified storage When legacy storage is still used, this will offer a shim", + Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1486,11 +1525,30 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref common.ReferenceCal }, }, }, + "managed": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Stats for each manager", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ManagerStats"), + }, + }, + }, + }, + }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ManagerStats", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list index 95d45ccebb1..093db63eeab 100644 --- a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -3,14 +3,15 @@ API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provis API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobResourceSummary,Errors API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobStatus,Errors API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobStatus,Summary +API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ManagerStats,Stats API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryList,Items API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,Workflows API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryViewList,Items API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceStats,Items API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,TestResults,Errors API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookStatus,SubscribedEvents API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobSpec,PullRequest +API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ManagerStats,Identity API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitHub API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceWrapper,URLs API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,SyncStatus,JobID diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go b/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go index 6c0be497a97..8330fbdce42 100644 --- a/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go +++ b/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go @@ -7,10 +7,9 @@ package v0alpha1 // ResourceCountApplyConfiguration represents a declarative configuration of the ResourceCount type for use // with apply. type ResourceCountApplyConfiguration struct { - Repository *string `json:"repository,omitempty"` - Group *string `json:"group,omitempty"` - Resource *string `json:"resource,omitempty"` - Count *int64 `json:"count,omitempty"` + Group *string `json:"group,omitempty"` + Resource *string `json:"resource,omitempty"` + Count *int64 `json:"count,omitempty"` } // ResourceCountApplyConfiguration constructs a declarative configuration of the ResourceCount type for use with @@ -19,14 +18,6 @@ func ResourceCount() *ResourceCountApplyConfiguration { return &ResourceCountApplyConfiguration{} } -// WithRepository sets the Repository field in the declarative configuration to the given value -// and returns the receiver, so that objects can be built by chaining "With" function invocations. -// If called multiple times, the Repository field is set to the value of the last call. -func (b *ResourceCountApplyConfiguration) WithRepository(value string) *ResourceCountApplyConfiguration { - b.Repository = &value - return b -} - // WithGroup sets the Group field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Group field is set to the value of the last call. diff --git a/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json index 912222edee1..7e305e99568 100644 --- a/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json @@ -1092,6 +1092,855 @@ } ] }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/export": { + "post": { + "tags": [ + "Repository" + ], + "description": "Export from grafana into the remote repository", + "operationId": "createRepositoryExport", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "identifier" + ], + "properties": { + "branch": { + "description": "Target branch for export (only git)", + "type": "string" + }, + "folder": { + "description": "The source folder (or empty) to export", + "type": "string" + }, + "identifier": { + "description": "Include the identifier in the exported metadata", + "type": "boolean", + "default": false + }, + "prefix": { + "description": "Prefix in target file system", + "type": "string" + } + } + }, + "example": { + "folder": "grafan-folder-ref", + "branch": "target-branch", + "prefix": "prefix/in/repo/tree", + "identifier": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "Job" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the Job", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/files/": { + "get": { + "tags": [ + "Repository" + ], + "summary": "File listing", + "description": "Get the files and content hash", + "operationId": "getRepositoryFiles", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "description": "Information we can get just from the file listing", + "type": "object", + "properties": { + "apiVersion": { + "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" + }, + "items": { + "type": "array", + "items": { + "default": {} + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "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" + }, + "metadata": { + "default": {} + } + } + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "ResourceWrapper" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the ResourceWrapper", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/files/{path}": { + "get": { + "tags": [ + "Repository" + ], + "description": "Read value from upstream repository", + "operationId": "getRepositoryFilesWithPath", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "ResourceWrapper" + } + }, + "put": { + "tags": [ + "Repository" + ], + "description": "connect PUT requests to files of Repository", + "operationId": "replaceRepositoryFilesWithPath", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + }, + { + "name": "message", + "in": "query", + "description": "optional message sent with any changes", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + }, + "examples": { + "dashboard": { + "value": { + "spec": { + "hello": "dashboard" + } + } + }, + "playlist": { + "value": { + "spec": { + "hello": "playlist" + } + } + } + } + }, + "application/x-yaml": { + "schema": { + "type": "object", + "additionalProperties": true + }, + "examples": { + "dashboard": { + "value": "apiVersion: dashboards.grafana.app/v0alpha1\nkind: Dashboard\nspec:\n title: Sample dashboard\n" + }, + "playlist": { + "value": "apiVersion: playlist.grafana.app/v0alpha1\nkind: Playlist\nspec:\n title: Playlist from provisioning\n interval: 5m\n items:\n - type: dashboard_by_tag\n value: panel-tests\n" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "ResourceWrapper" + } + }, + "post": { + "tags": [ + "Repository" + ], + "description": "connect POST requests to files of Repository", + "operationId": "createRepositoryFilesWithPath", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + }, + { + "name": "message", + "in": "query", + "description": "optional message sent with any changes", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + }, + "examples": { + "dashboard": { + "value": { + "spec": { + "hello": "dashboard" + } + } + }, + "playlist": { + "value": { + "spec": { + "hello": "playlist" + } + } + } + } + }, + "application/x-yaml": { + "schema": { + "type": "object", + "additionalProperties": true + }, + "examples": { + "dashboard": { + "value": "apiVersion: dashboards.grafana.app/v0alpha1\nkind: Dashboard\nspec:\n title: Sample dashboard\n" + }, + "playlist": { + "value": "apiVersion: playlist.grafana.app/v0alpha1\nkind: Playlist\nspec:\n title: Playlist from provisioning\n interval: 5m\n items:\n - type: dashboard_by_tag\n value: panel-tests\n" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "ResourceWrapper" + } + }, + "delete": { + "tags": [ + "Repository" + ], + "description": "connect DELETE requests to files of Repository", + "operationId": "deleteRepositoryFilesWithPath", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + }, + { + "name": "message", + "in": "query", + "description": "optional message sent with any changes", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "ResourceWrapper" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the ResourceWrapper", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "path", + "in": "path", + "description": "path to the resource", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/history": { + "get": { + "tags": [ + "Repository" + ], + "description": "Get the history of the repository", + "operationId": "getRepositoryHistory", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoryList" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the HistoryList", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/history/{path}": { + "get": { + "tags": [ + "Repository" + ], + "description": "Get the history of a path", + "operationId": "getRepositoryHistoryWithPath", + "parameters": [ + { + "name": "ref", + "in": "query", + "description": "branch or commit hash", + "schema": { + "type": "string" + }, + "examples": { + "": { + "summary": "The default" + }, + "branch": { + "summary": "Select branch", + "value": "my-branch" + }, + "commit": { + "summary": "Commit hash (or prefix)", + "value": "7f7cc2153" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "HistoryList" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the HistoryList", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "path", + "in": "path", + "description": "path to the resource", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/migrate": { + "post": { + "tags": [ + "Repository" + ], + "description": "Export from grafana into the remote repository", + "operationId": "createRepositoryMigrate", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "identifier" + ], + "properties": { + "history": { + "description": "Preserve history (if possible)", + "type": "boolean" + }, + "identifier": { + "description": "Include the identifier in the exported metadata", + "type": "boolean", + "default": false + }, + "prefix": { + "description": "Target file prefix", + "type": "string" + } + } + }, + "example": { + "prefix": "prefix/in/repo/tree", + "history": true, + "identifier": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "Job" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the Job", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/render/{path}": { + "get": { + "tags": [ + "Repository" + ], + "description": "get a rendered preview image", + "operationId": "getRepositoryRenderWithPath", + "responses": { + "200": { + "description": "OK", + "content": { + "image/png": {} + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "Repository" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the Repository", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "path", + "in": "path", + "description": "path to the resource", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/resources": { + "get": { + "tags": [ + "Repository" + ], + "description": "connect GET requests to resources of Repository", + "operationId": "getRepositoryResources", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceList" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "ResourceList" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the ResourceList", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/status": { "get": { "tags": [ @@ -1381,10 +2230,324 @@ } } ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/sync": { + "post": { + "tags": [ + "Repository" + ], + "description": "Sync from repository into Grafana", + "operationId": "createRepositorySync", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "incremental" + ], + "properties": { + "incremental": { + "description": "Incremental synchronization for versioned repositories", + "type": "boolean", + "default": false + } + } + }, + "example": { + "incremental": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "Job" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the Job", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/test": { + "post": { + "tags": [ + "Repository" + ], + "description": "Check if the configuration is valid", + "operationId": "createRepositoryTest", + "requestBody": { + "content": { + "application/json": { + "schema": { + "description": "When this code is changed, make sure to update the code generation. As of writing, this can be done via the hack dir in the root of the repo: ./hack/update-codegen.sh provisioning If you've opened the generated files in this dir at some point in VSCode, you may also have to re-open them to clear errors.", + "type": "object", + "properties": { + "apiVersion": { + "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" + }, + "kind": { + "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" + }, + "metadata": { + "default": {} + }, + "spec": { + "default": {} + }, + "status": { + "default": {} + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.TestResults" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "TestResults" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the TestResults", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/repositories/{name}/webhook": { + "get": { + "tags": [ + "Repository" + ], + "description": "connect GET requests to webhook of Repository", + "operationId": "getRepositoryWebhook", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookResponse" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "WebhookResponse" + } + }, + "post": { + "tags": [ + "Repository" + ], + "description": "Currently only supports github webhooks", + "operationId": "createRepositoryWebhook", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookResponse" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "provisioning.grafana.app", + "version": "v0alpha1", + "kind": "WebhookResponse" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the WebhookResponse", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/settings": { + "get": { + "tags": [ + "Provisioning", + "Repository" + ], + "description": "Get the frontend settings for this namespace", + "operationId": "getFrontendSettings", + "parameters": [ + { + "name": "namespace", + "in": "path", + "description": "workspace", + "required": true, + "schema": { + "type": "string" + }, + "example": "default" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryViewList" + } + } + } + } + } + } + }, + "/apis/provisioning.grafana.app/v0alpha1/namespaces/{namespace}/stats": { + "get": { + "tags": [ + "Provisioning", + "Repository" + ], + "description": "Get resource stats for this namespace", + "operationId": "getResourceStats", + "parameters": [ + { + "name": "namespace", + "in": "path", + "description": "workspace", + "required": true, + "schema": { + "type": "string" + }, + "example": "default" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceStats" + } + } + } + } + } + } } }, "components": { "schemas": { + "com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured": { + "type": "object", + "additionalProperties": true, + "x-kubernetes-preserve-unknown-fields": true + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Author": { + "type": "object", + "required": [ + "name", + "username" + ], + "properties": { + "avatarURL": { + "type": "string" + }, + "name": { + "type": "string", + "default": "" + }, + "username": { + "type": "string", + "default": "" + } + } + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ExportJobOptions": { "type": "object", "required": [ @@ -1410,6 +2573,56 @@ } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.FileItem": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "author": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "modified": { + "type": "integer", + "format": "int64" + }, + "path": { + "type": "string", + "default": "" + }, + "size": { + "type": "integer", + "format": "int64" + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.FileList": { + "description": "Information we can get just from the file listing", + "type": "object", + "properties": { + "apiVersion": { + "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" + }, + "items": { + "type": "array", + "items": { + "default": {} + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "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" + }, + "metadata": { + "default": {} + } + } + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.GitHubRepositoryConfig": { "type": "object", "required": [ @@ -1468,6 +2681,61 @@ } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.HistoryItem": { + "type": "object", + "required": [ + "ref", + "message", + "authors", + "createdAt" + ], + "properties": { + "authors": { + "type": "array", + "items": { + "default": {} + }, + "x-kubernetes-list-type": "atomic" + }, + "createdAt": { + "type": "integer", + "format": "int64", + "default": 0 + }, + "message": { + "type": "string", + "default": "" + }, + "ref": { + "type": "string", + "default": "" + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.HistoryList": { + "description": "HistoryList is a list of versions of a resource", + "type": "object", + "properties": { + "apiVersion": { + "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" + }, + "items": { + "type": "array", + "items": { + "default": {} + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "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" + }, + "metadata": { + "default": {} + } + } + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job": { "description": "The repository name and type are stored as labels", "type": "object", @@ -1722,6 +2990,33 @@ } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ManagerStats": { + "type": "object", + "required": [ + "stats" + ], + "properties": { + "id": { + "description": "Manager identity", + "type": "string" + }, + "kind": { + "description": "Manager kind", + "type": "string" + }, + "stats": { + "description": "stats", + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceCount" + } + ] + } + } + } + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.MigrateJobOptions": { "type": "object", "required": [ @@ -1982,6 +3277,83 @@ } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryView": { + "type": "object", + "required": [ + "name", + "title", + "readOnly", + "type", + "target" + ], + "properties": { + "name": { + "description": "The k8s name for this repository", + "type": "string", + "default": "" + }, + "readOnly": { + "description": "Edit options within the repository", + "type": "boolean", + "default": false + }, + "target": { + "description": "When syncing, where values are saved\n\nPossible enum values:\n - `\"folder\"` Resources will be saved into a folder managed by this repository It will contain a copy of everything from the remote The folder k8s name will be the same as the repository k8s name\n - `\"instance\"` Resources are saved in the global context Only one repository may specify the `instance` target When this exists, the UI will promote writing to the instance repo rather than the grafana database (where possible)", + "type": "string", + "default": "", + "enum": [ + "folder", + "instance" + ] + }, + "title": { + "description": "Repository display", + "type": "string", + "default": "" + }, + "type": { + "description": "The repository type\n\nPossible enum values:\n - `\"github\"`\n - `\"local\"`", + "type": "string", + "default": "", + "enum": [ + "github", + "local" + ] + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryViewList": { + "description": "Summary shows a view of the configuration that is sanitized and is OK for logged in users to see", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "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" + }, + "items": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryView" + } + ] + }, + "x-kubernetes-map-type": "atomic" + }, + "kind": { + "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" + }, + "legacyStorage": { + "description": "The backend is using legacy storage FIXME: Not sure where this should be exposed... but we need it somewhere The UI should force the onboarding workflow when this is true", + "type": "boolean" + } + } + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceCount": { "type": "object", "required": [ @@ -1999,15 +3371,368 @@ "type": "string", "default": "" }, - "repository": { - "type": "string" - }, "resource": { "type": "string", "default": "" } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceList": { + "description": "Information we can get just from the file listing", + "type": "object", + "properties": { + "apiVersion": { + "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" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceListItem" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "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" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "ResourceList", + "version": "__internal" + }, + { + "group": "provisioning.grafana.app", + "kind": "ResourceList", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceListItem": { + "type": "object", + "required": [ + "path", + "group", + "resource", + "name", + "hash" + ], + "properties": { + "folder": { + "type": "string" + }, + "group": { + "type": "string", + "default": "" + }, + "hash": { + "description": "the k8s identifier", + "type": "string", + "default": "" + }, + "name": { + "type": "string", + "default": "" + }, + "path": { + "type": "string", + "default": "" + }, + "resource": { + "type": "string", + "default": "" + }, + "time": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string" + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceObjects": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "action": { + "description": "The action required/used for dryRun\n\nPossible enum values:\n - `\"create\"`\n - `\"delete\"`\n - `\"update\"`", + "type": "string", + "enum": [ + "create", + "delete", + "update" + ] + }, + "dryRun": { + "description": "The value returned from a dryRun request", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "existing": { + "description": "The same value, currently saved in the grafana database", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "file": { + "description": "The resource from the repository with all modifications applied eg, the name, folder etc will all be applied to this object", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "type": { + "description": "The identified type for this object", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceType" + } + ] + }, + "upsert": { + "description": "For write events, this will return the value that was added or updated", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceRepositoryInfo": { + "type": "object", + "required": [ + "type", + "title", + "namespace", + "name" + ], + "properties": { + "name": { + "description": "The name (identifier)", + "type": "string", + "default": "" + }, + "namespace": { + "description": "The namespace this belongs to", + "type": "string", + "default": "" + }, + "title": { + "description": "The display name for this repository", + "type": "string", + "default": "" + }, + "type": { + "description": "The repository type\n\nPossible enum values:\n - `\"github\"`\n - `\"local\"`", + "type": "string", + "default": "", + "enum": [ + "github", + "local" + ] + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceStats": { + "description": "Information we can get just from the file listing", + "type": "object", + "properties": { + "apiVersion": { + "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" + }, + "instance": { + "description": "Stats across all unified storage When legacy storage is still used, this will offer a shim", + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceCount" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "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" + }, + "managed": { + "description": "Stats for each manager", + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ManagerStats" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "metadata": { + "default": {} + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceType": { + "type": "object", + "properties": { + "classic": { + "description": "For non-k8s native formats, what did this start as\n\nPossible enum values:\n - `\"access-control\"` Access control https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/access-control/sample.yaml\n - `\"alerting\"` Alert configuration https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/alerting/sample.yaml\n - `\"dashboard\"` Dashboard JSON\n - `\"datasources\"` Datasource definitions eg: https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/datasources/sample.yaml", + "type": "string", + "enum": [ + "access-control", + "alerting", + "dashboard", + "datasources" + ] + }, + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceURLs": { + "type": "object", + "properties": { + "compareURL": { + "description": "Compare this version to the target branch", + "type": "string" + }, + "newPullRequestURL": { + "description": "A URL that will create a new pull requeset for this branch", + "type": "string" + }, + "repositoryURL": { + "description": "A URL pointing to the repository this lives in", + "type": "string" + }, + "sourceURL": { + "description": "A URL pointing to the this file in the repository", + "type": "string" + } + } + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper": { + "description": "This is a container type for any resource type", + "type": "object", + "required": [ + "repository", + "resource" + ], + "properties": { + "apiVersion": { + "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" + }, + "errors": { + "description": "If errors exist, show them here", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "hash": { + "description": "The repo hash value", + "type": "string" + }, + "kind": { + "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" + }, + "path": { + "description": "Path to the remote file", + "type": "string" + }, + "ref": { + "description": "The request ref (or branch if exists)", + "type": "string" + }, + "repository": { + "description": "Basic repository info", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceRepositoryInfo" + } + ] + }, + "resource": { + "description": "Different flavors of the same object", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceObjects" + } + ] + }, + "timestamp": { + "description": "The modified time in the remote file system", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "urls": { + "description": "Typed links for this file (only supported by external systems, github etc)", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceURLs" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "ResourceWrapper", + "version": "__internal" + }, + { + "group": "provisioning.grafana.app", + "kind": "ResourceWrapper", + "version": "v0alpha1" + } + ] + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.SyncJobOptions": { "type": "object", "required": [ @@ -2105,6 +3830,105 @@ } } }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.TestResults": { + "description": "HistoryList is a list of versions of a resource", + "type": "object", + "required": [ + "code", + "success" + ], + "properties": { + "apiVersion": { + "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" + }, + "code": { + "description": "HTTP status code", + "type": "integer", + "format": "int32", + "default": 0 + }, + "details": { + "description": "Optional details", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "errors": { + "description": "Error descriptions", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "kind": { + "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" + }, + "success": { + "description": "Is the connection healthy", + "type": "boolean", + "default": false + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "TestResults", + "version": "__internal" + }, + { + "group": "provisioning.grafana.app", + "kind": "TestResults", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookResponse": { + "type": "object", + "properties": { + "added": { + "description": "Optional message", + "type": "string" + }, + "apiVersion": { + "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" + }, + "code": { + "description": "HTTP Status code 200 implies that the payload was understood but nothing is required 202 implies that an async job has been scheduled to handle the request", + "type": "integer", + "format": "int32" + }, + "job": { + "description": "Jobs to be processed When the response is 202 (Accepted) the queued jobs will be returned", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobSpec" + } + ] + }, + "kind": { + "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" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "WebhookResponse", + "version": "__internal" + }, + { + "group": "provisioning.grafana.app", + "kind": "WebhookResponse", + "version": "v0alpha1" + } + ] + }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookStatus": { "type": "object", "properties": { diff --git a/pkg/tests/apis/openapi_test.go b/pkg/tests/apis/openapi_test.go index 28c17981efa..80e7d191bbd 100644 --- a/pkg/tests/apis/openapi_test.go +++ b/pkg/tests/apis/openapi_test.go @@ -68,9 +68,6 @@ func TestIntegrationOpenAPIs(t *testing.T) { }, { Group: "iam.grafana.app", Version: "v0alpha1", - }, { - Group: "provisioning.grafana.app", - Version: "v0alpha1", }, { Group: "investigations.grafana.app", Version: "v0alpha1", diff --git a/public/app/features/provisioning/api/endpoints.gen.ts b/public/app/features/provisioning/api/endpoints.gen.ts index b26ecf21837..fd8429fee34 100644 --- a/public/app/features/provisioning/api/endpoints.gen.ts +++ b/public/app/features/provisioning/api/endpoints.gen.ts @@ -1,5 +1,5 @@ import { baseAPI as api } from './baseAPI'; -export const addTagTypes = ['Job', 'Repository'] as const; +export const addTagTypes = ['Job', 'Repository', 'Provisioning'] as const; const injectedRtkApi = api .enhanceEndpoints({ addTagTypes, @@ -128,6 +128,102 @@ const injectedRtkApi = api }), invalidatesTags: ['Repository'], }), + createRepositoryExport: build.mutation({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/export`, method: 'POST', body: queryArg.body }), + invalidatesTags: ['Repository'], + }), + getRepositoryFiles: build.query({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/files/`, + params: { + ref: queryArg.ref, + }, + }), + providesTags: ['Repository'], + }), + getRepositoryFilesWithPath: build.query({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/files/${queryArg.path}`, + params: { + ref: queryArg.ref, + }, + }), + providesTags: ['Repository'], + }), + replaceRepositoryFilesWithPath: build.mutation< + ReplaceRepositoryFilesWithPathResponse, + ReplaceRepositoryFilesWithPathArg + >({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/files/${queryArg.path}`, + method: 'PUT', + body: queryArg.body, + params: { + ref: queryArg.ref, + message: queryArg.message, + }, + }), + invalidatesTags: ['Repository'], + }), + createRepositoryFilesWithPath: build.mutation< + CreateRepositoryFilesWithPathResponse, + CreateRepositoryFilesWithPathArg + >({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/files/${queryArg.path}`, + method: 'POST', + body: queryArg.body, + params: { + ref: queryArg.ref, + message: queryArg.message, + }, + }), + invalidatesTags: ['Repository'], + }), + deleteRepositoryFilesWithPath: build.mutation< + DeleteRepositoryFilesWithPathResponse, + DeleteRepositoryFilesWithPathArg + >({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/files/${queryArg.path}`, + method: 'DELETE', + params: { + ref: queryArg.ref, + message: queryArg.message, + }, + }), + invalidatesTags: ['Repository'], + }), + getRepositoryHistory: build.query({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/history`, + params: { + ref: queryArg.ref, + }, + }), + providesTags: ['Repository'], + }), + getRepositoryHistoryWithPath: build.query({ + query: (queryArg) => ({ + url: `/repositories/${queryArg.name}/history/${queryArg.path}`, + params: { + ref: queryArg.ref, + }, + }), + providesTags: ['Repository'], + }), + createRepositoryMigrate: build.mutation({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/migrate`, method: 'POST', body: queryArg.body }), + invalidatesTags: ['Repository'], + }), + getRepositoryRenderWithPath: build.query({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/render/${queryArg.path}` }), + providesTags: ['Repository'], + }), + getRepositoryResources: build.query({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/resources` }), + providesTags: ['Repository'], + }), getRepositoryStatus: build.query({ query: (queryArg) => ({ url: `/repositories/${queryArg.name}/status`, @@ -151,6 +247,30 @@ const injectedRtkApi = api }), invalidatesTags: ['Repository'], }), + createRepositorySync: build.mutation({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/sync`, method: 'POST', body: queryArg.body }), + invalidatesTags: ['Repository'], + }), + createRepositoryTest: build.mutation({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/test`, method: 'POST', body: queryArg.body }), + invalidatesTags: ['Repository'], + }), + getRepositoryWebhook: build.query({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/webhook` }), + providesTags: ['Repository'], + }), + createRepositoryWebhook: build.mutation({ + query: (queryArg) => ({ url: `/repositories/${queryArg.name}/webhook`, method: 'POST' }), + invalidatesTags: ['Repository'], + }), + getFrontendSettings: build.query({ + query: () => ({ url: `/settings` }), + providesTags: ['Provisioning', 'Repository'], + }), + getResourceStats: build.query({ + query: () => ({ url: `/stats` }), + providesTags: ['Provisioning', 'Repository'], + }), }), overrideExisting: false, }); @@ -356,6 +476,124 @@ export type DeleteRepositoryArg = { /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */ propagationPolicy?: string; }; +export type CreateRepositoryExportResponse = /** status 200 OK */ Job; +export type CreateRepositoryExportArg = { + /** name of the Job */ + name: string; + body: { + /** Target branch for export (only git) */ + branch?: string; + /** The source folder (or empty) to export */ + folder?: string; + /** Include the identifier in the exported metadata */ + identifier: boolean; + /** Prefix in target file system */ + prefix?: string; + }; +}; +export type GetRepositoryFilesResponse = /** status 200 OK */ { + /** 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 */ + apiVersion?: string; + items?: any[]; + /** 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 */ + kind?: string; + metadata?: any; +}; +export type GetRepositoryFilesArg = { + /** name of the ResourceWrapper */ + name: string; + /** branch or commit hash */ + ref?: string; +}; +export type GetRepositoryFilesWithPathResponse = /** status 200 OK */ ResourceWrapper; +export type GetRepositoryFilesWithPathArg = { + /** name of the ResourceWrapper */ + name: string; + /** path to the resource */ + path: string; + /** branch or commit hash */ + ref?: string; +}; +export type ReplaceRepositoryFilesWithPathResponse = /** status 200 OK */ ResourceWrapper; +export type ReplaceRepositoryFilesWithPathArg = { + /** name of the ResourceWrapper */ + name: string; + /** path to the resource */ + path: string; + /** branch or commit hash */ + ref?: string; + /** optional message sent with any changes */ + message?: string; + body: { + [key: string]: any; + }; +}; +export type CreateRepositoryFilesWithPathResponse = /** status 200 OK */ ResourceWrapper; +export type CreateRepositoryFilesWithPathArg = { + /** name of the ResourceWrapper */ + name: string; + /** path to the resource */ + path: string; + /** branch or commit hash */ + ref?: string; + /** optional message sent with any changes */ + message?: string; + body: { + [key: string]: any; + }; +}; +export type DeleteRepositoryFilesWithPathResponse = /** status 200 OK */ ResourceWrapper; +export type DeleteRepositoryFilesWithPathArg = { + /** name of the ResourceWrapper */ + name: string; + /** path to the resource */ + path: string; + /** branch or commit hash */ + ref?: string; + /** optional message sent with any changes */ + message?: string; +}; +export type GetRepositoryHistoryResponse = /** status 200 OK */ string; +export type GetRepositoryHistoryArg = { + /** name of the HistoryList */ + name: string; + /** branch or commit hash */ + ref?: string; +}; +export type GetRepositoryHistoryWithPathResponse = /** status 200 OK */ string; +export type GetRepositoryHistoryWithPathArg = { + /** name of the HistoryList */ + name: string; + /** path to the resource */ + path: string; + /** branch or commit hash */ + ref?: string; +}; +export type CreateRepositoryMigrateResponse = /** status 200 OK */ Job; +export type CreateRepositoryMigrateArg = { + /** name of the Job */ + name: string; + body: { + /** Preserve history (if possible) */ + history?: boolean; + /** Include the identifier in the exported metadata */ + identifier: boolean; + /** Target file prefix */ + prefix?: string; + }; +}; +export type GetRepositoryRenderWithPathResponse = unknown; +export type GetRepositoryRenderWithPathArg = { + /** name of the Repository */ + name: string; + /** path to the resource */ + path: string; +}; +export type GetRepositoryResourcesResponse = /** status 200 OK */ ResourceList; +export type GetRepositoryResourcesArg = { + /** name of the ResourceList */ + name: string; +}; export type GetRepositoryStatusResponse = /** status 200 OK */ Repository; export type GetRepositoryStatusArg = { /** name of the Repository */ @@ -377,6 +615,43 @@ export type ReplaceRepositoryStatusArg = { fieldValidation?: string; repository: Repository; }; +export type CreateRepositorySyncResponse = /** status 200 OK */ Job; +export type CreateRepositorySyncArg = { + /** name of the Job */ + name: string; + body: { + /** Incremental synchronization for versioned repositories */ + incremental: boolean; + }; +}; +export type CreateRepositoryTestResponse = /** status 200 OK */ TestResults; +export type CreateRepositoryTestArg = { + /** name of the TestResults */ + name: string; + body: { + /** 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 */ + apiVersion?: string; + /** 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 */ + kind?: string; + metadata?: any; + spec?: any; + status?: any; + }; +}; +export type GetRepositoryWebhookResponse = /** status 200 OK */ WebhookResponse; +export type GetRepositoryWebhookArg = { + /** name of the WebhookResponse */ + name: string; +}; +export type CreateRepositoryWebhookResponse = /** status 200 OK */ WebhookResponse; +export type CreateRepositoryWebhookArg = { + /** name of the WebhookResponse */ + name: string; +}; +export type GetFrontendSettingsResponse = /** status 200 undefined */ RepositoryViewList; +export type GetFrontendSettingsArg = void; +export type GetResourceStatsResponse = /** status 200 undefined */ ResourceStats; +export type GetResourceStatsArg = void; export type Time = string; export type FieldsV1 = object; export type ManagedFieldsEntry = { @@ -624,7 +899,6 @@ export type HealthStatus = { export type ResourceCount = { count: number; group: string; - repository?: string; resource: string; }; export type SyncStatus = { @@ -731,6 +1005,181 @@ export type Status = { /** Status of the operation. One of: "Success" or "Failure". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */ status?: string; }; +export type ResourceRepositoryInfo = { + /** The name (identifier) */ + name: string; + /** The namespace this belongs to */ + namespace: string; + /** The display name for this repository */ + title: string; + /** The repository type + + Possible enum values: + - `"github"` + - `"local"` */ + type: 'github' | 'local'; +}; +export type Unstructured = { + [key: string]: any; +}; +export type ResourceType = { + /** For non-k8s native formats, what did this start as + + Possible enum values: + - `"access-control"` Access control https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/access-control/sample.yaml + - `"alerting"` Alert configuration https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/alerting/sample.yaml + - `"dashboard"` Dashboard JSON + - `"datasources"` Datasource definitions eg: https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/datasources/sample.yaml */ + classic?: 'access-control' | 'alerting' | 'dashboard' | 'datasources'; + group?: string; + kind?: string; + resource?: string; + version?: string; +}; +export type ResourceObjects = { + /** The action required/used for dryRun + + Possible enum values: + - `"create"` + - `"delete"` + - `"update"` */ + action?: 'create' | 'delete' | 'update'; + /** The value returned from a dryRun request */ + dryRun?: Unstructured; + /** The same value, currently saved in the grafana database */ + existing?: Unstructured; + /** The resource from the repository with all modifications applied eg, the name, folder etc will all be applied to this object */ + file?: Unstructured; + /** The identified type for this object */ + type: ResourceType; + /** For write events, this will return the value that was added or updated */ + upsert?: Unstructured; +}; +export type ResourceUrLs = { + /** Compare this version to the target branch */ + compareURL?: string; + /** A URL that will create a new pull requeset for this branch */ + newPullRequestURL?: string; + /** A URL pointing to the repository this lives in */ + repositoryURL?: string; + /** A URL pointing to the this file in the repository */ + sourceURL?: string; +}; +export type ResourceWrapper = { + /** 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 */ + apiVersion?: string; + /** If errors exist, show them here */ + errors?: string[]; + /** The repo hash value */ + hash?: string; + /** 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 */ + kind?: string; + /** Path to the remote file */ + path?: string; + /** The request ref (or branch if exists) */ + ref?: string; + /** Basic repository info */ + repository: ResourceRepositoryInfo; + /** Different flavors of the same object */ + resource: ResourceObjects; + /** The modified time in the remote file system */ + timestamp?: Time; + /** Typed links for this file (only supported by external systems, github etc) */ + urls?: ResourceUrLs; +}; +export type ResourceListItem = { + folder?: string; + group: string; + /** the k8s identifier */ + hash: string; + name: string; + path: string; + resource: string; + time?: number; + title?: string; +}; +export type ResourceList = { + /** 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 */ + apiVersion?: string; + items?: ResourceListItem[]; + /** 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 */ + kind?: string; + metadata?: ListMeta; +}; +export type TestResults = { + /** 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 */ + apiVersion?: string; + /** HTTP status code */ + code: number; + /** Optional details */ + details?: Unstructured; + /** Error descriptions */ + errors?: string[]; + /** 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 */ + kind?: string; + /** Is the connection healthy */ + success: boolean; +}; +export type WebhookResponse = { + /** Optional message */ + added?: string; + /** 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 */ + apiVersion?: string; + /** HTTP Status code 200 implies that the payload was understood but nothing is required 202 implies that an async job has been scheduled to handle the request */ + code?: number; + /** Jobs to be processed When the response is 202 (Accepted) the queued jobs will be returned */ + job?: JobSpec; + /** 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 */ + kind?: string; +}; +export type RepositoryView = { + /** The k8s name for this repository */ + name: string; + /** Edit options within the repository */ + readOnly: boolean; + /** When syncing, where values are saved + + Possible enum values: + - `"folder"` Resources will be saved into a folder managed by this repository It will contain a copy of everything from the remote The folder k8s name will be the same as the repository k8s name + - `"instance"` Resources are saved in the global context Only one repository may specify the `instance` target When this exists, the UI will promote writing to the instance repo rather than the grafana database (where possible) */ + target: 'folder' | 'instance'; + /** Repository display */ + title: string; + /** The repository type + + Possible enum values: + - `"github"` + - `"local"` */ + type: 'github' | 'local'; +}; +export type RepositoryViewList = { + /** 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 */ + apiVersion?: string; + items: RepositoryView[]; + /** 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 */ + kind?: string; + /** The backend is using legacy storage FIXME: Not sure where this should be exposed... but we need it somewhere The UI should force the onboarding workflow when this is true */ + legacyStorage?: boolean; +}; +export type ManagerStats = { + /** Manager identity */ + id?: string; + /** Manager kind */ + kind?: string; + /** stats */ + stats: ResourceCount[]; +}; +export type ResourceStats = { + /** 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 */ + apiVersion?: string; + /** Stats across all unified storage When legacy storage is still used, this will offer a shim */ + instance?: ResourceCount[]; + /** 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 */ + kind?: string; + /** Stats for each manager */ + managed?: ManagerStats[]; + metadata?: any; +}; export const { useListJobQuery, useGetJobQuery, @@ -740,6 +1189,23 @@ export const { useGetRepositoryQuery, useReplaceRepositoryMutation, useDeleteRepositoryMutation, + useCreateRepositoryExportMutation, + useGetRepositoryFilesQuery, + useGetRepositoryFilesWithPathQuery, + useReplaceRepositoryFilesWithPathMutation, + useCreateRepositoryFilesWithPathMutation, + useDeleteRepositoryFilesWithPathMutation, + useGetRepositoryHistoryQuery, + useGetRepositoryHistoryWithPathQuery, + useCreateRepositoryMigrateMutation, + useGetRepositoryRenderWithPathQuery, + useGetRepositoryResourcesQuery, useGetRepositoryStatusQuery, useReplaceRepositoryStatusMutation, + useCreateRepositorySyncMutation, + useCreateRepositoryTestMutation, + useGetRepositoryWebhookQuery, + useCreateRepositoryWebhookMutation, + useGetFrontendSettingsQuery, + useGetResourceStatsQuery, } = injectedRtkApi; From 848d49e70f2aa2755a7e57a3f110320e591af76a Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Wed, 12 Mar 2025 08:26:41 +0100 Subject: [PATCH 213/312] Chore: Add username option for redis remote cache (#101787) * Chore: Add username option for redis remote cache (cherry picked from commit 25e28dc85e646e8cb7ab9ab582de8fb247b58b07) * Chore: Update docs and config with sample Redis conn with user+pass --------- Co-authored-by: Thomas Fournier --- conf/defaults.ini | 2 +- conf/sample.ini | 2 +- docs/sources/setup-grafana/configure-grafana/_index.md | 4 +++- pkg/infra/remotecache/redis_storage.go | 2 ++ pkg/infra/remotecache/redis_storage_test.go | 3 ++- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 8fc8296fead..ba04540e320 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -198,7 +198,7 @@ type = database # cache connectionstring options # database: will use Grafana primary database. -# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,username=grafana,password=grafanaRocks,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. # memcache: 127.0.0.1:11211 connstr = diff --git a/conf/sample.ini b/conf/sample.ini index 152fbf6fb96..15f94510ba6 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -197,7 +197,7 @@ # cache connectionstring options # database: will use Grafana primary database. -# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,username=grafana,password=grafanaRocks,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. # memcache: 127.0.0.1:11211 ;connstr = diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 9f16b072e63..c77d5cab8d7 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -491,11 +491,13 @@ Leave empty when using `database` and Grafana uses the primary database. ##### `redis` -Example connection string: `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false` +Example connection string: `addr=127.0.0.1:6379,pool_size=100,db=0,username=grafana,password=grafanaRocks,ssl=false` - `addr` is the host `:` port of the Redis server. - `pool_size` (optional) is the number of underlying connections that can be made to Redis. - `db` (optional) is the number identifier of the Redis database you want to use. +- `username` (optional) is the connection identifier to authenticate the current connection. +- `password` (optional) is the connection secret to authenticate the current connection. - `ssl` (optional) is if SSL should be used to connect to Redis server. The value may be `true`, `false`, or `insecure`. Setting the value to `insecure` skips verification of the certificate chain and hostname when making the connection. ##### `memcache` diff --git a/pkg/infra/remotecache/redis_storage.go b/pkg/infra/remotecache/redis_storage.go index 84c9a081f81..de10aa88eb3 100644 --- a/pkg/infra/remotecache/redis_storage.go +++ b/pkg/infra/remotecache/redis_storage.go @@ -39,6 +39,8 @@ func parseRedisConnStr(connStr string) (*redis.Options, error) { switch connKey { case "addr": options.Addr = connVal + case "username": + options.Username = connVal case "password": options.Password = connVal case "db": diff --git a/pkg/infra/remotecache/redis_storage_test.go b/pkg/infra/remotecache/redis_storage_test.go index 32138431f21..6f796bd5ab6 100644 --- a/pkg/infra/remotecache/redis_storage_test.go +++ b/pkg/infra/remotecache/redis_storage_test.go @@ -16,11 +16,12 @@ func Test_parseRedisConnStr(t *testing.T) { ShouldErr bool }{ "all redis options should parse": { - "addr=127.0.0.1:6379,pool_size=100,db=1,password=grafanaRocks,ssl=false", + "addr=127.0.0.1:6379,pool_size=100,db=1,username=grafana,password=grafanaRocks,ssl=false", &redis.Options{ Addr: "127.0.0.1:6379", PoolSize: 100, DB: 1, + Username: "grafana", Password: "grafanaRocks", Network: "tcp", TLSConfig: nil, From 13cd9c3c60e7105e0e5a6da1799a8ee5aa3451a9 Mon Sep 17 00:00:00 2001 From: "Ren Goto (@ren510dev)" Date: Wed, 12 Mar 2025 16:33:15 +0900 Subject: [PATCH 214/312] Docs: Fix incorrect label groupings (#101491) fix incorrect label groupings in alerting documents Co-authored-by: Matheus Macabu --- .../fundamentals/notifications/group-alert-notifications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md b/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md index 8e2eab652e8..4434a6f9a88 100644 --- a/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md +++ b/docs/sources/alerting/fundamentals/notifications/group-alert-notifications.md @@ -55,7 +55,7 @@ Alert instances are grouped together if they have the same exact label values fo For example, given the `Group by` option set to the `team` label: - `alertname:foo, team=frontend`, and `alertname:bar, team=frontend` are in one group. -- `alertname:foo, team=backend`, and `alertname:qux, team=backend` are in another group. +- `alertname:foo, team=frontend`, and `alertname:qux, team=backend` are in another group. ### Group by alert rule or labels From e28c993465dc32405ac51484bf1db0b37178b865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 12 Mar 2025 08:35:44 +0100 Subject: [PATCH 215/312] DashboardScene: De-select object after they are removed (#101940) --- .../dashboard-scene/edit-pane/DashboardEditPane.tsx | 8 +++++++- public/app/features/dashboard-scene/edit-pane/shared.ts | 4 ++++ .../scene/layout-default/DefaultGridLayoutManager.tsx | 4 +++- .../ResponsiveGridLayoutManager.tsx | 3 ++- .../scene/layout-rows/RowsLayoutManager.tsx | 3 ++- .../scene/layout-tabs/TabsLayoutManager.tsx | 3 +++ 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx index cef94e5be88..072d80ba367 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditPane.tsx @@ -21,7 +21,7 @@ import { DashboardAddPane } from './DashboardAddPane'; import { DashboardOutline } from './DashboardOutline'; import { ElementEditPane } from './ElementEditPane'; import { ElementSelection } from './ElementSelection'; -import { NewObjectAddedToCanvasEvent } from './shared'; +import { NewObjectAddedToCanvasEvent, ObjectRemovedFromCanvasEvent } from './shared'; import { useEditableElement } from './useEditableElement'; export interface DashboardEditPaneState extends SceneObjectState { @@ -53,6 +53,12 @@ export class DashboardEditPane extends SceneObjectBase { this.newObjectAddedToCanvas(payload); }) ); + + this._subs.add( + dashboard.subscribeToEvent(ObjectRemovedFromCanvasEvent, ({ payload }) => { + this.clearSelection(); + }) + ); } public enableSelection() { diff --git a/public/app/features/dashboard-scene/edit-pane/shared.ts b/public/app/features/dashboard-scene/edit-pane/shared.ts index 7f284ef945c..f599a50ef8c 100644 --- a/public/app/features/dashboard-scene/edit-pane/shared.ts +++ b/public/app/features/dashboard-scene/edit-pane/shared.ts @@ -58,3 +58,7 @@ export function hasEditableElement(sceneObj: SceneObject | undefined): boolean { export class NewObjectAddedToCanvasEvent extends BusEventWithPayload { static type = 'new-object-added-to-canvas'; } + +export class ObjectRemovedFromCanvasEvent extends BusEventWithPayload { + static type = 'object-removed-from-canvas'; +} diff --git a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx index 992a6f9102d..c30558f3221 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx @@ -15,7 +15,7 @@ import { GRID_COLUMN_COUNT } from 'app/core/constants'; import { t } from 'app/core/internationalization'; import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty'; -import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; +import { NewObjectAddedToCanvasEvent, ObjectRemovedFromCanvasEvent } from '../../edit-pane/shared'; import { isClonedKey, joinCloneKeys } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { @@ -108,6 +108,8 @@ export class DefaultGridLayoutManager this.state.grid.setState({ children: layout.state.children.filter((child) => child !== gridItem), }); + + this.publishEvent(new ObjectRemovedFromCanvasEvent(panel), true); } public duplicatePanel(vizPanel: VizPanel) { diff --git a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx index f610ceac675..eefe03a13a5 100644 --- a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutManager.tsx @@ -2,7 +2,7 @@ import { SceneComponentProps, SceneCSSGridLayout, SceneObjectBase, SceneObjectSt import { t } from 'app/core/internationalization'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; -import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; +import { NewObjectAddedToCanvasEvent, ObjectRemovedFromCanvasEvent } from '../../edit-pane/shared'; import { joinCloneKeys } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { getGridItemKeyForPanelId, getPanelIdForVizPanel, getVizPanelKeyForPanelId } from '../../utils/utils'; @@ -68,6 +68,7 @@ export class ResponsiveGridLayoutManager public removePanel(panel: VizPanel) { const element = panel.parent; this.state.layout.setState({ children: this.state.layout.state.children.filter((child) => child !== element) }); + this.publishEvent(new ObjectRemovedFromCanvasEvent(panel), true); } public duplicatePanel(panel: VizPanel) { diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx index ceccc165557..4c1acfe940a 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx @@ -1,7 +1,7 @@ import { SceneGridItemLike, SceneGridRow, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; -import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared'; +import { NewObjectAddedToCanvasEvent, ObjectRemovedFromCanvasEvent } from '../../edit-pane/shared'; import { isClonedKey } from '../../utils/clone'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { DashboardGridItem } from '../layout-default/DashboardGridItem'; @@ -128,6 +128,7 @@ export class RowsLayoutManager extends SceneObjectBase i public removeRow(row: RowItem) { const rows = this.state.rows.filter((r) => r !== row); this.setState({ rows: rows.length === 0 ? [new RowItem()] : rows }); + this.publishEvent(new ObjectRemovedFromCanvasEvent(row), true); } public moveRowUp(row: RowItem) { diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx index 319210697cd..3c7f1b24bd4 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabsLayoutManager.tsx @@ -7,6 +7,7 @@ import { } from '@grafana/scenes'; import { t } from 'app/core/internationalization'; +import { ObjectRemovedFromCanvasEvent } from '../../edit-pane/shared'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -115,6 +116,7 @@ export class TabsLayoutManager extends SceneObjectBase i if (currentTab === tabToRemove) { const nextTabIndex = this.state.currentTabIndex > 0 ? this.state.currentTabIndex - 1 : 0; this.setState({ tabs: this.state.tabs.filter((t) => t !== tabToRemove), currentTabIndex: nextTabIndex }); + this.publishEvent(new ObjectRemovedFromCanvasEvent(tabToRemove), true); return; } @@ -122,6 +124,7 @@ export class TabsLayoutManager extends SceneObjectBase i const tabs = filteredTab.length === 0 ? [new TabItem()] : filteredTab; this.setState({ tabs, currentTabIndex: 0 }); + this.publishEvent(new ObjectRemovedFromCanvasEvent(tabToRemove), true); } public addTabBefore(tab: TabItem) { From e6f682bc14ec73dcd67b9c4983aed1ad797bc9d5 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 12 Mar 2025 10:46:12 +0300 Subject: [PATCH 216/312] K8s/Dashboards: Fix title extraction (#101990) --- pkg/apimachinery/utils/meta.go | 16 ++++++++++++++++ .../migration/conversion/conversion_test.go | 14 +++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/pkg/apimachinery/utils/meta.go b/pkg/apimachinery/utils/meta.go index 49bbcd3eb02..53cb66fca4a 100644 --- a/pkg/apimachinery/utils/meta.go +++ b/pkg/apimachinery/utils/meta.go @@ -607,6 +607,22 @@ func (m *grafanaMetaAccessor) FindTitle(defaultTitle string) string { if name.IsValid() && name.Kind() == reflect.String { return name.String() } + + // Unstructured uses Object subtype + object := spec.FieldByName("Object") + if object.IsValid() && object.Kind() == reflect.Map { + key := reflect.ValueOf("title") + value := object.MapIndex(key) + if value.IsValid() { + if value.CanInterface() { + v := value.Interface() + t, ok := v.(string) + if ok { + return t + } + } + } + } } obj, ok := m.obj.(*unstructured.Unstructured) diff --git a/pkg/apis/dashboard/migration/conversion/conversion_test.go b/pkg/apis/dashboard/migration/conversion/conversion_test.go index a5c2b02decd..cddf508e9a3 100644 --- a/pkg/apis/dashboard/migration/conversion/conversion_test.go +++ b/pkg/apis/dashboard/migration/conversion/conversion_test.go @@ -2,12 +2,15 @@ package conversion import ( "fmt" + "strings" "testing" "github.com/stretchr/testify/require" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" + "github.com/grafana/grafana/pkg/apimachinery/utils" dashboardV0 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" dashboardV1 "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1" dashboardV2 "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1" @@ -15,9 +18,9 @@ import ( func TestConversionMatrixExist(t *testing.T) { versions := []v1.Object{ - &dashboardV0.Dashboard{}, - &dashboardV1.Dashboard{}, - &dashboardV2.Dashboard{}, + &dashboardV0.Dashboard{Spec: v0alpha1.Unstructured{Object: map[string]any{"title": "dashboardV0"}}}, + &dashboardV1.Dashboard{Spec: v0alpha1.Unstructured{Object: map[string]any{"title": "dashboardV1"}}}, + &dashboardV2.Dashboard{Spec: dashboardV2.DashboardSpec{Title: "dashboardV2"}}, } scheme := runtime.NewScheme() @@ -34,6 +37,11 @@ func TestConversionMatrixExist(t *testing.T) { err = scheme.Convert(in, out, nil) require.NoError(t, err) } + + // Make sure we get the right title for each value + meta, err := utils.MetaAccessor(in) + require.NoError(t, err) + require.True(t, strings.HasPrefix(meta.FindTitle(""), "dashboard")) }) } } From 89882749124f6d6e1ffb521bd89378e525756e13 Mon Sep 17 00:00:00 2001 From: Ben Sully Date: Wed, 12 Mar 2025 08:53:20 +0000 Subject: [PATCH 217/312] Dashboards: update `@grafana/llm` to v0.13.2 and update usage (#101814) This version of the package deprecates the `openai` object in favour of the vendor-agnostic `llm` object, so this PR also updates the usage of the package to use the new object and take advantage of the vendor-agnostic APIs. --- package.json | 2 +- .../components/GenAI/GenAIButton.test.tsx | 20 +- .../components/GenAI/GenAIButton.tsx | 13 +- .../GenAI/GenAIDashboardChangesButton.tsx | 4 +- .../components/GenAI/GenAIHistory.tsx | 8 +- .../dashboard/components/GenAI/hooks.ts | 20 +- .../dashboard/components/GenAI/utils.test.ts | 15 +- .../dashboard/components/GenAI/utils.ts | 15 +- yarn.lock | 865 ++---------------- 9 files changed, 128 insertions(+), 834 deletions(-) diff --git a/package.json b/package.json index d77bd8cef07..70e2bc7716c 100644 --- a/package.json +++ b/package.json @@ -270,7 +270,7 @@ "@grafana/flamegraph": "workspace:*", "@grafana/google-sdk": "0.1.2", "@grafana/lezer-logql": "0.2.7", - "@grafana/llm": "0.12.0", + "@grafana/llm": "0.13.2", "@grafana/monaco-logql": "^0.0.8", "@grafana/o11y-ds-frontend": "workspace:*", "@grafana/plugin-ui": "0.10.1", diff --git a/public/app/features/dashboard/components/GenAI/GenAIButton.test.tsx b/public/app/features/dashboard/components/GenAI/GenAIButton.test.tsx index ab003448e23..0f4e44263a9 100644 --- a/public/app/features/dashboard/components/GenAI/GenAIButton.test.tsx +++ b/public/app/features/dashboard/components/GenAI/GenAIButton.test.tsx @@ -6,11 +6,11 @@ import { render } from 'test/test-utils'; import { selectors } from '@grafana/e2e-selectors'; import { GenAIButton, GenAIButtonProps } from './GenAIButton'; -import { StreamStatus, useOpenAIStream } from './hooks'; +import { StreamStatus, useLLMStream } from './hooks'; import { EventTrackingSrc } from './tracking'; import { Role } from './utils'; -const mockedUseOpenAiStreamState = { +const mockedUseLLMStreamState = { messages: [], setMessages: jest.fn(), reply: 'I am a robot', @@ -20,7 +20,7 @@ const mockedUseOpenAiStreamState = { }; jest.mock('./hooks', () => ({ - useOpenAIStream: jest.fn(() => mockedUseOpenAiStreamState), + useLLMStream: jest.fn(() => mockedUseLLMStreamState), StreamStatus: { IDLE: 'idle', GENERATING: 'generating', @@ -37,7 +37,7 @@ describe('GenAIButton', () => { describe('when LLM plugin is not configured', () => { beforeAll(() => { - jest.mocked(useOpenAIStream).mockReturnValue({ + jest.mocked(useLLMStream).mockReturnValue({ messages: [], error: undefined, streamStatus: StreamStatus.IDLE, @@ -65,7 +65,7 @@ describe('GenAIButton', () => { setMessagesMock.mockClear(); setShouldStopMock.mockClear(); - jest.mocked(useOpenAIStream).mockReturnValue({ + jest.mocked(useLLMStream).mockReturnValue({ messages: [], error: undefined, streamStatus: StreamStatus.IDLE, @@ -151,7 +151,7 @@ describe('GenAIButton', () => { const setShouldStopMock = jest.fn(); beforeEach(() => { - jest.mocked(useOpenAIStream).mockReturnValue({ + jest.mocked(useLLMStream).mockReturnValue({ messages: [], error: undefined, streamStatus: StreamStatus.GENERATING, @@ -222,7 +222,7 @@ describe('GenAIButton', () => { }; jest - .mocked(useOpenAIStream) + .mocked(useLLMStream) .mockImplementationOnce((options) => { options?.onResponse?.(reply); return returnValue; @@ -257,7 +257,7 @@ describe('GenAIButton', () => { setMessagesMock.mockClear(); setShouldStopMock.mockClear(); - jest.mocked(useOpenAIStream).mockReturnValue({ + jest.mocked(useLLMStream).mockReturnValue({ messages: [], error: new Error('Something went wrong'), streamStatus: StreamStatus.IDLE, @@ -308,7 +308,7 @@ describe('GenAIButton', () => { await userEvent.hover(tooltip); expect(tooltip).toBeVisible(); expect(tooltip).toHaveTextContent( - 'Failed to generate content using OpenAI. Please try again or if the problem persists, contact your organization admin.' + 'Failed to generate content using LLM. Please try again or if the problem persists, contact your organization admin.' ); }); @@ -331,7 +331,7 @@ describe('GenAIButton', () => { await userEvent.hover(tooltip); expect(tooltip).toBeVisible(); expect(tooltip).toHaveTextContent( - 'Failed to generate content using OpenAI. Please try again or if the problem persists, contact your organization admin.' + 'Failed to generate content using LLM. Please try again or if the problem persists, contact your organization admin.' ); }); diff --git a/public/app/features/dashboard/components/GenAI/GenAIButton.tsx b/public/app/features/dashboard/components/GenAI/GenAIButton.tsx index 6b6f55823ae..ec51fc91275 100644 --- a/public/app/features/dashboard/components/GenAI/GenAIButton.tsx +++ b/public/app/features/dashboard/components/GenAI/GenAIButton.tsx @@ -3,12 +3,13 @@ import { useCallback, useState } from 'react'; import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { llm } from '@grafana/llm'; import { Button, Spinner, useStyles2, Tooltip, Toggletip, Text } from '@grafana/ui'; import { GenAIHistory } from './GenAIHistory'; -import { StreamStatus, useOpenAIStream } from './hooks'; +import { StreamStatus, useLLMStream } from './hooks'; import { AutoGenerateItem, EventTrackingSrc, reportAutoGenerateInteraction } from './tracking'; -import { OAI_MODEL, DEFAULT_OAI_MODEL, Message, sanitizeReply } from './utils'; +import { DEFAULT_LLM_MODEL, Message, sanitizeReply } from './utils'; export interface GenAIButtonProps { // Button label text @@ -23,7 +24,7 @@ export interface GenAIButtonProps { // Temperature for the LLM plugin. Default is 1. // Closer to 0 means more conservative, closer to 1 means more creative. temperature?: number; - model?: OAI_MODEL; + model?: llm.Model; // Event tracking source. Send as `src` to Rudderstack event eventTrackingSrc: EventTrackingSrc; // Whether the button should be disabled @@ -42,7 +43,7 @@ export const GenAIButton = ({ text = 'Auto-generate', toggleTipTitle = '', onClick: onClickProp, - model = DEFAULT_OAI_MODEL, + model = DEFAULT_LLM_MODEL, messages, onGenerate, temperature = 1, @@ -66,7 +67,7 @@ export const GenAIButton = ({ [onGenerate, unshiftHistoryEntry] ); - const { setMessages, stopGeneration, value, error, streamStatus } = useOpenAIStream({ + const { setMessages, stopGeneration, value, error, streamStatus } = useLLMStream({ model, temperature, onResponse, @@ -85,7 +86,7 @@ export const GenAIButton = ({ const showTooltip = error || tooltip ? undefined : false; const tooltipContent = error - ? 'Failed to generate content using OpenAI. Please try again or if the problem persists, contact your organization admin.' + ? 'Failed to generate content using LLM. Please try again or if the problem persists, contact your organization admin.' : tooltip || ''; const onClick = (e: React.MouseEvent) => { diff --git a/public/app/features/dashboard/components/GenAI/GenAIDashboardChangesButton.tsx b/public/app/features/dashboard/components/GenAI/GenAIDashboardChangesButton.tsx index 73be188f57e..0c3b1bdd774 100644 --- a/public/app/features/dashboard/components/GenAI/GenAIDashboardChangesButton.tsx +++ b/public/app/features/dashboard/components/GenAI/GenAIDashboardChangesButton.tsx @@ -1,5 +1,7 @@ import { useCallback } from 'react'; +import { llm } from '@grafana/llm'; + import { DashboardModel } from '../../state/DashboardModel'; import { GenAIButton } from './GenAIButton'; @@ -42,7 +44,7 @@ export const GenAIDashboardChangesButton = ({ dashboard, onGenerate, disabled }: messages={messages} onGenerate={onGenerate} temperature={0} - model={'gpt-3.5-turbo-16k'} + model={llm.Model.BASE} eventTrackingSrc={EventTrackingSrc.dashboardChanges} toggleTipTitle={'Improve your dashboard changes summary'} disabled={disabled} diff --git a/public/app/features/dashboard/components/GenAI/GenAIHistory.tsx b/public/app/features/dashboard/components/GenAI/GenAIHistory.tsx index 68cf5d68712..05a825bdf1d 100644 --- a/public/app/features/dashboard/components/GenAI/GenAIHistory.tsx +++ b/public/app/features/dashboard/components/GenAI/GenAIHistory.tsx @@ -8,9 +8,9 @@ import { Trans } from 'app/core/internationalization'; import { STOP_GENERATION_TEXT } from './GenAIButton'; import { GenerationHistoryCarousel } from './GenerationHistoryCarousel'; import { QuickFeedback } from './QuickFeedback'; -import { StreamStatus, useOpenAIStream } from './hooks'; +import { StreamStatus, useLLMStream } from './hooks'; import { AutoGenerateItem, EventTrackingSrc, reportAutoGenerateInteraction } from './tracking'; -import { getFeedbackMessage, Message, DEFAULT_OAI_MODEL, QuickFeedbackType, sanitizeReply } from './utils'; +import { getFeedbackMessage, Message, DEFAULT_LLM_MODEL, QuickFeedbackType, sanitizeReply } from './utils'; export interface GenAIHistoryProps { history: string[]; @@ -41,8 +41,8 @@ export const GenAIHistory = ({ [updateHistory] ); - const { setMessages, stopGeneration, reply, streamStatus, error } = useOpenAIStream({ - model: DEFAULT_OAI_MODEL, + const { setMessages, stopGeneration, reply, streamStatus, error } = useLLMStream({ + model: DEFAULT_LLM_MODEL, temperature, onResponse, }); diff --git a/public/app/features/dashboard/components/GenAI/hooks.ts b/public/app/features/dashboard/components/GenAI/hooks.ts index c33dafebb3f..e3640bd5a70 100644 --- a/public/app/features/dashboard/components/GenAI/hooks.ts +++ b/public/app/features/dashboard/components/GenAI/hooks.ts @@ -2,15 +2,15 @@ import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'reac import { useAsync } from 'react-use'; import { Subscription } from 'rxjs'; -import { openai } from '@grafana/llm'; +import { llm } from '@grafana/llm'; import { createMonitoringLogger } from '@grafana/runtime'; import { useAppNotification } from 'app/core/copy/appNotification'; -import { isLLMPluginEnabled, DEFAULT_OAI_MODEL } from './utils'; +import { isLLMPluginEnabled, DEFAULT_LLM_MODEL } from './utils'; // Declared instead of imported from utils to make this hook modular // Ideally we will want to move the hook itself to a different scope later. -type Message = openai.Message; +type Message = llm.Message; const genAILogger = createMonitoringLogger('features.dashboards.genai'); @@ -29,11 +29,11 @@ interface Options { } const defaultOptions = { - model: DEFAULT_OAI_MODEL, + model: DEFAULT_LLM_MODEL, temperature: 1, }; -interface UseOpenAIStreamResponse { +interface UseLLMStreamResponse { setMessages: Dispatch>; stopGeneration: () => void; messages: Message[]; @@ -47,7 +47,7 @@ interface UseOpenAIStreamResponse { } // TODO: Add tests -export function useOpenAIStream({ model, temperature, onResponse }: Options = defaultOptions): UseOpenAIStreamResponse { +export function useLLMStream({ model, temperature, onResponse }: Options = defaultOptions): UseLLMStreamResponse { // The messages array to send to the LLM, updated when the button is clicked. const [messages, setMessages] = useState([]); @@ -65,7 +65,7 @@ export function useOpenAIStream({ model, temperature, onResponse }: Options = de setMessages([]); setError(e); notifyError( - 'Failed to generate content using OpenAI', + 'Failed to generate content using LLM', 'Please try again or if the problem persists, contact your organization admin.' ); console.error(e); @@ -93,7 +93,7 @@ export function useOpenAIStream({ model, temperature, onResponse }: Options = de setStreamStatus(StreamStatus.GENERATING); setError(undefined); // Stream the completions. Each element is the next stream chunk. - const stream = openai + const stream = llm .streamChatCompletions({ model, temperature, @@ -102,7 +102,7 @@ export function useOpenAIStream({ model, temperature, onResponse }: Options = de .pipe( // Accumulate the stream content into a stream of strings, where each // element contains the accumulated message so far. - openai.accumulateContent() + llm.accumulateContent() // The stream is just a regular Observable, so we can use standard rxjs // functionality to update state, e.g. recording when the stream // has completed. @@ -148,7 +148,7 @@ export function useOpenAIStream({ model, temperature, onResponse }: Options = de let timeout: NodeJS.Timeout | undefined; if (streamStatus === StreamStatus.GENERATING && reply === '') { timeout = setTimeout(() => { - onError(new Error(`OpenAI stream timed out after ${TIMEOUT}ms`)); + onError(new Error(`LLM stream timed out after ${TIMEOUT}ms`)); }, TIMEOUT); } diff --git a/public/app/features/dashboard/components/GenAI/utils.test.ts b/public/app/features/dashboard/components/GenAI/utils.test.ts index 2731d5f4927..80060af4c7d 100644 --- a/public/app/features/dashboard/components/GenAI/utils.test.ts +++ b/public/app/features/dashboard/components/GenAI/utils.test.ts @@ -1,4 +1,4 @@ -import { openai } from '@grafana/llm'; +import { llm } from '@grafana/llm'; import { DASHBOARD_SCHEMA_VERSION } from '../../state/DashboardMigrator'; import { createDashboardModelFixture, createPanelSaveModel } from '../../state/__fixtures__/dashboardFixtures'; @@ -6,13 +6,14 @@ import { NEW_PANEL_TITLE } from '../../utils/dashboard'; import { getDashboardChanges, getPanelStrings, isLLMPluginEnabled, sanitizeReply } from './utils'; -// Mock the openai module +// Mock the llm module jest.mock('@grafana/llm', () => ({ ...jest.requireActual('@grafana/llm'), - openai: { + llm: { streamChatCompletions: jest.fn(), accumulateContent: jest.fn(), health: jest.fn(), + Model: { LARGE: 'large' }, }, })); @@ -99,8 +100,8 @@ describe('getDashboardChanges', () => { describe('isLLMPluginEnabled', () => { it('should return false if LLM plugin is not enabled', async () => { - // Mock openai.health to return false - jest.mocked(openai.health).mockResolvedValue({ ok: false, configured: false }); + // Mock llm.health to return false + jest.mocked(llm.health).mockResolvedValue({ ok: false, configured: false }); const enabled = await isLLMPluginEnabled(); @@ -108,8 +109,8 @@ describe('isLLMPluginEnabled', () => { }); it('should return true if LLM plugin is enabled', async () => { - // Mock openai.health to return true - jest.mocked(openai.health).mockResolvedValue({ ok: true, configured: false }); + // Mock llm.health to return true + jest.mocked(llm.health).mockResolvedValue({ ok: true, configured: false }); const enabled = await isLLMPluginEnabled(); diff --git a/public/app/features/dashboard/components/GenAI/utils.ts b/public/app/features/dashboard/components/GenAI/utils.ts index 8d8938827be..ffc3740b57c 100644 --- a/public/app/features/dashboard/components/GenAI/utils.ts +++ b/public/app/features/dashboard/components/GenAI/utils.ts @@ -1,6 +1,6 @@ import { pick } from 'lodash'; -import { openai } from '@grafana/llm'; +import { llm } from '@grafana/llm'; import { config } from '@grafana/runtime'; import { Panel } from '@grafana/schema'; @@ -18,7 +18,7 @@ export enum Role { 'user' = 'user', } -export type Message = openai.Message; +export type Message = llm.Message; export enum QuickFeedbackType { Shorter = 'Even shorter', @@ -27,11 +27,12 @@ export enum QuickFeedbackType { } /** - * The OpenAI model to be used. + * The LLM model to be used. + * + * The LLM app abstracts the actual model name since it depends on the provider. + * We want to default to whatever the 'large' model is. */ -export const DEFAULT_OAI_MODEL = 'gpt-4'; - -export type OAI_MODEL = 'gpt-4' | 'gpt-4-32k' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-16k'; +export const DEFAULT_LLM_MODEL: llm.Model = llm.Model.LARGE; /** * Sanitize the reply from OpenAI by removing the leading and trailing quotes. @@ -80,7 +81,7 @@ export async function isLLMPluginEnabled(): Promise { // Check if the LLM plugin is enabled. // If not, we won't be able to make requests, so return early. llmHealthCheck = new Promise((resolve) => { - openai.health().then((response) => { + llm.health().then((response) => { if (!response.ok) { // Health check fail clear cached promise so we can try again later llmHealthCheck = undefined; diff --git a/yarn.lock b/yarn.lock index 4c463770276..0e67aedf8af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1416,7 +1416,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.26.10, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": +"@babel/runtime@npm:7.26.10": version: 7.26.10 resolution: "@babel/runtime@npm:7.26.10" dependencies: @@ -1425,6 +1425,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.5, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.25.6, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.26.9 + resolution: "@babel/runtime@npm:7.26.9" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10/08edd07d774eafbf157fdc8450ed6ddd22416fdd8e2a53e4a00349daba1b502c03ab7f7ad3ad3a7c46b9a24d99b5697591d0f852ee2f84642082ef7dda90b83d + languageName: node + linkType: hard + "@babel/template@npm:^7.22.5, @babel/template@npm:^7.24.7, @babel/template@npm:^7.25.9, @babel/template@npm:^7.26.9, @babel/template@npm:^7.3.3": version: 7.26.9 resolution: "@babel/template@npm:7.26.9" @@ -1839,7 +1848,7 @@ __metadata: languageName: node linkType: hard -"@emotion/babel-plugin@npm:^11.11.0, @emotion/babel-plugin@npm:^11.12.0, @emotion/babel-plugin@npm:^11.13.5": +"@emotion/babel-plugin@npm:^11.11.0, @emotion/babel-plugin@npm:^11.13.5": version: 11.13.5 resolution: "@emotion/babel-plugin@npm:11.13.5" dependencies: @@ -1858,7 +1867,7 @@ __metadata: languageName: node linkType: hard -"@emotion/cache@npm:^11.11.0, @emotion/cache@npm:^11.13.0, @emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0, @emotion/cache@npm:^11.4.0": +"@emotion/cache@npm:^11.11.0, @emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0, @emotion/cache@npm:^11.4.0": version: 11.14.0 resolution: "@emotion/cache@npm:11.14.0" dependencies: @@ -1884,19 +1893,6 @@ __metadata: languageName: node linkType: hard -"@emotion/css@npm:11.13.4": - version: 11.13.4 - resolution: "@emotion/css@npm:11.13.4" - dependencies: - "@emotion/babel-plugin": "npm:^11.12.0" - "@emotion/cache": "npm:^11.13.0" - "@emotion/serialize": "npm:^1.3.0" - "@emotion/sheet": "npm:^1.4.0" - "@emotion/utils": "npm:^1.4.0" - checksum: 10/57565a8bd9b712b0ade1c8b972bf2f84d2026e4372b3b035fb9d93a85a8f36ca7f2fbe67ecf32cc3fd03956587ece56ab89dd5bd43a76d3aed542a76841c76e5 - languageName: node - linkType: hard - "@emotion/css@npm:11.13.5, @emotion/css@npm:^11.11.2": version: 11.13.5 resolution: "@emotion/css@npm:11.13.5" @@ -1951,27 +1947,6 @@ __metadata: languageName: node linkType: hard -"@emotion/react@npm:11.13.3": - version: 11.13.3 - resolution: "@emotion/react@npm:11.13.3" - dependencies: - "@babel/runtime": "npm:^7.18.3" - "@emotion/babel-plugin": "npm:^11.12.0" - "@emotion/cache": "npm:^11.13.0" - "@emotion/serialize": "npm:^1.3.1" - "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.1.0" - "@emotion/utils": "npm:^1.4.0" - "@emotion/weak-memoize": "npm:^0.4.0" - hoist-non-react-statics: "npm:^3.3.1" - peerDependencies: - react: ">=16.8.0" - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10/ee70d3afc2e8dd771e6fe176d27dd87a5e21a54e54d871438fd1caa5aa2312d848c6866292fdc65a6ea1c945147c8422bda2d22ed739178af9902dc86d6b298a - languageName: node - linkType: hard - "@emotion/react@npm:11.14.0, @emotion/react@npm:^11.8.1": version: 11.14.0 resolution: "@emotion/react@npm:11.14.0" @@ -1993,20 +1968,7 @@ __metadata: languageName: node linkType: hard -"@emotion/serialize@npm:1.3.2": - version: 1.3.2 - resolution: "@emotion/serialize@npm:1.3.2" - dependencies: - "@emotion/hash": "npm:^0.9.2" - "@emotion/memoize": "npm:^0.9.0" - "@emotion/unitless": "npm:^0.10.0" - "@emotion/utils": "npm:^1.4.1" - csstype: "npm:^3.0.2" - checksum: 10/ead557c1ff19d917ef8169c02738ef36f0851fbfdf0bf69a543045bddea3b7281dc8252ee466cc5fb44ed27d1e61280ff943bb60a2c04158751fb07b3457cc93 - languageName: node - linkType: hard - -"@emotion/serialize@npm:1.3.3, @emotion/serialize@npm:^1.1.2, @emotion/serialize@npm:^1.3.0, @emotion/serialize@npm:^1.3.1, @emotion/serialize@npm:^1.3.3": +"@emotion/serialize@npm:1.3.3, @emotion/serialize@npm:^1.1.2, @emotion/serialize@npm:^1.3.3": version: 1.3.3 resolution: "@emotion/serialize@npm:1.3.3" dependencies: @@ -2033,7 +1995,7 @@ __metadata: languageName: node linkType: hard -"@emotion/use-insertion-effect-with-fallbacks@npm:^1.1.0, @emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": version: 1.2.0 resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0" peerDependencies: @@ -2042,7 +2004,7 @@ __metadata: languageName: node linkType: hard -"@emotion/utils@npm:^1.2.1, @emotion/utils@npm:^1.4.0, @emotion/utils@npm:^1.4.1, @emotion/utils@npm:^1.4.2": +"@emotion/utils@npm:^1.2.1, @emotion/utils@npm:^1.4.2": version: 1.4.2 resolution: "@emotion/utils@npm:1.4.2" checksum: 10/e5f3b8bca066b3361a7ad9064baeb9d01ed1bf51d98416a67359b62cb3affec6bb0249802c4ed11f4f8030f93cc4b67506909420bdb110adec6983d712897208 @@ -2375,20 +2337,6 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react@npm:0.26.24": - version: 0.26.24 - resolution: "@floating-ui/react@npm:0.26.24" - dependencies: - "@floating-ui/react-dom": "npm:^2.1.2" - "@floating-ui/utils": "npm:^0.2.8" - tabbable: "npm:^6.0.0" - peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - checksum: 10/903ffbee2c6726d117086e2a83f43d6ad339970758ce7979fd16cc7cf8dc0f5b869bd72c2c8ee1bcd6c63b190bb0960effd4d403e63685fb5aeed6b185041b08 - languageName: node - linkType: hard - "@floating-ui/react@npm:0.27.5": version: 0.27.5 resolution: "@floating-ui/react@npm:0.27.5" @@ -2998,41 +2946,6 @@ __metadata: languageName: node linkType: hard -"@grafana/data@npm:11.4.0, @grafana/data@npm:^10.4.0 ||^11": - version: 11.4.0 - resolution: "@grafana/data@npm:11.4.0" - dependencies: - "@braintree/sanitize-url": "npm:7.0.1" - "@grafana/schema": "npm:11.4.0" - "@types/d3-interpolate": "npm:^3.0.0" - "@types/string-hash": "npm:1.1.3" - d3-interpolate: "npm:3.0.1" - date-fns: "npm:3.6.0" - dompurify: "npm:^3.0.0" - eventemitter3: "npm:5.0.1" - fast_array_intersect: "npm:1.1.0" - history: "npm:4.10.1" - lodash: "npm:4.17.21" - marked: "npm:12.0.2" - marked-mangle: "npm:1.1.9" - moment: "npm:2.30.1" - moment-timezone: "npm:0.5.46" - ol: "npm:7.4.0" - papaparse: "npm:5.4.1" - react-use: "npm:17.5.1" - rxjs: "npm:7.8.1" - string-hash: "npm:^1.1.3" - tinycolor2: "npm:1.6.0" - tslib: "npm:2.7.0" - uplot: "npm:1.6.31" - xss: "npm:^1.0.14" - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - checksum: 10/14bbf83a7c1fe3f8bbc3ddf44e2fd6393f46aa0cdf802e61ac4e2b2719e05008ceef8ddb1e4d0d164ae86ef01afef8310cf79b927bf6f1cfce45ed0c48a21af3 - languageName: node - linkType: hard - "@grafana/data@npm:11.6.0-pre, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data": version: 0.0.0-use.local resolution: "@grafana/data@workspace:packages/grafana-data" @@ -3085,17 +2998,6 @@ __metadata: languageName: unknown linkType: soft -"@grafana/e2e-selectors@npm:11.4.0": - version: 11.4.0 - resolution: "@grafana/e2e-selectors@npm:11.4.0" - dependencies: - "@grafana/tsconfig": "npm:^2.0.0" - tslib: "npm:2.7.0" - typescript: "npm:5.5.4" - checksum: 10/dd6861415430ab8e9a5e66d8f008dd4ceadd3a7f2ba50be6d6fa7c24455e43773a12ef094838e72ede617f14d202693e01b08db44f02657039ded2858fc9fa24 - languageName: node - linkType: hard - "@grafana/e2e-selectors@npm:11.6.0-pre, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors": version: 0.0.0-use.local resolution: "@grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors" @@ -3160,7 +3062,7 @@ __metadata: languageName: node linkType: hard -"@grafana/faro-web-sdk@npm:^1.13.2, @grafana/faro-web-sdk@npm:^1.3.6": +"@grafana/faro-web-sdk@npm:^1.13.2": version: 1.13.2 resolution: "@grafana/faro-web-sdk@npm:1.13.2" dependencies: @@ -3278,18 +3180,19 @@ __metadata: languageName: node linkType: hard -"@grafana/llm@npm:0.12.0": - version: 0.12.0 - resolution: "@grafana/llm@npm:0.12.0" +"@grafana/llm@npm:0.13.2": + version: 0.13.2 + resolution: "@grafana/llm@npm:0.13.2" dependencies: - "@grafana/data": "npm:^10.4.0 ||^11" - "@grafana/runtime": "npm:^10.4.0 || ^11" - react: "npm:^18" - react-use: "npm:^17.5.0" - rxjs: "npm:^7.8.1" + react-use: "npm:^17.6.0" semver: "npm:^7.6.3" - uuid: "npm:^10.0.0" - checksum: 10/5214e244f9ead7fdb17775d83a0463e151e63aa4c716a1e5b92e47b3675aff66dd8891a87fed3ace8361cfe24966c45baadfa821d0b6f46d910c6faecad8021d + uuid: "npm:^11.0.5" + peerDependencies: + "@grafana/data": ^10.4.0 ||^11 + "@grafana/runtime": ^10.4.0 || ^11 + react: ^18 + rxjs: ^7.8.1 + checksum: 10/f30a637902cd8a2de6c9bb82ed9e31d98a7817c833023c0930c658a4087a299d2e40036b0262fd4acca1335e029994664a221037cc9279d99bebe3e3f43322ac languageName: node linkType: hard @@ -3542,26 +3445,6 @@ __metadata: languageName: unknown linkType: soft -"@grafana/runtime@npm:^10.4.0 || ^11": - version: 11.4.0 - resolution: "@grafana/runtime@npm:11.4.0" - dependencies: - "@grafana/data": "npm:11.4.0" - "@grafana/e2e-selectors": "npm:11.4.0" - "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/schema": "npm:11.4.0" - "@grafana/ui": "npm:11.4.0" - history: "npm:4.10.1" - lodash: "npm:4.17.21" - rxjs: "npm:7.8.1" - tslib: "npm:2.7.0" - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - checksum: 10/4a462803fb4e5f0fff05a8b50bd6a518a4d30bb3a6bb8e2d3d2acbd411f62dffa5606d322b91ed8bc76a37258c429c07f55b5d8539d41b7c8d111d706fb9caff - languageName: node - linkType: hard - "@grafana/saga-icons@workspace:*, @grafana/saga-icons@workspace:packages/grafana-icons": version: 0.0.0-use.local resolution: "@grafana/saga-icons@workspace:packages/grafana-icons" @@ -3641,15 +3524,6 @@ __metadata: languageName: node linkType: hard -"@grafana/schema@npm:11.4.0": - version: 11.4.0 - resolution: "@grafana/schema@npm:11.4.0" - dependencies: - tslib: "npm:2.7.0" - checksum: 10/ed115437cf4a3c95194c4eeb1c2723be06887fc5c5b5e4f5eea1beb49f5bab15fab3d20de95e918019bd333f4e6505a2dace57cef60cbc9327bd4e643139b1b8 - languageName: node - linkType: hard - "@grafana/schema@npm:11.6.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema": version: 0.0.0-use.local resolution: "@grafana/schema@workspace:packages/grafana-schema" @@ -3719,83 +3593,6 @@ __metadata: languageName: node linkType: hard -"@grafana/ui@npm:11.4.0": - version: 11.4.0 - resolution: "@grafana/ui@npm:11.4.0" - dependencies: - "@emotion/css": "npm:11.13.4" - "@emotion/react": "npm:11.13.3" - "@emotion/serialize": "npm:1.3.2" - "@floating-ui/react": "npm:0.26.24" - "@grafana/data": "npm:11.4.0" - "@grafana/e2e-selectors": "npm:11.4.0" - "@grafana/faro-web-sdk": "npm:^1.3.6" - "@grafana/schema": "npm:11.4.0" - "@hello-pangea/dnd": "npm:16.6.0" - "@leeoniya/ufuzzy": "npm:1.0.14" - "@monaco-editor/react": "npm:4.6.0" - "@popperjs/core": "npm:2.11.8" - "@react-aria/dialog": "npm:3.5.18" - "@react-aria/focus": "npm:3.18.3" - "@react-aria/overlays": "npm:3.23.3" - "@react-aria/utils": "npm:3.25.3" - "@tanstack/react-virtual": "npm:^3.5.1" - "@types/jquery": "npm:3.5.31" - "@types/lodash": "npm:4.17.10" - "@types/react-table": "npm:7.7.20" - ansicolor: "npm:1.1.100" - calculate-size: "npm:1.1.1" - classnames: "npm:2.5.1" - d3: "npm:7.9.0" - date-fns: "npm:3.6.0" - downshift: "npm:^9.0.6" - hoist-non-react-statics: "npm:3.3.2" - i18next: "npm:^23.0.0" - i18next-browser-languagedetector: "npm:^7.0.2" - immutable: "npm:4.3.7" - is-hotkey: "npm:0.2.0" - jquery: "npm:3.7.1" - lodash: "npm:4.17.21" - micro-memoize: "npm:^4.1.2" - moment: "npm:2.30.1" - monaco-editor: "npm:0.34.1" - ol: "npm:7.4.0" - prismjs: "npm:1.29.0" - rc-cascader: "npm:3.28.1" - rc-drawer: "npm:7.2.0" - rc-slider: "npm:11.1.7" - rc-time-picker: "npm:^3.7.3" - rc-tooltip: "npm:6.2.1" - react-calendar: "npm:5.0.0" - react-colorful: "npm:5.6.1" - react-custom-scrollbars-2: "npm:4.5.0" - react-dropzone: "npm:14.2.9" - react-highlight-words: "npm:0.20.0" - react-hook-form: "npm:^7.49.2" - react-i18next: "npm:^14.0.0" - react-inlinesvg: "npm:3.0.2" - react-loading-skeleton: "npm:3.5.0" - react-router-dom-v5-compat: "npm:^6.26.1" - react-select: "npm:5.8.1" - react-table: "npm:7.8.0" - react-transition-group: "npm:4.4.5" - react-use: "npm:17.5.1" - react-window: "npm:1.8.10" - rxjs: "npm:7.8.1" - slate: "npm:0.47.9" - slate-plain-serializer: "npm:0.7.13" - slate-react: "npm:0.22.10" - tinycolor2: "npm:1.6.0" - tslib: "npm:2.7.0" - uplot: "npm:1.6.31" - uuid: "npm:9.0.1" - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - checksum: 10/9019f6b549ae70808476902bdbbda1d3d80077dd84663d64f9bbecd86708c3e96f56dd1f7c8eff605710bbdf644b52520dbf2848ba4f0684f202ab896827d1e2 - languageName: node - linkType: hard - "@grafana/ui@npm:11.6.0-pre, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui": version: 0.0.0-use.local resolution: "@grafana/ui@workspace:packages/grafana-ui" @@ -3950,24 +3747,6 @@ __metadata: languageName: node linkType: hard -"@hello-pangea/dnd@npm:16.6.0": - version: 16.6.0 - resolution: "@hello-pangea/dnd@npm:16.6.0" - dependencies: - "@babel/runtime": "npm:^7.24.1" - css-box-model: "npm:^1.2.1" - memoize-one: "npm:^6.0.0" - raf-schd: "npm:^4.0.3" - react-redux: "npm:^8.1.3" - redux: "npm:^4.2.1" - use-memo-one: "npm:^1.1.3" - peerDependencies: - react: ^16.8.5 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 - checksum: 10/f377461d400c8223174745e4d7ecf4fb0146f9e807413f98120ebbcf075282e631273988d336daaf1fb8e6b6c6a1a8e4f99beefecd7a6b68ccc3bb064d38f13f - languageName: node - linkType: hard - "@hello-pangea/dnd@npm:17.0.0, @hello-pangea/dnd@npm:^17.0.0": version: 17.0.0 resolution: "@hello-pangea/dnd@npm:17.0.0" @@ -4848,13 +4627,6 @@ __metadata: languageName: node linkType: hard -"@leeoniya/ufuzzy@npm:1.0.14": - version: 1.0.14 - resolution: "@leeoniya/ufuzzy@npm:1.0.14" - checksum: 10/852b580a8eaaf92e2d448f5b720e3c53e4bea22187bf5e8459256677c47183321b47b8384982e15751f42da7e77a216fd86c80e6185677d8270adeab4a4fb771 - languageName: node - linkType: hard - "@leeoniya/ufuzzy@npm:1.0.18, @leeoniya/ufuzzy@npm:^1.0.16": version: 1.0.18 resolution: "@leeoniya/ufuzzy@npm:1.0.18" @@ -6529,23 +6301,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/dialog@npm:3.5.18": - version: 3.5.18 - resolution: "@react-aria/dialog@npm:3.5.18" - dependencies: - "@react-aria/focus": "npm:^3.18.3" - "@react-aria/overlays": "npm:^3.23.3" - "@react-aria/utils": "npm:^3.25.3" - "@react-types/dialog": "npm:^3.5.13" - "@react-types/shared": "npm:^3.25.0" - "@swc/helpers": "npm:^0.5.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - checksum: 10/dbd40d14baeea7dae56956985234e29ada74a93899177c737c3312ec788b22a6d65179b7132cdbd6609e973d410f749071c68ceb6620d3cf6f60a03ddf648983 - languageName: node - linkType: hard - "@react-aria/dialog@npm:3.5.21": version: 3.5.21 resolution: "@react-aria/dialog@npm:3.5.21" @@ -6563,22 +6318,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/focus@npm:3.18.3": - version: 3.18.3 - resolution: "@react-aria/focus@npm:3.18.3" - dependencies: - "@react-aria/interactions": "npm:^3.22.3" - "@react-aria/utils": "npm:^3.25.3" - "@react-types/shared": "npm:^3.25.0" - "@swc/helpers": "npm:^0.5.0" - clsx: "npm:^2.0.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - checksum: 10/b11632e638de2f40ec12a4a8c818059b9bf7e90b288a93b46985350c887ae7ecdf037391537f86fbacb2a186dec7e7c41a8f2ff767fd232a8cac3189f03735b2 - languageName: node - linkType: hard - -"@react-aria/focus@npm:3.19.1, @react-aria/focus@npm:^3.18.3, @react-aria/focus@npm:^3.19.1": +"@react-aria/focus@npm:3.19.1, @react-aria/focus@npm:^3.19.1": version: 3.19.1 resolution: "@react-aria/focus@npm:3.19.1" dependencies: @@ -6594,7 +6334,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/i18n@npm:^3.12.3, @react-aria/i18n@npm:^3.12.5": +"@react-aria/i18n@npm:^3.12.5": version: 3.12.5 resolution: "@react-aria/i18n@npm:3.12.5" dependencies: @@ -6613,7 +6353,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/interactions@npm:^3.22.3, @react-aria/interactions@npm:^3.23.0": +"@react-aria/interactions@npm:^3.23.0": version: 3.23.0 resolution: "@react-aria/interactions@npm:3.23.0" dependencies: @@ -6628,29 +6368,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/overlays@npm:3.23.3": - version: 3.23.3 - resolution: "@react-aria/overlays@npm:3.23.3" - dependencies: - "@react-aria/focus": "npm:^3.18.3" - "@react-aria/i18n": "npm:^3.12.3" - "@react-aria/interactions": "npm:^3.22.3" - "@react-aria/ssr": "npm:^3.9.6" - "@react-aria/utils": "npm:^3.25.3" - "@react-aria/visually-hidden": "npm:^3.8.16" - "@react-stately/overlays": "npm:^3.6.11" - "@react-types/button": "npm:^3.10.0" - "@react-types/overlays": "npm:^3.8.10" - "@react-types/shared": "npm:^3.25.0" - "@swc/helpers": "npm:^0.5.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - checksum: 10/c70af63d4ae828963b9fa780330cabf49e5a70f8981ae65d173e32934fa190fc8df1283de65d6a8b71b6340050718df19c2e7353b406114962d85ee5deb811ee - languageName: node - linkType: hard - -"@react-aria/overlays@npm:3.25.0, @react-aria/overlays@npm:^3.23.3, @react-aria/overlays@npm:^3.25.0": +"@react-aria/overlays@npm:3.25.0, @react-aria/overlays@npm:^3.25.0": version: 3.25.0 resolution: "@react-aria/overlays@npm:3.25.0" dependencies: @@ -6672,7 +6390,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/ssr@npm:^3.9.6, @react-aria/ssr@npm:^3.9.7": +"@react-aria/ssr@npm:^3.9.7": version: 3.9.7 resolution: "@react-aria/ssr@npm:3.9.7" dependencies: @@ -6683,22 +6401,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/utils@npm:3.25.3": - version: 3.25.3 - resolution: "@react-aria/utils@npm:3.25.3" - dependencies: - "@react-aria/ssr": "npm:^3.9.6" - "@react-stately/utils": "npm:^3.10.4" - "@react-types/shared": "npm:^3.25.0" - "@swc/helpers": "npm:^0.5.0" - clsx: "npm:^2.0.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - checksum: 10/86aed35da5cb0d48d949e40bf8226d5a6d6c92a8cdc60e3e12d524d1f3cc91ab6b54c5e1642823773cbb889fb61af7da22e89488b704b56fc5f4d8d59da7519b - languageName: node - linkType: hard - -"@react-aria/utils@npm:3.27.0, @react-aria/utils@npm:^3.25.3, @react-aria/utils@npm:^3.27.0": +"@react-aria/utils@npm:3.27.0, @react-aria/utils@npm:^3.27.0": version: 3.27.0 resolution: "@react-aria/utils@npm:3.27.0" dependencies: @@ -6714,7 +6417,7 @@ __metadata: languageName: node linkType: hard -"@react-aria/visually-hidden@npm:^3.8.16, @react-aria/visually-hidden@npm:^3.8.19": +"@react-aria/visually-hidden@npm:^3.8.19": version: 3.8.19 resolution: "@react-aria/visually-hidden@npm:3.8.19" dependencies: @@ -6763,7 +6466,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/overlays@npm:^3.6.11, @react-stately/overlays@npm:^3.6.13": +"@react-stately/overlays@npm:^3.6.13": version: 3.6.13 resolution: "@react-stately/overlays@npm:3.6.13" dependencies: @@ -6776,7 +6479,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/utils@npm:^3.10.4, @react-stately/utils@npm:^3.10.5": +"@react-stately/utils@npm:^3.10.5": version: 3.10.5 resolution: "@react-stately/utils@npm:3.10.5" dependencies: @@ -6787,7 +6490,7 @@ __metadata: languageName: node linkType: hard -"@react-types/button@npm:3.10.2, @react-types/button@npm:^3.10.0, @react-types/button@npm:^3.10.2": +"@react-types/button@npm:3.10.2, @react-types/button@npm:^3.10.2": version: 3.10.2 resolution: "@react-types/button@npm:3.10.2" dependencies: @@ -6798,7 +6501,7 @@ __metadata: languageName: node linkType: hard -"@react-types/dialog@npm:^3.5.13, @react-types/dialog@npm:^3.5.15": +"@react-types/dialog@npm:^3.5.15": version: 3.5.15 resolution: "@react-types/dialog@npm:3.5.15" dependencies: @@ -6822,7 +6525,7 @@ __metadata: languageName: node linkType: hard -"@react-types/overlays@npm:3.8.12, @react-types/overlays@npm:^3.8.10, @react-types/overlays@npm:^3.8.12": +"@react-types/overlays@npm:3.8.12, @react-types/overlays@npm:^3.8.12": version: 3.8.12 resolution: "@react-types/overlays@npm:3.8.12" dependencies: @@ -6833,7 +6536,7 @@ __metadata: languageName: node linkType: hard -"@react-types/shared@npm:3.27.0, @react-types/shared@npm:^3.25.0, @react-types/shared@npm:^3.27.0": +"@react-types/shared@npm:3.27.0, @react-types/shared@npm:^3.27.0": version: 3.27.0 resolution: "@react-types/shared@npm:3.27.0" peerDependencies: @@ -9879,15 +9582,6 @@ __metadata: languageName: node linkType: hard -"@types/jquery@npm:3.5.31": - version: 3.5.31 - resolution: "@types/jquery@npm:3.5.31" - dependencies: - "@types/sizzle": "npm:*" - checksum: 10/c14b3db4d2c34eb44b30ae119f1983d9d94231a02d44357b08f3ef406852c777edd928eb35875e879515a96eb8eb2188ed5572a0de35f322019bf6de858ce610 - languageName: node - linkType: hard - "@types/jquery@npm:3.5.32": version: 3.5.32 resolution: "@types/jquery@npm:3.5.32" @@ -9968,13 +9662,6 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:4.17.10": - version: 4.17.10 - resolution: "@types/lodash@npm:4.17.10" - checksum: 10/10fe24a93adc6048cb23e4135c1ed1d52cc39033682e6513f4f51b74a9af6d7a24fbea92203c22dc4e01e35f1ab3aa0fd0a2b487e8a4a2bbdf1fc05970094066 - languageName: node - linkType: hard - "@types/lodash@npm:4.17.7": version: 4.17.7 resolution: "@types/lodash@npm:4.17.7" @@ -11607,13 +11294,6 @@ __metadata: languageName: node linkType: hard -"ansicolor@npm:1.1.100": - version: 1.1.100 - resolution: "ansicolor@npm:1.1.100" - checksum: 10/9420e96f44b578153dfd11e2d829d633ca2699452419aff48314219d66b7b69a9b6d994d2f2350b1d29a9826e33500b87fe85606629a8889cd52ce806908f4a9 - languageName: node - linkType: hard - "ansicolor@npm:2.0.3": version: 2.0.3 resolution: "ansicolor@npm:2.0.3" @@ -11979,7 +11659,7 @@ __metadata: languageName: node linkType: hard -"attr-accept@npm:^2.2.2, attr-accept@npm:^2.2.4": +"attr-accept@npm:^2.2.4": version: 2.2.5 resolution: "attr-accept@npm:2.2.5" checksum: 10/474b1c53e62c5b881c745d1f098196f190c8b493245e95d4b0fea9298d3acb56f551868fc12806885277e55e9d8ad3c5963e92d93456f4e4081dfc5190977bfd @@ -12216,16 +11896,6 @@ __metadata: languageName: node linkType: hard -"babel-runtime@npm:6.x, babel-runtime@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-runtime@npm:6.26.0" - dependencies: - core-js: "npm:^2.4.0" - regenerator-runtime: "npm:^0.11.0" - checksum: 10/2cdf0f083b9598a43cdb11cbf1e7060584079a9a2230f06aec997ba81e887ef17fdcb5ad813a484ee099e06d2de0cea832bdd3011c06325acb284284c754ee8f - languageName: node - linkType: hard - "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -13511,22 +13181,6 @@ __metadata: languageName: node linkType: hard -"component-classes@npm:^1.2.5": - version: 1.2.6 - resolution: "component-classes@npm:1.2.6" - dependencies: - component-indexof: "npm:0.0.3" - checksum: 10/aa70f282b85a19d7a190dabb2c72c9b8a2a5565fc42d72ca0661e8ce47e55e53da29f468467771d7e0bf4ba8c9723e5a21954fc38b942d9d873ee56be856adfc - languageName: node - linkType: hard - -"component-indexof@npm:0.0.3": - version: 0.0.3 - resolution: "component-indexof@npm:0.0.3" - checksum: 10/34a720e96fc0be1043a4517845b1b7483989736c559089eca285f7ac9ef049eacc8ab12fc31f924dfa2d4ecea0714611a695d087ce11caf30502b5a80646e9e9 - languageName: node - linkType: hard - "compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -13818,7 +13472,7 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^2.4.0, core-js@npm:^2.6.5": +"core-js@npm:^2.6.5": version: 2.6.12 resolution: "core-js@npm:2.6.12" checksum: 10/7c624eb00a59c74c769d5d80f751f3bf1fc6201205b6562f27286ad5e00bbca1483f2f7eb0c2854b86f526ef5c7dc958b45f2ff536f8a31b8e9cb1a13a96efca @@ -13997,16 +13651,6 @@ __metadata: languageName: node linkType: hard -"css-animation@npm:^1.3.2": - version: 1.6.1 - resolution: "css-animation@npm:1.6.1" - dependencies: - babel-runtime: "npm:6.x" - component-classes: "npm:^1.2.5" - checksum: 10/5aea8fd333300c6b15f523ac741dd2ed367489485c7a9a43647f7779fef9e5f338c405e3ad9ed8db338503b4aefea86108112a01bf78651b623f8bd2e146e0e0 - languageName: node - linkType: hard - "css-box-model@npm:^1.2.1": version: 1.2.1 resolution: "css-box-model@npm:1.2.1" @@ -14857,13 +14501,6 @@ __metadata: languageName: node linkType: hard -"date-fns@npm:3.6.0": - version: 3.6.0 - resolution: "date-fns@npm:3.6.0" - checksum: 10/cac35c58926a3b5d577082ff2b253612ec1c79eb6754fddef46b6a8e826501ea2cb346ecbd211205f1ba382ddd1f9d8c3f00bf433ad63cc3063454d294e3a6b8 - languageName: node - linkType: hard - "date-fns@npm:4.1.0": version: 4.1.0 resolution: "date-fns@npm:4.1.0" @@ -15379,7 +15016,7 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:3.2.4, dompurify@npm:^3.0.0": +"dompurify@npm:3.2.4": version: 3.2.4 resolution: "dompurify@npm:3.2.4" dependencies: @@ -16659,13 +16296,6 @@ __metadata: languageName: node linkType: hard -"exenv@npm:^1.2.2": - version: 1.2.2 - resolution: "exenv@npm:1.2.2" - checksum: 10/6840185e421394bcb143debb866d31d19c3e4a4bca87d2f319d68d61afff353b3c678f2eb389e3b98ab9aecbec19f6bebbdc4193984378af0a3366c498a7efc8 - languageName: node - linkType: hard - "exif-parser@npm:^0.1.12": version: 0.1.12 resolution: "exif-parser@npm:0.1.12" @@ -16996,15 +16626,6 @@ __metadata: languageName: node linkType: hard -"file-selector@npm:^0.6.0": - version: 0.6.0 - resolution: "file-selector@npm:0.6.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10/6add4098ae07fd1e9050b1e8d3fd9f128680c1d6648c0676af54ace4586e6e5bfcb8fdfa45b69e9131ffd8175bf630d54a445a5facf9be244f85b99ce309183e - languageName: node - linkType: hard - "file-selector@npm:^2.1.0": version: 2.1.0 resolution: "file-selector@npm:2.1.0" @@ -18063,7 +17684,7 @@ __metadata: "@grafana/flamegraph": "workspace:*" "@grafana/google-sdk": "npm:0.1.2" "@grafana/lezer-logql": "npm:0.2.7" - "@grafana/llm": "npm:0.12.0" + "@grafana/llm": "npm:0.13.2" "@grafana/monaco-logql": "npm:^0.0.8" "@grafana/o11y-ds-frontend": "workspace:*" "@grafana/plugin-e2e": "npm:1.17.1" @@ -19073,15 +18694,6 @@ __metadata: languageName: node linkType: hard -"i18next-browser-languagedetector@npm:^7.0.2": - version: 7.2.2 - resolution: "i18next-browser-languagedetector@npm:7.2.2" - dependencies: - "@babel/runtime": "npm:^7.23.2" - checksum: 10/6f6dd5db3e83c2ed3b24d7fb754d0c41fd2056a0f06fb3d2a4604541c6ee0031d3f2cbbcfad726bbfc9741bcbbd6cde3acf94d339d600b6d9f49f0bbe51179d9 - languageName: node - linkType: hard - "i18next-browser-languagedetector@npm:^8.0.0": version: 8.0.2 resolution: "i18next-browser-languagedetector@npm:8.0.2" @@ -19136,7 +18748,7 @@ __metadata: languageName: node linkType: hard -"i18next@npm:^23.0.0, i18next@npm:^23.11.5": +"i18next@npm:^23.11.5": version: 23.16.8 resolution: "i18next@npm:23.16.8" dependencies: @@ -19239,13 +18851,6 @@ __metadata: languageName: node linkType: hard -"immutable@npm:4.3.7, immutable@npm:^4.3.6": - version: 4.3.7 - resolution: "immutable@npm:4.3.7" - checksum: 10/37d963c5050f03ae5f3714ba7a43d469aa482051087f4c65d673d1501c309ea231d87480c792e19fa85e2eaf965f76af5d0aa92726505f3cfe4af91619dfb80b - languageName: node - linkType: hard - "immutable@npm:5.0.3, immutable@npm:^5.0.2": version: 5.0.3 resolution: "immutable@npm:5.0.3" @@ -19260,6 +18865,13 @@ __metadata: languageName: node linkType: hard +"immutable@npm:^4.3.6": + version: 4.3.7 + resolution: "immutable@npm:4.3.7" + checksum: 10/37d963c5050f03ae5f3714ba7a43d469aa482051087f4c65d673d1501c309ea231d87480c792e19fa85e2eaf965f76af5d0aa92726505f3cfe4af91619dfb80b + languageName: node + linkType: hard + "import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -22137,24 +21749,6 @@ __metadata: languageName: node linkType: hard -"marked-mangle@npm:1.1.9": - version: 1.1.9 - resolution: "marked-mangle@npm:1.1.9" - peerDependencies: - marked: ">=4 <15" - checksum: 10/745e44bea9b52bc9c52e41f5d2b146eb21072a92ddf85b4b2f210b091da93fcbf2f5447b02f849fd19e79224bdca385462a42b96882493778d1d0ff0a3da9a8c - languageName: node - linkType: hard - -"marked@npm:12.0.2": - version: 12.0.2 - resolution: "marked@npm:12.0.2" - bin: - marked: bin/marked.js - checksum: 10/24d4fc58d37c1779197fa7f93c504d8c71d4df54eb69cbbc14a55ba2a8e2ad83d723801fc25452c21ce74b38a483c5863c53449f130253a597be9e9c1d3e7e2b - languageName: node - linkType: hard - "marked@npm:15.0.6": version: 15.0.6 resolution: "marked@npm:15.0.6" @@ -22752,15 +22346,6 @@ __metadata: languageName: node linkType: hard -"moment-timezone@npm:0.5.46": - version: 0.5.46 - resolution: "moment-timezone@npm:0.5.46" - dependencies: - moment: "npm:^2.29.4" - checksum: 10/7613ba388fa6004af62675fb9945cb0d37758b559d07470a5e188419ffe1ac03eb2ed16fe80aa34d1e7dd39fc5bd67dc02cd59e8dcdab95504cface2c78e4b3d - languageName: node - linkType: hard - "moment-timezone@npm:0.5.47": version: 0.5.47 resolution: "moment-timezone@npm:0.5.47" @@ -22770,7 +22355,7 @@ __metadata: languageName: node linkType: hard -"moment@npm:2.30.1, moment@npm:2.x, moment@npm:^2.20.1, moment@npm:^2.29.4, moment@npm:^2.30.1": +"moment@npm:2.30.1, moment@npm:^2.20.1, moment@npm:^2.29.4, moment@npm:^2.30.1": version: 2.30.1 resolution: "moment@npm:2.30.1" checksum: 10/ae42d876d4ec831ef66110bdc302c0657c664991e45cf2afffc4b0f6cd6d251dde11375c982a5c0564ccc0fa593fc564576ddceb8c8845e87c15f58aa6baca69 @@ -24117,13 +23702,6 @@ __metadata: languageName: node linkType: hard -"papaparse@npm:5.4.1": - version: 5.4.1 - resolution: "papaparse@npm:5.4.1" - checksum: 10/5e6dc978187182ad2efa1d264ffe73d2042cd23b8fb1dcb0b0f5c8c7c772c11e3eb4e166fb0893880ed24529a96abe9065d704cc5b4cb96abf037413cfe43788 - languageName: node - linkType: hard - "papaparse@npm:5.5.2": version: 5.5.2 resolution: "papaparse@npm:5.5.2" @@ -25194,13 +24772,6 @@ __metadata: languageName: node linkType: hard -"prismjs@npm:1.29.0": - version: 1.29.0 - resolution: "prismjs@npm:1.29.0" - checksum: 10/2080db382c2dde0cfc7693769e89b501ef1bfc8ff4f8d25c07fd4c37ca31bc443f6133d5b7c145a73309dc396e829ddb7cc18560026d862a887ae08864ef6b07 - languageName: node - linkType: hard - "prismjs@npm:1.30.0, prismjs@npm:^1.27.0, prismjs@npm:^1.29.0": version: 1.30.0 resolution: "prismjs@npm:1.30.0" @@ -25535,7 +25106,7 @@ __metadata: languageName: node linkType: hard -"raf@npm:^3.1.0, raf@npm:^3.4.0, raf@npm:^3.4.1": +"raf@npm:^3.1.0, raf@npm:^3.4.1": version: 3.4.1 resolution: "raf@npm:3.4.1" dependencies: @@ -25617,18 +25188,6 @@ __metadata: languageName: node linkType: hard -"rc-align@npm:^2.4.0": - version: 2.4.5 - resolution: "rc-align@npm:2.4.5" - dependencies: - babel-runtime: "npm:^6.26.0" - dom-align: "npm:^1.7.0" - prop-types: "npm:^15.5.8" - rc-util: "npm:^4.0.4" - checksum: 10/6a82f7b47dda397b90c7b6d41e5500b9e3e427891d15a54f95708abf31a8aba19d882bd9e5ab42f4979b455aa2236b96cf6403152507bcd1fd7663be08a0ceb5 - languageName: node - linkType: hard - "rc-align@npm:^4.0.0": version: 4.0.15 resolution: "rc-align@npm:4.0.15" @@ -25645,21 +25204,6 @@ __metadata: languageName: node linkType: hard -"rc-animate@npm:2.x": - version: 2.11.1 - resolution: "rc-animate@npm:2.11.1" - dependencies: - babel-runtime: "npm:6.x" - classnames: "npm:^2.2.6" - css-animation: "npm:^1.3.2" - prop-types: "npm:15.x" - raf: "npm:^3.4.0" - rc-util: "npm:^4.15.3" - react-lifecycles-compat: "npm:^3.0.4" - checksum: 10/afb54ad896c9d50af212ae7a56a216b47b38238a4e8e187437fe965c2cf100f1ca82668ed90d38dcd3b0e3ced53f54383e417af09c6dc09f6db0f78cba2aa9e4 - languageName: node - linkType: hard - "rc-cascader@npm:1.0.1": version: 1.0.1 resolution: "rc-cascader@npm:1.0.1" @@ -25675,23 +25219,6 @@ __metadata: languageName: node linkType: hard -"rc-cascader@npm:3.28.1": - version: 3.28.1 - resolution: "rc-cascader@npm:3.28.1" - dependencies: - "@babel/runtime": "npm:^7.12.5" - array-tree-filter: "npm:^2.1.0" - classnames: "npm:^2.3.1" - rc-select: "npm:~14.15.0" - rc-tree: "npm:~5.9.0" - rc-util: "npm:^5.37.0" - peerDependencies: - react: ">=16.9.0" - react-dom: ">=16.9.0" - checksum: 10/bb2feb79c0db19f459b265e9a0afb87611f4ba06c4776a5ea8ddcb0bfc81403292ada3a1c29cd7511872f8d2bfda7c29eab6dbc32052a60baf49576a50866d77 - languageName: node - linkType: hard - "rc-cascader@npm:3.33.0": version: 3.33.0 resolution: "rc-cascader@npm:3.33.0" @@ -25813,24 +25340,6 @@ __metadata: languageName: node linkType: hard -"rc-select@npm:~14.15.0": - version: 14.15.2 - resolution: "rc-select@npm:14.15.2" - dependencies: - "@babel/runtime": "npm:^7.10.1" - "@rc-component/trigger": "npm:^2.1.1" - classnames: "npm:2.x" - rc-motion: "npm:^2.0.1" - rc-overflow: "npm:^1.3.1" - rc-util: "npm:^5.16.1" - rc-virtual-list: "npm:^3.5.2" - peerDependencies: - react: "*" - react-dom: "*" - checksum: 10/707d9de38aaf83063ede754a925b56d6f02740197a3bed93f886c132ce797321d9e70a2fe32cff0546c54d9a11414d6d2c8fc1f914ac665fa11ad2b30a08bc85 - languageName: node - linkType: hard - "rc-select@npm:~14.16.2": version: 14.16.3 resolution: "rc-select@npm:14.16.3" @@ -25849,20 +25358,6 @@ __metadata: languageName: node linkType: hard -"rc-slider@npm:11.1.7": - version: 11.1.7 - resolution: "rc-slider@npm:11.1.7" - dependencies: - "@babel/runtime": "npm:^7.10.1" - classnames: "npm:^2.2.5" - rc-util: "npm:^5.36.0" - peerDependencies: - react: ">=16.9.0" - react-dom: ">=16.9.0" - checksum: 10/3b484d7ba4e4b6fc695666c27b767622c64b5819d0386cc0afb6d186c08f8ed4f93dd72f55377af9a957c77ee77db4d7fa73f85ccdaab0f39d9670daf42a17c5 - languageName: node - linkType: hard - "rc-slider@npm:11.1.8": version: 11.1.8 resolution: "rc-slider@npm:11.1.8" @@ -25877,34 +25372,6 @@ __metadata: languageName: node linkType: hard -"rc-time-picker@npm:^3.7.3": - version: 3.7.3 - resolution: "rc-time-picker@npm:3.7.3" - dependencies: - classnames: "npm:2.x" - moment: "npm:2.x" - prop-types: "npm:^15.5.8" - raf: "npm:^3.4.1" - rc-trigger: "npm:^2.2.0" - react-lifecycles-compat: "npm:^3.0.4" - checksum: 10/236ba0dd1b1cee4dd398d2542c251a7e0da21b31d3390a23c45cc7f2ea266cfddd1f0b7f681fe205f2d92edaa72771cb087505790b5124eb0ca31668feb74da5 - languageName: node - linkType: hard - -"rc-tooltip@npm:6.2.1": - version: 6.2.1 - resolution: "rc-tooltip@npm:6.2.1" - dependencies: - "@babel/runtime": "npm:^7.11.2" - "@rc-component/trigger": "npm:^2.0.0" - classnames: "npm:^2.3.1" - peerDependencies: - react: ">=16.9.0" - react-dom: ">=16.9.0" - checksum: 10/a82064d6d521ba4c03d074505402f6c38f9f50439037235969546b694e38977bab3420b46080bde295aca2436e6725d64b0a330a5ccdd51932deabbb1bf7585b - languageName: node - linkType: hard - "rc-tooltip@npm:6.4.0": version: 6.4.0 resolution: "rc-tooltip@npm:6.4.0" @@ -25936,37 +25403,6 @@ __metadata: languageName: node linkType: hard -"rc-tree@npm:~5.9.0": - version: 5.9.0 - resolution: "rc-tree@npm:5.9.0" - dependencies: - "@babel/runtime": "npm:^7.10.1" - classnames: "npm:2.x" - rc-motion: "npm:^2.0.1" - rc-util: "npm:^5.16.1" - rc-virtual-list: "npm:^3.5.1" - peerDependencies: - react: "*" - react-dom: "*" - checksum: 10/d7525c4a524c6de8e177ebc90fe9b924046951a02bacee85efd4529fb05a66add936802b43d5ca8d84469f9c63b8d542437365b911480f62a78e03e2c7fbaca0 - languageName: node - linkType: hard - -"rc-trigger@npm:^2.2.0": - version: 2.6.5 - resolution: "rc-trigger@npm:2.6.5" - dependencies: - babel-runtime: "npm:6.x" - classnames: "npm:^2.2.6" - prop-types: "npm:15.x" - rc-align: "npm:^2.4.0" - rc-animate: "npm:2.x" - rc-util: "npm:^4.4.0" - react-lifecycles-compat: "npm:^3.0.4" - checksum: 10/a3ed5f0c453a37ab00fce302e9f40daa64870eb001576a3409a94550802af1e01cbd7b050b3adf7225af03e82e2070d095477957fcd07209ee32602a2f3fba31 - languageName: node - linkType: hard - "rc-trigger@npm:^4.0.0": version: 4.4.3 resolution: "rc-trigger@npm:4.4.3" @@ -25981,7 +25417,7 @@ __metadata: languageName: node linkType: hard -"rc-util@npm:^4.0.4, rc-util@npm:^4.15.3, rc-util@npm:^4.4.0": +"rc-util@npm:^4.0.4": version: 4.21.1 resolution: "rc-util@npm:4.21.1" dependencies: @@ -26072,25 +25508,6 @@ __metadata: languageName: node linkType: hard -"react-calendar@npm:5.0.0": - version: 5.0.0 - resolution: "react-calendar@npm:5.0.0" - dependencies: - "@wojtekmaj/date-utils": "npm:^1.1.3" - clsx: "npm:^2.0.0" - get-user-locale: "npm:^2.2.1" - warning: "npm:^4.0.0" - peerDependencies: - "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10/1172828652e796a946beec4f7f4125bfbe775a39c4cdab2179cef04b0688e892062f628ec9263bdfea6d9be41c3de1414586036d03d6694e6008cd4763649581 - languageName: node - linkType: hard - "react-calendar@npm:^4.8.0": version: 4.8.0 resolution: "react-calendar@npm:4.8.0" @@ -26267,19 +25684,6 @@ __metadata: languageName: node linkType: hard -"react-dropzone@npm:14.2.9": - version: 14.2.9 - resolution: "react-dropzone@npm:14.2.9" - dependencies: - attr-accept: "npm:^2.2.2" - file-selector: "npm:^0.6.0" - prop-types: "npm:^15.8.1" - peerDependencies: - react: ">= 16.8 || 18.0.0" - checksum: 10/a8ff584a9dbf952dbd630f4ddf59b0b7a010eff49c3b97b363e30ab357f9cc7b8a0c7694069badeb4cf32361a00f3bbd1063964bd6438e9a68c6fe49ff879a38 - languageName: node - linkType: hard - "react-dropzone@npm:14.3.5, react-dropzone@npm:^14.2.3": version: 14.3.5 resolution: "react-dropzone@npm:14.3.5" @@ -26309,15 +25713,6 @@ __metadata: languageName: node linkType: hard -"react-from-dom@npm:^0.6.2": - version: 0.6.2 - resolution: "react-from-dom@npm:0.6.2" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/f3954737c2677e82f72ecedcdcf5f187d2a1b86a6c5b915f7300796ac153437581ee0111c9f524e7c18124d25d0d31391d54cdee318ea390722ca57e66813ed7 - languageName: node - linkType: hard - "react-from-dom@npm:^0.7.5": version: 0.7.5 resolution: "react-from-dom@npm:0.7.5" @@ -26361,19 +25756,6 @@ __metadata: languageName: node linkType: hard -"react-highlight-words@npm:0.20.0": - version: 0.20.0 - resolution: "react-highlight-words@npm:0.20.0" - dependencies: - highlight-words-core: "npm:^1.2.0" - memoize-one: "npm:^4.0.0" - prop-types: "npm:^15.5.8" - peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 10/5adf2cfb1f325ae51ea4dd2cb7522eb433b25534355868d1a3f4556b2b9f7a774c2a1aaa143abebb63a1b3a5590e70ba3d765942a47ff754a1a513cdc5b2f58b - languageName: node - linkType: hard - "react-highlight-words@npm:0.21.0": version: 0.21.0 resolution: "react-highlight-words@npm:0.21.0" @@ -26404,24 +25786,6 @@ __metadata: languageName: node linkType: hard -"react-i18next@npm:^14.0.0": - version: 14.1.3 - resolution: "react-i18next@npm:14.1.3" - dependencies: - "@babel/runtime": "npm:^7.23.9" - html-parse-stringify: "npm:^3.0.1" - peerDependencies: - i18next: ">= 23.2.3" - react: ">= 16.8.0" - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - checksum: 10/d0fa0f2717103c60758f9ddc1710e529f52e341465ca3f106ffa9168d88ad2db1bdbae58c77cca389933ae14bc39835abb37d1982049551ca15f6d310e2b3f57 - languageName: node - linkType: hard - "react-i18next@npm:^15.0.0": version: 15.4.0 resolution: "react-i18next@npm:15.4.0" @@ -26462,18 +25826,6 @@ __metadata: languageName: node linkType: hard -"react-inlinesvg@npm:3.0.2": - version: 3.0.2 - resolution: "react-inlinesvg@npm:3.0.2" - dependencies: - exenv: "npm:^1.2.2" - react-from-dom: "npm:^0.6.2" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/740fa33c7a09012bb96509f9003dc26e4e412eed2fc861ca40bfee9a3dddcf7c4d86fd20f824d4c017e44526aa9d747d6c9543ef3f2215bc0ace72754e025316 - languageName: node - linkType: hard - "react-inlinesvg@npm:4.2.0": version: 4.2.0 resolution: "react-inlinesvg@npm:4.2.0" @@ -26798,26 +26150,6 @@ __metadata: languageName: node linkType: hard -"react-select@npm:5.8.1": - version: 5.8.1 - resolution: "react-select@npm:5.8.1" - dependencies: - "@babel/runtime": "npm:^7.12.0" - "@emotion/cache": "npm:^11.4.0" - "@emotion/react": "npm:^11.8.1" - "@floating-ui/dom": "npm:^1.0.1" - "@types/react-transition-group": "npm:^4.4.0" - memoize-one: "npm:^6.0.0" - prop-types: "npm:^15.6.0" - react-transition-group: "npm:^4.3.0" - use-isomorphic-layout-effect: "npm:^1.1.2" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/53168b156435c5bef7c271ae7ebe67bff912e568dd1638f37859ea0d76cbd273d422714b6cb9669aa811d3fb44bda0f666b5e397a90a76ac2888a9b0ab47495a - languageName: node - linkType: hard - "react-selecto@npm:^1.25.0": version: 1.26.3 resolution: "react-selecto@npm:1.26.3" @@ -26976,32 +26308,7 @@ __metadata: languageName: node linkType: hard -"react-use@npm:17.5.1": - version: 17.5.1 - resolution: "react-use@npm:17.5.1" - dependencies: - "@types/js-cookie": "npm:^2.2.6" - "@xobotyi/scrollbar-width": "npm:^1.9.5" - copy-to-clipboard: "npm:^3.3.1" - fast-deep-equal: "npm:^3.1.3" - fast-shallow-equal: "npm:^1.0.0" - js-cookie: "npm:^2.2.1" - nano-css: "npm:^5.6.2" - react-universal-interface: "npm:^0.6.2" - resize-observer-polyfill: "npm:^1.5.1" - screenfull: "npm:^5.1.0" - set-harmonic-interval: "npm:^1.0.1" - throttle-debounce: "npm:^3.0.1" - ts-easing: "npm:^0.2.0" - tslib: "npm:^2.1.0" - peerDependencies: - react: "*" - react-dom: "*" - checksum: 10/2da403a9949dbd964b9b8e20dcd354db66b7f7d5ca1f42572fbcdb06bd49ee828c295be4912cb87abc163d1b54820bb8c5fa85314a16c4579d9e30bf9cbd5759 - languageName: node - linkType: hard - -"react-use@npm:17.6.0, react-use@npm:^17.3.1, react-use@npm:^17.4.0, react-use@npm:^17.5.0": +"react-use@npm:17.6.0, react-use@npm:^17.3.1, react-use@npm:^17.4.0, react-use@npm:^17.6.0": version: 17.6.0 resolution: "react-use@npm:17.6.0" dependencies: @@ -27067,19 +26374,6 @@ __metadata: languageName: node linkType: hard -"react-window@npm:1.8.10": - version: 1.8.10 - resolution: "react-window@npm:1.8.10" - dependencies: - "@babel/runtime": "npm:^7.0.0" - memoize-one: "npm:>=3.1.1 <6" - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: 10/6f4a713a2012d605370ef4c7026a45ddd6801e428faa4cad558b12b05ba54c00de72de9a360db109db9666f972a3d955b63af9e5a4cd5fbc52411a382273107b - languageName: node - linkType: hard - "react-window@npm:1.8.11": version: 1.8.11 resolution: "react-window@npm:1.8.11" @@ -27103,7 +26397,7 @@ __metadata: languageName: node linkType: hard -"react@npm:18.3.1, react@npm:^18": +"react@npm:18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -27384,13 +26678,6 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.11.0": - version: 0.11.1 - resolution: "regenerator-runtime@npm:0.11.1" - checksum: 10/64e62d78594c227e7d5269811bca9e4aa6451332adaae8c79a30cab0fa98733b1ad90bdb9d038095c340c6fad3b414a49a8d9e0b6b424ab7ff8f94f35704f8a2 - languageName: node - linkType: hard - "regenerator-runtime@npm:^0.13.4": version: 0.13.11 resolution: "regenerator-runtime@npm:0.13.11" @@ -28032,7 +27319,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:7.8.1, rxjs@npm:^7.5.1, rxjs@npm:^7.5.5, rxjs@npm:^7.8.1": +"rxjs@npm:7.8.1, rxjs@npm:^7.5.1, rxjs@npm:^7.5.5": version: 7.8.1 resolution: "rxjs@npm:7.8.1" dependencies: @@ -30632,13 +29919,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.7.0": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 10/9a5b47ddac65874fa011c20ff76db69f97cf90c78cff5934799ab8894a5342db2d17b4e7613a087046bc1d133d21547ddff87ac558abeec31ffa929c88b7fce6 - languageName: node - linkType: hard - "tslib@npm:^1.10.0, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -31180,7 +30460,7 @@ __metadata: languageName: node linkType: hard -"use-isomorphic-layout-effect@npm:^1.1.2, use-isomorphic-layout-effect@npm:^1.2.0": +"use-isomorphic-layout-effect@npm:^1.2.0": version: 1.2.0 resolution: "use-isomorphic-layout-effect@npm:1.2.0" peerDependencies: @@ -31262,15 +30542,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:9.0.1, uuid@npm:^9.0.0": - version: 9.0.1 - resolution: "uuid@npm:9.0.1" - bin: - uuid: dist/bin/uuid - checksum: 10/9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2 - languageName: node - linkType: hard - "uuid@npm:^10.0.0": version: 10.0.0 resolution: "uuid@npm:10.0.0" @@ -31280,6 +30551,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^11.0.5": + version: 11.1.0 + resolution: "uuid@npm:11.1.0" + bin: + uuid: dist/esm/bin/uuid + checksum: 10/d2da43b49b154d154574891ced66d0c83fc70caaad87e043400cf644423b067542d6f3eb641b7c819224a7cd3b4c2f21906acbedd6ec9c6a05887aa9115a9cf5 + languageName: node + linkType: hard + "uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -31289,6 +30569,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.0": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10/9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" From e128c3612776f012aa83d67962344fafd0c5039d Mon Sep 17 00:00:00 2001 From: Selene Date: Wed, 12 Mar 2025 10:12:56 +0100 Subject: [PATCH 218/312] Codegen: Cog and go fixes (#101408) * Update to latest cog version and update workspaces * Update generated go files * Try to avoid concurrency issues * Update workspaces * Try to remove the sync... * Remove grafana dependency from xorm go.mod file --- apps/alerting/notifications/go.sum | 8 +- apps/investigations/go.mod | 1 + apps/investigations/go.sum | 4 +- apps/playlist/go.mod | 1 + apps/playlist/go.sum | 4 +- go.mod | 4 +- go.sum | 8 +- go.work.sum | 2 + pkg/aggregator/go.mod | 4 +- pkg/aggregator/go.sum | 8 +- pkg/apiserver/go.mod | 2 +- pkg/apiserver/go.sum | 4 +- pkg/build/go.mod | 2 +- pkg/build/go.sum | 4 +- pkg/build/wire/go.mod | 4 +- pkg/build/wire/go.sum | 8 +- pkg/codegen/go.mod | 6 +- pkg/codegen/go.sum | 12 +- pkg/codegen/jenny_go_spec.go | 4 +- pkg/kinds/dashboard/dashboard_spec_gen.go | 1402 +++++++++-------- .../librarypanel/librarypanel_spec_gen.go | 58 +- pkg/kinds/preferences/preferences_spec_gen.go | 54 +- pkg/plugins/codegen/go.mod | 6 +- pkg/plugins/codegen/go.sum | 12 +- pkg/promlib/go.mod | 4 +- pkg/promlib/go.sum | 8 +- pkg/storage/unified/apistore/go.mod | 4 +- pkg/storage/unified/apistore/go.sum | 8 +- pkg/storage/unified/resource/go.mod | 4 +- pkg/storage/unified/resource/go.sum | 8 +- .../kinds/dataquery/types_dataquery_gen.go | 214 +-- .../kinds/dataquery/types_dataquery_gen.go | 22 +- .../kinds/dataquery/types_dataquery_gen.go | 256 +-- .../kinds/dataquery/types_dataquery_gen.go | 1187 +++++++------- .../kinds/dataquery/types_dataquery_gen.go | 102 +- 35 files changed, 1726 insertions(+), 1713 deletions(-) diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 1c891acf02e..c2e7b8b7d13 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -214,8 +214,8 @@ golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -252,8 +252,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index a8362a39961..aada9c2c3b4 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -72,6 +72,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.30.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index 46332e3fe34..129a8163693 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -181,8 +181,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index cb656a1b84a..b8162370ed4 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -73,6 +73,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.30.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 46332e3fe34..129a8163693 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -181,8 +181,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/go.mod b/go.mod index 09c1ee4237e..e1efdbd7ed0 100644 --- a/go.mod +++ b/go.mod @@ -171,13 +171,13 @@ require ( gocloud.dev v0.40.0 // @grafana/grafana-app-platform-squad golang.org/x/crypto v0.35.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // @grafana/alerting-backend - golang.org/x/mod v0.22.0 // indirect; @grafana/grafana-backend-group + golang.org/x/mod v0.23.0 // indirect; @grafana/grafana-backend-group golang.org/x/net v0.36.0 // @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.27.0 // @grafana/identity-access-team golang.org/x/sync v0.11.0 // @grafana/alerting-backend golang.org/x/text v0.22.0 // @grafana/grafana-backend-group golang.org/x/time v0.9.0 // @grafana/grafana-backend-group - golang.org/x/tools v0.29.0 // indirect; @grafana/grafana-as-code + golang.org/x/tools v0.30.0 // indirect; @grafana/grafana-as-code gonum.org/v1/gonum v0.15.1 // @grafana/oss-big-tent google.golang.org/api v0.220.0 // @grafana/grafana-backend-group google.golang.org/grpc v1.70.0 // @grafana/plugins-platform-backend diff --git a/go.sum b/go.sum index 1b581e0291f..743a5da82aa 100644 --- a/go.sum +++ b/go.sum @@ -2655,8 +2655,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3052,8 +3052,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/go.work.sum b/go.work.sum index f29575e7e8d..5a25a1d8f71 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1447,6 +1447,7 @@ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1459,6 +1460,7 @@ golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index b2a85fd8e50..27ed1f96d1b 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -136,7 +136,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.35.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sync v0.11.0 // indirect @@ -144,7 +144,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index c54a584b8b2..7e1a462ca5b 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -408,8 +408,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -467,8 +467,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index d2b5e90046b..39a9e18756d 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -87,7 +87,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 97748fca3de..1d54379d869 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -287,8 +287,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index 46478d1b62f..14352adba3e 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -27,7 +27,7 @@ require ( go.opentelemetry.io/otel/sdk v1.35.0 // indirect; @grafana/grafana-backend-group go.opentelemetry.io/otel/trace v1.35.0 // indirect; @grafana/grafana-backend-group golang.org/x/crypto v0.35.0 // indirect; @grafana/grafana-backend-group - golang.org/x/mod v0.22.0 // @grafana/grafana-backend-group + golang.org/x/mod v0.23.0 // @grafana/grafana-backend-group golang.org/x/net v0.36.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.27.0 // @grafana/identity-access-team golang.org/x/sync v0.11.0 // indirect; @grafana/alerting-backend diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 2dface03828..1d55ac2a88b 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -303,8 +303,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/build/wire/go.mod b/pkg/build/wire/go.mod index 32bfb68353e..4083a088d6c 100644 --- a/pkg/build/wire/go.mod +++ b/pkg/build/wire/go.mod @@ -6,10 +6,10 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/subcommands v1.2.0 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - golang.org/x/tools v0.29.0 + golang.org/x/tools v0.30.0 ) require ( - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/sync v0.11.0 // indirect ) diff --git a/pkg/build/wire/go.sum b/pkg/build/wire/go.sum index 07103d75876..8cc9fd24b82 100644 --- a/pkg/build/wire/go.sum +++ b/pkg/build/wire/go.sum @@ -4,9 +4,9 @@ github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 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= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= diff --git a/pkg/codegen/go.mod b/pkg/codegen/go.mod index 192cb41aa83..ed8c2bdc787 100644 --- a/pkg/codegen/go.mod +++ b/pkg/codegen/go.mod @@ -6,7 +6,7 @@ require ( cuelang.org/go v0.11.1 github.com/dave/dst v0.27.3 github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d - github.com/grafana/cog v0.0.18 + github.com/grafana/cog v0.0.27 github.com/grafana/cuetsy v0.1.11 github.com/matryer/is v1.4.1 ) @@ -43,11 +43,11 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/codegen/go.sum b/pkg/codegen/go.sum index 9ad69c311b0..684b40898c0 100644 --- a/pkg/codegen/go.sum +++ b/pkg/codegen/go.sum @@ -31,8 +31,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= -github.com/grafana/cog v0.0.18 h1:pEmzo/yhIFZMHM58ua0M9Eb5frJj6CgTrTTUVlY8e2o= -github.com/grafana/cog v0.0.18/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0= +github.com/grafana/cog v0.0.27 h1:ZKipAtp6KuB08R16nZbqEjnje3e2r1O1bzOp1CetDEo= +github.com/grafana/cog v0.0.27/go.mod h1:JB5lhdn4Hqc0ztYCaNOTKZXoojzJvydBxMkMCGWS6+Q= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= github.com/grafana/cuetsy v0.1.11 h1:I3IwBhF+UaQxRM79HnImtrAn8REGdb5M3+C4QrYHoWk= @@ -98,16 +98,16 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/codegen/jenny_go_spec.go b/pkg/codegen/jenny_go_spec.go index d753d9cae55..1226c584829 100644 --- a/pkg/codegen/jenny_go_spec.go +++ b/pkg/codegen/jenny_go_spec.go @@ -24,10 +24,10 @@ func (jenny *GoSpecJenny) Generate(sfg ...SchemaForGen) (codejen.Files, error) { for i, v := range sfg { packageName := strings.ToLower(v.Name) - cueValue := v.CueFile.LookupPath(cue.ParsePath("lineage.schemas[0].schema.spec")) + cueValue := v.CueFile.LookupPath(cue.ParsePath("lineage.schemas[0].schema")) b, err := cog.TypesFromSchema(). - CUEValue(packageName, cueValue, cog.ForceEnvelope("Spec")). + CUEValue(packageName, cueValue). Golang(cog.GoConfig{}). Run(context.Background()) if err != nil { diff --git a/pkg/kinds/dashboard/dashboard_spec_gen.go b/pkg/kinds/dashboard/dashboard_spec_gen.go index 526c5a66056..2fd8eb8b060 100644 --- a/pkg/kinds/dashboard/dashboard_spec_gen.go +++ b/pkg/kinds/dashboard/dashboard_spec_gen.go @@ -18,6 +18,79 @@ import ( time "time" ) +type Spec struct { + // Unique numeric identifier for the dashboard. + // `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances. + // TODO eliminate this null option + Id *int64 `json:"id,omitempty"` + // Unique dashboard identifier that can be generated by anyone. string (8-40) + Uid *string `json:"uid,omitempty"` + // Title of dashboard. + Title *string `json:"title,omitempty"` + // Description of dashboard. + Description *string `json:"description,omitempty"` + // This property should only be used in dashboards defined by plugins. It is a quick check + // to see if the version has changed since the last time. + Revision *int64 `json:"revision,omitempty"` + // ID of a dashboard imported from the https://grafana.com/grafana/dashboards/ portal + GnetId *string `json:"gnetId,omitempty"` + // Tags associated with dashboard. + Tags []string `json:"tags,omitempty"` + // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". + Timezone *string `json:"timezone,omitempty"` + // Whether a dashboard is editable or not. + Editable *bool `json:"editable,omitempty"` + // Configuration of dashboard cursor sync behavior. + // Accepted values are 0 (sync turned off), 1 (shared crosshair), 2 (shared crosshair and tooltip). + GraphTooltip *DashboardCursorSync `json:"graphTooltip,omitempty"` + // Time range for dashboard. + // Accepted values are relative time strings like {from: 'now-6h', to: 'now'} or absolute time strings like {from: '2020-07-10T08:00:00.000Z', to: '2020-07-10T14:00:00.000Z'}. + Time *DashboardSpecTime `json:"time,omitempty"` + // Configuration of the time picker shown at the top of a dashboard. + Timepicker *TimePickerConfig `json:"timepicker,omitempty"` + // The month that the fiscal year starts on. 0 = January, 11 = December + FiscalYearStartMonth *uint8 `json:"fiscalYearStartMonth,omitempty"` + // When set to true, the dashboard will redraw panels at an interval matching the pixel width. + // This will keep data "moving left" regardless of the query refresh rate. This setting helps + // avoid dashboards presenting stale live data + LiveNow *bool `json:"liveNow,omitempty"` + // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". + WeekStart *string `json:"weekStart,omitempty"` + // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". + Refresh *string `json:"refresh,omitempty"` + // Version of the JSON schema, incremented each time a Grafana update brings + // changes to said schema. + SchemaVersion uint16 `json:"schemaVersion"` + // Version of the dashboard, incremented each time the dashboard is updated. + Version *uint32 `json:"version,omitempty"` + // List of dashboard panels + Panels []any `json:"panels,omitempty"` + // Configured template variables + Templating *DashboardSpecTemplating `json:"templating,omitempty"` + // Contains the list of annotations that are associated with the dashboard. + // Annotations are used to overlay event markers and overlay event tags on graphs. + // Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. + // See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ + Annotations *AnnotationContainer `json:"annotations,omitempty"` + // Links with references to other dashboards or external websites. + Links []DashboardLink `json:"links,omitempty"` + // Snapshot options. They are present only if the dashboard is a snapshot. + Snapshot *Snapshot `json:"snapshot,omitempty"` + // When set to true, the dashboard will load all panels in the dashboard when it's loaded. + Preload *bool `json:"preload,omitempty"` +} + +// NewSpec creates a new Spec object. +func NewSpec() *Spec { + return &Spec{ + Timezone: (func(input string) *string { return &input })("browser"), + Editable: (func(input bool) *bool { return &input })(true), + GraphTooltip: (func(input DashboardCursorSync) *DashboardCursorSync { return &input })(DashboardCursorSyncOff), + FiscalYearStartMonth: (func(input uint8) *uint8 { return &input })(0), + SchemaVersion: 41, + } +} + // 0 for no shared crosshair or tooltip (default). // 1 for shared crosshair. // 2 for shared crosshair AND shared tooltip. @@ -29,18 +102,6 @@ const ( DashboardCursorSyncTooltip DashboardCursorSync = 2 ) -// Counterpart for TypeScript's TimeOption type. -type TimeOption struct { - Display string `json:"display"` - From string `json:"from"` - To string `json:"to"` -} - -// NewTimeOption creates a new TimeOption object. -func NewTimeOption() *TimeOption { - return &TimeOption{} -} - // Time picker configuration // It defines the default config for the time picker and the refresh picker for the specific dashboard. type TimePickerConfig struct { @@ -62,434 +123,16 @@ func NewTimePickerConfig() *TimePickerConfig { } } -// Schema for panel targets is specified by datasource -// plugins. We use a placeholder definition, which the Go -// schema loader either left open/as-is with the Base -// variant of the Dashboard and Panel families, or filled -// with types derived from plugins in the Instance variant. -// When working directly from CUE, importers can extend this -// type directly to achieve the same effect. -type Target map[string]any - -// Ref to a DataSource instance -type DataSourceRef struct { - // The plugin type-id - Type *string `json:"type,omitempty"` - // Specific datasource instance - Uid *string `json:"uid,omitempty"` +// Counterpart for TypeScript's TimeOption type. +type TimeOption struct { + Display string `json:"display"` + From string `json:"from"` + To string `json:"to"` } -// NewDataSourceRef creates a new DataSourceRef object. -func NewDataSourceRef() *DataSourceRef { - return &DataSourceRef{} -} - -// Position and dimensions of a panel in the grid -type GridPos struct { - // Panel height. The height is the number of rows from the top edge of the panel. - H uint32 `json:"h"` - // Panel width. The width is the number of columns from the left edge of the panel. - W uint32 `json:"w"` - // Panel x. The x coordinate is the number of columns from the left edge of the grid - X uint32 `json:"x"` - // Panel y. The y coordinate is the number of rows from the top edge of the grid - Y uint32 `json:"y"` - // Whether the panel is fixed within the grid. If true, the panel will not be affected by other panels' interactions - Static *bool `json:"static,omitempty"` -} - -// NewGridPos creates a new GridPos object. -func NewGridPos() *GridPos { - return &GridPos{ - H: 9, - W: 12, - X: 0, - Y: 0, - } -} - -// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) -type DashboardLinkType string - -const ( - DashboardLinkTypeLink DashboardLinkType = "link" - DashboardLinkTypeDashboards DashboardLinkType = "dashboards" -) - -// Links with references to other dashboards or external resources -type DashboardLink struct { - // Title to display with the link - Title string `json:"title"` - // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) - Type DashboardLinkType `json:"type"` - // Icon name to be displayed with the link - Icon string `json:"icon"` - // Tooltip to display when the user hovers their mouse over it - Tooltip string `json:"tooltip"` - // Link URL. Only required/valid if the type is link - Url *string `json:"url,omitempty"` - // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards - Tags []string `json:"tags"` - // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards - AsDropdown bool `json:"asDropdown"` - // If true, the link will be opened in a new tab - TargetBlank bool `json:"targetBlank"` - // If true, includes current template variables values in the link as query params - IncludeVars bool `json:"includeVars"` - // If true, includes current time range in the link as query params - KeepTime bool `json:"keepTime"` -} - -// NewDashboardLink creates a new DashboardLink object. -func NewDashboardLink() *DashboardLink { - return &DashboardLink{ - AsDropdown: false, - TargetBlank: false, - IncludeVars: false, - KeepTime: false, - } -} - -// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. -// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. -type MatcherConfig struct { - // The matcher id. This is used to find the matcher implementation from registry. - Id string `json:"id"` - // The matcher options. This is specific to the matcher implementation. - Options any `json:"options,omitempty"` -} - -// NewMatcherConfig creates a new MatcherConfig object. -func NewMatcherConfig() *MatcherConfig { - return &MatcherConfig{ - Id: "", - } -} - -// Transformations allow to manipulate data returned by a query before the system applies a visualization. -// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, -// use the output of one transformation as the input to another transformation, etc. -type DataTransformerConfig struct { - // Unique identifier of transformer - Id string `json:"id"` - // Disabled transformations are skipped - Disabled *bool `json:"disabled,omitempty"` - // Optional frame matcher. When missing it will be applied to all results - Filter *MatcherConfig `json:"filter,omitempty"` - // Where to pull DataFrames from as input to transformation - // replaced with common.DataTopic - Topic *DataTransformerConfigTopic `json:"topic,omitempty"` - // Options to be passed to the transformer - // Valid options depend on the transformer id - Options any `json:"options"` -} - -// NewDataTransformerConfig creates a new DataTransformerConfig object. -func NewDataTransformerConfig() *DataTransformerConfig { - return &DataTransformerConfig{} -} - -// A library panel is a reusable panel that you can use in any dashboard. -// When you make a change to a library panel, that change propagates to all instances of where the panel is used. -// Library panels streamline reuse of panels across multiple dashboards. -type LibraryPanelRef struct { - // Library panel name - Name string `json:"name"` - // Library panel uid - Uid string `json:"uid"` -} - -// NewLibraryPanelRef creates a new LibraryPanelRef object. -func NewLibraryPanelRef() *LibraryPanelRef { - return &LibraryPanelRef{} -} - -// Result used as replacement with text and color when the value matches -type ValueMappingResult struct { - // Text to display when the value matches - Text *string `json:"text,omitempty"` - // Text to use when the value matches - Color *string `json:"color,omitempty"` - // Icon to display when the value matches. Only specific visualizations. - Icon *string `json:"icon,omitempty"` - // Position in the mapping array. Only used internally. - Index *int32 `json:"index,omitempty"` -} - -// NewValueMappingResult creates a new ValueMappingResult object. -func NewValueMappingResult() *ValueMappingResult { - return &ValueMappingResult{} -} - -// Maps text values to a color or different display text and color. -// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. -type ValueMap struct { - Type string `json:"type"` - // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } - Options map[string]ValueMappingResult `json:"options"` -} - -// NewValueMap creates a new ValueMap object. -func NewValueMap() *ValueMap { - return &ValueMap{ - Type: "value", - } -} - -// Maps numerical ranges to a display text and color. -// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. -type RangeMap struct { - Type string `json:"type"` - // Range to match against and the result to apply when the value is within the range - Options DashboardRangeMapOptions `json:"options"` -} - -// NewRangeMap creates a new RangeMap object. -func NewRangeMap() *RangeMap { - return &RangeMap{ - Type: "range", - Options: *NewDashboardRangeMapOptions(), - } -} - -// Maps regular expressions to replacement text and a color. -// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. -type RegexMap struct { - Type string `json:"type"` - // Regular expression to match against and the result to apply when the value matches the regex - Options DashboardRegexMapOptions `json:"options"` -} - -// NewRegexMap creates a new RegexMap object. -func NewRegexMap() *RegexMap { - return &RegexMap{ - Type: "regex", - Options: *NewDashboardRegexMapOptions(), - } -} - -// Special value types supported by the `SpecialValueMap` -type SpecialValueMatch string - -const ( - SpecialValueMatchTrue SpecialValueMatch = "true" - SpecialValueMatchFalse SpecialValueMatch = "false" - SpecialValueMatchNull SpecialValueMatch = "null" - SpecialValueMatchNaN SpecialValueMatch = "nan" - SpecialValueMatchNullAndNan SpecialValueMatch = "null+nan" - SpecialValueMatchEmpty SpecialValueMatch = "empty" -) - -// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. -// See SpecialValueMatch to see the list of special values. -// For example, you can configure a special value mapping so that null values appear as N/A. -type SpecialValueMap struct { - Type string `json:"type"` - Options DashboardSpecialValueMapOptions `json:"options"` -} - -// NewSpecialValueMap creates a new SpecialValueMap object. -func NewSpecialValueMap() *SpecialValueMap { - return &SpecialValueMap{ - Type: "special", - Options: *NewDashboardSpecialValueMapOptions(), - } -} - -// Allow to transform the visual representation of specific data values in a visualization, irrespective of their original units -type ValueMapping = ValueMapOrRangeMapOrRegexMapOrSpecialValueMap - -// NewValueMapping creates a new ValueMapping object. -func NewValueMapping() *ValueMapping { - return NewValueMapOrRangeMapOrRegexMapOrSpecialValueMap() -} - -// Thresholds can either be `absolute` (specific number) or `percentage` (relative to min or max, it will be values between 0 and 1). -type ThresholdsMode string - -const ( - ThresholdsModeAbsolute ThresholdsMode = "absolute" - ThresholdsModePercentage ThresholdsMode = "percentage" -) - -// User-defined value for a metric that triggers visual changes in a panel when this value is met or exceeded -// They are used to conditionally style and color visualizations based on query results , and can be applied to most visualizations. -type Threshold struct { - // Value represents a specified metric for the threshold, which triggers a visual change in the dashboard when this value is met or exceeded. - // Nulls currently appear here when serializing -Infinity to JSON. - Value *float64 `json:"value"` - // Color represents the color of the visual change that will occur in the dashboard when the threshold value is met or exceeded. - Color string `json:"color"` -} - -// NewThreshold creates a new Threshold object. -func NewThreshold() *Threshold { - return &Threshold{} -} - -// Thresholds configuration for the panel -type ThresholdsConfig struct { - // Thresholds mode. - Mode ThresholdsMode `json:"mode"` - // Must be sorted by 'value', first value is always -Infinity - Steps []Threshold `json:"steps"` -} - -// NewThresholdsConfig creates a new ThresholdsConfig object. -func NewThresholdsConfig() *ThresholdsConfig { - return &ThresholdsConfig{} -} - -// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. -// Continuous color interpolates a color using the percentage of a value relative to min and max. -// Accepted values are: -// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold -// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations -// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations -// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode -// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode -// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode -// `continuous-YlRd`: Continuous Yellow-Red palette mode -// `continuous-BlPu`: Continuous Blue-Purple palette mode -// `continuous-YlBl`: Continuous Yellow-Blue palette mode -// `continuous-blues`: Continuous Blue palette mode -// `continuous-reds`: Continuous Red palette mode -// `continuous-greens`: Continuous Green palette mode -// `continuous-purples`: Continuous Purple palette mode -// `shades`: Shades of a single color. Specify a single color, useful in an override rule. -// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. -type FieldColorModeId string - -const ( - FieldColorModeIdThresholds FieldColorModeId = "thresholds" - FieldColorModeIdPaletteClassic FieldColorModeId = "palette-classic" - FieldColorModeIdPaletteClassicByName FieldColorModeId = "palette-classic-by-name" - FieldColorModeIdContinuousGrYlRd FieldColorModeId = "continuous-GrYlRd" - FieldColorModeIdContinuousRdYlGr FieldColorModeId = "continuous-RdYlGr" - FieldColorModeIdContinuousBlYlRd FieldColorModeId = "continuous-BlYlRd" - FieldColorModeIdContinuousYlRd FieldColorModeId = "continuous-YlRd" - FieldColorModeIdContinuousBlPu FieldColorModeId = "continuous-BlPu" - FieldColorModeIdContinuousYlBl FieldColorModeId = "continuous-YlBl" - FieldColorModeIdContinuousBlues FieldColorModeId = "continuous-blues" - FieldColorModeIdContinuousReds FieldColorModeId = "continuous-reds" - FieldColorModeIdContinuousGreens FieldColorModeId = "continuous-greens" - FieldColorModeIdContinuousPurples FieldColorModeId = "continuous-purples" - FieldColorModeIdFixed FieldColorModeId = "fixed" - FieldColorModeIdShades FieldColorModeId = "shades" -) - -// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. -type FieldColorSeriesByMode string - -const ( - FieldColorSeriesByModeMin FieldColorSeriesByMode = "min" - FieldColorSeriesByModeMax FieldColorSeriesByMode = "max" - FieldColorSeriesByModeLast FieldColorSeriesByMode = "last" -) - -// Map a field to a color. -type FieldColor struct { - // The main color scheme mode. - Mode FieldColorModeId `json:"mode"` - // The fixed color value for fixed or shades color modes. - FixedColor *string `json:"fixedColor,omitempty"` - // Some visualizations need to know how to assign a series color from by value color schemes. - SeriesBy *FieldColorSeriesByMode `json:"seriesBy,omitempty"` -} - -// NewFieldColor creates a new FieldColor object. -func NewFieldColor() *FieldColor { - return &FieldColor{} -} - -// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. -// Each column within this structure is called a field. A field can represent a single time series or table column. -// Field options allow you to change how the data is displayed in your visualizations. -type FieldConfig struct { - // The display value for this field. This supports template variables blank is auto - DisplayName *string `json:"displayName,omitempty"` - // This can be used by data sources that return and explicit naming structure for values and labels - // When this property is configured, this value is used rather than the default naming strategy. - DisplayNameFromDS *string `json:"displayNameFromDS,omitempty"` - // Human readable field metadata - Description *string `json:"description,omitempty"` - // An explicit path to the field in the datasource. When the frame meta includes a path, - // This will default to `${frame.meta.path}/${field.name} - // - // When defined, this value can be used as an identifier within the datasource scope, and - // may be used to update the results - Path *string `json:"path,omitempty"` - // True if data source can write a value to the path. Auth/authz are supported separately - Writeable *bool `json:"writeable,omitempty"` - // True if data source field supports ad-hoc filters - Filterable *bool `json:"filterable,omitempty"` - // Unit a field should use. The unit you select is applied to all fields except time. - // You can use the units ID availables in Grafana or a custom unit. - // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts - // As custom unit, you can use the following formats: - // `suffix:` for custom unit that should go after value. - // `prefix:` for custom unit that should go before value. - // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. - // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. - // `count:` for a custom count unit. - // `currency:` for custom a currency unit. - Unit *string `json:"unit,omitempty"` - // Specify the number of decimals Grafana includes in the rendered value. - // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. - // For example 1.1234 will display as 1.12 and 100.456 will display as 100. - // To display all decimals, set the unit to `String`. - Decimals *float64 `json:"decimals,omitempty"` - // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. - Min *float64 `json:"min,omitempty"` - // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. - Max *float64 `json:"max,omitempty"` - // Convert input values into a display string - Mappings []ValueMapping `json:"mappings,omitempty"` - // Map numeric values to states - Thresholds *ThresholdsConfig `json:"thresholds,omitempty"` - // Panel color configuration - Color *FieldColor `json:"color,omitempty"` - // The behavior when clicking on a result - Links []any `json:"links,omitempty"` - // Alternative to empty string - NoValue *string `json:"noValue,omitempty"` - // custom is specified by the FieldConfig field - // in panel plugin schemas. - Custom map[string]any `json:"custom,omitempty"` -} - -// NewFieldConfig creates a new FieldConfig object. -func NewFieldConfig() *FieldConfig { - return &FieldConfig{} -} - -type DynamicConfigValue struct { - Id string `json:"id"` - Value any `json:"value,omitempty"` -} - -// NewDynamicConfigValue creates a new DynamicConfigValue object. -func NewDynamicConfigValue() *DynamicConfigValue { - return &DynamicConfigValue{ - Id: "", - } -} - -// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. -// Each column within this structure is called a field. A field can represent a single time series or table column. -// Field options allow you to change how the data is displayed in your visualizations. -type FieldConfigSource struct { - // Defaults are the options applied to all fields. - Defaults FieldConfig `json:"defaults"` - // Overrides are the options applied to specific fields overriding the defaults. - Overrides []DashboardFieldConfigSourceOverrides `json:"overrides"` -} - -// NewFieldConfigSource creates a new FieldConfigSource object. -func NewFieldConfigSource() *FieldConfigSource { - return &FieldConfigSource{ - Defaults: *NewFieldConfig(), - } +// NewTimeOption creates a new TimeOption object. +func NewTimeOption() *TimeOption { + return &TimeOption{} } // Dashboard panels are the basic visualization building blocks. @@ -568,6 +211,436 @@ func NewPanel() *Panel { } } +// Schema for panel targets is specified by datasource +// plugins. We use a placeholder definition, which the Go +// schema loader either left open/as-is with the Base +// variant of the Dashboard and Panel families, or filled +// with types derived from plugins in the Instance variant. +// When working directly from CUE, importers can extend this +// type directly to achieve the same effect. +type Target map[string]any + +// Ref to a DataSource instance +type DataSourceRef struct { + // The plugin type-id + Type *string `json:"type,omitempty"` + // Specific datasource instance + Uid *string `json:"uid,omitempty"` +} + +// NewDataSourceRef creates a new DataSourceRef object. +func NewDataSourceRef() *DataSourceRef { + return &DataSourceRef{} +} + +// Position and dimensions of a panel in the grid +type GridPos struct { + // Panel height. The height is the number of rows from the top edge of the panel. + H uint32 `json:"h"` + // Panel width. The width is the number of columns from the left edge of the panel. + W uint32 `json:"w"` + // Panel x. The x coordinate is the number of columns from the left edge of the grid + X uint32 `json:"x"` + // Panel y. The y coordinate is the number of rows from the top edge of the grid + Y uint32 `json:"y"` + // Whether the panel is fixed within the grid. If true, the panel will not be affected by other panels' interactions + Static *bool `json:"static,omitempty"` +} + +// NewGridPos creates a new GridPos object. +func NewGridPos() *GridPos { + return &GridPos{ + H: 9, + W: 12, + X: 0, + Y: 0, + } +} + +// Links with references to other dashboards or external resources +type DashboardLink struct { + // Title to display with the link + Title string `json:"title"` + // Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) + Type DashboardLinkType `json:"type"` + // Icon name to be displayed with the link + Icon string `json:"icon"` + // Tooltip to display when the user hovers their mouse over it + Tooltip string `json:"tooltip"` + // Link URL. Only required/valid if the type is link + Url *string `json:"url,omitempty"` + // List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards + Tags []string `json:"tags"` + // If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards + AsDropdown bool `json:"asDropdown"` + // If true, the link will be opened in a new tab + TargetBlank bool `json:"targetBlank"` + // If true, includes current template variables values in the link as query params + IncludeVars bool `json:"includeVars"` + // If true, includes current time range in the link as query params + KeepTime bool `json:"keepTime"` +} + +// NewDashboardLink creates a new DashboardLink object. +func NewDashboardLink() *DashboardLink { + return &DashboardLink{ + AsDropdown: false, + TargetBlank: false, + IncludeVars: false, + KeepTime: false, + } +} + +// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) +type DashboardLinkType string + +const ( + DashboardLinkTypeLink DashboardLinkType = "link" + DashboardLinkTypeDashboards DashboardLinkType = "dashboards" +) + +// Transformations allow to manipulate data returned by a query before the system applies a visualization. +// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, +// use the output of one transformation as the input to another transformation, etc. +type DataTransformerConfig struct { + // Unique identifier of transformer + Id string `json:"id"` + // Disabled transformations are skipped + Disabled *bool `json:"disabled,omitempty"` + // Optional frame matcher. When missing it will be applied to all results + Filter *MatcherConfig `json:"filter,omitempty"` + // Where to pull DataFrames from as input to transformation + // replaced with common.DataTopic + Topic *DataTransformerConfigTopic `json:"topic,omitempty"` + // Options to be passed to the transformer + // Valid options depend on the transformer id + Options any `json:"options"` +} + +// NewDataTransformerConfig creates a new DataTransformerConfig object. +func NewDataTransformerConfig() *DataTransformerConfig { + return &DataTransformerConfig{} +} + +// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. +// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. +type MatcherConfig struct { + // The matcher id. This is used to find the matcher implementation from registry. + Id string `json:"id"` + // The matcher options. This is specific to the matcher implementation. + Options any `json:"options,omitempty"` +} + +// NewMatcherConfig creates a new MatcherConfig object. +func NewMatcherConfig() *MatcherConfig { + return &MatcherConfig{ + Id: "", + } +} + +// A library panel is a reusable panel that you can use in any dashboard. +// When you make a change to a library panel, that change propagates to all instances of where the panel is used. +// Library panels streamline reuse of panels across multiple dashboards. +type LibraryPanelRef struct { + // Library panel name + Name string `json:"name"` + // Library panel uid + Uid string `json:"uid"` +} + +// NewLibraryPanelRef creates a new LibraryPanelRef object. +func NewLibraryPanelRef() *LibraryPanelRef { + return &LibraryPanelRef{} +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +type FieldConfigSource struct { + // Defaults are the options applied to all fields. + Defaults FieldConfig `json:"defaults"` + // Overrides are the options applied to specific fields overriding the defaults. + Overrides []DashboardFieldConfigSourceOverrides `json:"overrides"` +} + +// NewFieldConfigSource creates a new FieldConfigSource object. +func NewFieldConfigSource() *FieldConfigSource { + return &FieldConfigSource{ + Defaults: *NewFieldConfig(), + } +} + +// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results. +// Each column within this structure is called a field. A field can represent a single time series or table column. +// Field options allow you to change how the data is displayed in your visualizations. +type FieldConfig struct { + // The display value for this field. This supports template variables blank is auto + DisplayName *string `json:"displayName,omitempty"` + // This can be used by data sources that return and explicit naming structure for values and labels + // When this property is configured, this value is used rather than the default naming strategy. + DisplayNameFromDS *string `json:"displayNameFromDS,omitempty"` + // Human readable field metadata + Description *string `json:"description,omitempty"` + // An explicit path to the field in the datasource. When the frame meta includes a path, + // This will default to `${frame.meta.path}/${field.name} + // + // When defined, this value can be used as an identifier within the datasource scope, and + // may be used to update the results + Path *string `json:"path,omitempty"` + // True if data source can write a value to the path. Auth/authz are supported separately + Writeable *bool `json:"writeable,omitempty"` + // True if data source field supports ad-hoc filters + Filterable *bool `json:"filterable,omitempty"` + // Unit a field should use. The unit you select is applied to all fields except time. + // You can use the units ID availables in Grafana or a custom unit. + // Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts + // As custom unit, you can use the following formats: + // `suffix:` for custom unit that should go after value. + // `prefix:` for custom unit that should go before value. + // `time:` For custom date time formats type for example `time:YYYY-MM-DD`. + // `si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character. + // `count:` for a custom count unit. + // `currency:` for custom a currency unit. + Unit *string `json:"unit,omitempty"` + // Specify the number of decimals Grafana includes in the rendered value. + // If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. + // For example 1.1234 will display as 1.12 and 100.456 will display as 100. + // To display all decimals, set the unit to `String`. + Decimals *float64 `json:"decimals,omitempty"` + // The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + Min *float64 `json:"min,omitempty"` + // The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields. + Max *float64 `json:"max,omitempty"` + // Convert input values into a display string + Mappings []ValueMapping `json:"mappings,omitempty"` + // Map numeric values to states + Thresholds *ThresholdsConfig `json:"thresholds,omitempty"` + // Panel color configuration + Color *FieldColor `json:"color,omitempty"` + // The behavior when clicking on a result + Links []any `json:"links,omitempty"` + // Alternative to empty string + NoValue *string `json:"noValue,omitempty"` + // custom is specified by the FieldConfig field + // in panel plugin schemas. + Custom map[string]any `json:"custom,omitempty"` +} + +// NewFieldConfig creates a new FieldConfig object. +func NewFieldConfig() *FieldConfig { + return &FieldConfig{} +} + +// Allow to transform the visual representation of specific data values in a visualization, irrespective of their original units +type ValueMapping = ValueMapOrRangeMapOrRegexMapOrSpecialValueMap + +// NewValueMapping creates a new ValueMapping object. +func NewValueMapping() *ValueMapping { + return NewValueMapOrRangeMapOrRegexMapOrSpecialValueMap() +} + +// Maps text values to a color or different display text and color. +// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +type ValueMap struct { + Type MappingType `json:"type"` + // Map with : ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } } + Options map[string]ValueMappingResult `json:"options"` +} + +// NewValueMap creates a new ValueMap object. +func NewValueMap() *ValueMap { + return &ValueMap{ + Type: MappingTypeValueToText, + } +} + +// Result used as replacement with text and color when the value matches +type ValueMappingResult struct { + // Text to display when the value matches + Text *string `json:"text,omitempty"` + // Text to use when the value matches + Color *string `json:"color,omitempty"` + // Icon to display when the value matches. Only specific visualizations. + Icon *string `json:"icon,omitempty"` + // Position in the mapping array. Only used internally. + Index *int32 `json:"index,omitempty"` +} + +// NewValueMappingResult creates a new ValueMappingResult object. +func NewValueMappingResult() *ValueMappingResult { + return &ValueMappingResult{} +} + +// Maps numerical ranges to a display text and color. +// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +type RangeMap struct { + Type MappingType `json:"type"` + // Range to match against and the result to apply when the value is within the range + Options DashboardRangeMapOptions `json:"options"` +} + +// NewRangeMap creates a new RangeMap object. +func NewRangeMap() *RangeMap { + return &RangeMap{ + Type: MappingTypeRangeToText, + Options: *NewDashboardRangeMapOptions(), + } +} + +// Maps regular expressions to replacement text and a color. +// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +type RegexMap struct { + Type MappingType `json:"type"` + // Regular expression to match against and the result to apply when the value matches the regex + Options DashboardRegexMapOptions `json:"options"` +} + +// NewRegexMap creates a new RegexMap object. +func NewRegexMap() *RegexMap { + return &RegexMap{ + Type: MappingTypeRegexToText, + Options: *NewDashboardRegexMapOptions(), + } +} + +// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. +// See SpecialValueMatch to see the list of special values. +// For example, you can configure a special value mapping so that null values appear as N/A. +type SpecialValueMap struct { + Type MappingType `json:"type"` + Options DashboardSpecialValueMapOptions `json:"options"` +} + +// NewSpecialValueMap creates a new SpecialValueMap object. +func NewSpecialValueMap() *SpecialValueMap { + return &SpecialValueMap{ + Type: MappingTypeSpecialValue, + Options: *NewDashboardSpecialValueMapOptions(), + } +} + +// Special value types supported by the `SpecialValueMap` +type SpecialValueMatch string + +const ( + SpecialValueMatchTrue SpecialValueMatch = "true" + SpecialValueMatchFalse SpecialValueMatch = "false" + SpecialValueMatchNull SpecialValueMatch = "null" + SpecialValueMatchNaN SpecialValueMatch = "nan" + SpecialValueMatchNullAndNan SpecialValueMatch = "null+nan" + SpecialValueMatchEmpty SpecialValueMatch = "empty" +) + +// Thresholds configuration for the panel +type ThresholdsConfig struct { + // Thresholds mode. + Mode ThresholdsMode `json:"mode"` + // Must be sorted by 'value', first value is always -Infinity + Steps []Threshold `json:"steps"` +} + +// NewThresholdsConfig creates a new ThresholdsConfig object. +func NewThresholdsConfig() *ThresholdsConfig { + return &ThresholdsConfig{} +} + +// Thresholds can either be `absolute` (specific number) or `percentage` (relative to min or max, it will be values between 0 and 1). +type ThresholdsMode string + +const ( + ThresholdsModeAbsolute ThresholdsMode = "absolute" + ThresholdsModePercentage ThresholdsMode = "percentage" +) + +// User-defined value for a metric that triggers visual changes in a panel when this value is met or exceeded +// They are used to conditionally style and color visualizations based on query results , and can be applied to most visualizations. +type Threshold struct { + // Value represents a specified metric for the threshold, which triggers a visual change in the dashboard when this value is met or exceeded. + // Nulls currently appear here when serializing -Infinity to JSON. + Value *float64 `json:"value"` + // Color represents the color of the visual change that will occur in the dashboard when the threshold value is met or exceeded. + Color string `json:"color"` +} + +// NewThreshold creates a new Threshold object. +func NewThreshold() *Threshold { + return &Threshold{} +} + +// Map a field to a color. +type FieldColor struct { + // The main color scheme mode. + Mode FieldColorModeId `json:"mode"` + // The fixed color value for fixed or shades color modes. + FixedColor *string `json:"fixedColor,omitempty"` + // Some visualizations need to know how to assign a series color from by value color schemes. + SeriesBy *FieldColorSeriesByMode `json:"seriesBy,omitempty"` +} + +// NewFieldColor creates a new FieldColor object. +func NewFieldColor() *FieldColor { + return &FieldColor{} +} + +// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. +// Continuous color interpolates a color using the percentage of a value relative to min and max. +// Accepted values are: +// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold +// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations +// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations +// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode +// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode +// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode +// `continuous-YlRd`: Continuous Yellow-Red palette mode +// `continuous-BlPu`: Continuous Blue-Purple palette mode +// `continuous-YlBl`: Continuous Yellow-Blue palette mode +// `continuous-blues`: Continuous Blue palette mode +// `continuous-reds`: Continuous Red palette mode +// `continuous-greens`: Continuous Green palette mode +// `continuous-purples`: Continuous Purple palette mode +// `shades`: Shades of a single color. Specify a single color, useful in an override rule. +// `fixed`: Fixed color mode. Specify a single color, useful in an override rule. +type FieldColorModeId string + +const ( + FieldColorModeIdThresholds FieldColorModeId = "thresholds" + FieldColorModeIdPaletteClassic FieldColorModeId = "palette-classic" + FieldColorModeIdPaletteClassicByName FieldColorModeId = "palette-classic-by-name" + FieldColorModeIdContinuousGrYlRd FieldColorModeId = "continuous-GrYlRd" + FieldColorModeIdContinuousRdYlGr FieldColorModeId = "continuous-RdYlGr" + FieldColorModeIdContinuousBlYlRd FieldColorModeId = "continuous-BlYlRd" + FieldColorModeIdContinuousYlRd FieldColorModeId = "continuous-YlRd" + FieldColorModeIdContinuousBlPu FieldColorModeId = "continuous-BlPu" + FieldColorModeIdContinuousYlBl FieldColorModeId = "continuous-YlBl" + FieldColorModeIdContinuousBlues FieldColorModeId = "continuous-blues" + FieldColorModeIdContinuousReds FieldColorModeId = "continuous-reds" + FieldColorModeIdContinuousGreens FieldColorModeId = "continuous-greens" + FieldColorModeIdContinuousPurples FieldColorModeId = "continuous-purples" + FieldColorModeIdFixed FieldColorModeId = "fixed" + FieldColorModeIdShades FieldColorModeId = "shades" +) + +// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. +type FieldColorSeriesByMode string + +const ( + FieldColorSeriesByModeMin FieldColorSeriesByMode = "min" + FieldColorSeriesByModeMax FieldColorSeriesByMode = "max" + FieldColorSeriesByModeLast FieldColorSeriesByMode = "last" +) + +type DynamicConfigValue struct { + Id string `json:"id"` + Value any `json:"value,omitempty"` +} + +// NewDynamicConfigValue creates a new DynamicConfigValue object. +func NewDynamicConfigValue() *DynamicConfigValue { + return &DynamicConfigValue{ + Id: "", + } +} + // Row panel type RowPanel struct { // The panel type @@ -596,6 +669,55 @@ func NewRowPanel() *RowPanel { } } +// A variable is a placeholder for a value. You can use variables in metric queries and in panel titles. +type VariableModel struct { + // Type of variable + Type VariableType `json:"type"` + // Name of variable + Name string `json:"name"` + // Optional display name + Label *string `json:"label,omitempty"` + // Visibility configuration for the variable + Hide *VariableHide `json:"hide,omitempty"` + // Whether the variable value should be managed by URL query params or not + SkipUrlSync *bool `json:"skipUrlSync,omitempty"` + // Description of variable. It can be defined but `null`. + Description *string `json:"description,omitempty"` + // Query used to fetch values for a variable + Query *StringOrMap `json:"query,omitempty"` + // Data source used to fetch values for a variable. It can be defined but `null`. + Datasource *DataSourceRef `json:"datasource,omitempty"` + // Shows current selected variable text/value on the dashboard + Current *VariableOption `json:"current,omitempty"` + // Whether multiple values can be selected or not from variable value list + Multi *bool `json:"multi,omitempty"` + // Allow custom values to be entered in the variable + AllowCustomValue *bool `json:"allowCustomValue,omitempty"` + // Options that can be selected for a variable. + Options []VariableOption `json:"options,omitempty"` + // Options to config when to refresh a variable + Refresh *VariableRefresh `json:"refresh,omitempty"` + // Options sort order + Sort *VariableSort `json:"sort,omitempty"` + // Whether all value option is available or not + IncludeAll *bool `json:"includeAll,omitempty"` + // Custom all value + AllValue *string `json:"allValue,omitempty"` + // Optional field, if you want to extract part of a series name or metric node segment. + // Named capture groups can be used to separate the display text and value. + Regex *string `json:"regex,omitempty"` +} + +// NewVariableModel creates a new VariableModel object. +func NewVariableModel() *VariableModel { + return &VariableModel{ + SkipUrlSync: (func(input bool) *bool { return &input })(false), + Multi: (func(input bool) *bool { return &input })(false), + AllowCustomValue: (func(input bool) *bool { return &input })(true), + IncludeAll: (func(input bool) *bool { return &input })(false), + } +} + // Dashboard variable type // `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on. // `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only). @@ -685,52 +807,51 @@ const ( VariableSortNaturalDesc VariableSort = 8 ) -// A variable is a placeholder for a value. You can use variables in metric queries and in panel titles. -type VariableModel struct { - // Type of variable - Type VariableType `json:"type"` - // Name of variable - Name string `json:"name"` - // Optional display name - Label *string `json:"label,omitempty"` - // Visibility configuration for the variable - Hide *VariableHide `json:"hide,omitempty"` - // Whether the variable value should be managed by URL query params or not - SkipUrlSync *bool `json:"skipUrlSync,omitempty"` - // Description of variable. It can be defined but `null`. - Description *string `json:"description,omitempty"` - // Query used to fetch values for a variable - Query *StringOrMap `json:"query,omitempty"` - // Data source used to fetch values for a variable. It can be defined but `null`. - Datasource *DataSourceRef `json:"datasource,omitempty"` - // Shows current selected variable text/value on the dashboard - Current *VariableOption `json:"current,omitempty"` - // Whether multiple values can be selected or not from variable value list - Multi *bool `json:"multi,omitempty"` - // Allow custom values to be entered in the variable - AllowCustomValue *bool `json:"allowCustomValue,omitempty"` - // Options that can be selected for a variable. - Options []VariableOption `json:"options,omitempty"` - // Options to config when to refresh a variable - Refresh *VariableRefresh `json:"refresh,omitempty"` - // Options sort order - Sort *VariableSort `json:"sort,omitempty"` - // Whether all value option is available or not - IncludeAll *bool `json:"includeAll,omitempty"` - // Custom all value - AllValue *string `json:"allValue,omitempty"` - // Optional field, if you want to extract part of a series name or metric node segment. - // Named capture groups can be used to separate the display text and value. - Regex *string `json:"regex,omitempty"` +// Contains the list of annotations that are associated with the dashboard. +// Annotations are used to overlay event markers and overlay event tags on graphs. +// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. +// See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ +type AnnotationContainer struct { + // List of annotations + List []AnnotationQuery `json:"list,omitempty"` } -// NewVariableModel creates a new VariableModel object. -func NewVariableModel() *VariableModel { - return &VariableModel{ - SkipUrlSync: (func(input bool) *bool { return &input })(false), - Multi: (func(input bool) *bool { return &input })(false), - AllowCustomValue: (func(input bool) *bool { return &input })(true), - IncludeAll: (func(input bool) *bool { return &input })(false), +// NewAnnotationContainer creates a new AnnotationContainer object. +func NewAnnotationContainer() *AnnotationContainer { + return &AnnotationContainer{} +} + +// TODO docs +// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts +type AnnotationQuery struct { + // Name of annotation. + Name string `json:"name"` + // Datasource where the annotations data is + Datasource DataSourceRef `json:"datasource"` + // When enabled the annotation query is issued with every dashboard refresh + Enable bool `json:"enable"` + // Annotation queries can be toggled on or off at the top of the dashboard. + // When hide is true, the toggle is not shown in the dashboard. + Hide *bool `json:"hide,omitempty"` + // Color to use for the annotation event markers + IconColor string `json:"iconColor"` + // Filters to apply when fetching annotations + Filter *AnnotationPanelFilter `json:"filter,omitempty"` + // TODO.. this should just be a normal query target + Target *AnnotationTarget `json:"target,omitempty"` + // TODO -- this should not exist here, it is based on the --grafana-- datasource + Type *string `json:"type,omitempty"` + // Set to 1 for the standard annotation query all dashboards have by default. + BuiltIn *float64 `json:"builtIn,omitempty"` +} + +// NewAnnotationQuery creates a new AnnotationQuery object. +func NewAnnotationQuery() *AnnotationQuery { + return &AnnotationQuery{ + Datasource: *NewDataSourceRef(), + Enable: true, + Hide: (func(input bool) *bool { return &input })(false), + BuiltIn: (func(input float64) *float64 { return &input })(0), } } @@ -770,54 +891,6 @@ func NewAnnotationTarget() *AnnotationTarget { return &AnnotationTarget{} } -// TODO docs -// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts -type AnnotationQuery struct { - // Name of annotation. - Name string `json:"name"` - // Datasource where the annotations data is - Datasource DataSourceRef `json:"datasource"` - // When enabled the annotation query is issued with every dashboard refresh - Enable bool `json:"enable"` - // Annotation queries can be toggled on or off at the top of the dashboard. - // When hide is true, the toggle is not shown in the dashboard. - Hide *bool `json:"hide,omitempty"` - // Color to use for the annotation event markers - IconColor string `json:"iconColor"` - // Filters to apply when fetching annotations - Filter *AnnotationPanelFilter `json:"filter,omitempty"` - // TODO.. this should just be a normal query target - Target *AnnotationTarget `json:"target,omitempty"` - // TODO -- this should not exist here, it is based on the --grafana-- datasource - Type *string `json:"type,omitempty"` - // Set to 1 for the standard annotation query all dashboards have by default. - BuiltIn *float64 `json:"builtIn,omitempty"` -} - -// NewAnnotationQuery creates a new AnnotationQuery object. -func NewAnnotationQuery() *AnnotationQuery { - return &AnnotationQuery{ - Datasource: *NewDataSourceRef(), - Enable: true, - Hide: (func(input bool) *bool { return &input })(false), - BuiltIn: (func(input float64) *float64 { return &input })(0), - } -} - -// Contains the list of annotations that are associated with the dashboard. -// Annotations are used to overlay event markers and overlay event tags on graphs. -// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. -// See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ -type AnnotationContainer struct { - // List of annotations - List []AnnotationQuery `json:"list,omitempty"` -} - -// NewAnnotationContainer creates a new AnnotationContainer object. -func NewAnnotationContainer() *AnnotationContainer { - return &AnnotationContainer{} -} - // A dashboard snapshot shares an interactive dashboard publicly. // It is a read-only version of a dashboard, and is not editable. // It is possible to create a snapshot of a snapshot. @@ -855,93 +928,54 @@ func NewSnapshot() *Snapshot { return &Snapshot{} } -type Spec struct { - // Unique numeric identifier for the dashboard. - // `id` is internal to a specific Grafana instance. `uid` should be used to identify a dashboard across Grafana instances. - // TODO eliminate this null option - Id *int64 `json:"id,omitempty"` - // Unique dashboard identifier that can be generated by anyone. string (8-40) - Uid *string `json:"uid,omitempty"` - // Title of dashboard. - Title *string `json:"title,omitempty"` - // Description of dashboard. - Description *string `json:"description,omitempty"` - // This property should only be used in dashboards defined by plugins. It is a quick check - // to see if the version has changed since the last time. - Revision *int64 `json:"revision,omitempty"` - // ID of a dashboard imported from the https://grafana.com/grafana/dashboards/ portal - GnetId *string `json:"gnetId,omitempty"` - // Tags associated with dashboard. - Tags []string `json:"tags,omitempty"` - // Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc". - Timezone *string `json:"timezone,omitempty"` - // Whether a dashboard is editable or not. - Editable *bool `json:"editable,omitempty"` - // Configuration of dashboard cursor sync behavior. - // Accepted values are 0 (sync turned off), 1 (shared crosshair), 2 (shared crosshair and tooltip). - GraphTooltip *DashboardCursorSync `json:"graphTooltip,omitempty"` - // Time range for dashboard. - // Accepted values are relative time strings like {from: 'now-6h', to: 'now'} or absolute time strings like {from: '2020-07-10T08:00:00.000Z', to: '2020-07-10T14:00:00.000Z'}. - Time *DashboardSpecTime `json:"time,omitempty"` - // Configuration of the time picker shown at the top of a dashboard. - Timepicker *TimePickerConfig `json:"timepicker,omitempty"` - // The month that the fiscal year starts on. 0 = January, 11 = December - FiscalYearStartMonth *uint8 `json:"fiscalYearStartMonth,omitempty"` - // When set to true, the dashboard will redraw panels at an interval matching the pixel width. - // This will keep data "moving left" regardless of the query refresh rate. This setting helps - // avoid dashboards presenting stale live data - LiveNow *bool `json:"liveNow,omitempty"` - // Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday". - WeekStart *string `json:"weekStart,omitempty"` - // Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". - Refresh *string `json:"refresh,omitempty"` - // Version of the JSON schema, incremented each time a Grafana update brings - // changes to said schema. - SchemaVersion uint16 `json:"schemaVersion"` - // Version of the dashboard, incremented each time the dashboard is updated. - Version *uint32 `json:"version,omitempty"` - // List of dashboard panels - Panels []any `json:"panels,omitempty"` - // Configured template variables - Templating *DashboardSpecTemplating `json:"templating,omitempty"` - // Contains the list of annotations that are associated with the dashboard. - // Annotations are used to overlay event markers and overlay event tags on graphs. - // Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API. - // See https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/annotate-visualizations/ - Annotations *AnnotationContainer `json:"annotations,omitempty"` - // Links with references to other dashboards or external websites. - Links []DashboardLink `json:"links,omitempty"` - // Snapshot options. They are present only if the dashboard is a snapshot. - Snapshot *Snapshot `json:"snapshot,omitempty"` - // When set to true, the dashboard will load all panels in the dashboard when it's loaded. - Preload *bool `json:"preload,omitempty"` +// Supported value mapping types +// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. +// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. +// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. +// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. +type MappingType string + +const ( + MappingTypeValueToText MappingType = "value" + MappingTypeRangeToText MappingType = "range" + MappingTypeRegexToText MappingType = "regex" + MappingTypeSpecialValue MappingType = "special" +) + +type DashboardSpecTime struct { + From string `json:"from"` + To string `json:"to"` } -// NewSpec creates a new Spec object. -func NewSpec() *Spec { - return &Spec{ - Timezone: (func(input string) *string { return &input })("browser"), - Editable: (func(input bool) *bool { return &input })(true), - GraphTooltip: (func(input DashboardCursorSync) *DashboardCursorSync { return &input })(DashboardCursorSyncOff), - FiscalYearStartMonth: (func(input uint8) *uint8 { return &input })(0), - SchemaVersion: 41, +// NewDashboardSpecTime creates a new DashboardSpecTime object. +func NewDashboardSpecTime() *DashboardSpecTime { + return &DashboardSpecTime{ + From: "now-6h", + To: "now", } } -type DataTransformerConfigTopic string +type DashboardSpecTemplating struct { + // List of configured template variables with their saved values along with some other metadata + List []VariableModel `json:"list,omitempty"` +} -const ( - DataTransformerConfigTopicSeries DataTransformerConfigTopic = "series" - DataTransformerConfigTopicAnnotations DataTransformerConfigTopic = "annotations" - DataTransformerConfigTopicAlertStates DataTransformerConfigTopic = "alertStates" -) +// NewDashboardSpecTemplating creates a new DashboardSpecTemplating object. +func NewDashboardSpecTemplating() *DashboardSpecTemplating { + return &DashboardSpecTemplating{} +} -type PanelRepeatDirection string +type DashboardFieldConfigSourceOverrides struct { + Matcher MatcherConfig `json:"matcher"` + Properties []DynamicConfigValue `json:"properties"` +} -const ( - PanelRepeatDirectionH PanelRepeatDirection = "h" - PanelRepeatDirectionV PanelRepeatDirection = "v" -) +// NewDashboardFieldConfigSourceOverrides creates a new DashboardFieldConfigSourceOverrides object. +func NewDashboardFieldConfigSourceOverrides() *DashboardFieldConfigSourceOverrides { + return &DashboardFieldConfigSourceOverrides{ + Matcher: *NewMatcherConfig(), + } +} type DashboardRangeMapOptions struct { // Min value of the range. It can be null which means -Infinity @@ -987,40 +1021,20 @@ func NewDashboardSpecialValueMapOptions() *DashboardSpecialValueMapOptions { } } -type DashboardFieldConfigSourceOverrides struct { - Matcher MatcherConfig `json:"matcher"` - Properties []DynamicConfigValue `json:"properties"` -} +type PanelRepeatDirection string -// NewDashboardFieldConfigSourceOverrides creates a new DashboardFieldConfigSourceOverrides object. -func NewDashboardFieldConfigSourceOverrides() *DashboardFieldConfigSourceOverrides { - return &DashboardFieldConfigSourceOverrides{ - Matcher: *NewMatcherConfig(), - } -} +const ( + PanelRepeatDirectionH PanelRepeatDirection = "h" + PanelRepeatDirectionV PanelRepeatDirection = "v" +) -type DashboardSpecTime struct { - From string `json:"from"` - To string `json:"to"` -} +type DataTransformerConfigTopic string -// NewDashboardSpecTime creates a new DashboardSpecTime object. -func NewDashboardSpecTime() *DashboardSpecTime { - return &DashboardSpecTime{ - From: "now-6h", - To: "now", - } -} - -type DashboardSpecTemplating struct { - // List of configured template variables with their saved values along with some other metadata - List []VariableModel `json:"list,omitempty"` -} - -// NewDashboardSpecTemplating creates a new DashboardSpecTemplating object. -func NewDashboardSpecTemplating() *DashboardSpecTemplating { - return &DashboardSpecTemplating{} -} +const ( + DataTransformerConfigTopicSeries DataTransformerConfigTopic = "series" + DataTransformerConfigTopicAnnotations DataTransformerConfigTopic = "annotations" + DataTransformerConfigTopicAlertStates DataTransformerConfigTopic = "alertStates" +) type ValueMapOrRangeMapOrRegexMapOrSpecialValueMap struct { ValueMap *ValueMap `json:"ValueMap,omitempty"` @@ -1107,60 +1121,6 @@ func (resource *ValueMapOrRangeMapOrRegexMapOrSpecialValueMap) UnmarshalJSON(raw return fmt.Errorf("could not unmarshal resource with `type = %v`", discriminator) } -type StringOrArrayOfString struct { - String *string `json:"String,omitempty"` - ArrayOfString []string `json:"ArrayOfString,omitempty"` -} - -// NewStringOrArrayOfString creates a new StringOrArrayOfString object. -func NewStringOrArrayOfString() *StringOrArrayOfString { - return &StringOrArrayOfString{} -} - -// MarshalJSON implements a custom JSON marshalling logic to encode `StringOrArrayOfString` as JSON. -func (resource StringOrArrayOfString) MarshalJSON() ([]byte, error) { - if resource.String != nil { - return json.Marshal(resource.String) - } - - if resource.ArrayOfString != nil { - return json.Marshal(resource.ArrayOfString) - } - - return nil, fmt.Errorf("no value for disjunction of scalars") -} - -// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `StringOrArrayOfString` from JSON. -func (resource *StringOrArrayOfString) UnmarshalJSON(raw []byte) error { - if raw == nil { - return nil - } - - var errList []error - - // String - var String string - if err := json.Unmarshal(raw, &String); err != nil { - errList = append(errList, err) - resource.String = nil - } else { - resource.String = &String - return nil - } - - // ArrayOfString - var ArrayOfString []string - if err := json.Unmarshal(raw, &ArrayOfString); err != nil { - errList = append(errList, err) - resource.ArrayOfString = nil - } else { - resource.ArrayOfString = ArrayOfString - return nil - } - - return errors.Join(errList...) -} - type StringOrMap struct { String *string `json:"String,omitempty"` Map map[string]any `json:"Map,omitempty"` @@ -1214,3 +1174,57 @@ func (resource *StringOrMap) UnmarshalJSON(raw []byte) error { return errors.Join(errList...) } + +type StringOrArrayOfString struct { + String *string `json:"String,omitempty"` + ArrayOfString []string `json:"ArrayOfString,omitempty"` +} + +// NewStringOrArrayOfString creates a new StringOrArrayOfString object. +func NewStringOrArrayOfString() *StringOrArrayOfString { + return &StringOrArrayOfString{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `StringOrArrayOfString` as JSON. +func (resource StringOrArrayOfString) MarshalJSON() ([]byte, error) { + if resource.String != nil { + return json.Marshal(resource.String) + } + + if resource.ArrayOfString != nil { + return json.Marshal(resource.ArrayOfString) + } + + return nil, fmt.Errorf("no value for disjunction of scalars") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `StringOrArrayOfString` from JSON. +func (resource *StringOrArrayOfString) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + var errList []error + + // String + var String string + if err := json.Unmarshal(raw, &String); err != nil { + errList = append(errList, err) + resource.String = nil + } else { + resource.String = &String + return nil + } + + // ArrayOfString + var ArrayOfString []string + if err := json.Unmarshal(raw, &ArrayOfString); err != nil { + errList = append(errList, err) + resource.ArrayOfString = nil + } else { + resource.ArrayOfString = ArrayOfString + return nil + } + + return errors.Join(errList...) +} diff --git a/pkg/kinds/librarypanel/librarypanel_spec_gen.go b/pkg/kinds/librarypanel/librarypanel_spec_gen.go index 177d6c26c41..efc167da68e 100644 --- a/pkg/kinds/librarypanel/librarypanel_spec_gen.go +++ b/pkg/kinds/librarypanel/librarypanel_spec_gen.go @@ -15,35 +15,6 @@ import ( time "time" ) -type LibraryElementDTOMetaUser struct { - Id int64 `json:"id"` - Name string `json:"name"` - AvatarUrl string `json:"avatarUrl"` -} - -// NewLibraryElementDTOMetaUser creates a new LibraryElementDTOMetaUser object. -func NewLibraryElementDTOMetaUser() *LibraryElementDTOMetaUser { - return &LibraryElementDTOMetaUser{} -} - -type LibraryElementDTOMeta struct { - FolderName string `json:"folderName"` - FolderUid string `json:"folderUid"` - ConnectedDashboards int64 `json:"connectedDashboards"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - CreatedBy LibraryElementDTOMetaUser `json:"createdBy"` - UpdatedBy LibraryElementDTOMetaUser `json:"updatedBy"` -} - -// NewLibraryElementDTOMeta creates a new LibraryElementDTOMeta object. -func NewLibraryElementDTOMeta() *LibraryElementDTOMeta { - return &LibraryElementDTOMeta{ - CreatedBy: *NewLibraryElementDTOMetaUser(), - UpdatedBy: *NewLibraryElementDTOMetaUser(), - } -} - type Spec struct { // Folder UID FolderUid *string `json:"folderUid,omitempty"` @@ -70,3 +41,32 @@ type Spec struct { func NewSpec() *Spec { return &Spec{} } + +type LibraryElementDTOMeta struct { + FolderName string `json:"folderName"` + FolderUid string `json:"folderUid"` + ConnectedDashboards int64 `json:"connectedDashboards"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + CreatedBy LibraryElementDTOMetaUser `json:"createdBy"` + UpdatedBy LibraryElementDTOMetaUser `json:"updatedBy"` +} + +// NewLibraryElementDTOMeta creates a new LibraryElementDTOMeta object. +func NewLibraryElementDTOMeta() *LibraryElementDTOMeta { + return &LibraryElementDTOMeta{ + CreatedBy: *NewLibraryElementDTOMetaUser(), + UpdatedBy: *NewLibraryElementDTOMetaUser(), + } +} + +type LibraryElementDTOMetaUser struct { + Id int64 `json:"id"` + Name string `json:"name"` + AvatarUrl string `json:"avatarUrl"` +} + +// NewLibraryElementDTOMetaUser creates a new LibraryElementDTOMetaUser object. +func NewLibraryElementDTOMetaUser() *LibraryElementDTOMetaUser { + return &LibraryElementDTOMetaUser{} +} diff --git a/pkg/kinds/preferences/preferences_spec_gen.go b/pkg/kinds/preferences/preferences_spec_gen.go index 431d2f9e9a0..6e02a1a9eb6 100644 --- a/pkg/kinds/preferences/preferences_spec_gen.go +++ b/pkg/kinds/preferences/preferences_spec_gen.go @@ -11,6 +11,33 @@ package preferences +// Spec defines user, team or org Grafana preferences +// swagger:model Preferences +type Spec struct { + // UID for the home dashboard + HomeDashboardUID *string `json:"homeDashboardUID,omitempty"` + // The timezone selection + // TODO: this should use the timezone defined in common + Timezone *string `json:"timezone,omitempty"` + // day of the week (sunday, monday, etc) + WeekStart *string `json:"weekStart,omitempty"` + // light, dark, empty is default + Theme *string `json:"theme,omitempty"` + // Selected language (beta) + Language *string `json:"language,omitempty"` + // Explore query history preferences + QueryHistory *QueryHistoryPreference `json:"queryHistory,omitempty"` + // Cookie preferences + CookiePreferences *CookiePreferences `json:"cookiePreferences,omitempty"` + // Navigation preferences + Navbar *NavbarPreference `json:"navbar,omitempty"` +} + +// NewSpec creates a new Spec object. +func NewSpec() *Spec { + return &Spec{} +} + type QueryHistoryPreference struct { // one of: '' | 'query' | 'starred'; HomeTab *string `json:"homeTab,omitempty"` @@ -40,30 +67,3 @@ type NavbarPreference struct { func NewNavbarPreference() *NavbarPreference { return &NavbarPreference{} } - -// Spec defines user, team or org Grafana preferences -// swagger:model Preferences -type Spec struct { - // UID for the home dashboard - HomeDashboardUID *string `json:"homeDashboardUID,omitempty"` - // The timezone selection - // TODO: this should use the timezone defined in common - Timezone *string `json:"timezone,omitempty"` - // day of the week (sunday, monday, etc) - WeekStart *string `json:"weekStart,omitempty"` - // light, dark, empty is default - Theme *string `json:"theme,omitempty"` - // Selected language (beta) - Language *string `json:"language,omitempty"` - // Explore query history preferences - QueryHistory *QueryHistoryPreference `json:"queryHistory,omitempty"` - // Cookie preferences - CookiePreferences *CookiePreferences `json:"cookiePreferences,omitempty"` - // Navigation preferences - Navbar *NavbarPreference `json:"navbar,omitempty"` -} - -// NewSpec creates a new Spec object. -func NewSpec() *Spec { - return &Spec{} -} diff --git a/pkg/plugins/codegen/go.mod b/pkg/plugins/codegen/go.mod index b376cf0a489..619bbc470dc 100644 --- a/pkg/plugins/codegen/go.mod +++ b/pkg/plugins/codegen/go.mod @@ -7,7 +7,7 @@ replace github.com/grafana/grafana/pkg/codegen => ../../codegen require ( cuelang.org/go v0.11.1 github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d - github.com/grafana/cog v0.0.18 + github.com/grafana/cog v0.0.27 github.com/grafana/cuetsy v0.1.11 github.com/grafana/grafana/pkg/codegen v0.0.0-00010101000000-000000000000 ) @@ -42,11 +42,11 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/plugins/codegen/go.sum b/pkg/plugins/codegen/go.sum index d143048952d..159e028e5ae 100644 --- a/pkg/plugins/codegen/go.sum +++ b/pkg/plugins/codegen/go.sum @@ -30,8 +30,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= -github.com/grafana/cog v0.0.18 h1:pEmzo/yhIFZMHM58ua0M9Eb5frJj6CgTrTTUVlY8e2o= -github.com/grafana/cog v0.0.18/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0= +github.com/grafana/cog v0.0.27 h1:ZKipAtp6KuB08R16nZbqEjnje3e2r1O1bzOp1CetDEo= +github.com/grafana/cog v0.0.27/go.mod h1:JB5lhdn4Hqc0ztYCaNOTKZXoojzJvydBxMkMCGWS6+Q= github.com/grafana/cuetsy v0.1.11 h1:I3IwBhF+UaQxRM79HnImtrAn8REGdb5M3+C4QrYHoWk= github.com/grafana/cuetsy v0.1.11/go.mod h1:Ix97+CPD8ws9oSSxR3/Lf4ahU1I4Np83kjJmDVnLZvc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -92,8 +92,8 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= @@ -104,8 +104,8 @@ golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 57428aed803..12ccc4c4040 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -108,12 +108,12 @@ require ( go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/api v0.220.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index 36f1091f0ba..1135d5e1404 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -336,8 +336,8 @@ golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWB golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -377,8 +377,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 9765e457c45..81eebd46fcc 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -385,7 +385,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.35.0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sync v0.11.0 // indirect @@ -393,7 +393,7 @@ require ( golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.15.1 // indirect google.golang.org/api v0.220.0 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 1854eebc3fa..636fc8cd18e 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -1981,8 +1981,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2327,8 +2327,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 5d3a0c23caf..0bb4e887241 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -239,14 +239,14 @@ require ( go.uber.org/atomic v1.11.0 // indirect golang.org/x/crypto v0.35.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/term v0.29.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/api v0.220.0 // indirect google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 82c6ae452bd..0ba9d1ae931 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -1712,8 +1712,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2045,8 +2045,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= 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= diff --git a/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go index a51aef464a5..8be5bf520bf 100644 --- a/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go @@ -66,27 +66,6 @@ func NewAzureMonitorQuery() *AzureMonitorQuery { return &AzureMonitorQuery{} } -// Defines the supported queryTypes. GrafanaTemplateVariableFn is deprecated -type AzureQueryType string - -const ( - AzureQueryTypeAzureMonitor AzureQueryType = "Azure Monitor" - AzureQueryTypeLogAnalytics AzureQueryType = "Azure Log Analytics" - AzureQueryTypeAzureResourceGraph AzureQueryType = "Azure Resource Graph" - AzureQueryTypeAzureTraces AzureQueryType = "Azure Traces" - AzureQueryTypeSubscriptionsQuery AzureQueryType = "Azure Subscriptions" - AzureQueryTypeResourceGroupsQuery AzureQueryType = "Azure Resource Groups" - AzureQueryTypeNamespacesQuery AzureQueryType = "Azure Namespaces" - AzureQueryTypeResourceNamesQuery AzureQueryType = "Azure Resource Names" - AzureQueryTypeMetricNamesQuery AzureQueryType = "Azure Metric Names" - AzureQueryTypeWorkspacesQuery AzureQueryType = "Azure Workspaces" - AzureQueryTypeLocationsQuery AzureQueryType = "Azure Regions" - AzureQueryTypeGrafanaTemplateVariableFn AzureQueryType = "Grafana Template Variable Function" - AzureQueryTypeTraceExemplar AzureQueryType = "traceql" - AzureQueryTypeCustomNamespacesQuery AzureQueryType = "Azure Custom Namespaces" - AzureQueryTypeCustomMetricNamesQuery AzureQueryType = "Azure Custom Metric Names" -) - type AzureMetricQuery struct { // Array of resource URIs to be queried. Resources []AzureMonitorResource `json:"resources,omitempty"` @@ -133,6 +112,35 @@ func NewAzureMetricQuery() *AzureMetricQuery { return &AzureMetricQuery{} } +type AzureMonitorResource struct { + Subscription *string `json:"subscription,omitempty"` + ResourceGroup *string `json:"resourceGroup,omitempty"` + ResourceName *string `json:"resourceName,omitempty"` + MetricNamespace *string `json:"metricNamespace,omitempty"` + Region *string `json:"region,omitempty"` +} + +// NewAzureMonitorResource creates a new AzureMonitorResource object. +func NewAzureMonitorResource() *AzureMonitorResource { + return &AzureMonitorResource{} +} + +type AzureMetricDimension struct { + // Name of Dimension to be filtered on. + Dimension *string `json:"dimension,omitempty"` + // String denoting the filter operation. Supports 'eq' - equals,'ne' - not equals, 'sw' - starts with. Note that some dimensions may not support all operators. + Operator *string `json:"operator,omitempty"` + // Values to match with the filter. + Filters []string `json:"filters,omitempty"` + // @deprecated filter is deprecated in favour of filters to support multiselect. + Filter *string `json:"filter,omitempty"` +} + +// NewAzureMetricDimension creates a new AzureMetricDimension object. +func NewAzureMetricDimension() *AzureMetricDimension { + return &AzureMetricDimension{} +} + // Azure Monitor Logs sub-query properties type AzureLogsQuery struct { // KQL query to be executed. @@ -160,6 +168,27 @@ func NewAzureLogsQuery() *AzureLogsQuery { return &AzureLogsQuery{} } +type ResultFormat string + +const ( + ResultFormatTable ResultFormat = "table" + ResultFormatTimeSeries ResultFormat = "time_series" + ResultFormatTrace ResultFormat = "trace" + ResultFormatLogs ResultFormat = "logs" +) + +type AzureResourceGraphQuery struct { + // Azure Resource Graph KQL query to be executed. + Query *string `json:"query,omitempty"` + // Specifies the format results should be returned as. Defaults to table. + ResultFormat *string `json:"resultFormat,omitempty"` +} + +// NewAzureResourceGraphQuery creates a new AzureResourceGraphQuery object. +func NewAzureResourceGraphQuery() *AzureResourceGraphQuery { + return &AzureResourceGraphQuery{} +} + // Application Insights Traces sub-query properties type AzureTracesQuery struct { // Specifies the format results should be returned as. @@ -195,89 +224,11 @@ func NewAzureTracesFilter() *AzureTracesFilter { return &AzureTracesFilter{} } -type ResultFormat string +type GrafanaTemplateVariableQuery = AppInsightsMetricNameQueryOrAppInsightsGroupByQueryOrSubscriptionsQueryOrResourceGroupsQueryOrResourceNamesQueryOrMetricNamespaceQueryOrMetricDefinitionsQueryOrMetricNamesQueryOrWorkspacesQueryOrUnknownQuery -const ( - ResultFormatTable ResultFormat = "table" - ResultFormatTimeSeries ResultFormat = "time_series" - ResultFormatTrace ResultFormat = "trace" - ResultFormatLogs ResultFormat = "logs" -) - -type AzureResourceGraphQuery struct { - // Azure Resource Graph KQL query to be executed. - Query *string `json:"query,omitempty"` - // Specifies the format results should be returned as. Defaults to table. - ResultFormat *string `json:"resultFormat,omitempty"` -} - -// NewAzureResourceGraphQuery creates a new AzureResourceGraphQuery object. -func NewAzureResourceGraphQuery() *AzureResourceGraphQuery { - return &AzureResourceGraphQuery{} -} - -type AzureMonitorResource struct { - Subscription *string `json:"subscription,omitempty"` - ResourceGroup *string `json:"resourceGroup,omitempty"` - ResourceName *string `json:"resourceName,omitempty"` - MetricNamespace *string `json:"metricNamespace,omitempty"` - Region *string `json:"region,omitempty"` -} - -// NewAzureMonitorResource creates a new AzureMonitorResource object. -func NewAzureMonitorResource() *AzureMonitorResource { - return &AzureMonitorResource{} -} - -type AzureMetricDimension struct { - // Name of Dimension to be filtered on. - Dimension *string `json:"dimension,omitempty"` - // String denoting the filter operation. Supports 'eq' - equals,'ne' - not equals, 'sw' - starts with. Note that some dimensions may not support all operators. - Operator *string `json:"operator,omitempty"` - // Values to match with the filter. - Filters []string `json:"filters,omitempty"` - // @deprecated filter is deprecated in favour of filters to support multiselect. - Filter *string `json:"filter,omitempty"` -} - -// NewAzureMetricDimension creates a new AzureMetricDimension object. -func NewAzureMetricDimension() *AzureMetricDimension { - return &AzureMetricDimension{} -} - -type GrafanaTemplateVariableQueryType string - -const ( - GrafanaTemplateVariableQueryTypeAppInsightsMetricNameQuery GrafanaTemplateVariableQueryType = "AppInsightsMetricNameQuery" - GrafanaTemplateVariableQueryTypeAppInsightsGroupByQuery GrafanaTemplateVariableQueryType = "AppInsightsGroupByQuery" - GrafanaTemplateVariableQueryTypeSubscriptionsQuery GrafanaTemplateVariableQueryType = "SubscriptionsQuery" - GrafanaTemplateVariableQueryTypeResourceGroupsQuery GrafanaTemplateVariableQueryType = "ResourceGroupsQuery" - GrafanaTemplateVariableQueryTypeResourceNamesQuery GrafanaTemplateVariableQueryType = "ResourceNamesQuery" - GrafanaTemplateVariableQueryTypeMetricNamespaceQuery GrafanaTemplateVariableQueryType = "MetricNamespaceQuery" - GrafanaTemplateVariableQueryTypeMetricNamesQuery GrafanaTemplateVariableQueryType = "MetricNamesQuery" - GrafanaTemplateVariableQueryTypeWorkspacesQuery GrafanaTemplateVariableQueryType = "WorkspacesQuery" - GrafanaTemplateVariableQueryTypeUnknownQuery GrafanaTemplateVariableQueryType = "UnknownQuery" -) - -type BaseGrafanaTemplateVariableQuery struct { - RawQuery *string `json:"rawQuery,omitempty"` -} - -// NewBaseGrafanaTemplateVariableQuery creates a new BaseGrafanaTemplateVariableQuery object. -func NewBaseGrafanaTemplateVariableQuery() *BaseGrafanaTemplateVariableQuery { - return &BaseGrafanaTemplateVariableQuery{} -} - -type UnknownQuery struct { - RawQuery *string `json:"rawQuery,omitempty"` - Kind string `json:"kind"` -} - -// NewUnknownQuery creates a new UnknownQuery object. -func NewUnknownQuery() *UnknownQuery { - return &UnknownQuery{ - Kind: "UnknownQuery", - } +// NewGrafanaTemplateVariableQuery creates a new GrafanaTemplateVariableQuery object. +func NewGrafanaTemplateVariableQuery() *GrafanaTemplateVariableQuery { + return NewAppInsightsMetricNameQueryOrAppInsightsGroupByQueryOrSubscriptionsQueryOrResourceGroupsQueryOrResourceNamesQueryOrMetricNamespaceQueryOrMetricDefinitionsQueryOrMetricNamesQueryOrWorkspacesQueryOrUnknownQuery() } type AppInsightsMetricNameQuery struct { @@ -407,11 +358,60 @@ func NewWorkspacesQuery() *WorkspacesQuery { } } -type GrafanaTemplateVariableQuery = AppInsightsMetricNameQueryOrAppInsightsGroupByQueryOrSubscriptionsQueryOrResourceGroupsQueryOrResourceNamesQueryOrMetricNamespaceQueryOrMetricDefinitionsQueryOrMetricNamesQueryOrWorkspacesQueryOrUnknownQuery +type UnknownQuery struct { + RawQuery *string `json:"rawQuery,omitempty"` + Kind string `json:"kind"` +} -// NewGrafanaTemplateVariableQuery creates a new GrafanaTemplateVariableQuery object. -func NewGrafanaTemplateVariableQuery() *GrafanaTemplateVariableQuery { - return NewAppInsightsMetricNameQueryOrAppInsightsGroupByQueryOrSubscriptionsQueryOrResourceGroupsQueryOrResourceNamesQueryOrMetricNamespaceQueryOrMetricDefinitionsQueryOrMetricNamesQueryOrWorkspacesQueryOrUnknownQuery() +// NewUnknownQuery creates a new UnknownQuery object. +func NewUnknownQuery() *UnknownQuery { + return &UnknownQuery{ + Kind: "UnknownQuery", + } +} + +// Defines the supported queryTypes. GrafanaTemplateVariableFn is deprecated +type AzureQueryType string + +const ( + AzureQueryTypeAzureMonitor AzureQueryType = "Azure Monitor" + AzureQueryTypeLogAnalytics AzureQueryType = "Azure Log Analytics" + AzureQueryTypeAzureResourceGraph AzureQueryType = "Azure Resource Graph" + AzureQueryTypeAzureTraces AzureQueryType = "Azure Traces" + AzureQueryTypeSubscriptionsQuery AzureQueryType = "Azure Subscriptions" + AzureQueryTypeResourceGroupsQuery AzureQueryType = "Azure Resource Groups" + AzureQueryTypeNamespacesQuery AzureQueryType = "Azure Namespaces" + AzureQueryTypeResourceNamesQuery AzureQueryType = "Azure Resource Names" + AzureQueryTypeMetricNamesQuery AzureQueryType = "Azure Metric Names" + AzureQueryTypeWorkspacesQuery AzureQueryType = "Azure Workspaces" + AzureQueryTypeLocationsQuery AzureQueryType = "Azure Regions" + AzureQueryTypeGrafanaTemplateVariableFn AzureQueryType = "Grafana Template Variable Function" + AzureQueryTypeTraceExemplar AzureQueryType = "traceql" + AzureQueryTypeCustomNamespacesQuery AzureQueryType = "Azure Custom Namespaces" + AzureQueryTypeCustomMetricNamesQuery AzureQueryType = "Azure Custom Metric Names" +) + +type GrafanaTemplateVariableQueryType string + +const ( + GrafanaTemplateVariableQueryTypeAppInsightsMetricNameQuery GrafanaTemplateVariableQueryType = "AppInsightsMetricNameQuery" + GrafanaTemplateVariableQueryTypeAppInsightsGroupByQuery GrafanaTemplateVariableQueryType = "AppInsightsGroupByQuery" + GrafanaTemplateVariableQueryTypeSubscriptionsQuery GrafanaTemplateVariableQueryType = "SubscriptionsQuery" + GrafanaTemplateVariableQueryTypeResourceGroupsQuery GrafanaTemplateVariableQueryType = "ResourceGroupsQuery" + GrafanaTemplateVariableQueryTypeResourceNamesQuery GrafanaTemplateVariableQueryType = "ResourceNamesQuery" + GrafanaTemplateVariableQueryTypeMetricNamespaceQuery GrafanaTemplateVariableQueryType = "MetricNamespaceQuery" + GrafanaTemplateVariableQueryTypeMetricNamesQuery GrafanaTemplateVariableQueryType = "MetricNamesQuery" + GrafanaTemplateVariableQueryTypeWorkspacesQuery GrafanaTemplateVariableQueryType = "WorkspacesQuery" + GrafanaTemplateVariableQueryTypeUnknownQuery GrafanaTemplateVariableQueryType = "UnknownQuery" +) + +type BaseGrafanaTemplateVariableQuery struct { + RawQuery *string `json:"rawQuery,omitempty"` +} + +// NewBaseGrafanaTemplateVariableQuery creates a new BaseGrafanaTemplateVariableQuery object. +func NewBaseGrafanaTemplateVariableQuery() *BaseGrafanaTemplateVariableQuery { + return &BaseGrafanaTemplateVariableQuery{} } type AppInsightsMetricNameQueryOrAppInsightsGroupByQueryOrSubscriptionsQueryOrResourceGroupsQueryOrResourceNamesQueryOrMetricNamespaceQueryOrMetricDefinitionsQueryOrMetricNamesQueryOrWorkspacesQueryOrUnknownQuery struct { diff --git a/pkg/tsdb/cloud-monitoring/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/cloud-monitoring/kinds/dataquery/types_dataquery_gen.go index 272b4e3a1d9..35ccd8d15f2 100644 --- a/pkg/tsdb/cloud-monitoring/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/cloud-monitoring/kinds/dataquery/types_dataquery_gen.go @@ -47,17 +47,6 @@ func NewCloudMonitoringQuery() *CloudMonitoringQuery { return &CloudMonitoringQuery{} } -// Defines the supported queryTypes. -type QueryType string - -const ( - QueryTypeTIMESERIESLIST QueryType = "timeSeriesList" - QueryTypeTIMESERIESQUERY QueryType = "timeSeriesQuery" - QueryTypeSLO QueryType = "slo" - QueryTypeANNOTATION QueryType = "annotation" - QueryTypePROMQL QueryType = "promQL" -) - // Time Series List sub-query properties. type TimeSeriesList struct { // GCP project to execute the query against. @@ -163,6 +152,17 @@ func NewPromQLQuery() *PromQLQuery { return &PromQLQuery{} } +// Defines the supported queryTypes. +type QueryType string + +const ( + QueryTypeTIMESERIESLIST QueryType = "timeSeriesList" + QueryTypeTIMESERIESQUERY QueryType = "timeSeriesQuery" + QueryTypeSLO QueryType = "slo" + QueryTypeANNOTATION QueryType = "annotation" + QueryTypePROMQL QueryType = "promQL" +) + // @deprecated This type is for migration purposes only. Replaced by TimeSeriesList Metric sub-query properties. type MetricQuery struct { // GCP project to execute the query against. diff --git a/pkg/tsdb/cloudwatch/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/cloudwatch/kinds/dataquery/types_dataquery_gen.go index a2f35aa9eb4..db6d092792b 100644 --- a/pkg/tsdb/cloudwatch/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/cloudwatch/kinds/dataquery/types_dataquery_gen.go @@ -151,7 +151,7 @@ func NewSQLExpression() *SQLExpression { } type QueryEditorFunctionExpression struct { - Type string `json:"type"` + Type QueryEditorExpressionType `json:"type"` Name *string `json:"name,omitempty"` Parameters []QueryEditorFunctionParameterExpression `json:"parameters,omitempty"` } @@ -159,101 +159,35 @@ type QueryEditorFunctionExpression struct { // NewQueryEditorFunctionExpression creates a new QueryEditorFunctionExpression object. func NewQueryEditorFunctionExpression() *QueryEditorFunctionExpression { return &QueryEditorFunctionExpression{ - Type: "function", + Type: QueryEditorExpressionTypeFunction, } } -type QueryEditorExpressionType string - -const ( - QueryEditorExpressionTypeProperty QueryEditorExpressionType = "property" - QueryEditorExpressionTypeOperator QueryEditorExpressionType = "operator" - QueryEditorExpressionTypeOr QueryEditorExpressionType = "or" - QueryEditorExpressionTypeAnd QueryEditorExpressionType = "and" - QueryEditorExpressionTypeGroupBy QueryEditorExpressionType = "groupBy" - QueryEditorExpressionTypeFunction QueryEditorExpressionType = "function" - QueryEditorExpressionTypeFunctionParameter QueryEditorExpressionType = "functionParameter" -) - type QueryEditorFunctionParameterExpression struct { - Type string `json:"type"` - Name *string `json:"name,omitempty"` + Type QueryEditorExpressionType `json:"type"` + Name *string `json:"name,omitempty"` } // NewQueryEditorFunctionParameterExpression creates a new QueryEditorFunctionParameterExpression object. func NewQueryEditorFunctionParameterExpression() *QueryEditorFunctionParameterExpression { return &QueryEditorFunctionParameterExpression{ - Type: "functionParameter", + Type: QueryEditorExpressionTypeFunctionParameter, } } type QueryEditorPropertyExpression struct { - Type string `json:"type"` - Property QueryEditorProperty `json:"property"` + Type QueryEditorExpressionType `json:"type"` + Property QueryEditorProperty `json:"property"` } // NewQueryEditorPropertyExpression creates a new QueryEditorPropertyExpression object. func NewQueryEditorPropertyExpression() *QueryEditorPropertyExpression { return &QueryEditorPropertyExpression{ - Type: "property", + Type: QueryEditorExpressionTypeProperty, Property: *NewQueryEditorProperty(), } } -type QueryEditorGroupByExpression struct { - Type string `json:"type"` - Property QueryEditorProperty `json:"property"` -} - -// NewQueryEditorGroupByExpression creates a new QueryEditorGroupByExpression object. -func NewQueryEditorGroupByExpression() *QueryEditorGroupByExpression { - return &QueryEditorGroupByExpression{ - Type: "groupBy", - Property: *NewQueryEditorProperty(), - } -} - -type QueryEditorOperatorExpression struct { - Type string `json:"type"` - Property QueryEditorProperty `json:"property"` - // TS type is operator: QueryEditorOperator, extended in veneer - Operator QueryEditorOperator `json:"operator"` -} - -// NewQueryEditorOperatorExpression creates a new QueryEditorOperatorExpression object. -func NewQueryEditorOperatorExpression() *QueryEditorOperatorExpression { - return &QueryEditorOperatorExpression{ - Type: "operator", - Property: *NewQueryEditorProperty(), - Operator: *NewQueryEditorOperator(), - } -} - -// TS type is QueryEditorOperator, extended in veneer -type QueryEditorOperator struct { - Name *string `json:"name,omitempty"` - Value *StringOrBoolOrInt64OrArrayOfQueryEditorOperatorType `json:"value,omitempty"` -} - -// NewQueryEditorOperator creates a new QueryEditorOperator object. -func NewQueryEditorOperator() *QueryEditorOperator { - return &QueryEditorOperator{} -} - -type QueryEditorOperatorValueType = StringOrBoolOrInt64OrArrayOfQueryEditorOperatorType - -// NewQueryEditorOperatorValueType creates a new QueryEditorOperatorValueType object. -func NewQueryEditorOperatorValueType() *QueryEditorOperatorValueType { - return NewStringOrBoolOrInt64OrArrayOfQueryEditorOperatorType() -} - -type QueryEditorOperatorType = StringOrBoolOrInt64 - -// NewQueryEditorOperatorType creates a new QueryEditorOperatorType object. -func NewQueryEditorOperatorType() *QueryEditorOperatorType { - return NewStringOrBoolOrInt64() -} - type QueryEditorProperty struct { Type QueryEditorPropertyType `json:"type"` Name *string `json:"name,omitempty"` @@ -284,6 +218,72 @@ func NewQueryEditorArrayExpression() *QueryEditorArrayExpression { type QueryEditorExpression any +type QueryEditorGroupByExpression struct { + Type QueryEditorExpressionType `json:"type"` + Property QueryEditorProperty `json:"property"` +} + +// NewQueryEditorGroupByExpression creates a new QueryEditorGroupByExpression object. +func NewQueryEditorGroupByExpression() *QueryEditorGroupByExpression { + return &QueryEditorGroupByExpression{ + Type: QueryEditorExpressionTypeGroupBy, + Property: *NewQueryEditorProperty(), + } +} + +type QueryEditorOperatorExpression struct { + Type QueryEditorExpressionType `json:"type"` + Property QueryEditorProperty `json:"property"` + // TS type is operator: QueryEditorOperator, extended in veneer + Operator QueryEditorOperator `json:"operator"` +} + +// NewQueryEditorOperatorExpression creates a new QueryEditorOperatorExpression object. +func NewQueryEditorOperatorExpression() *QueryEditorOperatorExpression { + return &QueryEditorOperatorExpression{ + Type: QueryEditorExpressionTypeOperator, + Property: *NewQueryEditorProperty(), + Operator: *NewQueryEditorOperator(), + } +} + +// TS type is QueryEditorOperator, extended in veneer +type QueryEditorOperator struct { + Name *string `json:"name,omitempty"` + Value *StringOrBoolOrInt64OrArrayOfQueryEditorOperatorType `json:"value,omitempty"` +} + +// NewQueryEditorOperator creates a new QueryEditorOperator object. +func NewQueryEditorOperator() *QueryEditorOperator { + return &QueryEditorOperator{} +} + +type QueryEditorOperatorType = StringOrBoolOrInt64 + +// NewQueryEditorOperatorType creates a new QueryEditorOperatorType object. +func NewQueryEditorOperatorType() *QueryEditorOperatorType { + return NewStringOrBoolOrInt64() +} + +type QueryEditorExpressionType string + +const ( + QueryEditorExpressionTypeProperty QueryEditorExpressionType = "property" + QueryEditorExpressionTypeOperator QueryEditorExpressionType = "operator" + QueryEditorExpressionTypeOr QueryEditorExpressionType = "or" + QueryEditorExpressionTypeAnd QueryEditorExpressionType = "and" + QueryEditorExpressionTypeGroupBy QueryEditorExpressionType = "groupBy" + QueryEditorExpressionTypeFunction QueryEditorExpressionType = "function" + QueryEditorExpressionTypeFunctionParameter QueryEditorExpressionType = "functionParameter" +) + +type QueryEditorOperatorValueType = StringOrBoolOrInt64OrArrayOfQueryEditorOperatorType + +// NewQueryEditorOperatorValueType creates a new QueryEditorOperatorValueType object. +func NewQueryEditorOperatorValueType() *QueryEditorOperatorValueType { + return NewStringOrBoolOrInt64OrArrayOfQueryEditorOperatorType() +} + type LogsQueryLanguage string const ( @@ -525,6 +525,60 @@ func (resource *QueryEditorPropertyExpressionOrQueryEditorFunctionExpression) Un return fmt.Errorf("could not unmarshal resource with `type = %v`", discriminator) } +type ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression struct { + ArrayOfQueryEditorExpression []QueryEditorExpression `json:"ArrayOfQueryEditorExpression,omitempty"` + ArrayOfQueryEditorArrayExpression []QueryEditorArrayExpression `json:"ArrayOfQueryEditorArrayExpression,omitempty"` +} + +// NewArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression creates a new ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression object. +func NewArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression() *ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression { + return &ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression{} +} + +// MarshalJSON implements a custom JSON marshalling logic to encode `ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression` as JSON. +func (resource ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression) MarshalJSON() ([]byte, error) { + if resource.ArrayOfQueryEditorExpression != nil { + return json.Marshal(resource.ArrayOfQueryEditorExpression) + } + + if resource.ArrayOfQueryEditorArrayExpression != nil { + return json.Marshal(resource.ArrayOfQueryEditorArrayExpression) + } + + return nil, fmt.Errorf("no value for disjunction of scalars") +} + +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression` from JSON. +func (resource *ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression) UnmarshalJSON(raw []byte) error { + if raw == nil { + return nil + } + + var errList []error + + // ArrayOfQueryEditorExpression + var ArrayOfQueryEditorExpression []QueryEditorExpression + if err := json.Unmarshal(raw, &ArrayOfQueryEditorExpression); err != nil { + errList = append(errList, err) + resource.ArrayOfQueryEditorExpression = nil + } else { + resource.ArrayOfQueryEditorExpression = ArrayOfQueryEditorExpression + return nil + } + + // ArrayOfQueryEditorArrayExpression + var ArrayOfQueryEditorArrayExpression []QueryEditorArrayExpression + if err := json.Unmarshal(raw, &ArrayOfQueryEditorArrayExpression); err != nil { + errList = append(errList, err) + resource.ArrayOfQueryEditorArrayExpression = nil + } else { + resource.ArrayOfQueryEditorArrayExpression = ArrayOfQueryEditorArrayExpression + return nil + } + + return errors.Join(errList...) +} + type StringOrBoolOrInt64OrArrayOfQueryEditorOperatorType struct { String *string `json:"String,omitempty"` Bool *bool `json:"Bool,omitempty"` @@ -677,57 +731,3 @@ func (resource *StringOrBoolOrInt64) UnmarshalJSON(raw []byte) error { return errors.Join(errList...) } - -type ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression struct { - ArrayOfQueryEditorExpression []QueryEditorExpression `json:"ArrayOfQueryEditorExpression,omitempty"` - ArrayOfQueryEditorArrayExpression []QueryEditorArrayExpression `json:"ArrayOfQueryEditorArrayExpression,omitempty"` -} - -// NewArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression creates a new ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression object. -func NewArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression() *ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression { - return &ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression{} -} - -// MarshalJSON implements a custom JSON marshalling logic to encode `ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression` as JSON. -func (resource ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression) MarshalJSON() ([]byte, error) { - if resource.ArrayOfQueryEditorExpression != nil { - return json.Marshal(resource.ArrayOfQueryEditorExpression) - } - - if resource.ArrayOfQueryEditorArrayExpression != nil { - return json.Marshal(resource.ArrayOfQueryEditorArrayExpression) - } - - return nil, fmt.Errorf("no value for disjunction of scalars") -} - -// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression` from JSON. -func (resource *ArrayOfQueryEditorExpressionOrArrayOfQueryEditorArrayExpression) UnmarshalJSON(raw []byte) error { - if raw == nil { - return nil - } - - var errList []error - - // ArrayOfQueryEditorExpression - var ArrayOfQueryEditorExpression []QueryEditorExpression - if err := json.Unmarshal(raw, &ArrayOfQueryEditorExpression); err != nil { - errList = append(errList, err) - resource.ArrayOfQueryEditorExpression = nil - } else { - resource.ArrayOfQueryEditorExpression = ArrayOfQueryEditorExpression - return nil - } - - // ArrayOfQueryEditorArrayExpression - var ArrayOfQueryEditorArrayExpression []QueryEditorArrayExpression - if err := json.Unmarshal(raw, &ArrayOfQueryEditorArrayExpression); err != nil { - errList = append(errList, err) - resource.ArrayOfQueryEditorArrayExpression = nil - } else { - resource.ArrayOfQueryEditorArrayExpression = ArrayOfQueryEditorArrayExpression - return nil - } - - return errors.Join(errList...) -} diff --git a/pkg/tsdb/elasticsearch/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/elasticsearch/kinds/dataquery/types_dataquery_gen.go index 430a37af4e5..03f9ef7672a 100644 --- a/pkg/tsdb/elasticsearch/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/elasticsearch/kinds/dataquery/types_dataquery_gen.go @@ -24,6 +24,106 @@ func NewBucketAggregation() *BucketAggregation { return NewDateHistogramOrHistogramOrTermsOrFiltersOrGeoHashGridOrNested() } +type DateHistogram struct { + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Type BucketAggregationType `json:"type"` + Settings *DataqueryDateHistogramSettings `json:"settings,omitempty"` +} + +// NewDateHistogram creates a new DateHistogram object. +func NewDateHistogram() *DateHistogram { + return &DateHistogram{ + Type: BucketAggregationTypeDateHistogram, + } +} + +type Histogram struct { + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Type BucketAggregationType `json:"type"` + Settings *DataqueryHistogramSettings `json:"settings,omitempty"` +} + +// NewHistogram creates a new Histogram object. +func NewHistogram() *Histogram { + return &Histogram{ + Type: BucketAggregationTypeHistogram, + } +} + +type Terms struct { + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Type BucketAggregationType `json:"type"` + Settings *DataqueryTermsSettings `json:"settings,omitempty"` +} + +// NewTerms creates a new Terms object. +func NewTerms() *Terms { + return &Terms{ + Type: BucketAggregationTypeTerms, + } +} + +type TermsOrder string + +const ( + TermsOrderDesc TermsOrder = "desc" + TermsOrderAsc TermsOrder = "asc" +) + +type Filters struct { + Id string `json:"id"` + Type BucketAggregationType `json:"type"` + Settings *DataqueryFiltersSettings `json:"settings,omitempty"` +} + +// NewFilters creates a new Filters object. +func NewFilters() *Filters { + return &Filters{ + Type: BucketAggregationTypeFilters, + } +} + +type Filter struct { + Query string `json:"query"` + Label string `json:"label"` +} + +// NewFilter creates a new Filter object. +func NewFilter() *Filter { + return &Filter{} +} + +type GeoHashGrid struct { + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Type BucketAggregationType `json:"type"` + Settings *DataqueryGeoHashGridSettings `json:"settings,omitempty"` +} + +// NewGeoHashGrid creates a new GeoHashGrid object. +func NewGeoHashGrid() *GeoHashGrid { + return &GeoHashGrid{ + Type: BucketAggregationTypeGeohashGrid, + } +} + +type Nested struct { + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Type BucketAggregationType `json:"type"` + Settings any `json:"settings,omitempty"` +} + +// NewNested creates a new Nested object. +func NewNested() *Nested { + return &Nested{ + Type: BucketAggregationTypeNested, + } +} + type MetricAggregation = CountOrMovingAverageOrDerivativeOrCumulativeSumOrBucketScriptOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingFunctionOrLogsOrRateOrTopMetrics // NewMetricAggregation creates a new MetricAggregation object. @@ -31,6 +131,323 @@ func NewMetricAggregation() *MetricAggregation { return NewCountOrMovingAverageOrDerivativeOrCumulativeSumOrBucketScriptOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingFunctionOrLogsOrRateOrTopMetrics() } +type Count struct { + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Hide *bool `json:"hide,omitempty"` +} + +// NewCount creates a new Count object. +func NewCount() *Count { + return &Count{ + Type: MetricAggregationTypeCount, + } +} + +type PipelineMetricAggregation = MovingAverageOrDerivativeOrCumulativeSumOrBucketScript + +// NewPipelineMetricAggregation creates a new PipelineMetricAggregation object. +func NewPipelineMetricAggregation() *PipelineMetricAggregation { + return NewMovingAverageOrDerivativeOrCumulativeSumOrBucketScript() +} + +// #MovingAverage's settings are overridden in types.ts +type MovingAverage struct { + PipelineAgg *string `json:"pipelineAgg,omitempty"` + Field *string `json:"field,omitempty"` + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings map[string]any `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewMovingAverage creates a new MovingAverage object. +func NewMovingAverage() *MovingAverage { + return &MovingAverage{ + Type: MetricAggregationTypeMovingAvg, + } +} + +type Derivative struct { + PipelineAgg *string `json:"pipelineAgg,omitempty"` + Field *string `json:"field,omitempty"` + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryDerivativeSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewDerivative creates a new Derivative object. +func NewDerivative() *Derivative { + return &Derivative{ + Type: MetricAggregationTypeDerivative, + } +} + +type CumulativeSum struct { + PipelineAgg *string `json:"pipelineAgg,omitempty"` + Field *string `json:"field,omitempty"` + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryCumulativeSumSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewCumulativeSum creates a new CumulativeSum object. +func NewCumulativeSum() *CumulativeSum { + return &CumulativeSum{ + Type: MetricAggregationTypeCumulativeSum, + } +} + +type BucketScript struct { + Type MetricAggregationType `json:"type"` + PipelineVariables []PipelineVariable `json:"pipelineVariables,omitempty"` + Id string `json:"id"` + Settings *DataqueryBucketScriptSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewBucketScript creates a new BucketScript object. +func NewBucketScript() *BucketScript { + return &BucketScript{ + Type: MetricAggregationTypeBucketScript, + } +} + +type PipelineVariable struct { + Name string `json:"name"` + PipelineAgg string `json:"pipelineAgg"` +} + +// NewPipelineVariable creates a new PipelineVariable object. +func NewPipelineVariable() *PipelineVariable { + return &PipelineVariable{} +} + +type InlineScript = StringOrDataqueryInlineScript + +// NewInlineScript creates a new InlineScript object. +func NewInlineScript() *InlineScript { + return NewStringOrDataqueryInlineScript() +} + +type MetricAggregationWithSettings = BucketScriptOrCumulativeSumOrDerivativeOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingAverageOrMovingFunctionOrLogsOrRateOrTopMetrics + +// NewMetricAggregationWithSettings creates a new MetricAggregationWithSettings object. +func NewMetricAggregationWithSettings() *MetricAggregationWithSettings { + return NewBucketScriptOrCumulativeSumOrDerivativeOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingAverageOrMovingFunctionOrLogsOrRateOrTopMetrics() +} + +type SerialDiff struct { + PipelineAgg *string `json:"pipelineAgg,omitempty"` + Field *string `json:"field,omitempty"` + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataquerySerialDiffSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewSerialDiff creates a new SerialDiff object. +func NewSerialDiff() *SerialDiff { + return &SerialDiff{ + Type: MetricAggregationTypeSerialDiff, + } +} + +type RawData struct { + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryRawDataSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewRawData creates a new RawData object. +func NewRawData() *RawData { + return &RawData{ + Type: MetricAggregationTypeRawData, + } +} + +type RawDocument struct { + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryRawDocumentSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewRawDocument creates a new RawDocument object. +func NewRawDocument() *RawDocument { + return &RawDocument{ + Type: MetricAggregationTypeRawDocument, + } +} + +type UniqueCount struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataqueryUniqueCountSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewUniqueCount creates a new UniqueCount object. +func NewUniqueCount() *UniqueCount { + return &UniqueCount{ + Type: MetricAggregationTypeCardinality, + } +} + +type Percentiles struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataqueryPercentilesSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewPercentiles creates a new Percentiles object. +func NewPercentiles() *Percentiles { + return &Percentiles{ + Type: MetricAggregationTypePercentiles, + } +} + +type ExtendedStats struct { + Type MetricAggregationType `json:"type"` + Settings *DataqueryExtendedStatsSettings `json:"settings,omitempty"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Meta any `json:"meta,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewExtendedStats creates a new ExtendedStats object. +func NewExtendedStats() *ExtendedStats { + return &ExtendedStats{ + Type: MetricAggregationTypeExtendedStats, + } +} + +type Min struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataqueryMinSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewMin creates a new Min object. +func NewMin() *Min { + return &Min{ + Type: MetricAggregationTypeMin, + } +} + +type Max struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataqueryMaxSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewMax creates a new Max object. +func NewMax() *Max { + return &Max{ + Type: MetricAggregationTypeMax, + } +} + +type Sum struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataquerySumSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewSum creates a new Sum object. +func NewSum() *Sum { + return &Sum{ + Type: MetricAggregationTypeSum, + } +} + +type Average struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataqueryAverageSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewAverage creates a new Average object. +func NewAverage() *Average { + return &Average{ + Type: MetricAggregationTypeAvg, + } +} + +type MovingFunction struct { + PipelineAgg *string `json:"pipelineAgg,omitempty"` + Field *string `json:"field,omitempty"` + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryMovingFunctionSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewMovingFunction creates a new MovingFunction object. +func NewMovingFunction() *MovingFunction { + return &MovingFunction{ + Type: MetricAggregationTypeMovingFn, + } +} + +type Logs struct { + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryLogsSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewLogs creates a new Logs object. +func NewLogs() *Logs { + return &Logs{ + Type: MetricAggregationTypeLogs, + } +} + +type Rate struct { + Type MetricAggregationType `json:"type"` + Field *string `json:"field,omitempty"` + Id string `json:"id"` + Settings *DataqueryRateSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewRate creates a new Rate object. +func NewRate() *Rate { + return &Rate{ + Type: MetricAggregationTypeRate, + } +} + +type TopMetrics struct { + Type MetricAggregationType `json:"type"` + Id string `json:"id"` + Settings *DataqueryTopMetricsSettings `json:"settings,omitempty"` + Hide *bool `json:"hide,omitempty"` +} + +// NewTopMetrics creates a new TopMetrics object. +func NewTopMetrics() *TopMetrics { + return &TopMetrics{ + Type: MetricAggregationTypeTopMetrics, + } +} + type BucketAggregationType string const ( @@ -65,20 +482,6 @@ func NewBucketAggregationWithField() *BucketAggregationWithField { return &BucketAggregationWithField{} } -type DateHistogram struct { - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Type string `json:"type"` - Settings *DataqueryDateHistogramSettings `json:"settings,omitempty"` -} - -// NewDateHistogram creates a new DateHistogram object. -func NewDateHistogram() *DateHistogram { - return &DateHistogram{ - Type: "date_histogram", - } -} - type DateHistogramSettings struct { Interval *string `json:"interval,omitempty"` MinDocCount *string `json:"min_doc_count,omitempty"` @@ -92,20 +495,6 @@ func NewDateHistogramSettings() *DateHistogramSettings { return &DateHistogramSettings{} } -type Histogram struct { - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Type string `json:"type"` - Settings *DataqueryHistogramSettings `json:"settings,omitempty"` -} - -// NewHistogram creates a new Histogram object. -func NewHistogram() *Histogram { - return &Histogram{ - Type: "histogram", - } -} - type HistogramSettings struct { Interval *string `json:"interval,omitempty"` MinDocCount *string `json:"min_doc_count,omitempty"` @@ -116,41 +505,6 @@ func NewHistogramSettings() *HistogramSettings { return &HistogramSettings{} } -type TermsOrder string - -const ( - TermsOrderDesc TermsOrder = "desc" - TermsOrderAsc TermsOrder = "asc" -) - -type Nested struct { - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Type string `json:"type"` - Settings any `json:"settings,omitempty"` -} - -// NewNested creates a new Nested object. -func NewNested() *Nested { - return &Nested{ - Type: "nested", - } -} - -type Terms struct { - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Type string `json:"type"` - Settings *DataqueryTermsSettings `json:"settings,omitempty"` -} - -// NewTerms creates a new Terms object. -func NewTerms() *Terms { - return &Terms{ - Type: "terms", - } -} - type TermsSettings struct { Order *TermsOrder `json:"order,omitempty"` Size *string `json:"size,omitempty"` @@ -164,29 +518,6 @@ func NewTermsSettings() *TermsSettings { return &TermsSettings{} } -type Filters struct { - Id string `json:"id"` - Type string `json:"type"` - Settings *DataqueryFiltersSettings `json:"settings,omitempty"` -} - -// NewFilters creates a new Filters object. -func NewFilters() *Filters { - return &Filters{ - Type: "filters", - } -} - -type Filter struct { - Query string `json:"query"` - Label string `json:"label"` -} - -// NewFilter creates a new Filter object. -func NewFilter() *Filter { - return &Filter{} -} - type FiltersSettings struct { Filters []Filter `json:"filters,omitempty"` } @@ -196,20 +527,6 @@ func NewFiltersSettings() *FiltersSettings { return &FiltersSettings{} } -type GeoHashGrid struct { - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Type string `json:"type"` - Settings *DataqueryGeoHashGridSettings `json:"settings,omitempty"` -} - -// NewGeoHashGrid creates a new GeoHashGrid object. -func NewGeoHashGrid() *GeoHashGrid { - return &GeoHashGrid{ - Type: "geohash_grid", - } -} - type GeoHashGridSettings struct { Precision *string `json:"precision,omitempty"` } @@ -230,12 +547,29 @@ const ( PipelineMetricAggregationTypeBucketScript PipelineMetricAggregationType = "bucket_script" ) -type MetricAggregationType = StringOrPipelineMetricAggregationType +type MetricAggregationType string -// NewMetricAggregationType creates a new MetricAggregationType object. -func NewMetricAggregationType() *MetricAggregationType { - return NewStringOrPipelineMetricAggregationType() -} +const ( + MetricAggregationTypeCount MetricAggregationType = "count" + MetricAggregationTypeAvg MetricAggregationType = "avg" + MetricAggregationTypeSum MetricAggregationType = "sum" + MetricAggregationTypeMin MetricAggregationType = "min" + MetricAggregationTypeMax MetricAggregationType = "max" + MetricAggregationTypeExtendedStats MetricAggregationType = "extended_stats" + MetricAggregationTypePercentiles MetricAggregationType = "percentiles" + MetricAggregationTypeCardinality MetricAggregationType = "cardinality" + MetricAggregationTypeRawDocument MetricAggregationType = "raw_document" + MetricAggregationTypeRawData MetricAggregationType = "raw_data" + MetricAggregationTypeLogs MetricAggregationType = "logs" + MetricAggregationTypeRate MetricAggregationType = "rate" + MetricAggregationTypeTopMetrics MetricAggregationType = "top_metrics" + MetricAggregationTypeMovingAvg MetricAggregationType = "moving_avg" + MetricAggregationTypeMovingFn MetricAggregationType = "moving_fn" + MetricAggregationTypeDerivative MetricAggregationType = "derivative" + MetricAggregationTypeSerialDiff MetricAggregationType = "serial_diff" + MetricAggregationTypeCumulativeSum MetricAggregationType = "cumulative_sum" + MetricAggregationTypeBucketScript MetricAggregationType = "bucket_script" +) type BaseMetricAggregation struct { Type MetricAggregationType `json:"type"` @@ -245,19 +579,7 @@ type BaseMetricAggregation struct { // NewBaseMetricAggregation creates a new BaseMetricAggregation object. func NewBaseMetricAggregation() *BaseMetricAggregation { - return &BaseMetricAggregation{ - Type: *NewMetricAggregationType(), - } -} - -type PipelineVariable struct { - Name string `json:"name"` - PipelineAgg string `json:"pipelineAgg"` -} - -// NewPipelineVariable creates a new PipelineVariable object. -func NewPipelineVariable() *PipelineVariable { - return &PipelineVariable{} + return &BaseMetricAggregation{} } type MetricAggregationWithField struct { @@ -269,9 +591,7 @@ type MetricAggregationWithField struct { // NewMetricAggregationWithField creates a new MetricAggregationWithField object. func NewMetricAggregationWithField() *MetricAggregationWithField { - return &MetricAggregationWithField{ - Type: *NewMetricAggregationType(), - } + return &MetricAggregationWithField{} } type MetricAggregationWithMissingSupport struct { @@ -283,16 +603,7 @@ type MetricAggregationWithMissingSupport struct { // NewMetricAggregationWithMissingSupport creates a new MetricAggregationWithMissingSupport object. func NewMetricAggregationWithMissingSupport() *MetricAggregationWithMissingSupport { - return &MetricAggregationWithMissingSupport{ - Type: *NewMetricAggregationType(), - } -} - -type InlineScript = StringOrDataqueryInlineScript - -// NewInlineScript creates a new InlineScript object. -func NewInlineScript() *InlineScript { - return NewStringOrDataqueryInlineScript() + return &MetricAggregationWithMissingSupport{} } type MetricAggregationWithInlineScript struct { @@ -304,82 +615,7 @@ type MetricAggregationWithInlineScript struct { // NewMetricAggregationWithInlineScript creates a new MetricAggregationWithInlineScript object. func NewMetricAggregationWithInlineScript() *MetricAggregationWithInlineScript { - return &MetricAggregationWithInlineScript{ - Type: *NewMetricAggregationType(), - } -} - -type Count struct { - Type string `json:"type"` - Id string `json:"id"` - Hide *bool `json:"hide,omitempty"` -} - -// NewCount creates a new Count object. -func NewCount() *Count { - return &Count{ - Type: "count", - } -} - -type Average struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataqueryAverageSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewAverage creates a new Average object. -func NewAverage() *Average { - return &Average{ - Type: "avg", - } -} - -type Sum struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataquerySumSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewSum creates a new Sum object. -func NewSum() *Sum { - return &Sum{ - Type: "sum", - } -} - -type Max struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataqueryMaxSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewMax creates a new Max object. -func NewMax() *Max { - return &Max{ - Type: "max", - } -} - -type Min struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataqueryMinSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewMin creates a new Min object. -func NewMin() *Min { - return &Min{ - Type: "min", - } + return &MetricAggregationWithInlineScript{} } type ExtendedStatMetaType string @@ -405,109 +641,6 @@ func NewExtendedStat() *ExtendedStat { return &ExtendedStat{} } -type ExtendedStats struct { - Type string `json:"type"` - Settings *DataqueryExtendedStatsSettings `json:"settings,omitempty"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Meta any `json:"meta,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewExtendedStats creates a new ExtendedStats object. -func NewExtendedStats() *ExtendedStats { - return &ExtendedStats{ - Type: "extended_stats", - } -} - -type Percentiles struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataqueryPercentilesSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewPercentiles creates a new Percentiles object. -func NewPercentiles() *Percentiles { - return &Percentiles{ - Type: "percentiles", - } -} - -type UniqueCount struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataqueryUniqueCountSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewUniqueCount creates a new UniqueCount object. -func NewUniqueCount() *UniqueCount { - return &UniqueCount{ - Type: "cardinality", - } -} - -type RawDocument struct { - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryRawDocumentSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewRawDocument creates a new RawDocument object. -func NewRawDocument() *RawDocument { - return &RawDocument{ - Type: "raw_document", - } -} - -type RawData struct { - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryRawDataSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewRawData creates a new RawData object. -func NewRawData() *RawData { - return &RawData{ - Type: "raw_data", - } -} - -type Logs struct { - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryLogsSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewLogs creates a new Logs object. -func NewLogs() *Logs { - return &Logs{ - Type: "logs", - } -} - -type Rate struct { - Type string `json:"type"` - Field *string `json:"field,omitempty"` - Id string `json:"id"` - Settings *DataqueryRateSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewRate creates a new Rate object. -func NewRate() *Rate { - return &Rate{ - Type: "rate", - } -} - type BasePipelineMetricAggregation struct { PipelineAgg *string `json:"pipelineAgg,omitempty"` Field *string `json:"field,omitempty"` @@ -530,9 +663,7 @@ type PipelineMetricAggregationWithMultipleBucketPaths struct { // NewPipelineMetricAggregationWithMultipleBucketPaths creates a new PipelineMetricAggregationWithMultipleBucketPaths object. func NewPipelineMetricAggregationWithMultipleBucketPaths() *PipelineMetricAggregationWithMultipleBucketPaths { - return &PipelineMetricAggregationWithMultipleBucketPaths{ - Type: *NewMetricAggregationType(), - } + return &PipelineMetricAggregationWithMultipleBucketPaths{} } type MovingAverageModel string @@ -567,33 +698,33 @@ func NewBaseMovingAverageModelSettings() *BaseMovingAverageModelSettings { } type MovingAverageSimpleModelSettings struct { - Model string `json:"model"` - Window string `json:"window"` - Predict string `json:"predict"` + Model MovingAverageModel `json:"model"` + Window string `json:"window"` + Predict string `json:"predict"` } // NewMovingAverageSimpleModelSettings creates a new MovingAverageSimpleModelSettings object. func NewMovingAverageSimpleModelSettings() *MovingAverageSimpleModelSettings { return &MovingAverageSimpleModelSettings{ - Model: "simple", + Model: MovingAverageModelSimple, } } type MovingAverageLinearModelSettings struct { - Model string `json:"model"` - Window string `json:"window"` - Predict string `json:"predict"` + Model MovingAverageModel `json:"model"` + Window string `json:"window"` + Predict string `json:"predict"` } // NewMovingAverageLinearModelSettings creates a new MovingAverageLinearModelSettings object. func NewMovingAverageLinearModelSettings() *MovingAverageLinearModelSettings { return &MovingAverageLinearModelSettings{ - Model: "linear", + Model: MovingAverageModelLinear, } } type MovingAverageEWMAModelSettings struct { - Model string `json:"model"` + Model MovingAverageModel `json:"model"` Settings *DataqueryMovingAverageEWMAModelSettingsSettings `json:"settings,omitempty"` Window string `json:"window"` Minimize bool `json:"minimize"` @@ -603,12 +734,12 @@ type MovingAverageEWMAModelSettings struct { // NewMovingAverageEWMAModelSettings creates a new MovingAverageEWMAModelSettings object. func NewMovingAverageEWMAModelSettings() *MovingAverageEWMAModelSettings { return &MovingAverageEWMAModelSettings{ - Model: "ewma", + Model: MovingAverageModelEwma, } } type MovingAverageHoltModelSettings struct { - Model string `json:"model"` + Model MovingAverageModel `json:"model"` Settings DataqueryMovingAverageHoltModelSettingsSettings `json:"settings"` Window string `json:"window"` Minimize bool `json:"minimize"` @@ -618,13 +749,13 @@ type MovingAverageHoltModelSettings struct { // NewMovingAverageHoltModelSettings creates a new MovingAverageHoltModelSettings object. func NewMovingAverageHoltModelSettings() *MovingAverageHoltModelSettings { return &MovingAverageHoltModelSettings{ - Model: "holt", + Model: MovingAverageModelHolt, Settings: *NewDataqueryMovingAverageHoltModelSettingsSettings(), } } type MovingAverageHoltWintersModelSettings struct { - Model string `json:"model"` + Model MovingAverageModel `json:"model"` Settings DataqueryMovingAverageHoltWintersModelSettingsSettings `json:"settings"` Window string `json:"window"` Minimize bool `json:"minimize"` @@ -634,135 +765,11 @@ type MovingAverageHoltWintersModelSettings struct { // NewMovingAverageHoltWintersModelSettings creates a new MovingAverageHoltWintersModelSettings object. func NewMovingAverageHoltWintersModelSettings() *MovingAverageHoltWintersModelSettings { return &MovingAverageHoltWintersModelSettings{ - Model: "holt_winters", + Model: MovingAverageModelHoltWinters, Settings: *NewDataqueryMovingAverageHoltWintersModelSettingsSettings(), } } -// #MovingAverage's settings are overridden in types.ts -type MovingAverage struct { - PipelineAgg *string `json:"pipelineAgg,omitempty"` - Field *string `json:"field,omitempty"` - Type string `json:"type"` - Id string `json:"id"` - Settings map[string]any `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewMovingAverage creates a new MovingAverage object. -func NewMovingAverage() *MovingAverage { - return &MovingAverage{ - Type: "moving_avg", - } -} - -type MovingFunction struct { - PipelineAgg *string `json:"pipelineAgg,omitempty"` - Field *string `json:"field,omitempty"` - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryMovingFunctionSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewMovingFunction creates a new MovingFunction object. -func NewMovingFunction() *MovingFunction { - return &MovingFunction{ - Type: "moving_fn", - } -} - -type Derivative struct { - PipelineAgg *string `json:"pipelineAgg,omitempty"` - Field *string `json:"field,omitempty"` - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryDerivativeSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewDerivative creates a new Derivative object. -func NewDerivative() *Derivative { - return &Derivative{ - Type: "derivative", - } -} - -type SerialDiff struct { - PipelineAgg *string `json:"pipelineAgg,omitempty"` - Field *string `json:"field,omitempty"` - Type string `json:"type"` - Id string `json:"id"` - Settings *DataquerySerialDiffSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewSerialDiff creates a new SerialDiff object. -func NewSerialDiff() *SerialDiff { - return &SerialDiff{ - Type: "serial_diff", - } -} - -type CumulativeSum struct { - PipelineAgg *string `json:"pipelineAgg,omitempty"` - Field *string `json:"field,omitempty"` - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryCumulativeSumSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewCumulativeSum creates a new CumulativeSum object. -func NewCumulativeSum() *CumulativeSum { - return &CumulativeSum{ - Type: "cumulative_sum", - } -} - -type BucketScript struct { - Type string `json:"type"` - PipelineVariables []PipelineVariable `json:"pipelineVariables,omitempty"` - Id string `json:"id"` - Settings *DataqueryBucketScriptSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewBucketScript creates a new BucketScript object. -func NewBucketScript() *BucketScript { - return &BucketScript{ - Type: "bucket_script", - } -} - -type TopMetrics struct { - Type string `json:"type"` - Id string `json:"id"` - Settings *DataqueryTopMetricsSettings `json:"settings,omitempty"` - Hide *bool `json:"hide,omitempty"` -} - -// NewTopMetrics creates a new TopMetrics object. -func NewTopMetrics() *TopMetrics { - return &TopMetrics{ - Type: "top_metrics", - } -} - -type PipelineMetricAggregation = MovingAverageOrDerivativeOrCumulativeSumOrBucketScript - -// NewPipelineMetricAggregation creates a new PipelineMetricAggregation object. -func NewPipelineMetricAggregation() *PipelineMetricAggregation { - return NewMovingAverageOrDerivativeOrCumulativeSumOrBucketScript() -} - -type MetricAggregationWithSettings = BucketScriptOrCumulativeSumOrDerivativeOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingAverageOrMovingFunctionOrLogsOrRateOrTopMetrics - -// NewMetricAggregationWithSettings creates a new MetricAggregationWithSettings object. -func NewMetricAggregationWithSettings() *MetricAggregationWithSettings { - return NewBucketScriptOrCumulativeSumOrDerivativeOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingAverageOrMovingFunctionOrLogsOrRateOrTopMetrics() -} - type ElasticsearchDataQuery struct { // Alias pattern Alias *string `json:"alias,omitempty"` @@ -849,13 +856,31 @@ func NewDataqueryGeoHashGridSettings() *DataqueryGeoHashGridSettings { return &DataqueryGeoHashGridSettings{} } -type DataqueryMetricAggregationWithMissingSupportSettings struct { - Missing *string `json:"missing,omitempty"` +type DataqueryDerivativeSettings struct { + Unit *string `json:"unit,omitempty"` } -// NewDataqueryMetricAggregationWithMissingSupportSettings creates a new DataqueryMetricAggregationWithMissingSupportSettings object. -func NewDataqueryMetricAggregationWithMissingSupportSettings() *DataqueryMetricAggregationWithMissingSupportSettings { - return &DataqueryMetricAggregationWithMissingSupportSettings{} +// NewDataqueryDerivativeSettings creates a new DataqueryDerivativeSettings object. +func NewDataqueryDerivativeSettings() *DataqueryDerivativeSettings { + return &DataqueryDerivativeSettings{} +} + +type DataqueryCumulativeSumSettings struct { + Format *string `json:"format,omitempty"` +} + +// NewDataqueryCumulativeSumSettings creates a new DataqueryCumulativeSumSettings object. +func NewDataqueryCumulativeSumSettings() *DataqueryCumulativeSumSettings { + return &DataqueryCumulativeSumSettings{} +} + +type DataqueryBucketScriptSettings struct { + Script *InlineScript `json:"script,omitempty"` +} + +// NewDataqueryBucketScriptSettings creates a new DataqueryBucketScriptSettings object. +func NewDataqueryBucketScriptSettings() *DataqueryBucketScriptSettings { + return &DataqueryBucketScriptSettings{} } type DataqueryInlineScript struct { @@ -867,64 +892,41 @@ func NewDataqueryInlineScript() *DataqueryInlineScript { return &DataqueryInlineScript{} } -type DataqueryMetricAggregationWithInlineScriptSettings struct { - Script *InlineScript `json:"script,omitempty"` +type DataquerySerialDiffSettings struct { + Lag *string `json:"lag,omitempty"` } -// NewDataqueryMetricAggregationWithInlineScriptSettings creates a new DataqueryMetricAggregationWithInlineScriptSettings object. -func NewDataqueryMetricAggregationWithInlineScriptSettings() *DataqueryMetricAggregationWithInlineScriptSettings { - return &DataqueryMetricAggregationWithInlineScriptSettings{} +// NewDataquerySerialDiffSettings creates a new DataquerySerialDiffSettings object. +func NewDataquerySerialDiffSettings() *DataquerySerialDiffSettings { + return &DataquerySerialDiffSettings{} } -type DataqueryAverageSettings struct { - Script *InlineScript `json:"script,omitempty"` - Missing *string `json:"missing,omitempty"` +type DataqueryRawDataSettings struct { + Size *string `json:"size,omitempty"` } -// NewDataqueryAverageSettings creates a new DataqueryAverageSettings object. -func NewDataqueryAverageSettings() *DataqueryAverageSettings { - return &DataqueryAverageSettings{} +// NewDataqueryRawDataSettings creates a new DataqueryRawDataSettings object. +func NewDataqueryRawDataSettings() *DataqueryRawDataSettings { + return &DataqueryRawDataSettings{} } -type DataquerySumSettings struct { - Script *InlineScript `json:"script,omitempty"` - Missing *string `json:"missing,omitempty"` +type DataqueryRawDocumentSettings struct { + Size *string `json:"size,omitempty"` } -// NewDataquerySumSettings creates a new DataquerySumSettings object. -func NewDataquerySumSettings() *DataquerySumSettings { - return &DataquerySumSettings{} +// NewDataqueryRawDocumentSettings creates a new DataqueryRawDocumentSettings object. +func NewDataqueryRawDocumentSettings() *DataqueryRawDocumentSettings { + return &DataqueryRawDocumentSettings{} } -type DataqueryMaxSettings struct { - Script *InlineScript `json:"script,omitempty"` - Missing *string `json:"missing,omitempty"` +type DataqueryUniqueCountSettings struct { + PrecisionThreshold *string `json:"precision_threshold,omitempty"` + Missing *string `json:"missing,omitempty"` } -// NewDataqueryMaxSettings creates a new DataqueryMaxSettings object. -func NewDataqueryMaxSettings() *DataqueryMaxSettings { - return &DataqueryMaxSettings{} -} - -type DataqueryMinSettings struct { - Script *InlineScript `json:"script,omitempty"` - Missing *string `json:"missing,omitempty"` -} - -// NewDataqueryMinSettings creates a new DataqueryMinSettings object. -func NewDataqueryMinSettings() *DataqueryMinSettings { - return &DataqueryMinSettings{} -} - -type DataqueryExtendedStatsSettings struct { - Script *InlineScript `json:"script,omitempty"` - Missing *string `json:"missing,omitempty"` - Sigma *string `json:"sigma,omitempty"` -} - -// NewDataqueryExtendedStatsSettings creates a new DataqueryExtendedStatsSettings object. -func NewDataqueryExtendedStatsSettings() *DataqueryExtendedStatsSettings { - return &DataqueryExtendedStatsSettings{} +// NewDataqueryUniqueCountSettings creates a new DataqueryUniqueCountSettings object. +func NewDataqueryUniqueCountSettings() *DataqueryUniqueCountSettings { + return &DataqueryUniqueCountSettings{} } type DataqueryPercentilesSettings struct { @@ -938,32 +940,66 @@ func NewDataqueryPercentilesSettings() *DataqueryPercentilesSettings { return &DataqueryPercentilesSettings{} } -type DataqueryUniqueCountSettings struct { - PrecisionThreshold *string `json:"precision_threshold,omitempty"` - Missing *string `json:"missing,omitempty"` +type DataqueryExtendedStatsSettings struct { + Script *InlineScript `json:"script,omitempty"` + Missing *string `json:"missing,omitempty"` + Sigma *string `json:"sigma,omitempty"` } -// NewDataqueryUniqueCountSettings creates a new DataqueryUniqueCountSettings object. -func NewDataqueryUniqueCountSettings() *DataqueryUniqueCountSettings { - return &DataqueryUniqueCountSettings{} +// NewDataqueryExtendedStatsSettings creates a new DataqueryExtendedStatsSettings object. +func NewDataqueryExtendedStatsSettings() *DataqueryExtendedStatsSettings { + return &DataqueryExtendedStatsSettings{} } -type DataqueryRawDocumentSettings struct { - Size *string `json:"size,omitempty"` +type DataqueryMinSettings struct { + Script *InlineScript `json:"script,omitempty"` + Missing *string `json:"missing,omitempty"` } -// NewDataqueryRawDocumentSettings creates a new DataqueryRawDocumentSettings object. -func NewDataqueryRawDocumentSettings() *DataqueryRawDocumentSettings { - return &DataqueryRawDocumentSettings{} +// NewDataqueryMinSettings creates a new DataqueryMinSettings object. +func NewDataqueryMinSettings() *DataqueryMinSettings { + return &DataqueryMinSettings{} } -type DataqueryRawDataSettings struct { - Size *string `json:"size,omitempty"` +type DataqueryMaxSettings struct { + Script *InlineScript `json:"script,omitempty"` + Missing *string `json:"missing,omitempty"` } -// NewDataqueryRawDataSettings creates a new DataqueryRawDataSettings object. -func NewDataqueryRawDataSettings() *DataqueryRawDataSettings { - return &DataqueryRawDataSettings{} +// NewDataqueryMaxSettings creates a new DataqueryMaxSettings object. +func NewDataqueryMaxSettings() *DataqueryMaxSettings { + return &DataqueryMaxSettings{} +} + +type DataquerySumSettings struct { + Script *InlineScript `json:"script,omitempty"` + Missing *string `json:"missing,omitempty"` +} + +// NewDataquerySumSettings creates a new DataquerySumSettings object. +func NewDataquerySumSettings() *DataquerySumSettings { + return &DataquerySumSettings{} +} + +type DataqueryAverageSettings struct { + Script *InlineScript `json:"script,omitempty"` + Missing *string `json:"missing,omitempty"` +} + +// NewDataqueryAverageSettings creates a new DataqueryAverageSettings object. +func NewDataqueryAverageSettings() *DataqueryAverageSettings { + return &DataqueryAverageSettings{} +} + +type DataqueryMovingFunctionSettings struct { + Window *string `json:"window,omitempty"` + Script *InlineScript `json:"script,omitempty"` + Shift *string `json:"shift,omitempty"` +} + +// NewDataqueryMovingFunctionSettings creates a new DataqueryMovingFunctionSettings object. +func NewDataqueryMovingFunctionSettings() *DataqueryMovingFunctionSettings { + return &DataqueryMovingFunctionSettings{} } type DataqueryLogsSettings struct { @@ -985,6 +1021,35 @@ func NewDataqueryRateSettings() *DataqueryRateSettings { return &DataqueryRateSettings{} } +type DataqueryTopMetricsSettings struct { + Order *string `json:"order,omitempty"` + OrderBy *string `json:"orderBy,omitempty"` + Metrics []string `json:"metrics,omitempty"` +} + +// NewDataqueryTopMetricsSettings creates a new DataqueryTopMetricsSettings object. +func NewDataqueryTopMetricsSettings() *DataqueryTopMetricsSettings { + return &DataqueryTopMetricsSettings{} +} + +type DataqueryMetricAggregationWithMissingSupportSettings struct { + Missing *string `json:"missing,omitempty"` +} + +// NewDataqueryMetricAggregationWithMissingSupportSettings creates a new DataqueryMetricAggregationWithMissingSupportSettings object. +func NewDataqueryMetricAggregationWithMissingSupportSettings() *DataqueryMetricAggregationWithMissingSupportSettings { + return &DataqueryMetricAggregationWithMissingSupportSettings{} +} + +type DataqueryMetricAggregationWithInlineScriptSettings struct { + Script *InlineScript `json:"script,omitempty"` +} + +// NewDataqueryMetricAggregationWithInlineScriptSettings creates a new DataqueryMetricAggregationWithInlineScriptSettings object. +func NewDataqueryMetricAggregationWithInlineScriptSettings() *DataqueryMetricAggregationWithInlineScriptSettings { + return &DataqueryMetricAggregationWithInlineScriptSettings{} +} + type DataqueryMovingAverageEWMAModelSettingsSettings struct { Alpha *string `json:"alpha,omitempty"` } @@ -1017,64 +1082,6 @@ func NewDataqueryMovingAverageHoltWintersModelSettingsSettings() *DataqueryMovin return &DataqueryMovingAverageHoltWintersModelSettingsSettings{} } -type DataqueryMovingFunctionSettings struct { - Window *string `json:"window,omitempty"` - Script *InlineScript `json:"script,omitempty"` - Shift *string `json:"shift,omitempty"` -} - -// NewDataqueryMovingFunctionSettings creates a new DataqueryMovingFunctionSettings object. -func NewDataqueryMovingFunctionSettings() *DataqueryMovingFunctionSettings { - return &DataqueryMovingFunctionSettings{} -} - -type DataqueryDerivativeSettings struct { - Unit *string `json:"unit,omitempty"` -} - -// NewDataqueryDerivativeSettings creates a new DataqueryDerivativeSettings object. -func NewDataqueryDerivativeSettings() *DataqueryDerivativeSettings { - return &DataqueryDerivativeSettings{} -} - -type DataquerySerialDiffSettings struct { - Lag *string `json:"lag,omitempty"` -} - -// NewDataquerySerialDiffSettings creates a new DataquerySerialDiffSettings object. -func NewDataquerySerialDiffSettings() *DataquerySerialDiffSettings { - return &DataquerySerialDiffSettings{} -} - -type DataqueryCumulativeSumSettings struct { - Format *string `json:"format,omitempty"` -} - -// NewDataqueryCumulativeSumSettings creates a new DataqueryCumulativeSumSettings object. -func NewDataqueryCumulativeSumSettings() *DataqueryCumulativeSumSettings { - return &DataqueryCumulativeSumSettings{} -} - -type DataqueryBucketScriptSettings struct { - Script *InlineScript `json:"script,omitempty"` -} - -// NewDataqueryBucketScriptSettings creates a new DataqueryBucketScriptSettings object. -func NewDataqueryBucketScriptSettings() *DataqueryBucketScriptSettings { - return &DataqueryBucketScriptSettings{} -} - -type DataqueryTopMetricsSettings struct { - Order *string `json:"order,omitempty"` - OrderBy *string `json:"orderBy,omitempty"` - Metrics []string `json:"metrics,omitempty"` -} - -// NewDataqueryTopMetricsSettings creates a new DataqueryTopMetricsSettings object. -func NewDataqueryTopMetricsSettings() *DataqueryTopMetricsSettings { - return &DataqueryTopMetricsSettings{} -} - type DateHistogramOrHistogramOrTermsOrFiltersOrGeoHashGridOrNested struct { DateHistogram *DateHistogram `json:"DateHistogram,omitempty"` Histogram *Histogram `json:"Histogram,omitempty"` @@ -1449,28 +1456,6 @@ func (resource *CountOrMovingAverageOrDerivativeOrCumulativeSumOrBucketScriptOrS return fmt.Errorf("could not unmarshal resource with `type = %v`", discriminator) } -type StringOrPipelineMetricAggregationType struct { - String *string `json:"String,omitempty"` - PipelineMetricAggregationType *PipelineMetricAggregationType `json:"PipelineMetricAggregationType,omitempty"` -} - -// NewStringOrPipelineMetricAggregationType creates a new StringOrPipelineMetricAggregationType object. -func NewStringOrPipelineMetricAggregationType() *StringOrPipelineMetricAggregationType { - return &StringOrPipelineMetricAggregationType{ - String: (func(input string) *string { return &input })("count"), - } -} - -type StringOrDataqueryInlineScript struct { - String *string `json:"String,omitempty"` - DataqueryInlineScript *DataqueryInlineScript `json:"DataqueryInlineScript,omitempty"` -} - -// NewStringOrDataqueryInlineScript creates a new StringOrDataqueryInlineScript object. -func NewStringOrDataqueryInlineScript() *StringOrDataqueryInlineScript { - return &StringOrDataqueryInlineScript{} -} - type MovingAverageOrDerivativeOrCumulativeSumOrBucketScript struct { MovingAverage *MovingAverage `json:"MovingAverage,omitempty"` Derivative *Derivative `json:"Derivative,omitempty"` @@ -1556,6 +1541,16 @@ func (resource *MovingAverageOrDerivativeOrCumulativeSumOrBucketScript) Unmarsha return fmt.Errorf("could not unmarshal resource with `type = %v`", discriminator) } +type StringOrDataqueryInlineScript struct { + String *string `json:"String,omitempty"` + DataqueryInlineScript *DataqueryInlineScript `json:"DataqueryInlineScript,omitempty"` +} + +// NewStringOrDataqueryInlineScript creates a new StringOrDataqueryInlineScript object. +func NewStringOrDataqueryInlineScript() *StringOrDataqueryInlineScript { + return &StringOrDataqueryInlineScript{} +} + type BucketScriptOrCumulativeSumOrDerivativeOrSerialDiffOrRawDataOrRawDocumentOrUniqueCountOrPercentilesOrExtendedStatsOrMinOrMaxOrSumOrAverageOrMovingAverageOrMovingFunctionOrLogsOrRateOrTopMetrics struct { BucketScript *BucketScript `json:"BucketScript,omitempty"` CumulativeSum *CumulativeSum `json:"CumulativeSum,omitempty"` diff --git a/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go index 079185206f1..874c4161741 100644 --- a/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go @@ -70,57 +70,6 @@ func NewTempoQuery() *TempoQuery { return &TempoQuery{} } -type TempoQueryType string - -const ( - TempoQueryTypeTraceql TempoQueryType = "traceql" - TempoQueryTypeTraceqlSearch TempoQueryType = "traceqlSearch" - TempoQueryTypeServiceMap TempoQueryType = "serviceMap" - TempoQueryTypeUpload TempoQueryType = "upload" - TempoQueryTypeNativeSearch TempoQueryType = "nativeSearch" - TempoQueryTypeTraceId TempoQueryType = "traceId" - TempoQueryTypeClear TempoQueryType = "clear" -) - -type MetricsQueryType string - -const ( - MetricsQueryTypeRange MetricsQueryType = "range" - MetricsQueryTypeInstant MetricsQueryType = "instant" -) - -// The state of the TraceQL streaming search query -type SearchStreamingState string - -const ( - SearchStreamingStatePending SearchStreamingState = "pending" - SearchStreamingStateStreaming SearchStreamingState = "streaming" - SearchStreamingStateDone SearchStreamingState = "done" - SearchStreamingStateError SearchStreamingState = "error" -) - -// The type of the table that is used to display the search results -type SearchTableType string - -const ( - SearchTableTypeTraces SearchTableType = "traces" - SearchTableTypeSpans SearchTableType = "spans" - SearchTableTypeRaw SearchTableType = "raw" -) - -// static fields are pre-set in the UI, dynamic fields are added by the user -type TraceqlSearchScope string - -const ( - TraceqlSearchScopeIntrinsic TraceqlSearchScope = "intrinsic" - TraceqlSearchScopeUnscoped TraceqlSearchScope = "unscoped" - TraceqlSearchScopeEvent TraceqlSearchScope = "event" - TraceqlSearchScopeInstrumentation TraceqlSearchScope = "instrumentation" - TraceqlSearchScopeLink TraceqlSearchScope = "link" - TraceqlSearchScopeResource TraceqlSearchScope = "resource" - TraceqlSearchScopeSpan TraceqlSearchScope = "span" -) - type TraceqlFilter struct { // Uniquely identify the filter, will not be used in the query generation Id string `json:"id"` @@ -141,6 +90,57 @@ func NewTraceqlFilter() *TraceqlFilter { return &TraceqlFilter{} } +// static fields are pre-set in the UI, dynamic fields are added by the user +type TraceqlSearchScope string + +const ( + TraceqlSearchScopeIntrinsic TraceqlSearchScope = "intrinsic" + TraceqlSearchScopeUnscoped TraceqlSearchScope = "unscoped" + TraceqlSearchScopeEvent TraceqlSearchScope = "event" + TraceqlSearchScopeInstrumentation TraceqlSearchScope = "instrumentation" + TraceqlSearchScopeLink TraceqlSearchScope = "link" + TraceqlSearchScopeResource TraceqlSearchScope = "resource" + TraceqlSearchScopeSpan TraceqlSearchScope = "span" +) + +// The type of the table that is used to display the search results +type SearchTableType string + +const ( + SearchTableTypeTraces SearchTableType = "traces" + SearchTableTypeSpans SearchTableType = "spans" + SearchTableTypeRaw SearchTableType = "raw" +) + +type MetricsQueryType string + +const ( + MetricsQueryTypeRange MetricsQueryType = "range" + MetricsQueryTypeInstant MetricsQueryType = "instant" +) + +type TempoQueryType string + +const ( + TempoQueryTypeTraceql TempoQueryType = "traceql" + TempoQueryTypeTraceqlSearch TempoQueryType = "traceqlSearch" + TempoQueryTypeServiceMap TempoQueryType = "serviceMap" + TempoQueryTypeUpload TempoQueryType = "upload" + TempoQueryTypeNativeSearch TempoQueryType = "nativeSearch" + TempoQueryTypeTraceId TempoQueryType = "traceId" + TempoQueryTypeClear TempoQueryType = "clear" +) + +// The state of the TraceQL streaming search query +type SearchStreamingState string + +const ( + SearchStreamingStatePending SearchStreamingState = "pending" + SearchStreamingStateStreaming SearchStreamingState = "streaming" + SearchStreamingStateDone SearchStreamingState = "done" + SearchStreamingStateError SearchStreamingState = "error" +) + type StringOrArrayOfString struct { String *string `json:"String,omitempty"` ArrayOfString []string `json:"ArrayOfString,omitempty"` From 3bf6e3dc37036d0633284a060af6c496b90159ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 12 Mar 2025 10:26:59 +0100 Subject: [PATCH 219/312] Dashboards: Fix issues with panel selection and dragging (#102000) --- .../components/PanelChrome/PanelChrome.tsx | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx index b629dbeee83..310ae7fe7b4 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.tsx @@ -1,5 +1,5 @@ import { css, cx } from '@emotion/css'; -import { CSSProperties, PointerEvent, ReactElement, ReactNode, useId, useRef, useState } from 'react'; +import { CSSProperties, ReactElement, ReactNode, useId, useRef, useState } from 'react'; import * as React from 'react'; import { useMeasure, useToggle } from 'react-use'; @@ -143,7 +143,6 @@ export function PanelChrome({ onFocus, onMouseMove, onMouseEnter, - onDragStart, showMenuAlways = false, }: PanelChromeProps) { const theme = useTheme2(); @@ -151,7 +150,7 @@ export function PanelChrome({ const panelContentId = useId(); const panelTitleId = useId().replace(/:/g, '_'); const { isSelected, onSelect, isSelectable } = useElementSelection(selectionId); - const pointerDownEvt = useRef(null); + const pointerDownPos = useRef<{ screenX: number; screenY: number }>({ screenX: 0, screenY: 0 }); const hasHeader = !hoverHeader; @@ -196,6 +195,33 @@ export function PanelChrome({ const testid = typeof title === 'string' ? selectors.components.Panels.Panel.title(title) : 'Panel'; + // Handle drag & selection events + // Mainly the tricky bit of differentiating between dragging and selecting + + const onPointerUp = (evt: React.PointerEvent) => { + evt.stopPropagation(); + + const distance = Math.sqrt( + Math.pow(pointerDownPos.current.screenX - evt.screenX, 2) + + Math.pow(pointerDownPos.current.screenY - evt.screenY, 2) + ); + + // If we are dragging some distance or clicking on elements that should cancel dragging (panel menu, etc) + if ( + distance > 10 || + (dragClassCancel && evt.target instanceof HTMLElement && evt.target.closest(`.${dragClassCancel}`)) + ) { + return; + } + + onSelect?.(evt); + }; + + const onPointerDown = (evt: React.PointerEvent) => { + evt.stopPropagation(); + pointerDownPos.current = { screenX: evt.screenX, screenY: evt.screenY }; + }; + const headerContent = ( <> {/* Non collapsible title */} @@ -321,30 +347,10 @@ export function PanelChrome({ className={cx(styles.headerContainer, dragClass)} style={headerStyles} data-testid="header-container" - onPointerDown={(evt) => { - evt.stopPropagation(); - pointerDownEvt.current = evt; - }} - onPointerMove={() => { - if (pointerDownEvt.current) { - onDragStart?.(pointerDownEvt.current); - pointerDownEvt.current = null; - } - }} + onPointerDown={onPointerDown} onMouseEnter={isSelectable ? onHeaderEnter : undefined} onMouseLeave={isSelectable ? onHeaderLeave : undefined} - onPointerUp={(evt) => { - evt.stopPropagation(); - if ( - pointerDownEvt.current && - dragClassCancel && - evt.target instanceof HTMLElement && - !evt.target.closest(`.${dragClassCancel}`) - ) { - onSelect?.(pointerDownEvt.current); - pointerDownEvt.current = null; - } - }} + onPointerUp={onPointerUp} > {statusMessage && (
From 2d0b1c6154a640e65860700b87ab2b6f4d902b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 12 Mar 2025 10:29:07 +0100 Subject: [PATCH 220/312] Dashboards: Move settings button into edit pane (#101942) --- .../edit-pane/DashboardEditableElement.tsx | 25 ++++++++++-- .../edit-pane/EditPaneHeader.tsx | 1 + .../scene/NavToolbarActions.tsx | 40 ++++++++++--------- .../scene/types/EditableDashboardElement.ts | 7 ++++ public/locales/en-US/grafana.json | 3 ++ 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx index 7ccf14b4ff6..7d2c598c7ac 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx @@ -1,7 +1,7 @@ -import { useMemo } from 'react'; +import { ReactNode, useMemo } from 'react'; -import { Input, TextArea } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; +import { Button, Icon, Input, Stack, TextArea } from '@grafana/ui'; +import { t, Trans } from 'app/core/internationalization'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; @@ -60,6 +60,23 @@ export class DashboardEditableElement implements EditableDashboardElement { return [dashboardOptions]; } + + public renderActions(): ReactNode { + return ( + + ); + } } export function DashboardTitleInput({ dashboard }: { dashboard: DashboardScene }) { @@ -71,5 +88,5 @@ export function DashboardTitleInput({ dashboard }: { dashboard: DashboardScene } export function DashboardDescriptionInput({ dashboard }: { dashboard: DashboardScene }) { const { description } = dashboard.useState(); - return